forked from mirror/openmw-tes3mp
Merge remote branch 'upstream/master'
This commit is contained in:
commit
b945ce61a5
145 changed files with 4100 additions and 2481 deletions
133
CMakeLists.txt
133
CMakeLists.txt
|
@ -1,5 +1,9 @@
|
|||
project(OpenMW)
|
||||
|
||||
IF (APPLE)
|
||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
|
||||
ENDIF (APPLE)
|
||||
|
||||
# Sound source selection
|
||||
option(USE_AUDIERE "use Audiere for sound" OFF)
|
||||
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
||||
|
@ -10,15 +14,15 @@ find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
|||
# Location of morrowind data files
|
||||
if(DPKG_PROGRAM)
|
||||
set(MORROWIND_DATA_FILES "/usr/share/games/openmw/data/" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
|
||||
else()
|
||||
if (APPLE)
|
||||
# set path inside bundle
|
||||
set(MORROWIND_DATA_FILES "Contents/Resources/data" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources files")
|
||||
else()
|
||||
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of Morrowind data files")
|
||||
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
|
||||
endif(APPLE)
|
||||
endif(DPKG_PROGRAM)
|
||||
|
||||
|
@ -123,10 +127,48 @@ source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
|
|||
|
||||
file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp)
|
||||
set(ESM
|
||||
${COMP_DIR}/esm/load_impl.cpp
|
||||
${COMP_DIR}/esm/skill.cpp
|
||||
${COMP_DIR}/esm/attr.cpp
|
||||
${COMP_DIR}/esm/class.cpp
|
||||
${COMP_DIR}/esm/esm_reader.cpp
|
||||
${COMP_DIR}/esm/loadland.cpp
|
||||
${COMP_DIR}/esm/loadacti.cpp
|
||||
${COMP_DIR}/esm/loadalch.cpp
|
||||
${COMP_DIR}/esm/loadappa.cpp
|
||||
${COMP_DIR}/esm/loadarmo.cpp
|
||||
${COMP_DIR}/esm/loadbody.cpp
|
||||
${COMP_DIR}/esm/loadbook.cpp
|
||||
${COMP_DIR}/esm/loadbsgn.cpp
|
||||
${COMP_DIR}/esm/loadcell.cpp
|
||||
${COMP_DIR}/esm/loadclas.cpp
|
||||
${COMP_DIR}/esm/loadclot.cpp
|
||||
${COMP_DIR}/esm/loadcont.cpp
|
||||
${COMP_DIR}/esm/loadcrea.cpp
|
||||
${COMP_DIR}/esm/loaddial.cpp
|
||||
${COMP_DIR}/esm/loaddoor.cpp
|
||||
${COMP_DIR}/esm/loadench.cpp
|
||||
${COMP_DIR}/esm/loadfact.cpp
|
||||
${COMP_DIR}/esm/loadglob.cpp
|
||||
${COMP_DIR}/esm/loadgmst.cpp
|
||||
${COMP_DIR}/esm/loadinfo.cpp
|
||||
${COMP_DIR}/esm/loadingr.cpp
|
||||
${COMP_DIR}/esm/loadlevlist.cpp
|
||||
${COMP_DIR}/esm/loadligh.cpp
|
||||
${COMP_DIR}/esm/loadlocks.cpp
|
||||
${COMP_DIR}/esm/loadltex.cpp
|
||||
${COMP_DIR}/esm/loadmgef.cpp
|
||||
${COMP_DIR}/esm/loadmisc.cpp
|
||||
${COMP_DIR}/esm/loadnpc.cpp
|
||||
${COMP_DIR}/esm/loadpgrd.cpp
|
||||
${COMP_DIR}/esm/loadrace.cpp
|
||||
${COMP_DIR}/esm/loadregn.cpp
|
||||
${COMP_DIR}/esm/loadscpt.cpp
|
||||
${COMP_DIR}/esm/loadskil.cpp
|
||||
${COMP_DIR}/esm/loadsndg.cpp
|
||||
${COMP_DIR}/esm/loadsoun.cpp
|
||||
${COMP_DIR}/esm/loadspel.cpp
|
||||
${COMP_DIR}/esm/loadsscr.cpp
|
||||
${COMP_DIR}/esm/loadstat.cpp
|
||||
${COMP_DIR}/esm/loadweap.cpp
|
||||
|
||||
)
|
||||
source_group(components\\esm FILES ${ESM_HEADER} ${ESM})
|
||||
|
||||
|
@ -139,6 +181,18 @@ set(MISC_HEADER
|
|||
${COMP_DIR}/misc/stringops.hpp)
|
||||
source_group(components\\misc FILES ${MISC} ${MISC_HEADER})
|
||||
|
||||
set(FILES
|
||||
${COMP_DIR}/files/path.cpp
|
||||
${COMP_DIR}/files/multidircollection.cpp
|
||||
${COMP_DIR}/files/collections.cpp
|
||||
)
|
||||
set(FILES_HEADER
|
||||
${COMP_DIR}/files/path.hpp
|
||||
${COMP_DIR}/files/multidircollection.hpp
|
||||
${COMP_DIR}/files/collections.hpp
|
||||
)
|
||||
source_group(components\\files FILES ${FILES} ${FILES_HEADER})
|
||||
|
||||
file(GLOB COMPILER ${COMP_DIR}/compiler/*.cpp)
|
||||
file(GLOB COMPILER_HEADER ${COMP_DIR}/compiler/*.hpp)
|
||||
source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER})
|
||||
|
@ -148,10 +202,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp)
|
|||
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
|
||||
|
||||
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8}
|
||||
${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET})
|
||||
${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET} ${FILES})
|
||||
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
|
||||
${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER}
|
||||
${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER})
|
||||
${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER} ${FILES_HEADER})
|
||||
|
||||
# source directory: libs
|
||||
|
||||
|
@ -286,25 +340,55 @@ add_definitions(-DCAELUM_STATIC)
|
|||
|
||||
# Specify build paths
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
|
||||
if (APPLE)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
|
||||
else (APPLE)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
|
||||
endif (APPLE)
|
||||
|
||||
# Other files
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
|
||||
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
|
||||
|
||||
if (WIN32)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.win32
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg" COPYONLY)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.win32
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg" COPYONLY)
|
||||
endif (WIN32)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
|
||||
"${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
|
||||
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns
|
||||
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
|
||||
|
||||
|
||||
# prepare plugins
|
||||
configure_file(${OGRE_PLUGIN_DIR}/RenderSystem_GL.dylib
|
||||
"${APP_BUNDLE_DIR}/Contents/Plugins/RenderSystem_GL.dylib" COPYONLY)
|
||||
|
||||
configure_file(${OGRE_PLUGIN_DIR}/Plugin_OctreeSceneManager.dylib
|
||||
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_OctreeSceneManager.dylib" COPYONLY)
|
||||
|
||||
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
|
||||
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY)
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||
"${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg")
|
||||
|
||||
endif (APPLE)
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
||||
|
||||
# Compiler settings
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
|
@ -315,10 +399,15 @@ endif (CMAKE_COMPILER_IS_GNUCC)
|
|||
# Apple bundling
|
||||
if (APPLE)
|
||||
set(MISC_FILES
|
||||
${OpenMW_BINARY_DIR}/openmw.cfg
|
||||
${OpenMW_BINARY_DIR}/plugins.cfg)
|
||||
${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg
|
||||
${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg)
|
||||
|
||||
set(OGRE_PLUGINS
|
||||
${APP_BUNDLE_DIR}/Contents/Plugins/*)
|
||||
|
||||
install(FILES ${MISC_FILES} DESTINATION ../MacOS)
|
||||
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ../Resources)
|
||||
install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Plugins" DESTINATION ..)
|
||||
install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Resources/resources" DESTINATION ../Resources)
|
||||
set(CPACK_GENERATOR "Bundle")
|
||||
set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/files/mac/Info.plist")
|
||||
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw.icns")
|
||||
|
@ -352,7 +441,7 @@ if(DPKG_PROGRAM)
|
|||
endif()
|
||||
|
||||
#Install global configuration files
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
|
||||
#Install resources
|
||||
|
|
|
@ -49,8 +49,8 @@ Getting OpenMW Working
|
|||
--link-shared,static --prefix=$OMW_LIB_PREFIX install
|
||||
|
||||
|
||||
5. Download [Ogre][] SDK (tested with 1.7.2) and move `lib/Release/Ogre.framework` into
|
||||
`Library/Frameworks`.
|
||||
5. Download [Ogre][] SDK (tested with 1.7.2), unpack it and move
|
||||
`lib/Release/Ogre.framework` into `Library/Frameworks`.
|
||||
|
||||
6. Download [OIS][] and use the XCode project provided in
|
||||
`ois/Mac/XCode-2.2`. Be sure to set your build architecture to
|
||||
|
@ -92,6 +92,7 @@ Getting OpenMW Working
|
|||
$ cd /path/to/open/build/dir
|
||||
$ cmake \
|
||||
-D CMAKE_OSX_ARCHITECTURES=i386 \
|
||||
-D OGRESDK=/path/to/ogre/sdk \
|
||||
-D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \
|
||||
-D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \
|
||||
-D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
|
@ -109,31 +110,16 @@ Getting OpenMW Working
|
|||
if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug
|
||||
build.
|
||||
|
||||
11. In build directory create directory for game resources:
|
||||
$ cd /path/to/openmw/build/dir
|
||||
$ mkdir Contents
|
||||
$ mkdir Contents/Resources
|
||||
$ mkdir Contents/Plugins
|
||||
Copy Ogre plugins from Ogre SDK to Plugins subdir:
|
||||
$ cp /path/to/ogre/sdk/lib/*.dylib Contents/Plugins
|
||||
Create symlink to resources subdirectory:
|
||||
$ ln -s resources Contents/Resources/resources
|
||||
Create symlinks for *.cfg files:
|
||||
$ ln -s plugins.cfg Contents/MacOS/plugins.cfg
|
||||
$ ln -s openmw.cfg Contents/MacOS/openmw.cfg
|
||||
|
||||
12. Move your Morrowind `Data Files` directory into the `Contents/Resources`
|
||||
11. Copy your Morrowind `Data Files` directory into the OpenMW build dir
|
||||
with the name `data` or create symlink:
|
||||
$ ln -s /path/to/morrowind/data/files Contents/Resources/data
|
||||
$ ln -s /path/to/morrowind/data/files /path/to/openmw/build/dir/data
|
||||
|
||||
13. From your build directory run:
|
||||
$ ./openmw
|
||||
12. From your build directory run:
|
||||
$ OpenMW.app/Contents/MacOS/openmw
|
||||
or:
|
||||
$ open OpenMW.app
|
||||
Enjoy!
|
||||
|
||||
14. Optionally you can create .app bundle:
|
||||
$ make package
|
||||
But for now you shold manually copy Contents directory from build directory to bundle
|
||||
(because there is no plugins and resources in generated .app).
|
||||
|
||||
|
||||
[boost]: http://www.boost.org
|
||||
|
|
|
@ -5,7 +5,7 @@ project(OpenMW)
|
|||
set(GAME
|
||||
main.cpp
|
||||
engine.cpp
|
||||
path.cpp)
|
||||
)
|
||||
set(GAME_HEADER
|
||||
engine.hpp)
|
||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
@ -70,9 +70,17 @@ source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
|||
|
||||
set(GAMEDIALOGUE_HEADER
|
||||
mwdialogue/dialoguemanager.hpp
|
||||
mwdialogue/journal.hpp
|
||||
mwdialogue/journalentry.hpp
|
||||
mwdialogue/quest.hpp
|
||||
mwdialogue/topic.hpp
|
||||
)
|
||||
set(GAMEDIALOGUE
|
||||
mwdialogue/dialoguemanager.cpp
|
||||
mwdialogue/journal.cpp
|
||||
mwdialogue/journalentry.cpp
|
||||
mwdialogue/quest.cpp
|
||||
mwdialogue/topic.cpp
|
||||
)
|
||||
source_group(apps\\openmw\\mwdialogue FILES ${GAMEDIALOGUE_HEADER} ${GAMEDIALOGUE})
|
||||
|
||||
|
@ -91,6 +99,7 @@ set(GAMESCRIPT
|
|||
mwscript/controlextensions.cpp
|
||||
mwscript/extensions.cpp
|
||||
mwscript/globalscripts.cpp
|
||||
mwscript/dialogueextensions.cpp
|
||||
)
|
||||
set(GAMESCRIPT_HEADER
|
||||
mwscript/locals.hpp
|
||||
|
@ -109,6 +118,7 @@ set(GAMESCRIPT_HEADER
|
|||
mwscript/extensions.hpp
|
||||
mwscript/globalscripts.hpp
|
||||
mwscript/ref.hpp
|
||||
mwscript/dialogueextensions.hpp
|
||||
)
|
||||
source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER})
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <components/bsa/bsa_archive.hpp>
|
||||
#include <components/esm/loadregn.hpp>
|
||||
#include <components/esm/esm_reader.hpp>
|
||||
#include <components/files/path.hpp>
|
||||
|
||||
#include <openengine/gui/manager.hpp>
|
||||
#include "mwgui/window_manager.hpp"
|
||||
|
||||
|
@ -37,6 +39,7 @@
|
|||
#include "mwclass/classes.hpp"
|
||||
|
||||
#include "mwdialogue/dialoguemanager.hpp"
|
||||
#include "mwdialogue/journal.hpp"
|
||||
|
||||
#include "mwmechanics/mechanicsmanager.hpp"
|
||||
|
||||
|
@ -48,7 +51,6 @@
|
|||
|
||||
#include <MyGUI_WidgetManager.h>
|
||||
#include "mwgui/class.hpp"
|
||||
#include "path.hpp"
|
||||
|
||||
#include "components/nifbullet/bullet_nif_loader.hpp"
|
||||
|
||||
|
@ -75,7 +77,7 @@ void OMW::Engine::executeLocalScripts()
|
|||
}
|
||||
|
||||
|
||||
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
|
||||
bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
||||
{
|
||||
if(mShowFPS)
|
||||
{
|
||||
|
@ -264,6 +266,7 @@ OMW::Engine::Engine()
|
|||
, mScriptManager (0)
|
||||
, mScriptContext (0)
|
||||
, mGuiManager (0)
|
||||
, mFSStrict (false)
|
||||
{
|
||||
MWClass::registerClasses();
|
||||
}
|
||||
|
@ -276,6 +279,7 @@ OMW::Engine::~Engine()
|
|||
delete mEnvironment.mGlobalScripts;
|
||||
delete mEnvironment.mMechanicsManager;
|
||||
delete mEnvironment.mDialogueManager;
|
||||
delete mEnvironment.mJournal;
|
||||
delete mScriptManager;
|
||||
delete mScriptContext;
|
||||
delete mPhysicEngine;
|
||||
|
@ -285,15 +289,12 @@ OMW::Engine::~Engine()
|
|||
|
||||
void OMW::Engine::loadBSA()
|
||||
{
|
||||
boost::filesystem::directory_iterator end;
|
||||
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
|
||||
|
||||
for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter)
|
||||
for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
|
||||
{
|
||||
if (boost::filesystem::extension (iter->path())==".bsa")
|
||||
{
|
||||
std::cout << "Adding " << iter->path().string() << std::endl;
|
||||
addBSA(iter->path().string());
|
||||
}
|
||||
std::cout << "Adding " << iter->second.string() << std::endl;
|
||||
addBSA (iter->second.string());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,11 +307,20 @@ void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
|
|||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
|
||||
}
|
||||
|
||||
void OMW::Engine::enableFSStrict()
|
||||
{
|
||||
mFSStrict = true;
|
||||
}
|
||||
|
||||
// Set data dir
|
||||
|
||||
void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir)
|
||||
void OMW::Engine::setDataDirs (const std::vector<boost::filesystem::path>& dataDirs)
|
||||
{
|
||||
mDataDir = boost::filesystem::system_complete (dataDir);
|
||||
/// \todo remove mDataDir, once resources system can handle multiple directories
|
||||
assert (!dataDirs.empty());
|
||||
mDataDir = dataDirs[0];
|
||||
|
||||
mFileCollections = Files::Collections (dataDirs, !mFSStrict);
|
||||
}
|
||||
|
||||
// Set resource dir
|
||||
|
@ -363,19 +373,14 @@ void OMW::Engine::setNewGame()
|
|||
void OMW::Engine::go()
|
||||
{
|
||||
assert (!mEnvironment.mWorld);
|
||||
assert (!mDataDir.empty());
|
||||
assert (!mCellName.empty());
|
||||
assert (!mMaster.empty());
|
||||
|
||||
test.name = "";
|
||||
total = 0;
|
||||
|
||||
|
||||
|
||||
std::cout << "Data directory: " << mDataDir << "\n";
|
||||
|
||||
std::string cfgDir = OMW::Path::getPath(OMW::Path::GLOBAL_CFG_PATH, "openmw", "");
|
||||
std::string cfgUserDir = OMW::Path::getPath(OMW::Path::USER_CFG_PATH, "openmw", "");
|
||||
std::string cfgDir = Files::getPath (Files::Path_ConfigGlobal, "openmw", "");
|
||||
std::string cfgUserDir = Files::getPath (Files::Path_ConfigUser, "openmw", "");
|
||||
std::string plugCfg = "plugins.cfg";
|
||||
std::string ogreCfg = "ogre.cfg";
|
||||
ogreCfg.insert(0, cfgUserDir);
|
||||
|
@ -405,8 +410,8 @@ void OMW::Engine::go()
|
|||
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
|
||||
|
||||
// Create the world
|
||||
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mDataDir, mMaster, mResDir, mNewGame, mEnvironment);
|
||||
|
||||
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
|
||||
mResDir, mNewGame, mEnvironment);
|
||||
|
||||
// Set up the GUI system
|
||||
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
|
||||
|
@ -444,6 +449,7 @@ void OMW::Engine::go()
|
|||
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (mEnvironment);
|
||||
|
||||
// Create dialog system
|
||||
mEnvironment.mJournal = new MWDialogue::Journal (mEnvironment);
|
||||
mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment);
|
||||
|
||||
// load cell
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <openengine/ogre/renderer.hpp>
|
||||
#include <openengine/bullet/physic.hpp>
|
||||
#include <components/compiler/extensions.hpp>
|
||||
#include <components/files/collections.hpp>
|
||||
|
||||
#include "mwworld/environment.hpp"
|
||||
#include "mwworld/ptr.hpp"
|
||||
|
@ -55,8 +56,6 @@ namespace OMW
|
|||
|
||||
class Engine : private Ogre::FrameListener
|
||||
{
|
||||
|
||||
//int nFiles;
|
||||
boost::filesystem::path mDataDir;
|
||||
boost::filesystem::path mResDir;
|
||||
OEngine::Render::OgreRenderer mOgre;
|
||||
|
@ -84,6 +83,9 @@ namespace OMW
|
|||
|
||||
MWWorld::Ptr mIgnoreLocalPtr;
|
||||
|
||||
Files::Collections mFileCollections;
|
||||
bool mFSStrict;
|
||||
|
||||
// not implemented
|
||||
Engine (const Engine&);
|
||||
Engine& operator= (const Engine&);
|
||||
|
@ -98,7 +100,7 @@ namespace OMW
|
|||
|
||||
void executeLocalScripts();
|
||||
|
||||
virtual bool frameStarted(const Ogre::FrameEvent& evt);
|
||||
virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt);
|
||||
|
||||
/// Process pending commands
|
||||
|
||||
|
@ -108,8 +110,14 @@ namespace OMW
|
|||
|
||||
~Engine();
|
||||
|
||||
/// Set data dir
|
||||
void setDataDir (const boost::filesystem::path& dataDir);
|
||||
/// Enable strict filesystem mode (do not fold case)
|
||||
///
|
||||
/// \attention The strict mode must be specified before any path-related settings
|
||||
/// are given to the engine.
|
||||
void enableFSStrict();
|
||||
|
||||
/// Set data dirs
|
||||
void setDataDirs (const std::vector<boost::filesystem::path>& dataDirs);
|
||||
|
||||
/// Set resource dir
|
||||
void setResourceDir (const boost::filesystem::path& parResDir);
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <components/misc/fileops.hpp>
|
||||
#include <components/files/path.hpp>
|
||||
|
||||
#include "engine.hpp"
|
||||
#include "path.hpp"
|
||||
|
||||
#if defined(_WIN32) && !defined(_CONSOLE)
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
|
@ -41,29 +42,56 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
|||
|
||||
desc.add_options()
|
||||
("help", "print help message")
|
||||
("data", bpo::value<std::string>()->default_value ("data"),
|
||||
"set data directory")
|
||||
("data", bpo::value<std::vector<std::string> >()
|
||||
->default_value (std::vector<std::string>(), "data")
|
||||
->multitoken(),
|
||||
"set data directories (later directories have higher priority)")
|
||||
("data-local", bpo::value<std::string>()->default_value (""),
|
||||
"set local data directory (highest priority)")
|
||||
("resources", bpo::value<std::string>()->default_value ("resources"),
|
||||
"set resources directory")
|
||||
("start", bpo::value<std::string>()->default_value ("Beshara"),
|
||||
"set initial cell")
|
||||
("master", bpo::value<std::string>()->default_value ("Morrowind"),
|
||||
"master file")
|
||||
( "showfps", "show fps counter")
|
||||
( "debug", "debug mode" )
|
||||
( "nosound", "disable all sound" )
|
||||
( "script-verbose", "verbose script output" )
|
||||
( "new-game", "activate char gen/new game mechanics" )
|
||||
( "script-all", "compile all scripts (excluding dialogue scripts) at startup")
|
||||
("master", bpo::value<std::vector<std::string> >()
|
||||
->default_value (std::vector<std::string>(), "")
|
||||
->multitoken(),
|
||||
"master file(s)")
|
||||
("plugin", bpo::value<std::vector<std::string> >()
|
||||
->default_value (std::vector<std::string>(), "")
|
||||
->multitoken(),
|
||||
"plugin file(s)")
|
||||
( "fps", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false), "show fps counter")
|
||||
( "debug", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false), "debug mode" )
|
||||
( "nosound", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false), "disable all sound" )
|
||||
( "script-verbose", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false), "verbose script output" )
|
||||
( "new-game", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false),
|
||||
"activate char gen/new game mechanics" )
|
||||
( "script-all", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false),
|
||||
"compile all scripts (excluding dialogue scripts) at startup")
|
||||
( "fs-strict", boost::program_options::value<bool>()->
|
||||
implicit_value (true)->default_value (false),
|
||||
"strict file system handling (no case folding)")
|
||||
;
|
||||
|
||||
bpo::variables_map variables;
|
||||
|
||||
std::string cfgFile = OMW::Path::getPath(OMW::Path::GLOBAL_CFG_PATH, "openmw", "openmw.cfg");
|
||||
//If there is an openmw.cfg in the current path use that as global config
|
||||
//Otherwise try getPath
|
||||
std::string cfgFile = "openmw.cfg";
|
||||
if(!isFile(cfgFile.c_str()))
|
||||
{
|
||||
cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg");
|
||||
}
|
||||
std::cout << "Using global config file: " << cfgFile << std::endl;
|
||||
std::ifstream globalConfigFile(cfgFile.c_str());
|
||||
|
||||
cfgFile = OMW::Path::getPath(OMW::Path::USER_CFG_PATH, "openmw", "openmw.cfg");
|
||||
cfgFile = Files::getPath (Files::Path_ConfigUser, "openmw", "openmw.cfg");
|
||||
std::cout << "Using user config file: " << cfgFile << std::endl;
|
||||
std::ifstream userConfigFile(cfgFile.c_str());
|
||||
|
||||
|
@ -83,27 +111,63 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
|||
return false;
|
||||
}
|
||||
|
||||
engine.setDataDir (variables["data"].as<std::string>());
|
||||
// directory settings
|
||||
if (variables["fs-strict"].as<bool>()==true)
|
||||
engine.enableFSStrict();
|
||||
|
||||
std::vector<std::string> dataDirs = variables["data"].as<std::vector<std::string> >();
|
||||
std::vector<boost::filesystem::path> dataDirs2 (dataDirs.begin(), dataDirs.end());
|
||||
|
||||
std::string local = variables["data-local"].as<std::string>();
|
||||
if (!local.empty())
|
||||
dataDirs.push_back (local);
|
||||
|
||||
engine.setDataDirs (dataDirs2);
|
||||
|
||||
engine.setResourceDir (variables["resources"].as<std::string>());
|
||||
|
||||
// master and plugin
|
||||
std::vector<std::string> master = variables["master"].as<std::vector<std::string> >();
|
||||
if (master.empty())
|
||||
{
|
||||
std::cout << "No master file given. Assuming Morrowind.esm" << std::endl;
|
||||
master.push_back ("Morrowind");
|
||||
}
|
||||
|
||||
if (master.size()>1)
|
||||
{
|
||||
std::cout
|
||||
<< "Ignoring all but the first master file (multiple master files not yet supported)."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
engine.addMaster (master[0]);
|
||||
|
||||
std::vector<std::string> plugin = variables["plugin"].as<std::vector<std::string> >();
|
||||
|
||||
if (!plugin.empty())
|
||||
std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl;
|
||||
|
||||
// startup-settings
|
||||
engine.setCell (variables["start"].as<std::string>());
|
||||
engine.addMaster (variables["master"].as<std::string>());
|
||||
|
||||
if (variables.count ("showfps"))
|
||||
engine.showFPS();
|
||||
|
||||
if (variables.count ("debug"))
|
||||
engine.enableDebugMode();
|
||||
|
||||
if (variables.count ("nosound"))
|
||||
engine.disableSound();
|
||||
|
||||
if (variables.count ("script-verbose"))
|
||||
engine.enableVerboseScripts();
|
||||
|
||||
if (variables.count ("new-game"))
|
||||
if (variables["new-game"].as<bool>()==true)
|
||||
engine.setNewGame();
|
||||
|
||||
if (variables.count ("script-all"))
|
||||
// other settings
|
||||
if (variables["fps"].as<bool>()==true)
|
||||
engine.showFPS();
|
||||
|
||||
if (variables["debug"].as<bool>()==true)
|
||||
engine.enableDebugMode();
|
||||
|
||||
if (variables["nosound"].as<bool>()==true)
|
||||
engine.disableSound();
|
||||
|
||||
if (variables["script-verbose"].as<bool>()==true)
|
||||
engine.enableVerboseScripts();
|
||||
|
||||
if (variables["script-all"].as<bool>()==true)
|
||||
engine.setCompileAll (true);
|
||||
|
||||
return true;
|
||||
|
|
95
apps/openmw/mwdialogue/journal.cpp
Normal file
95
apps/openmw/mwdialogue/journal.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
|
||||
#include "journal.hpp"
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
Quest& Journal::getQuest (const std::string& id)
|
||||
{
|
||||
TQuestContainer::iterator iter = mQuests.find (id);
|
||||
|
||||
if (iter==mQuests.end())
|
||||
{
|
||||
std::pair<TQuestContainer::iterator, bool> result =
|
||||
mQuests.insert (std::make_pair (id, Quest (id)));
|
||||
|
||||
iter = result.first;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
Journal::Journal (MWWorld::Environment& environment)
|
||||
: mEnvironment (environment)
|
||||
{}
|
||||
|
||||
void Journal::addEntry (const std::string& id, int index)
|
||||
{
|
||||
StampedJournalEntry entry =
|
||||
StampedJournalEntry::makeFromQuest (id, index, *mEnvironment.mWorld);
|
||||
|
||||
mJournal.push_back (entry);
|
||||
|
||||
Quest& quest = getQuest (id);
|
||||
|
||||
quest.addEntry (entry, *mEnvironment.mWorld); // we are doing slicing on purpose here
|
||||
}
|
||||
|
||||
void Journal::setJournalIndex (const std::string& id, int index)
|
||||
{
|
||||
Quest& quest = getQuest (id);
|
||||
|
||||
quest.setIndex (index, *mEnvironment.mWorld);
|
||||
}
|
||||
|
||||
void Journal::addTopic (const std::string& topicId, const std::string& infoId)
|
||||
{
|
||||
TTopicContainer::iterator iter = mTopics.find (topicId);
|
||||
|
||||
if (iter==mTopics.end())
|
||||
{
|
||||
std::pair<TTopicContainer::iterator, bool> result
|
||||
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
|
||||
|
||||
iter = result.first;
|
||||
}
|
||||
|
||||
iter->second.addEntry (JournalEntry (topicId, infoId), *mEnvironment.mWorld);
|
||||
}
|
||||
|
||||
int Journal::getJournalIndex (const std::string& id) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Journal::TEntryIter Journal::begin() const
|
||||
{
|
||||
return mJournal.begin();
|
||||
}
|
||||
|
||||
Journal::TEntryIter Journal::end() const
|
||||
{
|
||||
return mJournal.end();
|
||||
}
|
||||
|
||||
Journal::TQuestIter Journal::questBegin() const
|
||||
{
|
||||
return mQuests.begin();
|
||||
}
|
||||
|
||||
Journal::TQuestIter Journal::questEnd() const
|
||||
{
|
||||
return mQuests.end();
|
||||
}
|
||||
|
||||
Journal::TTopicIter Journal::topicBegin() const
|
||||
{
|
||||
return mTopics.begin();
|
||||
}
|
||||
|
||||
Journal::TTopicIter Journal::topicEnd() const
|
||||
{
|
||||
return mTopics.end();
|
||||
}
|
||||
}
|
78
apps/openmw/mwdialogue/journal.hpp
Normal file
78
apps/openmw/mwdialogue/journal.hpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#ifndef GAME_MMDIALOG_JOURNAL_H
|
||||
#define GAME_MWDIALOG_JOURNAL_H
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
#include "journalentry.hpp"
|
||||
#include "quest.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct Environment;
|
||||
}
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
/// \brief The player's journal
|
||||
class Journal
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::deque<StampedJournalEntry> TEntryContainer;
|
||||
typedef TEntryContainer::const_iterator TEntryIter;
|
||||
typedef std::map<std::string, Quest> TQuestContainer; // topc, quest
|
||||
typedef TQuestContainer::const_iterator TQuestIter;
|
||||
typedef std::map<std::string, Topic> TTopicContainer; // topic-id, topic-content
|
||||
typedef TTopicContainer::const_iterator TTopicIter;
|
||||
|
||||
private:
|
||||
|
||||
MWWorld::Environment& mEnvironment;
|
||||
TEntryContainer mJournal;
|
||||
TQuestContainer mQuests;
|
||||
TTopicContainer mTopics;
|
||||
|
||||
Quest& getQuest (const std::string& id);
|
||||
|
||||
public:
|
||||
|
||||
Journal (MWWorld::Environment& environment);
|
||||
|
||||
void addEntry (const std::string& id, int index);
|
||||
///< Add a journal entry.
|
||||
|
||||
void setJournalIndex (const std::string& id, int index);
|
||||
///< Set the journal index without adding an entry.
|
||||
|
||||
int getJournalIndex (const std::string& id) const;
|
||||
///< Get the journal index.
|
||||
|
||||
void addTopic (const std::string& topicId, const std::string& infoId);
|
||||
|
||||
TEntryIter begin() const;
|
||||
///< Iterator pointing to the begin of the main journal.
|
||||
///
|
||||
/// \note Iterators to main journal entries will never become invalid.
|
||||
|
||||
TEntryIter end() const;
|
||||
///< Iterator pointing past the end of the main journal.
|
||||
|
||||
TQuestIter questBegin() const;
|
||||
///< Iterator pointing to the first quest (sorted by topic ID)
|
||||
|
||||
TQuestIter questEnd() const;
|
||||
///< Iterator pointing past the last quest.
|
||||
|
||||
TTopicIter topicBegin() const;
|
||||
///< Iterator pointing to the first topic (sorted by topic ID)
|
||||
///
|
||||
/// \note The topic ID is identical with the user-visible topic string.
|
||||
|
||||
TTopicIter topicEnd() const;
|
||||
///< Iterator pointing past the last topic.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
69
apps/openmw/mwdialogue/journalentry.cpp
Normal file
69
apps/openmw/mwdialogue/journalentry.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
#include "journalentry.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
JournalEntry::JournalEntry() {}
|
||||
|
||||
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
|
||||
: mTopic (topic), mInfoId (infoId)
|
||||
{}
|
||||
|
||||
std::string JournalEntry::getText (const ESMS::ESMStore& store) const
|
||||
{
|
||||
const ESM::Dialogue *dialogue = store.dialogs.find (mTopic);
|
||||
|
||||
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||
iter!=dialogue->mInfo.end(); ++iter)
|
||||
if (iter->id==mInfoId)
|
||||
return iter->response;
|
||||
|
||||
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic);
|
||||
}
|
||||
|
||||
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index,
|
||||
const MWWorld::World& world)
|
||||
{
|
||||
return JournalEntry (topic, idFromIndex (topic, index, world));
|
||||
}
|
||||
|
||||
std::string JournalEntry::idFromIndex (const std::string& topic, int index,
|
||||
const MWWorld::World& world)
|
||||
{
|
||||
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (topic);
|
||||
|
||||
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||
iter!=dialogue->mInfo.end(); ++iter)
|
||||
if (iter->data.disposition==index) /// \todo cleanup info structure
|
||||
{
|
||||
iter->id;
|
||||
}
|
||||
|
||||
throw std::runtime_error ("unknown journal index for topic " + topic);
|
||||
}
|
||||
|
||||
StampedJournalEntry::StampedJournalEntry()
|
||||
: mDay (0), mMonth (0), mDayOfMonth (0)
|
||||
{}
|
||||
|
||||
StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,
|
||||
int day, int month, int dayOfMonth)
|
||||
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
|
||||
{}
|
||||
|
||||
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index,
|
||||
const MWWorld::World& world)
|
||||
{
|
||||
int day = world.getGlobalVariable ("dayspassed").mLong;
|
||||
int month = world.getGlobalVariable ("day").mLong;
|
||||
int dayOfMonth = world.getGlobalVariable ("month").mLong;
|
||||
|
||||
return StampedJournalEntry (topic, idFromIndex (topic, index, world), day, month, dayOfMonth);
|
||||
}
|
||||
}
|
54
apps/openmw/mwdialogue/journalentry.hpp
Normal file
54
apps/openmw/mwdialogue/journalentry.hpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef GAME_MMDIALOGUE_JOURNALENTRY_H
|
||||
#define GAME_MMDIALOGUE_JOURNALENTRY_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ESMS
|
||||
{
|
||||
struct ESMStore;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
/// \brief A quest or dialogue entry
|
||||
struct JournalEntry
|
||||
{
|
||||
std::string mTopic;
|
||||
std::string mInfoId;
|
||||
|
||||
JournalEntry();
|
||||
|
||||
JournalEntry (const std::string& topic, const std::string& infoId);
|
||||
|
||||
std::string getText (const ESMS::ESMStore& store) const;
|
||||
|
||||
static JournalEntry makeFromQuest (const std::string& topic, int index,
|
||||
const MWWorld::World& world);
|
||||
|
||||
static std::string idFromIndex (const std::string& topic, int index,
|
||||
const MWWorld::World& world);
|
||||
};
|
||||
|
||||
/// \biref A quest entry with a timestamp.
|
||||
struct StampedJournalEntry : public JournalEntry
|
||||
{
|
||||
int mDay;
|
||||
int mMonth;
|
||||
int mDayOfMonth;
|
||||
|
||||
StampedJournalEntry();
|
||||
|
||||
StampedJournalEntry (const std::string& topic, const std::string& infoId,
|
||||
int day, int month, int dayOfMonth);
|
||||
|
||||
static StampedJournalEntry makeFromQuest (const std::string& topic, int index,
|
||||
const MWWorld::World& world);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
86
apps/openmw/mwdialogue/quest.cpp
Normal file
86
apps/openmw/mwdialogue/quest.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
|
||||
#include "quest.hpp"
|
||||
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
Quest::Quest()
|
||||
: Topic(), mIndex (0), mFinished (false)
|
||||
{}
|
||||
|
||||
Quest::Quest (const std::string& topic)
|
||||
: Topic (topic), mIndex (0), mFinished (false)
|
||||
{}
|
||||
|
||||
const std::string Quest::getName (const MWWorld::World& world) const
|
||||
{
|
||||
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
|
||||
|
||||
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||
iter!=dialogue->mInfo.end(); ++iter)
|
||||
if (iter->questStatus==ESM::DialInfo::QS_Name)
|
||||
return iter->response;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int Quest::getIndex() const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
void Quest::setIndex (int index, const MWWorld::World& world)
|
||||
{
|
||||
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
|
||||
|
||||
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||
iter!=dialogue->mInfo.end(); ++iter)
|
||||
if (iter->data.disposition==index && iter->questStatus!=ESM::DialInfo::QS_Name)
|
||||
{
|
||||
mIndex = index;
|
||||
|
||||
if (iter->questStatus==ESM::DialInfo::QS_Finished)
|
||||
mFinished = true;
|
||||
else if (iter->questStatus==ESM::DialInfo::QS_Restart)
|
||||
mFinished = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw std::runtime_error ("unknown journal index for topic " + mTopic);
|
||||
}
|
||||
|
||||
bool Quest::isFinished() const
|
||||
{
|
||||
return mFinished;
|
||||
}
|
||||
|
||||
void Quest::addEntry (const JournalEntry& entry, const MWWorld::World& world)
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (entry.mTopic);
|
||||
|
||||
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
|
||||
iter!=dialogue->mInfo.end(); ++iter)
|
||||
if (iter->id==entry.mInfoId)
|
||||
{
|
||||
index = iter->data.disposition; /// \todo cleanup info structure
|
||||
break;
|
||||
}
|
||||
|
||||
if (index==-1)
|
||||
throw std::runtime_error ("unknown journal entry for topic " + mTopic);
|
||||
|
||||
setIndex (index, world);
|
||||
|
||||
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
|
||||
if (*iter==entry.mInfoId)
|
||||
return;
|
||||
|
||||
mEntries.push_back (entry.mInfoId);
|
||||
}
|
||||
}
|
37
apps/openmw/mwdialogue/quest.hpp
Normal file
37
apps/openmw/mwdialogue/quest.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef GAME_MMDIALOG_QUEST_H
|
||||
#define GAME_MWDIALOG_QUEST_H
|
||||
|
||||
#include "topic.hpp"
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
/// \brief A quest in progress or a compelted quest
|
||||
class Quest : public Topic
|
||||
{
|
||||
int mIndex;
|
||||
bool mFinished;
|
||||
|
||||
public:
|
||||
|
||||
Quest();
|
||||
|
||||
Quest (const std::string& topic);
|
||||
|
||||
const std::string getName (const MWWorld::World& world) const;
|
||||
///< May be an empty string
|
||||
|
||||
int getIndex() const;
|
||||
|
||||
void setIndex (int index, const MWWorld::World& world);
|
||||
///< Calling this function with a non-existant index while throw an exception.
|
||||
|
||||
bool isFinished() const;
|
||||
|
||||
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
|
||||
///< Add entry and adjust index accordingly.
|
||||
///
|
||||
/// \note Redundant entries are ignored, but the index is still adjusted.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
46
apps/openmw/mwdialogue/topic.cpp
Normal file
46
apps/openmw/mwdialogue/topic.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
#include "topic.hpp"
|
||||
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
Topic::Topic()
|
||||
{}
|
||||
|
||||
Topic::Topic (const std::string& topic)
|
||||
: mTopic (topic)
|
||||
{}
|
||||
|
||||
Topic::~Topic()
|
||||
{}
|
||||
|
||||
void Topic::addEntry (const JournalEntry& entry, const MWWorld::World& world)
|
||||
{
|
||||
if (entry.mTopic!=mTopic)
|
||||
throw std::runtime_error ("topic does not match: " + mTopic);
|
||||
|
||||
for (TEntryIter iter = begin(); iter!=end(); ++iter)
|
||||
if (*iter==entry.mInfoId)
|
||||
return;
|
||||
|
||||
mEntries.push_back (entry.mInfoId);
|
||||
}
|
||||
|
||||
Topic::TEntryIter Topic::begin()
|
||||
{
|
||||
return mEntries.begin();
|
||||
}
|
||||
|
||||
Topic::TEntryIter Topic::end()
|
||||
{
|
||||
return mEntries.end();
|
||||
}
|
||||
|
||||
JournalEntry Topic::getEntry (const std::string& infoId)
|
||||
{
|
||||
return JournalEntry (mTopic, infoId);
|
||||
}
|
||||
}
|
52
apps/openmw/mwdialogue/topic.hpp
Normal file
52
apps/openmw/mwdialogue/topic.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef GAME_MMDIALOG_TOPIC_H
|
||||
#define GAME_MWDIALOG_TOPIC_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "journalentry.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
}
|
||||
|
||||
namespace MWDialogue
|
||||
{
|
||||
/// \brief Collection of seen responses for a topic
|
||||
class Topic
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<std::string> TEntryContainer;
|
||||
typedef TEntryContainer::const_iterator TEntryIter;
|
||||
|
||||
protected:
|
||||
|
||||
std::string mTopic;
|
||||
TEntryContainer mEntries; // info-IDs
|
||||
|
||||
public:
|
||||
|
||||
Topic();
|
||||
|
||||
Topic (const std::string& topic);
|
||||
|
||||
virtual ~Topic();
|
||||
|
||||
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
|
||||
///< Add entry
|
||||
///
|
||||
/// \note Redundant entries are ignored.
|
||||
|
||||
TEntryIter begin();
|
||||
///< Iterator pointing to the begin of the journal for this topic.
|
||||
|
||||
TEntryIter end();
|
||||
///< Iterator pointing past the end of the journal for this topic.
|
||||
|
||||
JournalEntry getEntry (const std::string& infoId);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -119,7 +119,7 @@ void BirthDialog::updateBirths()
|
|||
{
|
||||
birthList->removeAllItems();
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
|
||||
ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator it = store.birthSigns.list.begin();
|
||||
ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator end = store.birthSigns.list.end();
|
||||
|
@ -149,7 +149,7 @@ void BirthDialog::updateSpells()
|
|||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, spellArea->getWidth(), 18);
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESM::BirthSign *birth = store.birthSigns.find(currentBirthId);
|
||||
|
||||
std::string texturePath = std::string("textures\\") + birth->texture;
|
||||
|
|
|
@ -50,7 +50,7 @@ void GenerateClassResultDialog::setClassId(const std::string &classId)
|
|||
{
|
||||
currentClassId = classId;
|
||||
classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds");
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
className->setCaption(store.classes.find(currentClassId)->name);
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ void PickClassDialog::updateClasses()
|
|||
{
|
||||
classList->removeAllItems();
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
|
||||
ESMS::RecListT<ESM::Class>::MapType::const_iterator it = store.classes.list.begin();
|
||||
ESMS::RecListT<ESM::Class>::MapType::const_iterator end = store.classes.list.end();
|
||||
|
@ -220,7 +220,7 @@ void PickClassDialog::updateStats()
|
|||
{
|
||||
if (currentClassId.empty())
|
||||
return;
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESM::Class *klass = store.classes.search(currentClassId);
|
||||
if (!klass)
|
||||
return;
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace MWGui
|
|||
|
||||
ConsoleInterpreterContext (Console& console, MWWorld::Environment& environment,
|
||||
MWWorld::Ptr reference);
|
||||
|
||||
virtual void report (const std::string& message);
|
||||
};
|
||||
|
||||
ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console,
|
||||
|
@ -26,6 +28,11 @@ namespace MWGui
|
|||
mConsole (console)
|
||||
{}
|
||||
|
||||
void ConsoleInterpreterContext::report (const std::string& message)
|
||||
{
|
||||
mConsole.printOK (message);
|
||||
}
|
||||
|
||||
bool Console::compile (const std::string& cmd, Compiler::Output& output)
|
||||
{
|
||||
try
|
||||
|
@ -216,11 +223,11 @@ namespace MWGui
|
|||
try
|
||||
{
|
||||
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
|
||||
Interpreter::Interpreter interpreter (interpreterContext);
|
||||
Interpreter::Interpreter interpreter;
|
||||
MWScript::installOpcodes (interpreter);
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
output.getCode (code);
|
||||
interpreter.run (&code[0], code.size());
|
||||
interpreter.run (&code[0], code.size(), interpreterContext);
|
||||
}
|
||||
catch (const std::exception& error)
|
||||
{
|
||||
|
|
|
@ -212,7 +212,7 @@ void RaceDialog::updateRaces()
|
|||
{
|
||||
raceList->removeAllItems();
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
|
||||
ESMS::RecListT<ESM::Race>::MapType::const_iterator it = store.races.list.begin();
|
||||
ESMS::RecListT<ESM::Race>::MapType::const_iterator end = store.races.list.end();
|
||||
|
@ -246,7 +246,7 @@ void RaceDialog::updateSkills()
|
|||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord1(0, 0, skillList->getWidth(), 18);
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESM::Race *race = store.races.find(currentRaceId);
|
||||
int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE?
|
||||
for (int i = 0; i < count; ++i)
|
||||
|
@ -282,7 +282,7 @@ void RaceDialog::updateSpellPowers()
|
|||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, spellPowerList->getWidth(), 18);
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESM::Race *race = store.races.find(currentRaceId);
|
||||
|
||||
std::vector<std::string>::const_iterator it = race->powers.list.begin();
|
||||
|
|
|
@ -323,7 +323,7 @@ void StatsWindow::updateSkillArea()
|
|||
if (!miscSkills.empty())
|
||||
addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2);
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager.getStore();
|
||||
|
||||
if (!factions.empty())
|
||||
{
|
||||
|
|
|
@ -278,7 +278,7 @@ void MWSpell::setSpellId(const std::string &spellId)
|
|||
|
||||
void MWSpell::createEffectWidgets(std::vector<MyGUI::WidgetPtr> &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord)
|
||||
{
|
||||
ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESM::Spell *spell = store.spells.search(id);
|
||||
MYGUI_ASSERT(spell, "spell with id '" << id << "' not found");
|
||||
|
||||
|
@ -298,7 +298,7 @@ void MWSpell::updateWidgets()
|
|||
{
|
||||
if (spellNameWidget && mWindowManager)
|
||||
{
|
||||
ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESM::Spell *spell = store.spells.search(id);
|
||||
if (spell)
|
||||
spellNameWidget->setCaption(spell->name);
|
||||
|
@ -363,7 +363,7 @@ void MWSpellEffect::updateWidgets()
|
|||
if (!mWindowManager)
|
||||
return;
|
||||
|
||||
ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESMS::ESMStore &store = mWindowManager->getStore();
|
||||
const ESM::MagicEffect *magicEffect = store.magicEffects.search(effect.effectID);
|
||||
if (textWidget)
|
||||
{
|
||||
|
|
|
@ -940,7 +940,7 @@ void WindowManager::onReviewActivateDialog(int parDialog)
|
|||
};
|
||||
}
|
||||
|
||||
ESMS::ESMStore& WindowManager::getStore()
|
||||
const ESMS::ESMStore& WindowManager::getStore() const
|
||||
{
|
||||
return environment.mWorld->getStore();
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ namespace MWGui
|
|||
*/
|
||||
const std::string &getGameSettingString(const std::string &id, const std::string &default_);
|
||||
|
||||
ESMS::ESMStore& getStore();
|
||||
const ESMS::ESMStore& getStore() const;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ namespace MWInput
|
|||
}
|
||||
|
||||
//NOTE: Used to check for movement keys
|
||||
bool frameStarted(const Ogre::FrameEvent &evt)
|
||||
bool frameRenderingQueued (const Ogre::FrameEvent &evt)
|
||||
{
|
||||
// Tell OIS to handle all input events
|
||||
input.capture();
|
||||
|
|
|
@ -191,7 +191,7 @@ void MWScene::scaleObject (const std::string& handle, float scale)
|
|||
|
||||
}
|
||||
|
||||
void MWScene::toggleCollisionMode()
|
||||
bool MWScene::toggleCollisionMode()
|
||||
{
|
||||
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++)
|
||||
{
|
||||
|
@ -203,6 +203,7 @@ void MWScene::toggleCollisionMode()
|
|||
act->setGravity(0.);
|
||||
act->setVerticalVelocity(0);
|
||||
mFreeFly = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -210,11 +211,15 @@ void MWScene::toggleCollisionMode()
|
|||
act->enableCollisions(true);
|
||||
act->setGravity(4.);
|
||||
act->setVerticalVelocity(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // This should never happen, but it shall not bother us now, since
|
||||
// this part of the code needs a rewrite anyway.
|
||||
}
|
||||
|
||||
void MWScene::toggleRenderMode (int mode)
|
||||
bool MWScene::toggleRenderMode (int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
|
@ -223,6 +228,8 @@ void MWScene::toggleRenderMode (int mode)
|
|||
// TODO use a proper function instead of accessing the member variable
|
||||
// directly.
|
||||
eng->setDebugRenderingMode (!eng->isDebugCreated);
|
||||
break;
|
||||
return eng->isDebugCreated;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -91,12 +91,14 @@ namespace MWRender
|
|||
|
||||
/// Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
void toggleCollisionMode();
|
||||
/// \return Resulting mode
|
||||
bool toggleCollisionMode();
|
||||
|
||||
/// Toggle render mode
|
||||
/// \todo Using an int instead of a enum here to avoid cyclic includes. Will be fixed
|
||||
/// when the mw*-refactoring is done.
|
||||
void toggleRenderMode (int mode);
|
||||
/// \return Resulting mode
|
||||
bool toggleRenderMode (int mode);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,10 +102,42 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
class OpGetPCCell : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
const ESM::Cell *cell = context.getWorld().getPlayer().getPlayer().getCell()->cell;
|
||||
|
||||
std::string current = cell->name;
|
||||
|
||||
if (!(cell->data.flags & ESM::Cell::Interior) && current.empty())
|
||||
{
|
||||
const ESM::Region *region =
|
||||
context.getWorld().getStore().regions.find (cell->region);
|
||||
|
||||
current = region->name;
|
||||
}
|
||||
|
||||
bool match = current.length()>=name.length() &&
|
||||
current.substr (0, name.length())==name;
|
||||
|
||||
runtime.push (match ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeCellChanged = 0x2000000;
|
||||
const int opcodeCOC = 0x2000026;
|
||||
const int opcodeCOE = 0x200008e;
|
||||
const int opcodeGetInterior = 0x2000131;
|
||||
const int opcodeGetPCCell = 0x2000136;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
|
@ -115,6 +147,7 @@ namespace MWScript
|
|||
extensions.registerInstruction ("coe", "ll", opcodeCOE);
|
||||
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
|
||||
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
|
||||
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -123,6 +156,7 @@ namespace MWScript
|
|||
interpreter.installSegment5 (opcodeCOC, new OpCOC);
|
||||
interpreter.installSegment5 (opcodeCOE, new OpCOE);
|
||||
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
|
||||
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,9 @@ namespace MWScript
|
|||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
context.getWorld().toggleCollisionMode();
|
||||
bool enabled = context.getWorld().toggleCollisionMode();
|
||||
|
||||
context.report (enabled ? "Collsion -> On" : "Collision -> Off");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,6 +76,7 @@ namespace MWScript
|
|||
}
|
||||
|
||||
extensions.registerInstruction ("togglecollision", "", opcodeToggleCollision);
|
||||
extensions.registerInstruction ("tcl", "", opcodeToggleCollision);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
|
94
apps/openmw/mwscript/dialogueextensions.cpp
Normal file
94
apps/openmw/mwscript/dialogueextensions.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
|
||||
#include "dialogueextensions.hpp"
|
||||
|
||||
#include <components/compiler/extensions.hpp>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
#include <components/interpreter/runtime.hpp>
|
||||
#include <components/interpreter/opcodes.hpp>
|
||||
|
||||
#include "../mwdialogue/journal.hpp"
|
||||
|
||||
#include "interpretercontext.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
namespace Dialogue
|
||||
{
|
||||
class OpJournal : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
Interpreter::Type_Integer index = runtime[0].mInteger;
|
||||
runtime.pop();
|
||||
|
||||
context.getEnvironment().mJournal->addEntry (quest, index);
|
||||
}
|
||||
};
|
||||
|
||||
class OpSetJournalIndex : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
Interpreter::Type_Integer index = runtime[0].mInteger;
|
||||
runtime.pop();
|
||||
|
||||
context.getEnvironment().mJournal->setJournalIndex (quest, index);
|
||||
}
|
||||
};
|
||||
|
||||
class OpGetJournalIndex : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
MWScript::InterpreterContext& context
|
||||
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
|
||||
|
||||
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
int index = context.getEnvironment().mJournal->getJournalIndex (quest);
|
||||
|
||||
runtime.push (index);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeJournal = 0x2000133;
|
||||
const int opcodeSetJournalIndex = 0x2000134;
|
||||
const int opcodeGetJournalIndex = 0x2000135;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
extensions.registerInstruction ("journal", "cl", opcodeJournal);
|
||||
extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
|
||||
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
{
|
||||
interpreter.installSegment5 (opcodeJournal, new OpJournal);
|
||||
interpreter.installSegment5 (opcodeSetJournalIndex, new OpSetJournalIndex);
|
||||
interpreter.installSegment5 (opcodeGetJournalIndex, new OpGetJournalIndex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
apps/openmw/mwscript/dialogueextensions.hpp
Normal file
25
apps/openmw/mwscript/dialogueextensions.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef GAME_SCRIPT_DIALOGUEEXTENSIONS_H
|
||||
#define GAME_SCRIPT_DIALOGUEEXTENSIONS_H
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
class Extensions;
|
||||
}
|
||||
|
||||
namespace Interpreter
|
||||
{
|
||||
class Interpreter;
|
||||
}
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
/// \brief Dialogue/Journal-related script functionality
|
||||
namespace Dialogue
|
||||
{
|
||||
void registerExtensions (Compiler::Extensions& extensions);
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -104,4 +104,8 @@ op 0x2000115-0x200012f: ModSKill, explicit reference
|
|||
op 0x2000130: ToggleCollision
|
||||
op 0x2000131: GetInterior
|
||||
op 0x2000132: ToggleCollsionDebug
|
||||
opcodes 0x2000133-0x3ffffff unused
|
||||
op 0x2000133: Journal
|
||||
op 0x2000134: SetJournalIndex
|
||||
op 0x2000135: GetJournalIndex
|
||||
op 0x2000136: GetPCCell
|
||||
opcodes 0x2000137-0x3ffffff unused
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "containerextensions.hpp"
|
||||
#include "aiextensions.hpp"
|
||||
#include "controlextensions.hpp"
|
||||
#include "dialogueextensions.hpp"
|
||||
|
||||
namespace MWScript
|
||||
{
|
||||
|
@ -27,6 +28,7 @@ namespace MWScript
|
|||
Container::registerExtensions (extensions);
|
||||
Ai::registerExtensions (extensions);
|
||||
Control::registerExtensions (extensions);
|
||||
Dialogue::registerExtensions (extensions);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -41,5 +43,6 @@ namespace MWScript
|
|||
Container::installOpcodes (interpreter);
|
||||
Ai::installOpcodes (interpreter);
|
||||
Control::installOpcodes (interpreter);
|
||||
Dialogue::installOpcodes (interpreter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,11 @@ namespace MWScript
|
|||
mEnvironment.mWindowManager->messageBox (message, buttons);
|
||||
}
|
||||
|
||||
void InterpreterContext::report (const std::string& message)
|
||||
{
|
||||
messageBox (message);
|
||||
}
|
||||
|
||||
bool InterpreterContext::menuMode()
|
||||
{
|
||||
return mEnvironment.mWindowManager->isGuiMode();
|
||||
|
@ -260,6 +265,11 @@ namespace MWScript
|
|||
mEnvironment.mWorld->disable (ref);
|
||||
}
|
||||
|
||||
MWWorld::Environment& InterpreterContext::getEnvironment()
|
||||
{
|
||||
return mEnvironment;
|
||||
}
|
||||
|
||||
MWGui::WindowManager& InterpreterContext::getWindowManager()
|
||||
{
|
||||
return *mEnvironment.mWindowManager;
|
||||
|
|
|
@ -57,9 +57,13 @@ namespace MWScript
|
|||
virtual void setLocalFloat (int index, float value);
|
||||
|
||||
using Interpreter::Context::messageBox;
|
||||
|
||||
virtual void messageBox (const std::string& message,
|
||||
const std::vector<std::string>& buttons);
|
||||
|
||||
virtual void report (const std::string& message);
|
||||
///< By default echo via messageBox.
|
||||
|
||||
virtual bool menuMode();
|
||||
|
||||
virtual int getGlobalShort (const std::string& name) const;
|
||||
|
@ -106,6 +110,10 @@ namespace MWScript
|
|||
|
||||
virtual void disable (const std::string& id = "");
|
||||
|
||||
MWWorld::Environment& getEnvironment();
|
||||
|
||||
/// \todo remove the following functions (extentions should use getEnvironment instead)
|
||||
|
||||
MWWorld::World& getWorld();
|
||||
|
||||
MWSound::SoundManager& getSoundManager();
|
||||
|
|
|
@ -99,7 +99,11 @@ namespace MWScript
|
|||
InterpreterContext& context =
|
||||
static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
context.getWorld().toggleRenderMode (MWWorld::World::Render_CollisionDebug);
|
||||
bool enabled =
|
||||
context.getWorld().toggleRenderMode (MWWorld::World::Render_CollisionDebug);
|
||||
|
||||
context.report (enabled ?
|
||||
"Collsion Mesh Rendering -> On" : "Collision Mesh Rendering -> Off");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <components/compiler/scanner.hpp>
|
||||
#include <components/compiler/context.hpp>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
|
||||
#include "extensions.hpp"
|
||||
|
||||
namespace MWScript
|
||||
|
@ -21,7 +19,8 @@ namespace MWScript
|
|||
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||
Compiler::Context& compilerContext)
|
||||
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
||||
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext)
|
||||
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
|
||||
mOpcodesInstalled (false)
|
||||
{}
|
||||
|
||||
bool ScriptManager::compile (const std::string& name)
|
||||
|
@ -99,9 +98,13 @@ namespace MWScript
|
|||
if (!iter->second.empty())
|
||||
try
|
||||
{
|
||||
Interpreter::Interpreter interpreter (interpreterContext);
|
||||
installOpcodes (interpreter);
|
||||
interpreter.run (&iter->second[0], iter->second.size());
|
||||
if (!mOpcodesInstalled)
|
||||
{
|
||||
installOpcodes (mInterpreter);
|
||||
mOpcodesInstalled = true;
|
||||
}
|
||||
|
||||
mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <components/compiler/streamerrorhandler.hpp>
|
||||
#include <components/compiler/fileparser.hpp>
|
||||
|
||||
#include <components/interpreter/interpreter.hpp>
|
||||
#include <components/interpreter/types.hpp>
|
||||
|
||||
namespace ESMS
|
||||
|
@ -35,6 +36,8 @@ namespace MWScript
|
|||
bool mVerbose;
|
||||
Compiler::Context& mCompilerContext;
|
||||
Compiler::FileParser mParser;
|
||||
Interpreter::Interpreter mInterpreter;
|
||||
bool mOpcodesInstalled;
|
||||
|
||||
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ namespace MWScript
|
|||
InterpreterContext& context =
|
||||
static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
context.getWorld().toggleSky();
|
||||
bool enabled = context.getWorld().toggleSky();
|
||||
|
||||
context.report (enabled ? "Sky -> On" : "Sky -> Off");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -104,4 +106,3 @@ namespace MWScript
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace MWMechanics
|
|||
namespace MWDialogue
|
||||
{
|
||||
class DialogueManager;
|
||||
class Journal;
|
||||
}
|
||||
|
||||
namespace MWInput
|
||||
|
@ -41,7 +42,7 @@ namespace MWWorld
|
|||
public:
|
||||
Environment()
|
||||
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
|
||||
mMechanicsManager (0), mDialogueManager (0), mFrameDuration (0),
|
||||
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mFrameDuration (0),
|
||||
mInputManager (0)
|
||||
{}
|
||||
|
||||
|
@ -51,6 +52,7 @@ namespace MWWorld
|
|||
MWGui::WindowManager *mWindowManager;
|
||||
MWMechanics::MechanicsManager *mMechanicsManager;
|
||||
MWDialogue::DialogueManager *mDialogueManager;
|
||||
MWDialogue::Journal *mJournal;
|
||||
float mFrameDuration;
|
||||
|
||||
// For setting GUI mode
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <components/bsa/bsa_archive.hpp>
|
||||
#include <components/files/collections.hpp>
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
#include "../mwrender/interior.hpp"
|
||||
|
@ -406,15 +407,16 @@ namespace MWWorld
|
|||
mCellChanged = true;
|
||||
}
|
||||
|
||||
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir,
|
||||
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
||||
const Files::Collections& fileCollections,
|
||||
const std::string& master, const boost::filesystem::path& resDir,
|
||||
bool newGame, Environment& environment)
|
||||
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
|
||||
mSky (false), mCellChanged (false), mEnvironment (environment)
|
||||
{
|
||||
mPhysEngine = physEng;
|
||||
boost::filesystem::path masterPath (dataDir);
|
||||
masterPath /= master;
|
||||
|
||||
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master));
|
||||
|
||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||
|
||||
|
@ -460,7 +462,7 @@ namespace MWWorld
|
|||
return *mPlayer;
|
||||
}
|
||||
|
||||
ESMS::ESMStore& World::getStore()
|
||||
const ESMS::ESMStore& World::getStore() const
|
||||
{
|
||||
return mStore;
|
||||
}
|
||||
|
@ -480,6 +482,11 @@ namespace MWWorld
|
|||
return (*mGlobalVariables)[name];
|
||||
}
|
||||
|
||||
Globals::Data World::getGlobalVariable (const std::string& name) const
|
||||
{
|
||||
return (*mGlobalVariables)[name];
|
||||
}
|
||||
|
||||
char World::getGlobalVariableType (const std::string& name) const
|
||||
{
|
||||
return mGlobalVariables->getType (name);
|
||||
|
@ -645,12 +652,13 @@ namespace MWWorld
|
|||
mSkyManager->setDate (mGlobalVariables->getInt ("day"), month);
|
||||
}
|
||||
|
||||
void World::toggleSky()
|
||||
bool World::toggleSky()
|
||||
{
|
||||
if (mSky)
|
||||
{
|
||||
mSky = false;
|
||||
mSkyManager->disable();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -660,6 +668,7 @@ namespace MWWorld
|
|||
mSkyManager->setDate (mGlobalVariables->getInt ("day"),
|
||||
mGlobalVariables->getInt ("month"));
|
||||
mSkyManager->enable();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -853,13 +862,13 @@ namespace MWWorld
|
|||
mScene.doPhysics (duration, *this, actors);
|
||||
}
|
||||
|
||||
void World::toggleCollisionMode()
|
||||
bool World::toggleCollisionMode()
|
||||
{
|
||||
mScene.toggleCollisionMode();
|
||||
return mScene.toggleCollisionMode();
|
||||
}
|
||||
|
||||
void World::toggleRenderMode (RenderMode mode)
|
||||
bool World::toggleRenderMode (RenderMode mode)
|
||||
{
|
||||
mScene.toggleRenderMode (mode);
|
||||
return mScene.toggleRenderMode (mode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@ namespace ESM
|
|||
struct Position;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
class Collections;
|
||||
}
|
||||
|
||||
namespace Render
|
||||
{
|
||||
class OgreRenderer;
|
||||
|
@ -107,7 +112,8 @@ namespace MWWorld
|
|||
/// interior cell.
|
||||
public:
|
||||
|
||||
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir,
|
||||
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
||||
const Files::Collections& fileCollections,
|
||||
const std::string& master, const boost::filesystem::path& resDir, bool newGame,
|
||||
Environment& environment);
|
||||
|
||||
|
@ -115,7 +121,7 @@ namespace MWWorld
|
|||
|
||||
MWWorld::Player& getPlayer();
|
||||
|
||||
ESMS::ESMStore& getStore();
|
||||
const ESMS::ESMStore& getStore() const;
|
||||
|
||||
const ScriptList& getLocalScripts() const;
|
||||
///< Names and local variable state of all local scripts in active cells.
|
||||
|
@ -125,6 +131,8 @@ namespace MWWorld
|
|||
|
||||
Globals::Data& getGlobalVariable (const std::string& name);
|
||||
|
||||
Globals::Data getGlobalVariable (const std::string& name) const;
|
||||
|
||||
char getGlobalVariableType (const std::string& name) const;
|
||||
///< Return ' ', if there is no global variable with this name.
|
||||
|
||||
|
@ -147,7 +155,8 @@ namespace MWWorld
|
|||
|
||||
void setDay (int day);
|
||||
|
||||
void toggleSky();
|
||||
bool toggleSky();
|
||||
///< \return Resulting mode
|
||||
|
||||
int getMasserPhase() const;
|
||||
|
||||
|
@ -185,12 +194,14 @@ namespace MWWorld
|
|||
float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
||||
void toggleCollisionMode();
|
||||
bool toggleCollisionMode();
|
||||
///< Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
///< \return Resulting mode
|
||||
|
||||
void toggleRenderMode (RenderMode mode);
|
||||
bool toggleRenderMode (RenderMode mode);
|
||||
///< Toggle a render mode.
|
||||
///< \return Resulting mode
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef PATH__HPP
|
||||
#define PATH__HPP
|
||||
|
||||
#include <OgrePlatform.h>
|
||||
#include <string>
|
||||
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||
#include <OSX/macUtils.h>
|
||||
#endif
|
||||
|
||||
namespace OMW
|
||||
{
|
||||
class Path
|
||||
{
|
||||
public:
|
||||
enum PathTypeEnum
|
||||
{
|
||||
USER_CFG_PATH,
|
||||
GLOBAL_CFG_PATH
|
||||
};
|
||||
|
||||
static std::string getPath(PathTypeEnum parType, const std::string parApp, const std::string parFile);
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -12,9 +12,9 @@
|
|||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
CMAKE_POLICY(PUSH)
|
||||
|
||||
IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR)
|
||||
IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR)
|
||||
SET(OGRE_FIND_QUIETLY TRUE) # Already in cache, be silent
|
||||
ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR)
|
||||
ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR)
|
||||
|
||||
IF (WIN32) #Windows
|
||||
MESSAGE(STATUS "Looking for OGRE")
|
||||
|
@ -47,7 +47,16 @@ IF (UNIX AND NOT APPLE)
|
|||
PKG_CHECK_MODULES(OGRE OGRE)
|
||||
ENDIF (UNIX AND NOT APPLE)
|
||||
|
||||
# on OS X we need Ogre SDK because framework doesn't include all libs, just Ogre Main lib
|
||||
IF (APPLE)
|
||||
IF (OGRESDK)
|
||||
MESSAGE(STATUS "Using Ogre SDK")
|
||||
SET(OGRE_LIB_DIR ${OGRESDK}/lib)
|
||||
ELSE (OGRESDK)
|
||||
MESSAGE(FATAL_ERROR "Path to Ogre SDK not specified. Specify OGRESDK.")
|
||||
ENDIF (OGRESDK)
|
||||
|
||||
|
||||
FIND_PATH(OGRE_INCLUDE_DIR Ogre.h
|
||||
PATHS
|
||||
/Library/Frameworks
|
||||
|
@ -71,7 +80,12 @@ SET(OGRE_LIB_DIR ${OGRE_LIB_DIR} CACHE PATH "")
|
|||
|
||||
if(OGRE_LIB_DIR)
|
||||
CMAKE_POLICY(SET CMP0009 NEW)
|
||||
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so")
|
||||
IF (NOT APPLE)
|
||||
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so")
|
||||
ENDIF (NOT APPLE)
|
||||
IF (APPLE)
|
||||
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.dylib")
|
||||
ENDIF (APPLE)
|
||||
FOREACH (OGRE_PLUGINS_FILE ${OGRE_PLUGINS})
|
||||
STRING(REGEX REPLACE "/[^/]*$" "" OGRE_PLUGIN_DIR ${OGRE_PLUGINS_FILE})
|
||||
ENDFOREACH(OGRE_PLUGINS_FILE)
|
||||
|
@ -85,6 +99,7 @@ IF (OGRE_FOUND)
|
|||
IF (NOT OGRE_FIND_QUIETLY)
|
||||
MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}")
|
||||
MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}")
|
||||
MESSAGE(STATUS " plugins : ${OGRE_PLUGIN_DIR}")
|
||||
ENDIF (NOT OGRE_FIND_QUIETLY)
|
||||
ELSE (OGRE_FOUND)
|
||||
IF (OGRE_FIND_REQUIRED)
|
||||
|
|
|
@ -125,6 +125,11 @@ namespace
|
|||
code.push_back (Compiler::Generator::segment3 (0, buttons));
|
||||
}
|
||||
|
||||
void opReport (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (58));
|
||||
}
|
||||
|
||||
void opFetchLocalShort (Compiler::Generator::CodeContainer& code)
|
||||
{
|
||||
code.push_back (Compiler::Generator::segment5 (21));
|
||||
|
@ -516,6 +521,14 @@ namespace Compiler
|
|||
opMessageBox (code, buttons);
|
||||
}
|
||||
|
||||
void report (CodeContainer& code, Literals& literals, const std::string& message)
|
||||
{
|
||||
int index = literals.addString (message);
|
||||
|
||||
opPushInt (code, index);
|
||||
opReport (code);
|
||||
}
|
||||
|
||||
void fetchLocal (CodeContainer& code, char localType, int localIndex)
|
||||
{
|
||||
opPushInt (code, localIndex);
|
||||
|
|
|
@ -81,6 +81,8 @@ namespace Compiler
|
|||
void message (CodeContainer& code, Literals& literals, const std::string& message,
|
||||
int buttons);
|
||||
|
||||
void report (CodeContainer& code, Literals& literals, const std::string& message);
|
||||
|
||||
void fetchLocal (CodeContainer& code, char localType, int localIndex);
|
||||
|
||||
void jump (CodeContainer& code, int offset);
|
||||
|
|
|
@ -30,12 +30,12 @@ namespace Compiler
|
|||
{
|
||||
case 'l':
|
||||
|
||||
Generator::message (mCode, mLiterals, "%g", 0);
|
||||
Generator::report (mCode, mLiterals, "%g");
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
|
||||
Generator::message (mCode, mLiterals, "%f", 0);
|
||||
Generator::report (mCode, mLiterals, "%f");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
|
||||
typedef int32_t Color;
|
||||
|
||||
enum VarType
|
||||
{
|
||||
{
|
||||
VT_Unknown,
|
||||
VT_None,
|
||||
VT_Short,
|
||||
|
@ -18,79 +19,79 @@ enum VarType
|
|||
VT_Float,
|
||||
VT_String,
|
||||
VT_Ignored
|
||||
};
|
||||
};
|
||||
|
||||
enum Specialization
|
||||
{
|
||||
SPC_Combat = 0,
|
||||
SPC_Magic = 1,
|
||||
{
|
||||
SPC_Combat = 0,
|
||||
SPC_Magic = 1,
|
||||
SPC_Stealth = 2
|
||||
};
|
||||
};
|
||||
|
||||
enum RangeType
|
||||
{
|
||||
{
|
||||
RT_Self = 0,
|
||||
RT_Touch = 1,
|
||||
RT_Target = 2
|
||||
};
|
||||
};
|
||||
|
||||
/** A list of references to spells and spell effects. This is shared
|
||||
between the records BSGN, NPC and RACE.
|
||||
*/
|
||||
between the records BSGN, NPC and RACE.
|
||||
*/
|
||||
struct SpellList
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
std::vector<std::string> list;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
while(esm.isNextSub("NPCS"))
|
||||
list.push_back(esm.getHString());
|
||||
}
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("NPCS"))
|
||||
list.push_back(esm.getHString());
|
||||
}
|
||||
};
|
||||
|
||||
/** Defines a spell effect. Shared between SPEL (Spells), ALCH
|
||||
(Potions) and ENCH (Item enchantments) records
|
||||
*/
|
||||
(Potions) and ENCH (Item enchantments) records
|
||||
*/
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
// Position and rotation
|
||||
struct Position
|
||||
{
|
||||
float pos[3];
|
||||
float rot[3];
|
||||
float pos[3];
|
||||
float rot[3];
|
||||
};
|
||||
|
||||
struct ENAMstruct
|
||||
{
|
||||
// Magical effect, hard-coded ID
|
||||
short effectID;
|
||||
// Magical effect, hard-coded ID
|
||||
short effectID;
|
||||
|
||||
// Which skills/attributes are affected (for restore/drain spells
|
||||
// etc.)
|
||||
signed char skill, attribute; // -1 if N/A
|
||||
// Which skills/attributes are affected (for restore/drain spells
|
||||
// etc.)
|
||||
signed char skill, attribute; // -1 if N/A
|
||||
|
||||
// Other spell parameters
|
||||
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||
int area, duration, magnMin, magnMax;
|
||||
// Other spell parameters
|
||||
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||
int area, duration, magnMin, magnMax;
|
||||
|
||||
// Struct size should be 24 bytes
|
||||
// Struct size should be 24 bytes
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct EffectList
|
||||
{
|
||||
std::vector<ENAMstruct> list;
|
||||
std::vector<ENAMstruct> list;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
ENAMstruct s;
|
||||
while(esm.isNextSub("ENAM"))
|
||||
{
|
||||
esm.getHT(s, 24);
|
||||
list.push_back(s);
|
||||
}
|
||||
}
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
ENAMstruct s;
|
||||
while (esm.isNextSub("ENAM"))
|
||||
{
|
||||
esm.getHT(s, 24);
|
||||
list.push_back(s);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
345
components/esm/esm_reader.cpp
Normal file
345
components/esm/esm_reader.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
ESM_Context ESMReader::getContext()
|
||||
{
|
||||
// Update the file position before returning
|
||||
mCtx.filePos = mEsm->tell();
|
||||
return mCtx;
|
||||
}
|
||||
|
||||
void ESMReader::restoreContext(const ESM_Context &rc)
|
||||
{
|
||||
// Reopen the file if necessary
|
||||
if (mCtx.filename != rc.filename)
|
||||
openRaw(rc.filename);
|
||||
|
||||
// Copy the data
|
||||
mCtx = rc;
|
||||
|
||||
// Make sure we seek to the right place
|
||||
mEsm->seek(mCtx.filePos);
|
||||
}
|
||||
|
||||
void ESMReader::close()
|
||||
{
|
||||
mEsm.reset();
|
||||
mCtx.filename.clear();
|
||||
mCtx.leftFile = 0;
|
||||
mCtx.leftRec = 0;
|
||||
mCtx.leftSub = 0;
|
||||
mCtx.subCached = false;
|
||||
mCtx.recName.val = 0;
|
||||
mCtx.subName.val = 0;
|
||||
}
|
||||
|
||||
void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||
{
|
||||
close();
|
||||
mEsm = _esm;
|
||||
mCtx.filename = name;
|
||||
mCtx.leftFile = mEsm->size();
|
||||
|
||||
// Flag certain files for special treatment, based on the file
|
||||
// name.
|
||||
const char *cstr = mCtx.filename.c_str();
|
||||
if (iends(cstr, "Morrowind.esm"))
|
||||
mSpf = SF_Morrowind;
|
||||
else if (iends(cstr, "Tribunal.esm"))
|
||||
mSpf = SF_Tribunal;
|
||||
else if (iends(cstr, "Bloodmoon.esm"))
|
||||
mSpf = SF_Bloodmoon;
|
||||
else
|
||||
mSpf = SF_Other;
|
||||
}
|
||||
|
||||
void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||
{
|
||||
openRaw(_esm, name);
|
||||
|
||||
if (getRecName() != "TES3")
|
||||
fail("Not a valid Morrowind file");
|
||||
|
||||
getRecHeader();
|
||||
|
||||
// Get the header
|
||||
getHNT(mCtx.header, "HEDR", 300);
|
||||
|
||||
if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13)
|
||||
fail("Unsupported file format version");
|
||||
|
||||
while (isNextSub("MAST"))
|
||||
{
|
||||
MasterData m;
|
||||
m.name = getHString();
|
||||
m.size = getHNLong("DATA");
|
||||
mMasters.push_back(m);
|
||||
}
|
||||
|
||||
if (mCtx.header.type == FT_ESS)
|
||||
{
|
||||
// Savegame-related data
|
||||
|
||||
// Player position etc
|
||||
getHNT(mSaveData, "GMDT", 124);
|
||||
|
||||
/* Image properties, five ints. Is always:
|
||||
Red-mask: 0xff0000
|
||||
Blue-mask: 0x00ff00
|
||||
Green-mask: 0x0000ff
|
||||
Alpha-mask: 0x000000
|
||||
Bpp: 32
|
||||
*/
|
||||
getSubNameIs("SCRD");
|
||||
skipHSubSize(20);
|
||||
|
||||
/* Savegame screenshot:
|
||||
128x128 pixels * 4 bytes per pixel
|
||||
*/
|
||||
getSubNameIs("SCRS");
|
||||
skipHSubSize(65536);
|
||||
}
|
||||
}
|
||||
|
||||
void ESMReader::open(const std::string &file)
|
||||
{
|
||||
using namespace Mangle::Stream;
|
||||
open(StreamPtr(new FileStream(file)), file);
|
||||
}
|
||||
|
||||
void ESMReader::openRaw(const std::string &file)
|
||||
{
|
||||
using namespace Mangle::Stream;
|
||||
openRaw(StreamPtr(new FileStream(file)), file);
|
||||
}
|
||||
|
||||
int64_t ESMReader::getHNLong(const char *name)
|
||||
{
|
||||
int64_t val;
|
||||
getHNT(val, name);
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string ESMReader::getHNOString(const char* name)
|
||||
{
|
||||
if (isNextSub(name))
|
||||
return getHString();
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ESMReader::getHNString(const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
return getHString();
|
||||
}
|
||||
|
||||
std::string ESMReader::getHString()
|
||||
{
|
||||
getSubHeader();
|
||||
|
||||
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||
// occur in any of the official mods, but MultiMark makes use of
|
||||
// them. For some reason, they break the rules, and contain a byte
|
||||
// (value 0) even if the header says there is no data. If
|
||||
// Morrowind accepts it, so should we.
|
||||
if (mCtx.leftSub == 0)
|
||||
{
|
||||
// Skip the following zero byte
|
||||
mCtx.leftRec--;
|
||||
char c;
|
||||
mEsm->read(&c, 1);
|
||||
return "";
|
||||
}
|
||||
|
||||
return getString(mCtx.leftSub);
|
||||
}
|
||||
|
||||
void ESMReader::getHExact(void*p, int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if (size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getHExact() size mismatch");
|
||||
getExact(p, size);
|
||||
}
|
||||
|
||||
// Read the given number of bytes from a named subrecord
|
||||
void ESMReader::getHNExact(void*p, int size, const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
getHExact(p, size);
|
||||
}
|
||||
|
||||
// Get the next subrecord name and check if it matches the parameter
|
||||
void ESMReader::getSubNameIs(const char* name)
|
||||
{
|
||||
getSubName();
|
||||
if (mCtx.subName != name)
|
||||
fail(
|
||||
"Expected subrecord " + std::string(name) + " but got "
|
||||
+ mCtx.subName.toString());
|
||||
}
|
||||
|
||||
bool ESMReader::isNextSub(const char* name)
|
||||
{
|
||||
if (!mCtx.leftRec)
|
||||
return false;
|
||||
|
||||
getSubName();
|
||||
|
||||
// If the name didn't match, then mark the it as 'cached' so it's
|
||||
// available for the next call to getSubName.
|
||||
mCtx.subCached = (mCtx.subName != name);
|
||||
|
||||
// If subCached is false, then subName == name.
|
||||
return !mCtx.subCached;
|
||||
}
|
||||
|
||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||
// slightly.
|
||||
void ESMReader::getSubName()
|
||||
{
|
||||
// If the name has already been read, do nothing
|
||||
if (mCtx.subCached)
|
||||
{
|
||||
mCtx.subCached = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// reading the subrecord data anyway.
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
}
|
||||
|
||||
bool ESMReader::isEmptyOrGetName()
|
||||
{
|
||||
if (mCtx.leftRec)
|
||||
{
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ESMReader::skipHSub()
|
||||
{
|
||||
getSubHeader();
|
||||
skip(mCtx.leftSub);
|
||||
}
|
||||
|
||||
void ESMReader::skipHSubSize(int size)
|
||||
{
|
||||
skipHSub();
|
||||
if (static_cast<int> (mCtx.leftSub) != size)
|
||||
fail("skipHSubSize() mismatch");
|
||||
}
|
||||
|
||||
void ESMReader::getSubHeader()
|
||||
{
|
||||
if (mCtx.leftRec < 4)
|
||||
fail("End of record while reading sub-record header");
|
||||
|
||||
// Get subrecord size
|
||||
getT(mCtx.leftSub);
|
||||
|
||||
// Adjust number of record bytes left
|
||||
mCtx.leftRec -= mCtx.leftSub + 4;
|
||||
}
|
||||
|
||||
void ESMReader::getSubHeaderIs(int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if (size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getSubHeaderIs(): Sub header mismatch");
|
||||
}
|
||||
|
||||
NAME ESMReader::getRecName()
|
||||
{
|
||||
if (!hasMoreRecs())
|
||||
fail("No more records, getRecName() failed");
|
||||
getName(mCtx.recName);
|
||||
mCtx.leftFile -= 4;
|
||||
|
||||
// Make sure we don't carry over any old cached subrecord
|
||||
// names. This can happen in some cases when we skip parts of a
|
||||
// record.
|
||||
mCtx.subCached = false;
|
||||
|
||||
return mCtx.recName;
|
||||
}
|
||||
|
||||
void ESMReader::skipRecord()
|
||||
{
|
||||
skip(mCtx.leftRec);
|
||||
mCtx.leftRec = 0;
|
||||
}
|
||||
|
||||
void ESMReader::skipHRecord()
|
||||
{
|
||||
if (!mCtx.leftFile)
|
||||
return;
|
||||
getRecHeader();
|
||||
skipRecord();
|
||||
}
|
||||
|
||||
void ESMReader::getRecHeader(uint32_t &flags)
|
||||
{
|
||||
// General error checking
|
||||
if (mCtx.leftFile < 12)
|
||||
fail("End of file while reading record header");
|
||||
if (mCtx.leftRec)
|
||||
fail("Previous record contains unread bytes");
|
||||
|
||||
getUint(mCtx.leftRec);
|
||||
getUint(flags);// This header entry is always zero
|
||||
getUint(flags);
|
||||
mCtx.leftFile -= 12;
|
||||
|
||||
// Check that sizes add up
|
||||
if (mCtx.leftFile < mCtx.leftRec)
|
||||
fail("Record size is larger than rest of file");
|
||||
|
||||
// Adjust number of bytes mCtx.left in file
|
||||
mCtx.leftFile -= mCtx.leftRec;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
* Lowest level data reading and misc methods
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
void ESMReader::getExact(void*x, int size)
|
||||
{
|
||||
int t = mEsm->read(x, size);
|
||||
if (t != size)
|
||||
fail("Read error");
|
||||
}
|
||||
|
||||
std::string ESMReader::getString(int size)
|
||||
{
|
||||
char *ptr = ToUTF8::getBuffer(size);
|
||||
mEsm->read(ptr, size);
|
||||
|
||||
// Convert to UTF8 and return
|
||||
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
||||
}
|
||||
|
||||
void ESMReader::fail(const std::string &msg)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
stringstream ss;
|
||||
|
||||
ss << "ESM Error: " << msg;
|
||||
ss << "\n File: " << mCtx.filename;
|
||||
ss << "\n Record: " << mCtx.recName.toString();
|
||||
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||
if (mEsm != NULL)
|
||||
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
}
|
|
@ -171,122 +171,27 @@ public:
|
|||
/** Save the current file position and information in a ESM_Context
|
||||
struct
|
||||
*/
|
||||
ESM_Context getContext()
|
||||
{
|
||||
// Update the file position before returning
|
||||
mCtx.filePos = mEsm->tell();
|
||||
return mCtx;
|
||||
}
|
||||
ESM_Context getContext();
|
||||
|
||||
/** Restore a previously saved context */
|
||||
void restoreContext(const ESM_Context &rc)
|
||||
{
|
||||
// Reopen the file if necessary
|
||||
if(mCtx.filename != rc.filename)
|
||||
openRaw(rc.filename);
|
||||
|
||||
// Copy the data
|
||||
mCtx = rc;
|
||||
|
||||
// Make sure we seek to the right place
|
||||
mEsm->seek(mCtx.filePos);
|
||||
}
|
||||
void restoreContext(const ESM_Context &rc);
|
||||
|
||||
/** Close the file, resets all information. After calling close()
|
||||
the structure may be reused to load a new file.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
mEsm.reset();
|
||||
mCtx.filename.clear();
|
||||
mCtx.leftFile = 0;
|
||||
mCtx.leftRec = 0;
|
||||
mCtx.leftSub = 0;
|
||||
mCtx.subCached = false;
|
||||
mCtx.recName.val = 0;
|
||||
mCtx.subName.val = 0;
|
||||
}
|
||||
void close();
|
||||
|
||||
/// Raw opening. Opens the file and sets everything up but doesn't
|
||||
/// parse the header.
|
||||
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||
{
|
||||
close();
|
||||
mEsm = _esm;
|
||||
mCtx.filename = name;
|
||||
mCtx.leftFile = mEsm->size();
|
||||
|
||||
// Flag certain files for special treatment, based on the file
|
||||
// name.
|
||||
const char *cstr = mCtx.filename.c_str();
|
||||
if(iends(cstr, "Morrowind.esm")) mSpf = SF_Morrowind;
|
||||
else if(iends(cstr, "Tribunal.esm")) mSpf = SF_Tribunal;
|
||||
else if(iends(cstr, "Bloodmoon.esm")) mSpf = SF_Bloodmoon;
|
||||
else mSpf = SF_Other;
|
||||
}
|
||||
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name);
|
||||
|
||||
/// Load ES file from a new stream, parses the header. Closes the
|
||||
/// currently open file first, if any.
|
||||
void open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||
{
|
||||
openRaw(_esm, name);
|
||||
void open(Mangle::Stream::StreamPtr _esm, const std::string &name);
|
||||
|
||||
if(getRecName() != "TES3")
|
||||
fail("Not a valid Morrowind file");
|
||||
void open(const std::string &file);
|
||||
|
||||
getRecHeader();
|
||||
|
||||
// Get the header
|
||||
getHNT(mCtx.header, "HEDR", 300);
|
||||
|
||||
if(mCtx.header.version != VER_12 &&
|
||||
mCtx.header.version != VER_13)
|
||||
fail("Unsupported file format version");
|
||||
|
||||
while(isNextSub("MAST"))
|
||||
{
|
||||
MasterData m;
|
||||
m.name = getHString();
|
||||
m.size = getHNLong("DATA");
|
||||
mMasters.push_back(m);
|
||||
}
|
||||
|
||||
if(mCtx.header.type == FT_ESS)
|
||||
{
|
||||
// Savegame-related data
|
||||
|
||||
// Player position etc
|
||||
getHNT(mSaveData, "GMDT", 124);
|
||||
|
||||
/* Image properties, five ints. Is always:
|
||||
Red-mask: 0xff0000
|
||||
Blue-mask: 0x00ff00
|
||||
Green-mask: 0x0000ff
|
||||
Alpha-mask: 0x000000
|
||||
Bpp: 32
|
||||
*/
|
||||
getSubNameIs("SCRD");
|
||||
skipHSubSize(20);
|
||||
|
||||
/* Savegame screenshot:
|
||||
128x128 pixels * 4 bytes per pixel
|
||||
*/
|
||||
getSubNameIs("SCRS");
|
||||
skipHSubSize(65536);
|
||||
}
|
||||
}
|
||||
|
||||
void open(const std::string &file)
|
||||
{
|
||||
using namespace Mangle::Stream;
|
||||
open(StreamPtr(new FileStream(file)), file);
|
||||
}
|
||||
|
||||
void openRaw(const std::string &file)
|
||||
{
|
||||
using namespace Mangle::Stream;
|
||||
openRaw(StreamPtr(new FileStream(file)), file);
|
||||
}
|
||||
void openRaw(const std::string &file);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
|
@ -306,8 +211,8 @@ public:
|
|||
template <typename X>
|
||||
void getHNOT(X &x, const char* name)
|
||||
{
|
||||
if(isNextSub(name))
|
||||
getHT(x);
|
||||
if(isNextSub(name))
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
// Version with extra size checking, to make sure the compiler
|
||||
|
@ -315,26 +220,21 @@ public:
|
|||
template <typename X>
|
||||
void getHNT(X &x, const char* name, int size)
|
||||
{
|
||||
assert(sizeof(X) == size);
|
||||
getSubNameIs(name);
|
||||
getHT(x);
|
||||
assert(sizeof(X) == size);
|
||||
getSubNameIs(name);
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
int64_t getHNLong(const char *name)
|
||||
{
|
||||
int64_t val;
|
||||
getHNT(val, name);
|
||||
return val;
|
||||
}
|
||||
int64_t getHNLong(const char *name);
|
||||
|
||||
// Get data of a given type/size, including subrecord header
|
||||
template <typename X>
|
||||
void getHT(X &x)
|
||||
{
|
||||
getSubHeader();
|
||||
if(mCtx.leftSub != sizeof(X))
|
||||
fail("getHT(): subrecord size mismatch");
|
||||
getT(x);
|
||||
getSubHeader();
|
||||
if (mCtx.leftSub != sizeof(X))
|
||||
fail("getHT(): subrecord size mismatch");
|
||||
getT(x);
|
||||
}
|
||||
|
||||
// Version with extra size checking, to make sure the compiler
|
||||
|
@ -342,62 +242,24 @@ public:
|
|||
template <typename X>
|
||||
void getHT(X &x, int size)
|
||||
{
|
||||
assert(sizeof(X) == size);
|
||||
getHT(x);
|
||||
assert(sizeof(X) == size);
|
||||
getHT(x);
|
||||
}
|
||||
|
||||
// Read a string by the given name if it is the next record.
|
||||
std::string getHNOString(const char* name)
|
||||
{
|
||||
if(isNextSub(name))
|
||||
return getHString();
|
||||
return "";
|
||||
}
|
||||
std::string getHNOString(const char* name);
|
||||
|
||||
// Read a string with the given sub-record name
|
||||
std::string getHNString(const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
return getHString();
|
||||
}
|
||||
std::string getHNString(const char* name);
|
||||
|
||||
// Read a string, including the sub-record header (but not the name)
|
||||
std::string getHString()
|
||||
{
|
||||
getSubHeader();
|
||||
|
||||
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||
// occur in any of the official mods, but MultiMark makes use of
|
||||
// them. For some reason, they break the rules, and contain a byte
|
||||
// (value 0) even if the header says there is no data. If
|
||||
// Morrowind accepts it, so should we.
|
||||
if(mCtx.leftSub == 0)
|
||||
{
|
||||
// Skip the following zero byte
|
||||
mCtx.leftRec--;
|
||||
char c;
|
||||
mEsm->read(&c,1);
|
||||
return "";
|
||||
}
|
||||
|
||||
return getString(mCtx.leftSub);
|
||||
}
|
||||
std::string getHString();
|
||||
|
||||
// Read the given number of bytes from a subrecord
|
||||
void getHExact(void*p, int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if(size !=static_cast<int> (mCtx.leftSub))
|
||||
fail("getHExact() size mismatch");
|
||||
getExact(p,size);
|
||||
}
|
||||
void getHExact(void*p, int size);
|
||||
|
||||
// Read the given number of bytes from a named subrecord
|
||||
void getHNExact(void*p, int size, const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
getHExact(p,size);
|
||||
}
|
||||
void getHNExact(void*p, int size, const char* name);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
|
@ -406,100 +268,37 @@ public:
|
|||
*************************************************************************/
|
||||
|
||||
// Get the next subrecord name and check if it matches the parameter
|
||||
void getSubNameIs(const char* name)
|
||||
{
|
||||
getSubName();
|
||||
if(mCtx.subName != name)
|
||||
fail("Expected subrecord " + std::string(name) + " but got " + mCtx.subName.toString());
|
||||
}
|
||||
void getSubNameIs(const char* name);
|
||||
|
||||
/** Checks if the next sub record name matches the parameter. If it
|
||||
does, it is read into 'subName' just as if getSubName() was
|
||||
called. If not, the read name will still be available for future
|
||||
calls to getSubName(), isNextSub() and getSubNameIs().
|
||||
*/
|
||||
bool isNextSub(const char* name)
|
||||
{
|
||||
if(!mCtx.leftRec) return false;
|
||||
|
||||
getSubName();
|
||||
|
||||
// If the name didn't match, then mark the it as 'cached' so it's
|
||||
// available for the next call to getSubName.
|
||||
mCtx.subCached = (mCtx.subName != name);
|
||||
|
||||
// If subCached is false, then subName == name.
|
||||
return !mCtx.subCached;
|
||||
}
|
||||
bool isNextSub(const char* name);
|
||||
|
||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||
// slightly.
|
||||
void getSubName()
|
||||
{
|
||||
// If the name has already been read, do nothing
|
||||
if(mCtx.subCached)
|
||||
{
|
||||
mCtx.subCached = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't bother with error checking, we will catch an EOF upon
|
||||
// reading the subrecord data anyway.
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
}
|
||||
void getSubName();
|
||||
|
||||
// This is specially optimized for LoadINFO.
|
||||
bool isEmptyOrGetName()
|
||||
{
|
||||
if(mCtx.leftRec)
|
||||
{
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool isEmptyOrGetName();
|
||||
|
||||
// Skip current sub record, including header (but not including
|
||||
// name.)
|
||||
void skipHSub()
|
||||
{
|
||||
getSubHeader();
|
||||
skip(mCtx.leftSub);
|
||||
}
|
||||
void skipHSub();
|
||||
|
||||
// Skip sub record and check its size
|
||||
void skipHSubSize(int size)
|
||||
{
|
||||
skipHSub();
|
||||
if(static_cast<int> (mCtx.leftSub) != size)
|
||||
fail("skipHSubSize() mismatch");
|
||||
}
|
||||
void skipHSubSize(int size);
|
||||
|
||||
/* Sub-record header. This updates leftRec beyond the current
|
||||
sub-record as well. leftSub contains size of current sub-record.
|
||||
*/
|
||||
void getSubHeader()
|
||||
{
|
||||
if(mCtx.leftRec < 4)
|
||||
fail("End of record while reading sub-record header");
|
||||
|
||||
// Get subrecord size
|
||||
getT(mCtx.leftSub);
|
||||
|
||||
// Adjust number of record bytes left
|
||||
mCtx.leftRec -= mCtx.leftSub + 4;
|
||||
}
|
||||
void getSubHeader();
|
||||
|
||||
/** Get sub header and check the size
|
||||
*/
|
||||
void getSubHeaderIs(int size)
|
||||
{
|
||||
getSubHeader();
|
||||
if(size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getSubHeaderIs(): Sub header mismatch");
|
||||
}
|
||||
void getSubHeaderIs(int size);
|
||||
|
||||
/*************************************************************************
|
||||
*
|
||||
|
@ -508,62 +307,21 @@ public:
|
|||
*************************************************************************/
|
||||
|
||||
// Get the next record name
|
||||
NAME getRecName()
|
||||
{
|
||||
if(!hasMoreRecs())
|
||||
fail("No more records, getRecName() failed");
|
||||
getName(mCtx.recName);
|
||||
mCtx.leftFile -= 4;
|
||||
|
||||
// Make sure we don't carry over any old cached subrecord
|
||||
// names. This can happen in some cases when we skip parts of a
|
||||
// record.
|
||||
mCtx.subCached = false;
|
||||
|
||||
return mCtx.recName;
|
||||
}
|
||||
NAME getRecName();
|
||||
|
||||
// Skip the rest of this record. Assumes the name and header have
|
||||
// already been read
|
||||
void skipRecord()
|
||||
{
|
||||
skip(mCtx.leftRec);
|
||||
mCtx.leftRec = 0;
|
||||
}
|
||||
void skipRecord();
|
||||
|
||||
// Skip an entire record, including the header (but not the name)
|
||||
void skipHRecord()
|
||||
{
|
||||
if(!mCtx.leftFile) return;
|
||||
getRecHeader();
|
||||
skipRecord();
|
||||
}
|
||||
void skipHRecord();
|
||||
|
||||
/* Read record header. This updatesleftFile BEYOND the data that
|
||||
follows the header, ie beyond the entire record. You should use
|
||||
leftRec to orient yourself inside the record itself.
|
||||
*/
|
||||
void getRecHeader() { uint32_t u; getRecHeader(u); }
|
||||
void getRecHeader(uint32_t &flags)
|
||||
{
|
||||
// General error checking
|
||||
if(mCtx.leftFile < 12)
|
||||
fail("End of file while reading record header");
|
||||
if(mCtx.leftRec)
|
||||
fail("Previous record contains unread bytes");
|
||||
|
||||
getUint(mCtx.leftRec);
|
||||
getUint(flags);// This header entry is always zero
|
||||
getUint(flags);
|
||||
mCtx.leftFile -= 12;
|
||||
|
||||
// Check that sizes add up
|
||||
if(mCtx.leftFile < mCtx.leftRec)
|
||||
fail("Record size is larger than rest of file");
|
||||
|
||||
// Adjust number of bytes mCtx.left in file
|
||||
mCtx.leftFile -= mCtx.leftRec;
|
||||
}
|
||||
void getRecHeader(uint32_t &flags);
|
||||
|
||||
bool hasMoreRecs() { return mCtx.leftFile > 0; }
|
||||
bool hasMoreSubs() { return mCtx.leftRec > 0; }
|
||||
|
@ -578,44 +336,19 @@ public:
|
|||
template <typename X>
|
||||
void getT(X &x) { getExact(&x, sizeof(X)); }
|
||||
|
||||
void getExact(void*x, int size)
|
||||
{
|
||||
int t = mEsm->read(x, size);
|
||||
if(t != size)
|
||||
fail("Read error");
|
||||
}
|
||||
void getExact(void*x, int size);
|
||||
void getName(NAME &name) { getT(name); }
|
||||
void getUint(uint32_t &u) { getT(u); }
|
||||
|
||||
// Read the next 'size' bytes and return them as a string. Converts
|
||||
// them from native encoding to UTF8 in the process.
|
||||
std::string getString(int size)
|
||||
{
|
||||
char *ptr = ToUTF8::getBuffer(size);
|
||||
mEsm->read(ptr,size);
|
||||
|
||||
// Convert to UTF8 and return
|
||||
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
||||
}
|
||||
std::string getString(int size);
|
||||
|
||||
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
||||
uint64_t getOffset() { return mEsm->tell(); }
|
||||
|
||||
/// Used for error handling
|
||||
void fail(const std::string &msg)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
stringstream ss;
|
||||
|
||||
ss << "ESM Error: " << msg;
|
||||
ss << "\n File: " << mCtx.filename;
|
||||
ss << "\n Record: " << mCtx.recName.toString();
|
||||
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||
if(mEsm != NULL)
|
||||
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
void fail(const std::string &msg);
|
||||
|
||||
private:
|
||||
Mangle::Stream::StreamPtr mEsm;
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
#include "records.hpp"
|
||||
|
||||
/** Implementation for some of the load() functions. Most are found in
|
||||
the header files themselves, but this is a bit irritating to
|
||||
compile if you're changing the functions often, as virtually the
|
||||
entire engine depends on these headers.
|
||||
*/
|
||||
|
||||
/*
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
*/
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void NPC::load(ESMReader &esm, const std::string& id)
|
||||
{
|
||||
mId = id;
|
||||
|
||||
npdt52.gold = -10;
|
||||
|
||||
model = esm.getHNOString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
|
||||
race = esm.getHNString("RNAM");
|
||||
cls = esm.getHNString("CNAM");
|
||||
faction = esm.getHNString("ANAM");
|
||||
head = esm.getHNString("BNAM");
|
||||
hair = esm.getHNString("KNAM");
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getSubNameIs("NPDT");
|
||||
esm.getSubHeader();
|
||||
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
|
||||
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
|
||||
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
|
||||
|
||||
esm.getHNT(flags, "FLAG");
|
||||
|
||||
inventory.load(esm);
|
||||
spells.load(esm);
|
||||
|
||||
if(esm.isNextSub("AIDT"))
|
||||
{
|
||||
esm.getHExact(&AI, sizeof(AI));
|
||||
hasAI = true;
|
||||
}
|
||||
else hasAI = false;
|
||||
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void DialInfo::load(ESMReader &esm)
|
||||
{
|
||||
id = esm.getHNString("INAM");
|
||||
prev = esm.getHNString("PNAM");
|
||||
next = esm.getHNString("NNAM");
|
||||
|
||||
// Not present if deleted
|
||||
if(esm.isNextSub("DATA"))
|
||||
esm.getHT(data, 12);
|
||||
|
||||
// What follows is somewhat spaghetti-ish, but it's worth if for
|
||||
// an extra speedup. INFO is by far the most common record type.
|
||||
|
||||
// subName is a reference to the original, so it changes whenever
|
||||
// a new sub name is read. esm.isEmptyOrGetName() will get the
|
||||
// next name for us, or return true if there are no more records.
|
||||
esm.getSubName();
|
||||
const NAME &subName = esm.retSubName();
|
||||
|
||||
if(subName.val == REC_ONAM)
|
||||
{
|
||||
actor = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_RNAM)
|
||||
{
|
||||
race = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_CNAM)
|
||||
{
|
||||
clas = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
|
||||
factionLess = false;
|
||||
if(subName.val == REC_FNAM)
|
||||
{
|
||||
npcFaction = esm.getHString();
|
||||
if(npcFaction == "FFFF") factionLess = true;
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_ANAM)
|
||||
{
|
||||
cell = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_DNAM)
|
||||
{
|
||||
pcFaction = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_SNAM)
|
||||
{
|
||||
sound = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
if(subName.val == REC_NAME)
|
||||
{
|
||||
response = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
|
||||
while(subName.val == REC_SCVR)
|
||||
{
|
||||
SelectStruct ss;
|
||||
|
||||
ss.selectRule = esm.getHString();
|
||||
esm.isEmptyOrGetName();
|
||||
|
||||
if(subName.val == REC_INTV)
|
||||
{
|
||||
ss.type = VT_Int;
|
||||
esm.getHT(ss.i);
|
||||
}
|
||||
else if(subName.val == REC_FLTV)
|
||||
{
|
||||
ss.type = VT_Float;
|
||||
esm.getHT(ss.f);
|
||||
}
|
||||
else
|
||||
esm.fail("INFO.SCVR must precede INTV or FLTV, not "
|
||||
+ subName.toString());
|
||||
|
||||
selects.push_back(ss);
|
||||
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
|
||||
if(subName.val == REC_BNAM)
|
||||
{
|
||||
resultScript = esm.getHString();
|
||||
if(esm.isEmptyOrGetName()) return;
|
||||
}
|
||||
|
||||
questStatus = QS_None;
|
||||
|
||||
if (subName.val == REC_QSTN) questStatus = QS_Name;
|
||||
else if(subName.val == REC_QSTF) questStatus = QS_Finished;
|
||||
else if(subName.val == REC_QSTR) questStatus = QS_Restart;
|
||||
else if(subName.val == REC_DELE) questStatus = QS_Deleted;
|
||||
else
|
||||
esm.fail("Don't know what to do with " + subName.toString() + " in INFO " + id);
|
||||
|
||||
if(questStatus != QS_None)
|
||||
// Skip rest of record
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void Sound::load(ESMReader &esm)
|
||||
{
|
||||
sound = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "DATA", 3);
|
||||
/*
|
||||
cout << "vol=" << (int)data.volume
|
||||
<< " min=" << (int)data.minRange
|
||||
<< " max=" << (int)data.maxRange
|
||||
<< endl;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
11
components/esm/loadacti.cpp
Normal file
11
components/esm/loadacti.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "loadacti.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Activator::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
}
|
||||
}
|
|
@ -3,18 +3,14 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
struct Activator
|
||||
{
|
||||
std::string name, script, model;
|
||||
std::string name, script, model;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
14
components/esm/loadalch.cpp
Normal file
14
components/esm/loadalch.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "loadalch.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Potion::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||
script = esm.getHNOString("SCRI");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "ALDT", 12);
|
||||
effects.load(esm);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Alchemy item (potions)
|
||||
|
@ -12,26 +13,18 @@ namespace ESM {
|
|||
|
||||
struct Potion
|
||||
{
|
||||
struct ALDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int autoCalc;
|
||||
};
|
||||
ALDTstruct data;
|
||||
struct ALDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int autoCalc;
|
||||
};
|
||||
ALDTstruct data;
|
||||
|
||||
std::string name, model, icon, script;
|
||||
EffectList effects;
|
||||
std::string name, model, icon, script;
|
||||
EffectList effects;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||
script = esm.getHNOString("SCRI");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "ALDT", 12);
|
||||
effects.load(esm);
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
13
components/esm/loadappa.cpp
Normal file
13
components/esm/loadappa.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "loadappa.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Apparatus::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "AADT", 16);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNString("ITEX");
|
||||
}
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Alchemist apparatus
|
||||
|
@ -11,33 +12,26 @@ namespace ESM {
|
|||
|
||||
struct Apparatus
|
||||
{
|
||||
enum AppaType
|
||||
enum AppaType
|
||||
{
|
||||
MortarPestle = 0,
|
||||
Albemic = 1,
|
||||
Calcinator = 2,
|
||||
Retort = 3
|
||||
MortarPestle = 0,
|
||||
Albemic = 1,
|
||||
Calcinator = 2,
|
||||
Retort = 3
|
||||
};
|
||||
|
||||
struct AADTstruct
|
||||
{
|
||||
int type;
|
||||
float quality;
|
||||
float weight;
|
||||
int value;
|
||||
};
|
||||
struct AADTstruct
|
||||
{
|
||||
int type;
|
||||
float quality;
|
||||
float weight;
|
||||
int value;
|
||||
};
|
||||
|
||||
AADTstruct data;
|
||||
std::string model, icon, script, name;
|
||||
AADTstruct data;
|
||||
std::string model, icon, script, name;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "AADT", 16);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNString("ITEX");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
28
components/esm/loadarmo.cpp
Normal file
28
components/esm/loadarmo.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "loadarmo.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void PartReferenceList::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("INDX"))
|
||||
{
|
||||
PartReference pr;
|
||||
esm.getHT(pr.part); // The INDX byte
|
||||
pr.male = esm.getHNOString("BNAM");
|
||||
pr.female = esm.getHNOString("CNAM");
|
||||
}
|
||||
}
|
||||
|
||||
void Armor::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
esm.getHNT(data, "AODT", 24);
|
||||
icon = esm.getHNOString("ITEX");
|
||||
parts.load(esm);
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,102 +3,85 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
enum PartReferenceType
|
||||
{
|
||||
PRT_Head = 0,
|
||||
PRT_Hair = 1,
|
||||
PRT_Neck = 2,
|
||||
PRT_Cuirass = 3,
|
||||
PRT_Groin = 4,
|
||||
PRT_Skirt = 5,
|
||||
PRT_RHand = 6,
|
||||
PRT_LHand = 7,
|
||||
PRT_RWrist = 8,
|
||||
PRT_LWrist = 9,
|
||||
PRT_Shield = 10,
|
||||
PRT_RForearm = 11,
|
||||
PRT_LForearm = 12,
|
||||
PRT_RUpperarm = 13,
|
||||
PRT_LUpperarm = 14,
|
||||
PRT_RFoot = 15,
|
||||
PRT_LFoot = 16,
|
||||
PRT_RAnkle = 17,
|
||||
PRT_LAnkle = 18,
|
||||
PRT_RKnee = 19,
|
||||
PRT_LKnee = 20,
|
||||
PRT_RLeg = 21,
|
||||
PRT_LLeg = 22,
|
||||
PRT_RPauldron = 23,
|
||||
PRT_LPauldron = 24,
|
||||
PRT_Weapon = 25,
|
||||
PRT_Tail = 26
|
||||
};
|
||||
{
|
||||
PRT_Head = 0,
|
||||
PRT_Hair = 1,
|
||||
PRT_Neck = 2,
|
||||
PRT_Cuirass = 3,
|
||||
PRT_Groin = 4,
|
||||
PRT_Skirt = 5,
|
||||
PRT_RHand = 6,
|
||||
PRT_LHand = 7,
|
||||
PRT_RWrist = 8,
|
||||
PRT_LWrist = 9,
|
||||
PRT_Shield = 10,
|
||||
PRT_RForearm = 11,
|
||||
PRT_LForearm = 12,
|
||||
PRT_RUpperarm = 13,
|
||||
PRT_LUpperarm = 14,
|
||||
PRT_RFoot = 15,
|
||||
PRT_LFoot = 16,
|
||||
PRT_RAnkle = 17,
|
||||
PRT_LAnkle = 18,
|
||||
PRT_RKnee = 19,
|
||||
PRT_LKnee = 20,
|
||||
PRT_RLeg = 21,
|
||||
PRT_LLeg = 22,
|
||||
PRT_RPauldron = 23,
|
||||
PRT_LPauldron = 24,
|
||||
PRT_Weapon = 25,
|
||||
PRT_Tail = 26
|
||||
};
|
||||
|
||||
// Reference to body parts
|
||||
struct PartReference
|
||||
{
|
||||
char part;
|
||||
std::string male, female;
|
||||
char part;
|
||||
std::string male, female;
|
||||
};
|
||||
|
||||
// A list of references to body parts
|
||||
struct PartReferenceList
|
||||
{
|
||||
std::vector<PartReference> parts;
|
||||
std::vector<PartReference> parts;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
while(esm.isNextSub("INDX"))
|
||||
{
|
||||
PartReference pr;
|
||||
esm.getHT(pr.part); // The INDX byte
|
||||
pr.male = esm.getHNOString("BNAM");
|
||||
pr.female = esm.getHNOString("CNAM");
|
||||
}
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
|
||||
struct Armor
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Helmet = 0,
|
||||
Cuirass = 1,
|
||||
LPauldron = 2,
|
||||
RPauldron = 3,
|
||||
Greaves = 4,
|
||||
Boots = 5,
|
||||
LGauntlet = 6,
|
||||
RGauntlet = 7,
|
||||
Shield = 8,
|
||||
LBracer = 9,
|
||||
RBracer = 10
|
||||
};
|
||||
enum Type
|
||||
{
|
||||
Helmet = 0,
|
||||
Cuirass = 1,
|
||||
LPauldron = 2,
|
||||
RPauldron = 3,
|
||||
Greaves = 4,
|
||||
Boots = 5,
|
||||
LGauntlet = 6,
|
||||
RGauntlet = 7,
|
||||
Shield = 8,
|
||||
LBracer = 9,
|
||||
RBracer = 10
|
||||
};
|
||||
|
||||
struct AODTstruct
|
||||
{
|
||||
int type;
|
||||
float weight;
|
||||
int value, health, enchant, armor;
|
||||
};
|
||||
struct AODTstruct
|
||||
{
|
||||
int type;
|
||||
float weight;
|
||||
int value, health, enchant, armor;
|
||||
};
|
||||
|
||||
AODTstruct data;
|
||||
PartReferenceList parts;
|
||||
AODTstruct data;
|
||||
PartReferenceList parts;
|
||||
|
||||
std::string name, model, icon, script, enchant;
|
||||
std::string name, model, icon, script, enchant;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
esm.getHNT(data, "AODT", 24);
|
||||
icon = esm.getHNOString("ITEX");
|
||||
parts.load(esm);
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
13
components/esm/loadbody.cpp
Normal file
13
components/esm/loadbody.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "loadbody.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void BodyPart::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "BYDT", 4);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,59 +3,55 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
struct BodyPart
|
||||
{
|
||||
enum MeshPart
|
||||
enum MeshPart
|
||||
{
|
||||
MP_Head = 0,
|
||||
MP_Hair = 1,
|
||||
MP_Neck = 2,
|
||||
MP_Chest = 3,
|
||||
MP_Groin = 4,
|
||||
MP_Hand = 5,
|
||||
MP_Wrist = 6,
|
||||
MP_Forearm = 7,
|
||||
MP_Upperarm = 8,
|
||||
MP_Foot = 9,
|
||||
MP_Ankle = 10,
|
||||
MP_Knee = 11,
|
||||
MP_Upperleg = 12,
|
||||
MP_Clavicle = 13,
|
||||
MP_Tail = 14
|
||||
MP_Head = 0,
|
||||
MP_Hair = 1,
|
||||
MP_Neck = 2,
|
||||
MP_Chest = 3,
|
||||
MP_Groin = 4,
|
||||
MP_Hand = 5,
|
||||
MP_Wrist = 6,
|
||||
MP_Forearm = 7,
|
||||
MP_Upperarm = 8,
|
||||
MP_Foot = 9,
|
||||
MP_Ankle = 10,
|
||||
MP_Knee = 11,
|
||||
MP_Upperleg = 12,
|
||||
MP_Clavicle = 13,
|
||||
MP_Tail = 14
|
||||
};
|
||||
|
||||
enum Flags
|
||||
enum Flags
|
||||
{
|
||||
BPF_Female = 1,
|
||||
BPF_Playable = 2
|
||||
BPF_Female = 1,
|
||||
BPF_Playable = 2
|
||||
};
|
||||
|
||||
enum MeshType
|
||||
enum MeshType
|
||||
{
|
||||
MT_Skin = 0,
|
||||
MT_Clothing = 1,
|
||||
MT_Armor = 2
|
||||
MT_Skin = 0,
|
||||
MT_Clothing = 1,
|
||||
MT_Armor = 2
|
||||
};
|
||||
|
||||
struct BYDTstruct
|
||||
{
|
||||
char part;
|
||||
char vampire;
|
||||
char flags;
|
||||
char type;
|
||||
};
|
||||
struct BYDTstruct
|
||||
{
|
||||
char part;
|
||||
char vampire;
|
||||
char flags;
|
||||
char type;
|
||||
};
|
||||
|
||||
BYDTstruct data;
|
||||
std::string model, name;
|
||||
BYDTstruct data;
|
||||
std::string model, name;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "BYDT", 4);
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
17
components/esm/loadbook.cpp
Normal file
17
components/esm/loadbook.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "loadbook.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Book::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "BKDT", 20);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
text = esm.getHNOString("TEXT");
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Books, magic scrolls, notes and so on
|
||||
|
@ -11,25 +12,16 @@ namespace ESM {
|
|||
|
||||
struct Book
|
||||
{
|
||||
struct BKDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value, isScroll, skillID, enchant;
|
||||
};
|
||||
struct BKDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value, isScroll, skillID, enchant;
|
||||
};
|
||||
|
||||
BKDTstruct data;
|
||||
std::string name, model, icon, script, enchant, text;
|
||||
BKDTstruct data;
|
||||
std::string name, model, icon, script, enchant, text;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "BKDT", 20);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
text = esm.getHNOString("TEXT");
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
15
components/esm/loadbsgn.cpp
Normal file
15
components/esm/loadbsgn.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "loadbsgn.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void BirthSign::load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
texture = esm.getHNOString("TNAM");
|
||||
description = esm.getHNOString("DESC");
|
||||
|
||||
powers.load(esm);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,23 +4,17 @@
|
|||
#include "defs.hpp"
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
struct BirthSign
|
||||
{
|
||||
std::string name, description, texture;
|
||||
std::string name, description, texture;
|
||||
|
||||
// List of powers and abilities that come with this birth sign.
|
||||
SpellList powers;
|
||||
// List of powers and abilities that come with this birth sign.
|
||||
SpellList powers;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
texture = esm.getHNOString("TNAM");
|
||||
description = esm.getHNOString("DESC");
|
||||
|
||||
powers.load(esm);
|
||||
};
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
113
components/esm/loadcell.cpp
Normal file
113
components/esm/loadcell.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
#include "loadcell.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Cell::load(ESMReader &esm)
|
||||
{
|
||||
// Ignore this for now, it might mean we should delete the entire
|
||||
// cell?
|
||||
if (esm.isNextSub("DELE"))
|
||||
esm.skipHSub();
|
||||
|
||||
esm.getHNT(data, "DATA", 12);
|
||||
|
||||
// Water level
|
||||
water = 0;
|
||||
|
||||
if (data.flags & Interior)
|
||||
{
|
||||
// Interior cells
|
||||
|
||||
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
||||
esm.getHT(water);
|
||||
|
||||
// Quasi-exterior cells have a region (which determines the
|
||||
// weather), pure interior cells have ambient lighting
|
||||
// instead.
|
||||
if (data.flags & QuasiEx)
|
||||
region = esm.getHNOString("RGNN");
|
||||
else
|
||||
esm.getHNT(ambi, "AMBI", 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exterior cells
|
||||
region = esm.getHNOString("RGNN");
|
||||
esm.getHNOT(mapColor, "NAM5");
|
||||
}
|
||||
|
||||
// Save position of the cell references and move on
|
||||
context = esm.getContext();
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void Cell::restore(ESMReader &esm) const
|
||||
{
|
||||
esm.restoreContext(context);
|
||||
}
|
||||
|
||||
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||
{
|
||||
if (!esm.hasMoreSubs())
|
||||
return false;
|
||||
|
||||
// Number of references in the cell? Maximum once in each cell,
|
||||
// but not always at the beginning, and not always right. In other
|
||||
// words, completely useless.
|
||||
{
|
||||
int i;
|
||||
esm.getHNOT(i, "NAM0");
|
||||
}
|
||||
|
||||
esm.getHNT(ref.refnum, "FRMR");
|
||||
ref.refID = esm.getHNString("NAME");
|
||||
|
||||
// getHNOT will not change the existing value if the subrecord is
|
||||
// missing
|
||||
ref.scale = 1.0;
|
||||
esm.getHNOT(ref.scale, "XSCL");
|
||||
|
||||
ref.owner = esm.getHNOString("ANAM");
|
||||
ref.glob = esm.getHNOString("BNAM");
|
||||
ref.soul = esm.getHNOString("XSOL");
|
||||
|
||||
ref.faction = esm.getHNOString("CNAM");
|
||||
ref.factIndex = -1;
|
||||
esm.getHNOT(ref.factIndex, "INDX");
|
||||
|
||||
ref.charge = -1.0;
|
||||
esm.getHNOT(ref.charge, "XCHG");
|
||||
|
||||
ref.intv = 0;
|
||||
ref.nam9 = 0;
|
||||
esm.getHNOT(ref.intv, "INTV");
|
||||
esm.getHNOT(ref.nam9, "NAM9");
|
||||
|
||||
// Present for doors that teleport you to another cell.
|
||||
if (esm.isNextSub("DODT"))
|
||||
{
|
||||
ref.teleport = true;
|
||||
esm.getHT(ref.doorDest);
|
||||
ref.destCell = esm.getHNOString("DNAM");
|
||||
}
|
||||
else
|
||||
ref.teleport = false;
|
||||
|
||||
// Integer, despite the name suggesting otherwise
|
||||
ref.lockLevel = 0;
|
||||
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||
ref.key = esm.getHNOString("KNAM");
|
||||
ref.trap = esm.getHNOString("TNAM");
|
||||
|
||||
ref.unam = 0;
|
||||
ref.fltv = 0;
|
||||
esm.getHNOT(ref.unam, "UNAM");
|
||||
esm.getHNOT(ref.fltv, "FLTV");
|
||||
|
||||
esm.getHNT(ref.pos, "DATA", 24);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -117,51 +117,14 @@ struct Cell
|
|||
int water; // Water level
|
||||
int mapColor;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
// Ignore this for now, it might mean we should delete the entire
|
||||
// cell?
|
||||
if(esm.isNextSub("DELE")) esm.skipHSub();
|
||||
|
||||
esm.getHNT(data, "DATA", 12);
|
||||
|
||||
// Water level
|
||||
water = 0;
|
||||
|
||||
if(data.flags & Interior)
|
||||
{
|
||||
// Interior cells
|
||||
|
||||
if(esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
||||
esm.getHT(water);
|
||||
|
||||
// Quasi-exterior cells have a region (which determines the
|
||||
// weather), pure interior cells have ambient lighting
|
||||
// instead.
|
||||
if(data.flags & QuasiEx)
|
||||
region = esm.getHNOString("RGNN");
|
||||
else
|
||||
esm.getHNT(ambi, "AMBI", 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exterior cells
|
||||
region = esm.getHNOString("RGNN");
|
||||
esm.getHNOT(mapColor, "NAM5");
|
||||
}
|
||||
|
||||
// Save position of the cell references and move on
|
||||
context = esm.getContext();
|
||||
esm.skipRecord();
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
|
||||
// Restore the given reader to the stored position. Will try to open
|
||||
// the file matching the stored file name. If you want to read from
|
||||
// somewhere other than the file system, you need to pre-open the
|
||||
// ESMReader, and the filename must match the stored filename
|
||||
// exactly.
|
||||
void restore(ESMReader &esm) const
|
||||
{ esm.restoreContext(context); }
|
||||
void restore(ESMReader &esm) const;
|
||||
|
||||
/* Get the next reference in this cell, if any. Returns false when
|
||||
there are no more references in the cell.
|
||||
|
@ -169,66 +132,7 @@ struct Cell
|
|||
All fields of the CellRef struct are overwritten. You can safely
|
||||
reuse one memory location without blanking it between calls.
|
||||
*/
|
||||
static bool getNextRef(ESMReader &esm, CellRef &ref)
|
||||
{
|
||||
if(!esm.hasMoreSubs()) return false;
|
||||
|
||||
// Number of references in the cell? Maximum once in each cell,
|
||||
// but not always at the beginning, and not always right. In other
|
||||
// words, completely useless.
|
||||
{
|
||||
int i;
|
||||
esm.getHNOT(i, "NAM0");
|
||||
}
|
||||
|
||||
esm.getHNT(ref.refnum, "FRMR");
|
||||
ref.refID = esm.getHNString("NAME");
|
||||
|
||||
// getHNOT will not change the existing value if the subrecord is
|
||||
// missing
|
||||
ref.scale = 1.0;
|
||||
esm.getHNOT(ref.scale, "XSCL");
|
||||
|
||||
ref.owner = esm.getHNOString("ANAM");
|
||||
ref.glob = esm.getHNOString("BNAM");
|
||||
ref.soul = esm.getHNOString("XSOL");
|
||||
|
||||
ref.faction = esm.getHNOString("CNAM");
|
||||
ref.factIndex = -1;
|
||||
esm.getHNOT(ref.factIndex, "INDX");
|
||||
|
||||
ref.charge = -1.0;
|
||||
esm.getHNOT(ref.charge, "XCHG");
|
||||
|
||||
ref.intv = 0;
|
||||
ref.nam9 = 0;
|
||||
esm.getHNOT(ref.intv, "INTV");
|
||||
esm.getHNOT(ref.nam9, "NAM9");
|
||||
|
||||
// Present for doors that teleport you to another cell.
|
||||
if(esm.isNextSub("DODT"))
|
||||
{
|
||||
ref.teleport = true;
|
||||
esm.getHT(ref.doorDest);
|
||||
ref.destCell = esm.getHNOString("DNAM");
|
||||
}
|
||||
else ref.teleport = false;
|
||||
|
||||
// Integer, despite the name suggesting otherwise
|
||||
ref.lockLevel = 0;
|
||||
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||
ref.key = esm.getHNOString("KNAM");
|
||||
ref.trap = esm.getHNOString("TNAM");
|
||||
|
||||
ref.unam = 0;
|
||||
ref.fltv = 0;
|
||||
esm.getHNOT(ref.unam, "UNAM");
|
||||
esm.getHNOT(ref.fltv, "FLTV");
|
||||
|
||||
esm.getHNT(ref.pos, "DATA", 24);
|
||||
|
||||
return true;
|
||||
}
|
||||
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "loadclas.hpp"
|
||||
|
||||
using namespace ESM;
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
const Class::Specialization Class::specializationIds[3] = {
|
||||
Class::Combat,
|
||||
|
@ -13,3 +14,16 @@ const char *Class::gmstSpecializationIds[3] = {
|
|||
"sSpecializationMagic",
|
||||
"sSpecializationStealth"
|
||||
};
|
||||
|
||||
void Class::load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "CLDT", 60);
|
||||
|
||||
if (data.isPlayable > 1)
|
||||
esm.fail("Unknown bool value");
|
||||
|
||||
description = esm.getHNOString("DESC");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Character class definitions
|
||||
|
@ -13,62 +14,53 @@ namespace ESM {
|
|||
// class
|
||||
struct Class
|
||||
{
|
||||
enum AutoCalc
|
||||
enum AutoCalc
|
||||
{
|
||||
Weapon = 0x00001,
|
||||
Armor = 0x00002,
|
||||
Clothing = 0x00004,
|
||||
Books = 0x00008,
|
||||
Ingredient = 0x00010,
|
||||
Lockpick = 0x00020,
|
||||
Probe = 0x00040,
|
||||
Lights = 0x00080,
|
||||
Apparatus = 0x00100,
|
||||
Repair = 0x00200,
|
||||
Misc = 0x00400,
|
||||
Spells = 0x00800,
|
||||
MagicItems = 0x01000,
|
||||
Potions = 0x02000,
|
||||
Training = 0x04000,
|
||||
Spellmaking = 0x08000,
|
||||
Enchanting = 0x10000,
|
||||
RepairItem = 0x20000
|
||||
Weapon = 0x00001,
|
||||
Armor = 0x00002,
|
||||
Clothing = 0x00004,
|
||||
Books = 0x00008,
|
||||
Ingredient = 0x00010,
|
||||
Lockpick = 0x00020,
|
||||
Probe = 0x00040,
|
||||
Lights = 0x00080,
|
||||
Apparatus = 0x00100,
|
||||
Repair = 0x00200,
|
||||
Misc = 0x00400,
|
||||
Spells = 0x00800,
|
||||
MagicItems = 0x01000,
|
||||
Potions = 0x02000,
|
||||
Training = 0x04000,
|
||||
Spellmaking = 0x08000,
|
||||
Enchanting = 0x10000,
|
||||
RepairItem = 0x20000
|
||||
};
|
||||
|
||||
enum Specialization
|
||||
enum Specialization
|
||||
{
|
||||
Combat = 0,
|
||||
Magic = 1,
|
||||
Stealth = 2
|
||||
Combat = 0,
|
||||
Magic = 1,
|
||||
Stealth = 2
|
||||
};
|
||||
|
||||
static const Specialization specializationIds[3];
|
||||
static const char *gmstSpecializationIds[3];
|
||||
static const Specialization specializationIds[3];
|
||||
static const char *gmstSpecializationIds[3];
|
||||
|
||||
struct CLDTstruct
|
||||
{
|
||||
int attribute[2]; // Attributes that get class bonus
|
||||
int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
|
||||
int skills[5][2]; // Minor and major skills.
|
||||
int isPlayable; // 0x0001 - Playable class
|
||||
|
||||
// I have no idea how to autocalculate these items...
|
||||
int calc;
|
||||
}; // 60 bytes
|
||||
|
||||
std::string name, description;
|
||||
CLDTstruct data;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
struct CLDTstruct
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "CLDT", 60);
|
||||
int attribute[2]; // Attributes that get class bonus
|
||||
int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
|
||||
int skills[5][2]; // Minor and major skills.
|
||||
int isPlayable; // 0x0001 - Playable class
|
||||
|
||||
if(data.isPlayable > 1)
|
||||
esm.fail("Unknown bool value");
|
||||
// I have no idea how to autocalculate these items...
|
||||
int calc;
|
||||
}; // 60 bytes
|
||||
|
||||
description = esm.getHNOString("DESC");
|
||||
}
|
||||
std::string name, description;
|
||||
CLDTstruct data;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
20
components/esm/loadclot.cpp
Normal file
20
components/esm/loadclot.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "loadclot.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Clothing::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "CTDT", 12);
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
|
||||
parts.load(esm);
|
||||
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "loadarmo.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Clothing
|
||||
|
@ -12,46 +13,34 @@ namespace ESM {
|
|||
|
||||
struct Clothing
|
||||
{
|
||||
enum Type
|
||||
enum Type
|
||||
{
|
||||
Pants = 0,
|
||||
Shoes = 1,
|
||||
Shirt = 2,
|
||||
Belt = 3,
|
||||
Robe = 4,
|
||||
RGlove = 5,
|
||||
LGlove = 6,
|
||||
Skirt = 7,
|
||||
Ring = 8,
|
||||
Amulet = 9
|
||||
Pants = 0,
|
||||
Shoes = 1,
|
||||
Shirt = 2,
|
||||
Belt = 3,
|
||||
Robe = 4,
|
||||
RGlove = 5,
|
||||
LGlove = 6,
|
||||
Skirt = 7,
|
||||
Ring = 8,
|
||||
Amulet = 9
|
||||
};
|
||||
|
||||
struct CTDTstruct
|
||||
{
|
||||
int type;
|
||||
float weight;
|
||||
short value;
|
||||
short enchant;
|
||||
};
|
||||
CTDTstruct data;
|
||||
struct CTDTstruct
|
||||
{
|
||||
int type;
|
||||
float weight;
|
||||
short value;
|
||||
short enchant;
|
||||
};
|
||||
CTDTstruct data;
|
||||
|
||||
PartReferenceList parts;
|
||||
PartReferenceList parts;
|
||||
|
||||
std::string name, model, icon, enchant, script;
|
||||
std::string name, model, icon, enchant, script;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(data, "CTDT", 12);
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
|
||||
parts.load(esm);
|
||||
|
||||
enchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
33
components/esm/loadcont.cpp
Normal file
33
components/esm/loadcont.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "loadcont.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void InventoryList::load(ESMReader &esm)
|
||||
{
|
||||
ContItem ci;
|
||||
while (esm.isNextSub("NPCO"))
|
||||
{
|
||||
esm.getHT(ci, 36);
|
||||
list.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(weight, "CNDT", 4);
|
||||
esm.getHNT(flags, "FLAG", 4);
|
||||
|
||||
if (flags & 0xf4)
|
||||
esm.fail("Unknown flags");
|
||||
if (!(flags & 0x8))
|
||||
esm.fail("Flag 8 not set");
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
|
||||
inventory.load(esm);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Container definition
|
||||
|
@ -11,54 +12,33 @@ namespace ESM {
|
|||
|
||||
struct ContItem
|
||||
{
|
||||
int count;
|
||||
NAME32 item;
|
||||
int count;
|
||||
NAME32 item;
|
||||
};
|
||||
|
||||
struct InventoryList
|
||||
{
|
||||
std::vector<ContItem> list;
|
||||
std::vector<ContItem> list;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
ContItem ci;
|
||||
while(esm.isNextSub("NPCO"))
|
||||
{
|
||||
esm.getHT(ci, 36);
|
||||
list.push_back(ci);
|
||||
}
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
|
||||
struct Container
|
||||
{
|
||||
enum Flags
|
||||
enum Flags
|
||||
{
|
||||
Organic = 1, // Objects cannot be placed in this container
|
||||
Respawn = 2, // Respawns after 4 months
|
||||
Unknown = 8
|
||||
Organic = 1, // Objects cannot be placed in this container
|
||||
Respawn = 2, // Respawns after 4 months
|
||||
Unknown = 8
|
||||
};
|
||||
|
||||
std::string name, model, script;
|
||||
std::string name, model, script;
|
||||
|
||||
float weight; // Not sure, might be max total weight allowed?
|
||||
int flags;
|
||||
InventoryList inventory;
|
||||
float weight; // Not sure, might be max total weight allowed?
|
||||
int flags;
|
||||
InventoryList inventory;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
esm.getHNT(weight, "CNDT", 4);
|
||||
esm.getHNT(flags, "FLAG", 4);
|
||||
|
||||
if(flags & 0xf4) esm.fail("Unknown flags");
|
||||
if(!(flags & 0x8)) esm.fail("Flag 8 not set");
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
|
||||
inventory.load(esm);
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
40
components/esm/loadcrea.cpp
Normal file
40
components/esm/loadcrea.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "loadcrea.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
void Creature::load(ESMReader &esm, const std::string& id)
|
||||
{
|
||||
mId = id;
|
||||
|
||||
model = esm.getHNString("MODL");
|
||||
original = esm.getHNOString("CNAM");
|
||||
name = esm.getHNOString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getHNT(data, "NPDT", 96);
|
||||
|
||||
esm.getHNT(flags, "FLAG");
|
||||
scale = 1.0;
|
||||
esm.getHNOT(scale, "XSCL");
|
||||
|
||||
inventory.load(esm);
|
||||
|
||||
// More subrecords:
|
||||
|
||||
// AIDT - data (12 bytes, unknown)
|
||||
// AI_W - wander (14 bytes, i don't understand it)
|
||||
// short distance
|
||||
// byte duration
|
||||
// byte timeOfDay
|
||||
// byte idle[10]
|
||||
//
|
||||
// Rest is optional:
|
||||
// AI_T - travel?
|
||||
// AI_F - follow?
|
||||
// AI_E - escort?
|
||||
// AI_A - activate?
|
||||
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "loadcont.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Creature definition
|
||||
|
@ -13,91 +14,56 @@ namespace ESM {
|
|||
|
||||
struct Creature
|
||||
{
|
||||
// Default is 0x48?
|
||||
enum Flags
|
||||
// Default is 0x48?
|
||||
enum Flags
|
||||
{
|
||||
Biped = 0x001,
|
||||
Respawn = 0x002,
|
||||
Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
Swims = 0x010,
|
||||
Flies = 0x020, // Don't know what happens if several
|
||||
Walks = 0x040, // of these are set
|
||||
Essential = 0x080,
|
||||
Skeleton = 0x400, // Does not have normal blood
|
||||
Metal = 0x800 // Has 'golden' blood
|
||||
Biped = 0x001, Respawn = 0x002, Weapon = 0x004, // Has weapon and shield
|
||||
None = 0x008, // ??
|
||||
Swims = 0x010,
|
||||
Flies = 0x020, // Don't know what happens if several
|
||||
Walks = 0x040, // of these are set
|
||||
Essential = 0x080,
|
||||
Skeleton = 0x400, // Does not have normal blood
|
||||
Metal = 0x800
|
||||
// Has 'golden' blood
|
||||
};
|
||||
|
||||
enum Type
|
||||
enum Type
|
||||
{
|
||||
Creatures = 0,
|
||||
Deadra = 1,
|
||||
Undead = 2,
|
||||
Humanoid = 3
|
||||
Creatures = 0,
|
||||
Deadra = 1,
|
||||
Undead = 2,
|
||||
Humanoid = 3
|
||||
};
|
||||
|
||||
struct NPDTstruct
|
||||
{
|
||||
int type;
|
||||
// For creatures we obviously have to use ints, not shorts and
|
||||
// bytes like we use for NPCs.... this file format just makes so
|
||||
// much sense! (Still, _much_ easier to decode than the NIFs.)
|
||||
int level;
|
||||
int strength, intelligence, willpower, agility, speed, endurance,
|
||||
personality, luck, health, mana, fatigue; // Stats
|
||||
int soul; // The creatures soul value (used with soul gems.)
|
||||
int combat, magic, stealth; // Don't know yet.
|
||||
int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3
|
||||
int gold;
|
||||
}; // 96 bytes
|
||||
struct NPDTstruct
|
||||
{
|
||||
int type;
|
||||
// For creatures we obviously have to use ints, not shorts and
|
||||
// bytes like we use for NPCs.... this file format just makes so
|
||||
// much sense! (Still, _much_ easier to decode than the NIFs.)
|
||||
int level;
|
||||
int strength, intelligence, willpower, agility, speed, endurance,
|
||||
personality, luck, health, mana, fatigue; // Stats
|
||||
int soul; // The creatures soul value (used with soul gems.)
|
||||
int combat, magic, stealth; // Don't know yet.
|
||||
int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3
|
||||
int gold;
|
||||
}; // 96 bytes
|
||||
|
||||
NPDTstruct data;
|
||||
NPDTstruct data;
|
||||
|
||||
int flags;
|
||||
float scale;
|
||||
int flags;
|
||||
float scale;
|
||||
|
||||
std::string model, name, script,
|
||||
original; // Base creature that this is a modification of
|
||||
std::string model, name, script, original; // Base creature that this is a modification of
|
||||
|
||||
// Defined in loadcont.hpp
|
||||
InventoryList inventory;
|
||||
// Defined in loadcont.hpp
|
||||
InventoryList inventory;
|
||||
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm, const std::string& id)
|
||||
{
|
||||
mId = id;
|
||||
|
||||
model = esm.getHNString("MODL");
|
||||
original = esm.getHNOString("CNAM");
|
||||
name = esm.getHNOString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getHNT(data, "NPDT", 96);
|
||||
|
||||
esm.getHNT(flags, "FLAG");
|
||||
scale = 1.0;
|
||||
esm.getHNOT(scale, "XSCL");
|
||||
|
||||
inventory.load(esm);
|
||||
|
||||
// More subrecords:
|
||||
|
||||
// AIDT - data (12 bytes, unknown)
|
||||
// AI_W - wander (14 bytes, i don't understand it)
|
||||
// short distance
|
||||
// byte duration
|
||||
// byte timeOfDay
|
||||
// byte idle[10]
|
||||
//
|
||||
// Rest is optional:
|
||||
// AI_T - travel?
|
||||
// AI_F - follow?
|
||||
// AI_E - escort?
|
||||
// AI_A - activate?
|
||||
|
||||
esm.skipRecord();
|
||||
}
|
||||
void load(ESMReader &esm, const std::string& id);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
25
components/esm/loaddial.cpp
Normal file
25
components/esm/loaddial.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "loaddial.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Dialogue::load(ESMReader &esm)
|
||||
{
|
||||
esm.getSubNameIs("DATA");
|
||||
esm.getSubHeader();
|
||||
int si = esm.getSubSize();
|
||||
if (si == 1)
|
||||
esm.getT(type);
|
||||
else if (si == 4)
|
||||
{
|
||||
// These are just markers, their values are not used.
|
||||
int i;
|
||||
esm.getT(i);
|
||||
esm.getHNT(i, "DELE");
|
||||
type = Deleted;
|
||||
}
|
||||
else
|
||||
esm.fail("Unknown sub record size");
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "loadinfo.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Dialogue topic and journal entries. The actual data is contained in
|
||||
|
@ -15,36 +16,20 @@ namespace ESM {
|
|||
|
||||
struct Dialogue
|
||||
{
|
||||
enum Type
|
||||
enum Type
|
||||
{
|
||||
Topic = 0,
|
||||
Voice = 1,
|
||||
Greeting = 2,
|
||||
Persuasion = 3,
|
||||
Journal = 4,
|
||||
Deleted = -1
|
||||
Topic = 0,
|
||||
Voice = 1,
|
||||
Greeting = 2,
|
||||
Persuasion = 3,
|
||||
Journal = 4,
|
||||
Deleted = -1
|
||||
};
|
||||
|
||||
char type;
|
||||
std::vector<DialInfo> mInfo;
|
||||
char type;
|
||||
std::vector<DialInfo> mInfo;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.getSubNameIs("DATA");
|
||||
esm.getSubHeader();
|
||||
int si = esm.getSubSize();
|
||||
if(si == 1)
|
||||
esm.getT(type);
|
||||
else if(si == 4)
|
||||
{
|
||||
// These are just markers, their values are not used.
|
||||
int i;
|
||||
esm.getT(i);
|
||||
esm.getHNT(i,"DELE");
|
||||
type = Deleted;
|
||||
}
|
||||
else esm.fail("Unknown sub record size");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
15
components/esm/loaddoor.cpp
Normal file
15
components/esm/loaddoor.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "loaddoor.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Door::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
openSound = esm.getHNOString("SNAM");
|
||||
closeSound = esm.getHNOString("ANAM");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,20 +3,14 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
struct Door
|
||||
{
|
||||
std::string name, model, script, openSound, closeSound;
|
||||
std::string name, model, script, openSound, closeSound;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
script = esm.getHNOString("SCRI");
|
||||
openSound = esm.getHNOString("SNAM");
|
||||
closeSound = esm.getHNOString("ANAM");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
12
components/esm/loadench.cpp
Normal file
12
components/esm/loadench.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "loadench.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Enchantment::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(data, "ENDT", 16);
|
||||
effects.load(esm);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
#define _ESM_ENCH_H
|
||||
|
||||
#include "esm_reader.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Enchantments
|
||||
|
@ -11,31 +13,27 @@ namespace ESM {
|
|||
|
||||
struct Enchantment
|
||||
{
|
||||
enum Type
|
||||
enum Type
|
||||
{
|
||||
CastOnce = 0,
|
||||
WhenStrikes = 1,
|
||||
WhenUsed = 2,
|
||||
ConstantEffect = 3
|
||||
CastOnce = 0,
|
||||
WhenStrikes = 1,
|
||||
WhenUsed = 2,
|
||||
ConstantEffect = 3
|
||||
};
|
||||
|
||||
struct ENDTstruct
|
||||
{
|
||||
int type;
|
||||
int cost;
|
||||
int charge;
|
||||
int autocalc; // Guessing this is 1 if we are supposed to auto
|
||||
// calculate
|
||||
};
|
||||
struct ENDTstruct
|
||||
{
|
||||
int type;
|
||||
int cost;
|
||||
int charge;
|
||||
int autocalc; // Guessing this is 1 if we are supposed to auto
|
||||
// calculate
|
||||
};
|
||||
|
||||
ENDTstruct data;
|
||||
EffectList effects;
|
||||
ENDTstruct data;
|
||||
EffectList effects;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(data, "ENDT", 16);
|
||||
effects.load(esm);
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
31
components/esm/loadfact.cpp
Normal file
31
components/esm/loadfact.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "loadfact.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Faction::load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
|
||||
// Read rank names. These are optional.
|
||||
int i = 0;
|
||||
while (esm.isNextSub("RNAM") && i < 10)
|
||||
ranks[i++] = esm.getHString();
|
||||
|
||||
// Main data struct
|
||||
esm.getHNT(data, "FADT", 240);
|
||||
|
||||
if (data.isHidden > 1)
|
||||
esm.fail("Unknown flag!");
|
||||
|
||||
// Read faction response values
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
Reaction r;
|
||||
r.faction = esm.getHNString("ANAM");
|
||||
esm.getHNT(r.reaction, "INTV");
|
||||
reactions.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Faction definitions
|
||||
|
@ -12,67 +13,46 @@ namespace ESM {
|
|||
// Requirements for each rank
|
||||
struct RankData
|
||||
{
|
||||
int attribute1, attribute2; // Attribute level
|
||||
int attribute1, attribute2; // Attribute level
|
||||
|
||||
int skill1, skill2; // Skill level (faction skills given in
|
||||
// skillID below.) You need one skill at
|
||||
// level 'skill1' and two skills at level
|
||||
// 'skill2' to advance to this rank.
|
||||
int skill1, skill2; // Skill level (faction skills given in
|
||||
// skillID below.) You need one skill at
|
||||
// level 'skill1' and two skills at level
|
||||
// 'skill2' to advance to this rank.
|
||||
|
||||
int factReaction; // Reaction from faction members
|
||||
int factReaction; // Reaction from faction members
|
||||
};
|
||||
|
||||
struct Faction
|
||||
{
|
||||
std::string id, name;
|
||||
std::string id, name;
|
||||
|
||||
struct FADTstruct
|
||||
{
|
||||
// Which attributes we like
|
||||
int attribute1, attribute2;
|
||||
struct FADTstruct
|
||||
{
|
||||
// Which attributes we like
|
||||
int attribute1, attribute2;
|
||||
|
||||
RankData rankData[10];
|
||||
RankData rankData[10];
|
||||
|
||||
int skillID[6]; // IDs of skills this faction require
|
||||
int unknown; // Always -1?
|
||||
int isHidden; // 1 - hidden from player
|
||||
}; // 240 bytes
|
||||
int skillID[6]; // IDs of skills this faction require
|
||||
int unknown; // Always -1?
|
||||
int isHidden; // 1 - hidden from player
|
||||
}; // 240 bytes
|
||||
|
||||
FADTstruct data;
|
||||
FADTstruct data;
|
||||
|
||||
struct Reaction
|
||||
{
|
||||
std::string faction;
|
||||
int reaction;
|
||||
};
|
||||
struct Reaction
|
||||
{
|
||||
std::string faction;
|
||||
int reaction;
|
||||
};
|
||||
|
||||
std::vector<Reaction> reactions;
|
||||
std::vector<Reaction> reactions;
|
||||
|
||||
// Name of faction ranks (may be empty for NPC factions)
|
||||
std::string ranks[10];
|
||||
// Name of faction ranks (may be empty for NPC factions)
|
||||
std::string ranks[10];
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
name = esm.getHNString("FNAM");
|
||||
|
||||
// Read rank names. These are optional.
|
||||
int i = 0;
|
||||
while(esm.isNextSub("RNAM") && i<10) ranks[i++] = esm.getHString();
|
||||
|
||||
// Main data struct
|
||||
esm.getHNT(data, "FADT", 240);
|
||||
|
||||
if(data.isHidden > 1) esm.fail("Unknown flag!");
|
||||
|
||||
// Read faction response values
|
||||
while(esm.hasMoreSubs())
|
||||
{
|
||||
Reaction r;
|
||||
r.faction = esm.getHNString("ANAM");
|
||||
esm.getHNT(r.reaction, "INTV");
|
||||
reactions.push_back(r);
|
||||
}
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
24
components/esm/loadglob.cpp
Normal file
24
components/esm/loadglob.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "loadglob.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Global::load(ESMReader &esm)
|
||||
{
|
||||
VarType t;
|
||||
std::string tmp = esm.getHNString("FNAM");
|
||||
if (tmp == "s")
|
||||
t = VT_Short;
|
||||
else if (tmp == "l")
|
||||
t = VT_Int;
|
||||
else if (tmp == "f")
|
||||
t = VT_Float;
|
||||
else
|
||||
esm.fail("Illegal global variable type " + tmp);
|
||||
type = t;
|
||||
|
||||
// Note: Both floats and longs are represented as floats.
|
||||
esm.getHNT(value, "FLTV");
|
||||
}
|
||||
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
#define _ESM_GLOB_H
|
||||
|
||||
#include "esm_reader.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Global script variables
|
||||
|
@ -11,22 +13,10 @@ namespace ESM {
|
|||
|
||||
struct Global
|
||||
{
|
||||
unsigned value;
|
||||
VarType type;
|
||||
unsigned value;
|
||||
VarType type;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
VarType t;
|
||||
std::string tmp = esm.getHNString("FNAM");
|
||||
if(tmp == "s") t = VT_Short;
|
||||
else if(tmp == "l") t = VT_Int;
|
||||
else if(tmp == "f") t = VT_Float;
|
||||
else esm.fail("Illegal global variable type " + tmp);
|
||||
type = t;
|
||||
|
||||
// Note: Both floats and longs are represented as floats.
|
||||
esm.getHNT(value, "FLTV");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
172
components/esm/loadgmst.cpp
Normal file
172
components/esm/loadgmst.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "loadgmst.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
// Some handy macros
|
||||
#define cI(s,x) { if(id == (s)) return (i == (x)); }
|
||||
#define cF(s,x) { if(id == (s)) return (f == (x)); }
|
||||
#define cS(s,x) { if(id == (s)) return (str == (x)); }
|
||||
|
||||
bool GameSetting::isDirtyTribunal()
|
||||
{
|
||||
/*
|
||||
Here, id contains the game setting name, and we check the
|
||||
setting for certain values. If it matches, this is a "dirty"
|
||||
entry. The correct entry (as defined in Tribunal and Bloodmoon
|
||||
esms) are given in the comments. Many of the values are correct,
|
||||
and are marked as 'same'. We still ignore them though, as they
|
||||
are still in the wrong file and might override custom values
|
||||
from other mods.
|
||||
*/
|
||||
|
||||
// Strings
|
||||
cS("sProfitValue", "Profit Value"); // 'Profit:'
|
||||
cS("sEditNote", "Edit Note"); // same
|
||||
cS("sDeleteNote", "Delete Note?"); // same
|
||||
cS("sMaxSale", "Max Sale"); // 'Seller Max'
|
||||
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon'
|
||||
cS("sTeleportDisabled",
|
||||
"Teleportation magic does not work here.");// same
|
||||
cS("sLevitateDisabled",
|
||||
"Levitation magic does not work here."); // same
|
||||
cS("sCompanionShare", "Companion Share"); // 'Share'
|
||||
cS("sCompanionWarningButtonOne",
|
||||
"Let the mercenary quit."); // same
|
||||
cS("sCompanionWarningButtonTwo",
|
||||
"Return to Companion Share display."); // same
|
||||
cS("sCompanionWarningMessage",
|
||||
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value.");
|
||||
// 'Your mercenary is poorer now than when he contracted with
|
||||
// you. Your mercenary will quit if you do not give him gold
|
||||
// or goods to bring his Profit to a positive value.'
|
||||
// [The difference here is "Profit Value" -> "Profit"]
|
||||
|
||||
// Strings that matches the id
|
||||
cS("sEffectSummonFabricant", id);// 'Summon Fabricant'
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bloodmoon variant
|
||||
bool GameSetting::isDirtyBloodmoon()
|
||||
{
|
||||
// Strings
|
||||
cS("sWerewolfPopup", "Werewolf"); // same
|
||||
cS("sWerewolfRestMessage",
|
||||
"You cannot rest in werewolf form."); // same
|
||||
cS("sWerewolfRefusal",
|
||||
"You cannot do this as a werewolf."); // same
|
||||
cS("sWerewolfAlarmMessage",
|
||||
"You have been detected changing from a werewolf state.");
|
||||
// 'You have been detected as a known werewolf.'
|
||||
|
||||
// Strings that matches the id
|
||||
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon'
|
||||
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon'
|
||||
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
|
||||
cS("sMagicCreature04ID", id); // same
|
||||
cS("sMagicCreature05ID", id); // same
|
||||
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
|
||||
cS("sEffectSummonCreature02", id); // 'Calf Bear'
|
||||
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
|
||||
cS("sEffectSummonCreature04", id); // same
|
||||
cS("sEffectSummonCreature05", id); // same
|
||||
|
||||
// Integers
|
||||
cI("iWereWolfBounty", 10000); // 1000
|
||||
cI("iWereWolfFightMod", 100); // same
|
||||
cI("iWereWolfFleeMod", 100); // same
|
||||
cI("iWereWolfLevelToAttack", 20); // same
|
||||
|
||||
// Floats
|
||||
cF("fFleeDistance", 3000); // same
|
||||
cF("fCombatDistanceWerewolfMod", 0.3); // same
|
||||
cF("fWereWolfFatigue", 400); // same
|
||||
cF("fWereWolfEnchant", 1); // 0
|
||||
cF("fWereWolfArmorer", 1); // 0
|
||||
cF("fWereWolfBlock", 1); // 0
|
||||
cF("fWereWolfSneak", 1); // 95
|
||||
cF("fWereWolfDestruction", 1); // 0
|
||||
cF("fWereWolfEndurance", 150); // same
|
||||
cF("fWereWolfConjuration", 1); // 0
|
||||
cF("fWereWolfRestoration", 1); // 0
|
||||
cF("fWereWolfAthletics", 150); // 50
|
||||
cF("fWereWolfLuck", 1); // 25
|
||||
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
|
||||
cF("fWereWolfMediumArmor", 1); // 0
|
||||
cF("fWereWolfShortBlade", 1); // 0
|
||||
cF("fWereWolfAcrobatics", 150); // 80
|
||||
cF("fWereWolfSpeechcraft", 1); // 0
|
||||
cF("fWereWolfAlteration", 1); // 0
|
||||
cF("fWereWolfIllusion", 1); // 0
|
||||
cF("fWereWolfLongBlade", 1); // 0
|
||||
cF("fWereWolfMarksman", 1); // 0
|
||||
cF("fWereWolfHandtoHand", 100); // same
|
||||
cF("fWereWolfIntellegence", 1); // 0
|
||||
cF("fWereWolfAlchemy", 1); // 0
|
||||
cF("fWereWolfUnarmored", 100); // same
|
||||
cF("fWereWolfAxe", 1); // 0
|
||||
cF("fWereWolfRunMult", 1.5); // 1.3
|
||||
cF("fWereWolfMagicka", 100); // same
|
||||
cF("fWereWolfAgility", 150); // same
|
||||
cF("fWereWolfBluntWeapon", 1); // 0
|
||||
cF("fWereWolfSecurity", 1); // 0
|
||||
cF("fWereWolfPersonality", 1); // 0
|
||||
cF("fWereWolfMerchantile", 1); // 0
|
||||
cF("fWereWolfHeavyArmor", 1); // 0
|
||||
cF("fWereWolfSpear", 1); // 0
|
||||
cF("fWereWolfStrength", 150); // same
|
||||
cF("fWereWolfHealth", 2); // same
|
||||
cF("fWereWolfMysticism", 1); // 0
|
||||
cF("fWereWolfLightArmor", 1); // 0
|
||||
cF("fWereWolfWillPower", 1); // 0
|
||||
cF("fWereWolfSpeed", 150); // 90
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameSetting::load(ESMReader &esm)
|
||||
{
|
||||
assert(id != "");
|
||||
|
||||
dirty = false;
|
||||
|
||||
// We are apparently allowed to be empty
|
||||
if (!esm.hasMoreSubs())
|
||||
{
|
||||
type = VT_None;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load some data
|
||||
esm.getSubName();
|
||||
NAME n = esm.retSubName();
|
||||
if (n == "STRV")
|
||||
{
|
||||
str = esm.getHString();
|
||||
type = VT_String;
|
||||
}
|
||||
else if (n == "INTV")
|
||||
{
|
||||
esm.getHT(i);
|
||||
type = VT_Int;
|
||||
}
|
||||
else if (n == "FLTV")
|
||||
{
|
||||
esm.getHT(f);
|
||||
type = VT_Float;
|
||||
}
|
||||
else
|
||||
esm.fail("Unwanted subrecord type");
|
||||
|
||||
int spf = esm.getSpecial();
|
||||
|
||||
// Check if this is one of the dirty values mentioned above. If it
|
||||
// is, we set the dirty flag. This will ONLY work if you've set
|
||||
// the 'id' string correctly before calling load().
|
||||
|
||||
if ((spf != SF_Tribunal && isDirtyTribunal()) || (spf != SF_Bloodmoon
|
||||
&& isDirtyBloodmoon()))
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Game setting, with automatic cleaning of "dirty" entries.
|
||||
|
@ -13,238 +14,75 @@ namespace ESM {
|
|||
|
||||
struct GameSetting
|
||||
{
|
||||
std::string id;
|
||||
std::string id;
|
||||
|
||||
// One of these is used depending on the variable type
|
||||
std::string str;
|
||||
int i;
|
||||
float f;
|
||||
VarType type;
|
||||
// One of these is used depending on the variable type
|
||||
std::string str;
|
||||
int i;
|
||||
float f;
|
||||
VarType type;
|
||||
|
||||
// Set to true if this is a 'dirty' entry which should be ignored
|
||||
bool dirty;
|
||||
// Set to true if this is a 'dirty' entry which should be ignored
|
||||
bool dirty;
|
||||
|
||||
/*
|
||||
These functions check if this game setting is one of the "dirty"
|
||||
GMST records found in many mods. These are due to a serious bug in
|
||||
the official TES3 editor. It only occurs in the newer editor
|
||||
versions that came with Tribunal and Bloodmoon, and only if a
|
||||
modder tries to make a mod without loading the corresponding
|
||||
expansion master file. For example, if you have Tribunal installed
|
||||
and try to make a mod without loading Tribunal.esm, the editor
|
||||
will insert these GMST records as a replacement for the entries it
|
||||
cannot find in the ESMs.
|
||||
|
||||
The values of these "dirty" records differ in general from their
|
||||
values as defined in Tribunal.esm and Bloodmoon.esm, and are
|
||||
always set to the same "default" values. Most of these values are
|
||||
nonsensical, ie. changing the "Seller Max" string to "Max Sale",
|
||||
or change the stats of werewolves to useless values like 1. Some
|
||||
of them break certain spell effects.
|
||||
|
||||
It is most likely that these values are just leftover values from
|
||||
an early stage of development that are inserted as default values
|
||||
by the editor code. They are supposed to be overridden when the
|
||||
correct esm file is loaded. When it isn't loaded however, you get
|
||||
stuck with the initial value, and this gets written to every mod
|
||||
by the editor, for some reason.
|
||||
|
||||
Bethesda themselves have fallen for this bug. If you install both
|
||||
Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
|
||||
dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
|
||||
some of the dirty settings from Tribunal. In other words, this bug
|
||||
affects the game EVEN IF YOU DO NOT USE ANY MODS!
|
||||
|
||||
The guys at Bethesda are well aware of this bug (and many others),
|
||||
as the mod community and fan base complained about them for a long
|
||||
time. But unfortunately it was never fixed.
|
||||
|
||||
There are several tools available to help modders remove these
|
||||
records from their files, but not all modders use them, and they
|
||||
really shouldn't have to. In this file we choose instead to reject
|
||||
all the corrupt values at load time.
|
||||
|
||||
These functions checks if the current game setting is one of the
|
||||
"dirty" ones as described above. TODO: I have not checked this
|
||||
against other sources yet, do that later. Currently recognizes 22
|
||||
values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
|
||||
(setting values other than the default "dirty" ones) are not
|
||||
affected and will work correctly.
|
||||
*/
|
||||
|
||||
// Some handy macros
|
||||
#define cI(s,x) { if(id == (s)) return (i == (x)); }
|
||||
#define cF(s,x) { if(id == (s)) return (f == (x)); }
|
||||
#define cS(s,x) { if(id == (s)) return (str == (x)); }
|
||||
|
||||
/*
|
||||
Checks for dirty tribunal values. These will be ignored if found
|
||||
in any file except when they are found in "Tribunal.esm".
|
||||
*/
|
||||
bool isDirtyTribunal()
|
||||
{
|
||||
/*
|
||||
Here, id contains the game setting name, and we check the
|
||||
setting for certain values. If it matches, this is a "dirty"
|
||||
entry. The correct entry (as defined in Tribunal and Bloodmoon
|
||||
esms) are given in the comments. Many of the values are correct,
|
||||
and are marked as 'same'. We still ignore them though, as they
|
||||
are still in the wrong file and might override custom values
|
||||
from other mods.
|
||||
*/
|
||||
These functions check if this game setting is one of the "dirty"
|
||||
GMST records found in many mods. These are due to a serious bug in
|
||||
the official TES3 editor. It only occurs in the newer editor
|
||||
versions that came with Tribunal and Bloodmoon, and only if a
|
||||
modder tries to make a mod without loading the corresponding
|
||||
expansion master file. For example, if you have Tribunal installed
|
||||
and try to make a mod without loading Tribunal.esm, the editor
|
||||
will insert these GMST records as a replacement for the entries it
|
||||
cannot find in the ESMs.
|
||||
|
||||
// Strings
|
||||
cS("sProfitValue", "Profit Value"); // 'Profit:'
|
||||
cS("sEditNote", "Edit Note"); // same
|
||||
cS("sDeleteNote", "Delete Note?"); // same
|
||||
cS("sMaxSale", "Max Sale"); // 'Seller Max'
|
||||
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon'
|
||||
cS("sTeleportDisabled",
|
||||
"Teleportation magic does not work here.");// same
|
||||
cS("sLevitateDisabled",
|
||||
"Levitation magic does not work here."); // same
|
||||
cS("sCompanionShare", "Companion Share"); // 'Share'
|
||||
cS("sCompanionWarningButtonOne",
|
||||
"Let the mercenary quit."); // same
|
||||
cS("sCompanionWarningButtonTwo",
|
||||
"Return to Companion Share display."); // same
|
||||
cS("sCompanionWarningMessage",
|
||||
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value.");
|
||||
// 'Your mercenary is poorer now than when he contracted with
|
||||
// you. Your mercenary will quit if you do not give him gold
|
||||
// or goods to bring his Profit to a positive value.'
|
||||
// [The difference here is "Profit Value" -> "Profit"]
|
||||
The values of these "dirty" records differ in general from their
|
||||
values as defined in Tribunal.esm and Bloodmoon.esm, and are
|
||||
always set to the same "default" values. Most of these values are
|
||||
nonsensical, ie. changing the "Seller Max" string to "Max Sale",
|
||||
or change the stats of werewolves to useless values like 1. Some
|
||||
of them break certain spell effects.
|
||||
|
||||
// Strings that matches the id
|
||||
cS("sEffectSummonFabricant", id);// 'Summon Fabricant'
|
||||
return false;
|
||||
}
|
||||
It is most likely that these values are just leftover values from
|
||||
an early stage of development that are inserted as default values
|
||||
by the editor code. They are supposed to be overridden when the
|
||||
correct esm file is loaded. When it isn't loaded however, you get
|
||||
stuck with the initial value, and this gets written to every mod
|
||||
by the editor, for some reason.
|
||||
|
||||
// Bloodmoon variant
|
||||
bool isDirtyBloodmoon()
|
||||
{
|
||||
// Strings
|
||||
cS("sWerewolfPopup", "Werewolf"); // same
|
||||
cS("sWerewolfRestMessage",
|
||||
"You cannot rest in werewolf form."); // same
|
||||
cS("sWerewolfRefusal",
|
||||
"You cannot do this as a werewolf."); // same
|
||||
cS("sWerewolfAlarmMessage",
|
||||
"You have been detected changing from a werewolf state.");
|
||||
// 'You have been detected as a known werewolf.'
|
||||
Bethesda themselves have fallen for this bug. If you install both
|
||||
Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
|
||||
dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
|
||||
some of the dirty settings from Tribunal. In other words, this bug
|
||||
affects the game EVEN IF YOU DO NOT USE ANY MODS!
|
||||
|
||||
// Strings that matches the id
|
||||
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon'
|
||||
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon'
|
||||
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
|
||||
cS("sMagicCreature04ID", id); // same
|
||||
cS("sMagicCreature05ID", id); // same
|
||||
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
|
||||
cS("sEffectSummonCreature02", id); // 'Calf Bear'
|
||||
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
|
||||
cS("sEffectSummonCreature04", id); // same
|
||||
cS("sEffectSummonCreature05", id); // same
|
||||
The guys at Bethesda are well aware of this bug (and many others),
|
||||
as the mod community and fan base complained about them for a long
|
||||
time. But unfortunately it was never fixed.
|
||||
|
||||
// Integers
|
||||
cI("iWereWolfBounty", 10000); // 1000
|
||||
cI("iWereWolfFightMod", 100); // same
|
||||
cI("iWereWolfFleeMod", 100); // same
|
||||
cI("iWereWolfLevelToAttack", 20); // same
|
||||
There are several tools available to help modders remove these
|
||||
records from their files, but not all modders use them, and they
|
||||
really shouldn't have to. In this file we choose instead to reject
|
||||
all the corrupt values at load time.
|
||||
|
||||
// Floats
|
||||
cF("fFleeDistance", 3000); // same
|
||||
cF("fCombatDistanceWerewolfMod", 0.3); // same
|
||||
cF("fWereWolfFatigue", 400); // same
|
||||
cF("fWereWolfEnchant", 1); // 0
|
||||
cF("fWereWolfArmorer", 1); // 0
|
||||
cF("fWereWolfBlock", 1); // 0
|
||||
cF("fWereWolfSneak", 1); // 95
|
||||
cF("fWereWolfDestruction", 1); // 0
|
||||
cF("fWereWolfEndurance", 150); // same
|
||||
cF("fWereWolfConjuration", 1); // 0
|
||||
cF("fWereWolfRestoration", 1); // 0
|
||||
cF("fWereWolfAthletics", 150); // 50
|
||||
cF("fWereWolfLuck", 1); // 25
|
||||
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
|
||||
cF("fWereWolfMediumArmor", 1); // 0
|
||||
cF("fWereWolfShortBlade", 1); // 0
|
||||
cF("fWereWolfAcrobatics", 150); // 80
|
||||
cF("fWereWolfSpeechcraft", 1); // 0
|
||||
cF("fWereWolfAlteration", 1); // 0
|
||||
cF("fWereWolfIllusion", 1); // 0
|
||||
cF("fWereWolfLongBlade", 1); // 0
|
||||
cF("fWereWolfMarksman", 1); // 0
|
||||
cF("fWereWolfHandtoHand", 100); // same
|
||||
cF("fWereWolfIntellegence", 1); // 0
|
||||
cF("fWereWolfAlchemy", 1); // 0
|
||||
cF("fWereWolfUnarmored", 100); // same
|
||||
cF("fWereWolfAxe", 1); // 0
|
||||
cF("fWereWolfRunMult", 1.5); // 1.3
|
||||
cF("fWereWolfMagicka", 100); // same
|
||||
cF("fWereWolfAgility", 150); // same
|
||||
cF("fWereWolfBluntWeapon", 1); // 0
|
||||
cF("fWereWolfSecurity", 1); // 0
|
||||
cF("fWereWolfPersonality", 1); // 0
|
||||
cF("fWereWolfMerchantile", 1); // 0
|
||||
cF("fWereWolfHeavyArmor", 1); // 0
|
||||
cF("fWereWolfSpear", 1); // 0
|
||||
cF("fWereWolfStrength", 150); // same
|
||||
cF("fWereWolfHealth", 2); // same
|
||||
cF("fWereWolfMysticism", 1); // 0
|
||||
cF("fWereWolfLightArmor", 1); // 0
|
||||
cF("fWereWolfWillPower", 1); // 0
|
||||
cF("fWereWolfSpeed", 150); // 90
|
||||
return false;
|
||||
}
|
||||
These functions checks if the current game setting is one of the
|
||||
"dirty" ones as described above. TODO: I have not checked this
|
||||
against other sources yet, do that later. Currently recognizes 22
|
||||
values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
|
||||
(setting values other than the default "dirty" ones) are not
|
||||
affected and will work correctly.
|
||||
*/
|
||||
|
||||
#undef cI
|
||||
#undef cF
|
||||
#undef cS
|
||||
/*
|
||||
Checks for dirty tribunal values. These will be ignored if found
|
||||
in any file except when they are found in "Tribunal.esm".
|
||||
*/
|
||||
bool isDirtyTribunal();
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
assert(id != "");
|
||||
// Bloodmoon variant
|
||||
bool isDirtyBloodmoon();
|
||||
|
||||
dirty = false;
|
||||
|
||||
// We are apparently allowed to be empty
|
||||
if(!esm.hasMoreSubs())
|
||||
{
|
||||
type = VT_None;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load some data
|
||||
esm.getSubName();
|
||||
NAME n = esm.retSubName();
|
||||
if(n == "STRV")
|
||||
{
|
||||
str = esm.getHString();
|
||||
type = VT_String;
|
||||
}
|
||||
else if(n == "INTV")
|
||||
{
|
||||
esm.getHT(i);
|
||||
type = VT_Int;
|
||||
}
|
||||
else if(n == "FLTV")
|
||||
{
|
||||
esm.getHT(f);
|
||||
type = VT_Float;
|
||||
}
|
||||
else
|
||||
esm.fail("Unwanted subrecord type");
|
||||
|
||||
int spf = esm.getSpecial();
|
||||
|
||||
// Check if this is one of the dirty values mentioned above. If it
|
||||
// is, we set the dirty flag. This will ONLY work if you've set
|
||||
// the 'id' string correctly before calling load().
|
||||
|
||||
if( ( spf != SF_Tribunal && isDirtyTribunal() ) ||
|
||||
( spf != SF_Bloodmoon && isDirtyBloodmoon() ) )
|
||||
dirty = true;
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
133
components/esm/loadinfo.cpp
Normal file
133
components/esm/loadinfo.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#include "loadinfo.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void DialInfo::load(ESMReader &esm)
|
||||
{
|
||||
id = esm.getHNString("INAM");
|
||||
prev = esm.getHNString("PNAM");
|
||||
next = esm.getHNString("NNAM");
|
||||
|
||||
// Not present if deleted
|
||||
if (esm.isNextSub("DATA"))
|
||||
esm.getHT(data, 12);
|
||||
|
||||
// What follows is somewhat spaghetti-ish, but it's worth if for
|
||||
// an extra speedup. INFO is by far the most common record type.
|
||||
|
||||
// subName is a reference to the original, so it changes whenever
|
||||
// a new sub name is read. esm.isEmptyOrGetName() will get the
|
||||
// next name for us, or return true if there are no more records.
|
||||
esm.getSubName();
|
||||
const NAME &subName = esm.retSubName();
|
||||
|
||||
if (subName.val == REC_ONAM)
|
||||
{
|
||||
actor = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_RNAM)
|
||||
{
|
||||
race = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_CNAM)
|
||||
{
|
||||
clas = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
|
||||
factionLess = false;
|
||||
if (subName.val == REC_FNAM)
|
||||
{
|
||||
npcFaction = esm.getHString();
|
||||
if (npcFaction == "FFFF")
|
||||
factionLess = true;
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_ANAM)
|
||||
{
|
||||
cell = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_DNAM)
|
||||
{
|
||||
pcFaction = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_SNAM)
|
||||
{
|
||||
sound = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
if (subName.val == REC_NAME)
|
||||
{
|
||||
response = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
|
||||
while (subName.val == REC_SCVR)
|
||||
{
|
||||
SelectStruct ss;
|
||||
|
||||
ss.selectRule = esm.getHString();
|
||||
esm.isEmptyOrGetName();
|
||||
|
||||
if (subName.val == REC_INTV)
|
||||
{
|
||||
ss.type = VT_Int;
|
||||
esm.getHT(ss.i);
|
||||
}
|
||||
else if (subName.val == REC_FLTV)
|
||||
{
|
||||
ss.type = VT_Float;
|
||||
esm.getHT(ss.f);
|
||||
}
|
||||
else
|
||||
esm.fail(
|
||||
"INFO.SCVR must precede INTV or FLTV, not "
|
||||
+ subName.toString());
|
||||
|
||||
selects.push_back(ss);
|
||||
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
|
||||
if (subName.val == REC_BNAM)
|
||||
{
|
||||
resultScript = esm.getHString();
|
||||
if (esm.isEmptyOrGetName())
|
||||
return;
|
||||
}
|
||||
|
||||
questStatus = QS_None;
|
||||
|
||||
if (subName.val == REC_QSTN)
|
||||
questStatus = QS_Name;
|
||||
else if (subName.val == REC_QSTF)
|
||||
questStatus = QS_Finished;
|
||||
else if (subName.val == REC_QSTR)
|
||||
questStatus = QS_Restart;
|
||||
else if (subName.val == REC_DELE)
|
||||
questStatus = QS_Deleted;
|
||||
else
|
||||
esm.fail(
|
||||
"Don't know what to do with " + subName.toString()
|
||||
+ " in INFO " + id);
|
||||
|
||||
if (questStatus != QS_None)
|
||||
// Skip rest of record
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
#include "esm_reader.hpp"
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
// NOT DONE
|
||||
|
||||
|
@ -15,88 +16,88 @@ namespace ESM {
|
|||
|
||||
struct DialInfo
|
||||
{
|
||||
enum Gender
|
||||
enum Gender
|
||||
{
|
||||
Male = 0,
|
||||
Female = 1,
|
||||
NA = -1
|
||||
Male = 0,
|
||||
Female = 1,
|
||||
NA = -1
|
||||
};
|
||||
|
||||
struct DATAstruct
|
||||
{
|
||||
int unknown1;
|
||||
int disposition;
|
||||
char rank; // Rank of NPC
|
||||
char gender; // See Gender enum
|
||||
char PCrank; // Player rank
|
||||
char unknown2;
|
||||
}; // 12 bytes
|
||||
DATAstruct data;
|
||||
|
||||
// The rules for whether or not we will select this dialog item.
|
||||
struct SelectStruct
|
||||
{
|
||||
std::string selectRule; // This has a complicated format
|
||||
float f; // Only one of 'f' or 'i' is used
|
||||
int i;
|
||||
VarType type;
|
||||
};
|
||||
|
||||
// Journal quest indices (introduced with the quest system in Tribunal)
|
||||
enum QuestStatus
|
||||
struct DATAstruct
|
||||
{
|
||||
QS_None,
|
||||
QS_Name,
|
||||
QS_Finished,
|
||||
QS_Restart,
|
||||
QS_Deleted
|
||||
int unknown1;
|
||||
int disposition;
|
||||
char rank; // Rank of NPC
|
||||
char gender; // See Gender enum
|
||||
char PCrank; // Player rank
|
||||
char unknown2;
|
||||
}; // 12 bytes
|
||||
DATAstruct data;
|
||||
|
||||
// The rules for whether or not we will select this dialog item.
|
||||
struct SelectStruct
|
||||
{
|
||||
std::string selectRule; // This has a complicated format
|
||||
float f; // Only one of 'f' or 'i' is used
|
||||
int i;
|
||||
VarType type;
|
||||
};
|
||||
|
||||
// Rules for when to include this item in the final list of options
|
||||
// visible to the player.
|
||||
std::vector<SelectStruct> selects;
|
||||
|
||||
// Id of this, previous and next INFO items
|
||||
std::string id, prev, next,
|
||||
|
||||
// Various references used in determining when to select this item.
|
||||
actor, race, clas, npcFaction, pcFaction, cell,
|
||||
|
||||
// Sound and text associated with this item
|
||||
sound, response,
|
||||
|
||||
// Result script (uncomiled) to run whenever this dialog item is
|
||||
// selected
|
||||
resultScript;
|
||||
|
||||
// ONLY include this item the NPC is not part of any faction.
|
||||
bool factionLess;
|
||||
|
||||
// Status of this quest item
|
||||
QuestStatus questStatus;
|
||||
|
||||
// Hexadecimal versions of the various subrecord names.
|
||||
enum SubNames
|
||||
// Journal quest indices (introduced with the quest system in Tribunal)
|
||||
enum QuestStatus
|
||||
{
|
||||
REC_ONAM = 0x4d414e4f,
|
||||
REC_RNAM = 0x4d414e52,
|
||||
REC_CNAM = 0x4d414e43,
|
||||
REC_FNAM = 0x4d414e46,
|
||||
REC_ANAM = 0x4d414e41,
|
||||
REC_DNAM = 0x4d414e44,
|
||||
REC_SNAM = 0x4d414e53,
|
||||
REC_NAME = 0x454d414e,
|
||||
REC_SCVR = 0x52564353,
|
||||
REC_INTV = 0x56544e49,
|
||||
REC_FLTV = 0x56544c46,
|
||||
REC_BNAM = 0x4d414e42,
|
||||
REC_QSTN = 0x4e545351,
|
||||
REC_QSTF = 0x46545351,
|
||||
REC_QSTR = 0x52545351,
|
||||
REC_DELE = 0x454c4544
|
||||
QS_None,
|
||||
QS_Name,
|
||||
QS_Finished,
|
||||
QS_Restart,
|
||||
QS_Deleted
|
||||
};
|
||||
|
||||
void load(ESMReader &esm);
|
||||
// Rules for when to include this item in the final list of options
|
||||
// visible to the player.
|
||||
std::vector<SelectStruct> selects;
|
||||
|
||||
// Id of this, previous and next INFO items
|
||||
std::string id, prev, next,
|
||||
|
||||
// Various references used in determining when to select this item.
|
||||
actor, race, clas, npcFaction, pcFaction, cell,
|
||||
|
||||
// Sound and text associated with this item
|
||||
sound, response,
|
||||
|
||||
// Result script (uncomiled) to run whenever this dialog item is
|
||||
// selected
|
||||
resultScript;
|
||||
|
||||
// ONLY include this item the NPC is not part of any faction.
|
||||
bool factionLess;
|
||||
|
||||
// Status of this quest item
|
||||
QuestStatus questStatus;
|
||||
|
||||
// Hexadecimal versions of the various subrecord names.
|
||||
enum SubNames
|
||||
{
|
||||
REC_ONAM = 0x4d414e4f,
|
||||
REC_RNAM = 0x4d414e52,
|
||||
REC_CNAM = 0x4d414e43,
|
||||
REC_FNAM = 0x4d414e46,
|
||||
REC_ANAM = 0x4d414e41,
|
||||
REC_DNAM = 0x4d414e44,
|
||||
REC_SNAM = 0x4d414e53,
|
||||
REC_NAME = 0x454d414e,
|
||||
REC_SCVR = 0x52564353,
|
||||
REC_INTV = 0x56544e49,
|
||||
REC_FLTV = 0x56544c46,
|
||||
REC_BNAM = 0x4d414e42,
|
||||
REC_QSTN = 0x4e545351,
|
||||
REC_QSTF = 0x46545351,
|
||||
REC_QSTR = 0x52545351,
|
||||
REC_DELE = 0x454c4544
|
||||
};
|
||||
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
15
components/esm/loadingr.cpp
Normal file
15
components/esm/loadingr.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "loadingr.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Ingredient::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "IRDT", 56);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Alchemy ingredient
|
||||
|
@ -11,26 +12,19 @@ namespace ESM {
|
|||
|
||||
struct Ingredient
|
||||
{
|
||||
struct IRDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int effectID[4]; // Effect, 0 or -1 means none
|
||||
int skills[4]; // SkillEnum related to effect
|
||||
int attributes[4]; // Attribute related to effect
|
||||
};
|
||||
struct IRDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int effectID[4]; // Effect, 0 or -1 means none
|
||||
int skills[4]; // SkillEnum related to effect
|
||||
int attributes[4]; // Attribute related to effect
|
||||
};
|
||||
|
||||
IRDTstruct data;
|
||||
std::string name, model, icon, script;
|
||||
IRDTstruct data;
|
||||
std::string name, model, icon, script;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
esm.getHNT(data, "IRDT", 56);
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
32
components/esm/loadland.cpp
Normal file
32
components/esm/loadland.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "loadland.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
void Land::load(ESMReader &esm)
|
||||
{
|
||||
// Get the grid location
|
||||
esm.getSubNameIs("INTV");
|
||||
esm.getSubHeaderIs(8);
|
||||
esm.getT<int>(X);
|
||||
esm.getT<int>(Y);
|
||||
|
||||
esm.getHNT(flags, "DATA");
|
||||
|
||||
// Store the file position
|
||||
context = esm.getContext();
|
||||
|
||||
hasData = false;
|
||||
int cnt = 0;
|
||||
|
||||
// Skip these here. Load the actual data when the cell is loaded.
|
||||
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
||||
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
||||
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
||||
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
||||
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
||||
|
||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||
// landscape.
|
||||
hasData = (cnt == 3);
|
||||
}
|
||||
}
|
|
@ -3,51 +3,25 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
/*
|
||||
* Landscape data.
|
||||
*/
|
||||
|
||||
struct Land
|
||||
{
|
||||
int flags; // Only first four bits seem to be used, don't know what
|
||||
// they mean.
|
||||
int X, Y; // Map coordinates.
|
||||
int flags; // Only first four bits seem to be used, don't know what
|
||||
// they mean.
|
||||
int X, Y; // Map coordinates.
|
||||
|
||||
// File context. This allows the ESM reader to be 'reset' to this
|
||||
// location later when we are ready to load the full data set.
|
||||
ESM_Context context;
|
||||
// File context. This allows the ESM reader to be 'reset' to this
|
||||
// location later when we are ready to load the full data set.
|
||||
ESM_Context context;
|
||||
|
||||
bool hasData;
|
||||
bool hasData;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
// Get the grid location
|
||||
esm.getSubNameIs("INTV");
|
||||
esm.getSubHeaderIs(8);
|
||||
esm.getT<int>(X);
|
||||
esm.getT<int>(Y);
|
||||
|
||||
esm.getHNT(flags, "DATA");
|
||||
|
||||
// Store the file position
|
||||
context = esm.getContext();
|
||||
|
||||
hasData = false;
|
||||
int cnt = 0;
|
||||
|
||||
// Skip these here. Load the actual data when the cell is loaded.
|
||||
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
||||
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
||||
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
||||
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
||||
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
||||
|
||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||
// landscape.
|
||||
hasData = (cnt == 3);
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
34
components/esm/loadlevlist.cpp
Normal file
34
components/esm/loadlevlist.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include "loadlevlist.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void LeveledListBase::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(flags, "DATA");
|
||||
esm.getHNT(chanceNone, "NNAM");
|
||||
|
||||
if (esm.isNextSub("INDX"))
|
||||
{
|
||||
int len;
|
||||
esm.getHT(len);
|
||||
list.resize(len);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// TODO: Merge with an existing lists here. This can be done
|
||||
// simply by adding the lists together, making sure that they are
|
||||
// sorted by level. A better way might be to exclude repeated
|
||||
// items. Also, some times we don't want to merge lists, just
|
||||
// overwrite. Figure out a way to give the user this option.
|
||||
|
||||
for (size_t i = 0; i < list.size(); i++)
|
||||
{
|
||||
LevelItem &li = list[i];
|
||||
li.id = esm.getHNString(recName);
|
||||
esm.getHNT(li.level, "INTV");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Leveled lists. Since these have identical layout, I only bothered
|
||||
|
@ -15,65 +16,50 @@ namespace ESM {
|
|||
|
||||
struct LeveledListBase
|
||||
{
|
||||
enum Flags
|
||||
enum Flags
|
||||
{
|
||||
AllLevels = 0x01, // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
// player.
|
||||
Each = 0x02 // Select a new item each time this
|
||||
// list is instantiated, instead of
|
||||
// giving several identical items
|
||||
}; // (used when a container has more
|
||||
// than one instance of one leveled
|
||||
// list.)
|
||||
int flags;
|
||||
unsigned char chanceNone; // Chance that none are selected (0-255?)
|
||||
AllLevels = 0x01, // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
// player.
|
||||
Each = 0x02 // Select a new item each time this
|
||||
// list is instantiated, instead of
|
||||
// giving several identical items
|
||||
}; // (used when a container has more
|
||||
// than one instance of one leveled
|
||||
// list.)
|
||||
int flags;
|
||||
unsigned char chanceNone; // Chance that none are selected (0-255?)
|
||||
|
||||
// Record name used to read references. Must be set before load() is
|
||||
// called.
|
||||
const char *recName;
|
||||
// Record name used to read references. Must be set before load() is
|
||||
// called.
|
||||
const char *recName;
|
||||
|
||||
struct LevelItem
|
||||
{
|
||||
std::string id;
|
||||
short level;
|
||||
};
|
||||
struct LevelItem
|
||||
{
|
||||
std::string id;
|
||||
short level;
|
||||
};
|
||||
|
||||
std::vector<LevelItem> list;
|
||||
std::vector<LevelItem> list;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(flags, "DATA");
|
||||
esm.getHNT(chanceNone, "NNAM");
|
||||
|
||||
if(esm.isNextSub("INDX"))
|
||||
{
|
||||
int len;
|
||||
esm.getHT(len);
|
||||
list.resize(len);
|
||||
}
|
||||
else return;
|
||||
|
||||
// TODO: Merge with an existing lists here. This can be done
|
||||
// simply by adding the lists together, making sure that they are
|
||||
// sorted by level. A better way might be to exclude repeated
|
||||
// items. Also, some times we don't want to merge lists, just
|
||||
// overwrite. Figure out a way to give the user this option.
|
||||
|
||||
for(size_t i=0; i<list.size(); i++)
|
||||
{
|
||||
LevelItem &li = list[i];
|
||||
li.id = esm.getHNString(recName);
|
||||
esm.getHNT(li.level, "INTV");
|
||||
}
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
|
||||
struct CreatureLevList : LeveledListBase
|
||||
{ CreatureLevList() { recName = "CNAM"; } };
|
||||
struct CreatureLevList: LeveledListBase
|
||||
{
|
||||
CreatureLevList()
|
||||
{
|
||||
recName = "CNAM";
|
||||
}
|
||||
};
|
||||
|
||||
struct ItemLevList : LeveledListBase
|
||||
{ ItemLevList() { recName = "INAM"; } };
|
||||
struct ItemLevList: LeveledListBase
|
||||
{
|
||||
ItemLevList()
|
||||
{
|
||||
recName = "INAM";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
17
components/esm/loadligh.cpp
Normal file
17
components/esm/loadligh.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "loadligh.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Light::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
assert(sizeof(data) == 24);
|
||||
esm.getHNT(data, "LHDT", 24);
|
||||
script = esm.getHNOString("SCRI");
|
||||
sound = esm.getHNOString("SNAM");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Lights. Includes static light sources and also carryable candles
|
||||
|
@ -12,43 +13,34 @@ namespace ESM {
|
|||
|
||||
struct Light
|
||||
{
|
||||
enum Flags
|
||||
enum Flags
|
||||
{
|
||||
Dynamic = 0x001,
|
||||
Carry = 0x002, // Can be carried
|
||||
Negative = 0x004, // Negative light?
|
||||
Flicker = 0x008,
|
||||
Fire = 0x010,
|
||||
OffDefault = 0x020, // Off by default
|
||||
FlickerSlow = 0x040,
|
||||
Pulse = 0x080,
|
||||
PulseSlow = 0x100
|
||||
Dynamic = 0x001,
|
||||
Carry = 0x002, // Can be carried
|
||||
Negative = 0x004, // Negative light?
|
||||
Flicker = 0x008,
|
||||
Fire = 0x010,
|
||||
OffDefault = 0x020, // Off by default
|
||||
FlickerSlow = 0x040,
|
||||
Pulse = 0x080,
|
||||
PulseSlow = 0x100
|
||||
};
|
||||
|
||||
struct LHDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int time; // Duration
|
||||
int radius;
|
||||
int color; // 4-byte rgba value
|
||||
int flags;
|
||||
}; // Size = 24 bytes
|
||||
struct LHDTstruct
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
int time; // Duration
|
||||
int radius;
|
||||
int color; // 4-byte rgba value
|
||||
int flags;
|
||||
}; // Size = 24 bytes
|
||||
|
||||
LHDTstruct data;
|
||||
LHDTstruct data;
|
||||
|
||||
std::string sound, script, model, icon, name;
|
||||
std::string sound, script, model, icon, name;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNOString("FNAM");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
assert(sizeof(data) == 24);
|
||||
esm.getHNT(data, "LHDT", 24);
|
||||
script = esm.getHNOString("SCRI");
|
||||
sound = esm.getHNOString("SNAM");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
30
components/esm/loadlocks.cpp
Normal file
30
components/esm/loadlocks.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "loadlocks.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Tool::load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
|
||||
esm.getSubName();
|
||||
NAME n = esm.retSubName();
|
||||
// The data name varies, RIDT for repair items, LKDT for lock
|
||||
// picks, PBDT for probes
|
||||
|
||||
esm.getHT(data, 16);
|
||||
|
||||
if (n == "RIDT")
|
||||
{
|
||||
// Swap t.data.quality and t.data.uses for repair items (sigh)
|
||||
float tmp = *((float*) &data.uses);
|
||||
data.uses = *((int*) &data.quality);
|
||||
data.quality = tmp;
|
||||
}
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* This file covers lockpicks (LOCK), probes (PROB) and armor repair
|
||||
|
@ -12,51 +13,29 @@ namespace ESM {
|
|||
|
||||
struct Tool
|
||||
{
|
||||
struct Data
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
struct Data
|
||||
{
|
||||
float weight;
|
||||
int value;
|
||||
|
||||
float quality; // And when I say nearly identical structure, I
|
||||
int uses; // mean perfectly identical except that these two
|
||||
// variables are swaped for repair items. Don't ask
|
||||
// me why.
|
||||
}; // Size = 16
|
||||
float quality; // And when I say nearly identical structure, I
|
||||
int uses; // mean perfectly identical except that these two
|
||||
// variables are swaped for repair items. Don't ask
|
||||
// me why.
|
||||
}; // Size = 16
|
||||
|
||||
Data data;
|
||||
std::string name, model, icon, script;
|
||||
Data data;
|
||||
std::string name, model, icon, script;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
model = esm.getHNString("MODL");
|
||||
name = esm.getHNString("FNAM");
|
||||
|
||||
esm.getSubName();
|
||||
NAME n = esm.retSubName();
|
||||
// The data name varies, RIDT for repair items, LKDT for lock
|
||||
// picks, PBDT for probes
|
||||
|
||||
esm.getHT(data, 16);
|
||||
|
||||
if(n == "RIDT")
|
||||
{
|
||||
// Swap t.data.quality and t.data.uses for repair items (sigh)
|
||||
float tmp = *((float*)&data.uses);
|
||||
data.uses = *((int*)&data.quality);
|
||||
data.quality = tmp;
|
||||
}
|
||||
|
||||
script = esm.getHNOString("SCRI");
|
||||
icon = esm.getHNOString("ITEX");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
|
||||
struct Probe : Tool
|
||||
struct Probe: Tool
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct Repair : Tool
|
||||
struct Repair: Tool
|
||||
{
|
||||
|
||||
};
|
||||
|
|
12
components/esm/loadltex.cpp
Normal file
12
components/esm/loadltex.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "loadltex.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void LandTexture::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(index, "INTV");
|
||||
texture = esm.getHNString("DATA");
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
#include "esm_reader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
/*
|
||||
* Texture used for texturing landscape.
|
||||
|
@ -23,14 +24,10 @@ namespace ESM {
|
|||
|
||||
struct LandTexture
|
||||
{
|
||||
std::string id, texture;
|
||||
int index;
|
||||
std::string id, texture;
|
||||
int index;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(index, "INTV");
|
||||
texture = esm.getHNString("DATA");
|
||||
}
|
||||
void load(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
27
components/esm/loadmgef.cpp
Normal file
27
components/esm/loadmgef.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "loadmgef.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void MagicEffect::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(index, "INDX");
|
||||
|
||||
esm.getHNT(data, "MEDT", 36);
|
||||
icon = esm.getHNOString("ITEX");
|
||||
particle = esm.getHNOString("PTEX");
|
||||
|
||||
boltSound = esm.getHNOString("BSND");
|
||||
castSound = esm.getHNOString("CSND");
|
||||
hitSound = esm.getHNOString("HSND");
|
||||
areaSound = esm.getHNOString("ASND");
|
||||
|
||||
casting = esm.getHNOString("CVFX");
|
||||
bolt = esm.getHNOString("BVFX");
|
||||
hit = esm.getHNOString("HVFX");
|
||||
area = esm.getHNOString("AVFX");
|
||||
|
||||
description = esm.getHNOString("DESC");
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue