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

improves animation-controlled velocity check

This commit is contained in:
mrcheko 2014-02-08 16:59:15 +02:00
commit a6be72673c
230 changed files with 6274 additions and 1752 deletions

1
.gitignore vendored
View file

@ -40,6 +40,7 @@ resources
## generated objects ## generated objects
apps/openmw/config.hpp apps/openmw/config.hpp
components/version/version.hpp
Docs/mainpage.hpp Docs/mainpage.hpp
moc_*.cxx moc_*.cxx
*.cxx_parameters *.cxx_parameters

View file

@ -14,15 +14,30 @@ endif (APPLE)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
include (OpenMWMacros) include(OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) include(GetGitRevisionDescription)
set (OPENMW_VERSION_MINOR 27)
set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if (MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...")
else (MATCH)
message(FATAL_ERROR "Failed to get valid version information from Git")
endif (MATCH)
# doxygen main page # doxygen main page
@ -319,7 +334,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
"${OpenMW_BINARY_DIR}/opencs.cfg") "${OpenMW_BINARY_DIR}/opencs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)

View file

@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
// Loop through all the references // Loop through all the references
ESM::CellRef ref; ESM::CellRef ref;
if(!quiet) std::cout << " References:\n"; if(!quiet) std::cout << " References:\n";
while(cell.getNextRef(esm, ref))
bool deleted = false;
while(cell.getNextRef(esm, ref, deleted))
{ {
if (save) { if (save) {
info.data.mCellRefs[&cell].push_back(ref); info.data.mCellRefs[&cell].push_back(ref);
@ -244,13 +246,14 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(quiet) continue; if(quiet) continue;
std::cout << " Refnum: " << ref.mRefnum << std::endl; std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
} }
} }

View file

@ -1,5 +1,10 @@
#include "maindialog.hpp" #include "maindialog.hpp"
#include <components/version/version.hpp>
#include <QLabel>
#include <QDate>
#include <QTime>
#include <QPushButton> #include <QPushButton>
#include <QFontDatabase> #include <QFontDatabase>
#include <QInputDialog> #include <QInputDialog>
@ -67,6 +72,22 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
// Remove what's this? button // Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
// Add version information to bottom of the window
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
}
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
createIcons(); createIcons();
} }

View file

@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
state==CSMWorld::RecordBase::State_ModifiedOnly || state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified) infoModified)
{ {
// always write the topic record mState.getWriter().startRecord (topic.mModified.sRecordId);
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&topic.mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter()); topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (topic.mModified.sRecordId);
// write modified selected info records // write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
@ -178,15 +172,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1); next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
} }
std::string type; mState.getWriter().startRecord (info.sRecordId);
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&info.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("INAM", info.mId); mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter()); info.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (info.sRecordId);
} }
} }
} }

View file

@ -104,10 +104,10 @@ namespace CSMDoc
/// \todo make endianess agnostic (change ESMWriter interface?) /// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i]; type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type); mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter()); mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
} }
else if (state==CSMWorld::RecordBase::State_Deleted) else if (state==CSMWorld::RecordBase::State_Deleted)
{ {

View file

@ -8,6 +8,5 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string
mId = id; mId = id;
mCell = cell.mId; mCell = cell.mId;
if (!mDeleted) cell.addRef (mId);
cell.addRef (mId);
} }

View file

@ -16,7 +16,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref; CellRef ref;
while (cell2.getNextRef (reader, ref)) bool deleted = false;
while (cell2.getNextRef (reader, ref, deleted))
{ {
/// \todo handle deleted and moved references /// \todo handle deleted and moved references
ref.load (reader, cell2, getNewId()); ref.load (reader, cell2, getNewId());

View file

@ -147,15 +147,10 @@ namespace CSMWorld
if (state==CSMWorld::RecordBase::State_Modified || if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly) state==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
std::string type; writer.startRecord (mContainer.at (index).mModified.sRecordId);
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
writer.startRecord (type);
writer.writeHNCString ("NAME", getId (index)); writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer); mContainer.at (index).mModified.save (writer);
writer.endRecord (type); writer.endRecord (mContainer.at (index).mModified.sRecordId);
} }
else if (state==CSMWorld::RecordBase::State_Deleted) else if (state==CSMWorld::RecordBase::State_Deleted)
{ {

View file

@ -1,7 +1,3 @@
# config file
configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp")
# local files # local files
set(GAME set(GAME
main.cpp main.cpp
@ -12,7 +8,6 @@ if(NOT WIN32)
endif() endif()
set(GAME_HEADER set(GAME_HEADER
engine.hpp engine.hpp
config.hpp
) )
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
@ -74,12 +69,16 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
disease pickpocket levelledlist combat disease pickpocket levelledlist combat steering
)
add_openmw_dir (mwstate
statemanagerimp charactermanager character
) )
add_openmw_dir (mwbase add_openmw_dir (mwbase
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
inputmanager windowmanager inputmanager windowmanager statemanager
) )
# Main executable # Main executable

View file

@ -7,6 +7,8 @@
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include <SDL.h>
#include <components/compiler/extensions0.hpp> #include <components/compiler/extensions0.hpp>
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
@ -41,8 +43,7 @@
#include "mwmechanics/mechanicsmanagerimp.hpp" #include "mwmechanics/mechanicsmanagerimp.hpp"
#include "mwstate/statemanagerimp.hpp"
#include <SDL.h>
void OMW::Engine::executeLocalScripts() void OMW::Engine::executeLocalScripts()
{ {
@ -88,31 +89,47 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (mUseSound) if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime); MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); // update game state
MWBase::Environment::get().getStateManager()->update (frametime);
// local scripts if (MWBase::Environment::get().getStateManager()->getState()==
executeLocalScripts(); // This does not handle the case where a global script causes a cell MWBase::StateManager::State_Running)
// change, followed by a cell change in a local script during the same {
// frame. // global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// passing of time bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a
// cell change, followed by a cell change in a local script during
// the same frame.
if (changed) // keep change flag for another frame, if cell changed happened in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
if (!paused)
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
}
if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors // update actors
MWBase::Environment::get().getMechanicsManager()->update(frametime, MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getWindowManager()->isGuiMode()); paused);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!paused && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame();
}
// update world // update world
MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); MWBase::Environment::get().getWorld()->update(frametime, paused);
// update GUI // update GUI
Ogre::RenderWindow* window = mOgre->getWindow(); Ogre::RenderWindow* window = mOgre->getWindow();
@ -135,7 +152,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0) : mOgre (0)
, mFpsLevel(0) , mFpsLevel(0)
, mVerboseScripts (false) , mVerboseScripts (false)
, mNewGame (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
, mCompileAll (false) , mCompileAll (false)
, mScriptContext (0) , mScriptContext (0)
@ -265,12 +282,7 @@ void OMW::Engine::setCell (const std::string& cellName)
void OMW::Engine::addContentFile(const std::string& file) void OMW::Engine::addContentFile(const std::string& file)
{ {
if (file.find_last_of(".") == std::string::npos) mContentFiles.push_back(file);
{
throw std::runtime_error("Missing extension in content file!");
}
mContentFiles.push_back(file);
} }
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -278,9 +290,9 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
mVerboseScripts = scriptsVerbosity; mVerboseScripts = scriptsVerbosity;
} }
void OMW::Engine::setNewGame(bool newGame) void OMW::Engine::setSkipMenu (bool skipMenu)
{ {
mNewGame = newGame; mSkipMenu = skipMenu;
} }
std::string OMW::Engine::loadSettings (Settings::Manager & settings) std::string OMW::Engine::loadSettings (Settings::Manager & settings)
@ -326,6 +338,9 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings)
{ {
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
Nif::NIFFile::CacheLock cachelock; Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video"); std::string renderSystem = settings.getString("render system", "Video");
@ -392,10 +407,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
input->setPlayer(&mEnvironment.getWorld()->getPlayer()); input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI(); window->initUI();
if (mNewGame)
// still redundant work here: recreate CharacterCreation(),
// double update visibility etc.
window->setNewGame(true);
window->renderWorldMap(); window->renderWorldMap();
//Load translation data //Load translation data
@ -403,7 +414,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
for (size_t i = 0; i < mContentFiles.size(); i++) for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions); Compiler::registerExtensions (mExtensions);
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
@ -427,12 +438,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer(); mechanics->buildPlayer();
window->updatePlayer(); window->updatePlayer();
if (!mNewGame) // load cell
{ ESM::Position pos;
// load cell MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) { if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos); world->changeToExteriorCell (pos);
} }
@ -442,7 +453,11 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
} }
} }
else else
mEnvironment.getWorld()->startNewGame(); {
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event; Ogre::FrameEvent event;
event.timeSinceLastEvent = 0; event.timeSinceLastEvent = 0;
@ -468,7 +483,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go() void OMW::Engine::go()
{ {
assert (!mCellName.empty());
assert (!mContentFiles.empty()); assert (!mContentFiles.empty());
assert (!mOgre); assert (!mOgre);
@ -489,8 +503,14 @@ void OMW::Engine::go()
if (!mStartupScript.empty()) if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu
if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else
MWBase::Environment::get().getStateManager()->newGame (true);
// Start the main rendering loop // Start the main rendering loop
while (!mEnvironment.getRequestExit()) while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();
// Save user settings // Save user settings

View file

@ -71,7 +71,7 @@ namespace OMW
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
int mFpsLevel; int mFpsLevel;
bool mVerboseScripts; bool mVerboseScripts;
bool mNewGame; bool mSkipMenu;
bool mUseSound; bool mUseSound;
bool mCompileAll; bool mCompileAll;
std::string mFocusName; std::string mFocusName;
@ -151,8 +151,7 @@ namespace OMW
/// Disable or enable all sounds /// Disable or enable all sounds
void setSoundUsage(bool soundUsage); void setSoundUsage(bool soundUsage);
/// Start as a new game. void setSkipMenu (bool skipMenu);
void setNewGame(bool newGame);
void setGrabMouse(bool grab) { mGrab = grab; } void setGrabMouse(bool grab) { mGrab = grab; }

View file

@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include <cstdio> #include <cstdio>
#include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <SDL.h> #include <SDL.h>
@ -30,8 +31,6 @@ extern int is_debugger_attached(void);
#include <OSX/macUtils.h> #include <OSX/macUtils.h>
#endif #endif
#include "config.hpp"
#include <boost/version.hpp> #include <boost/version.hpp>
/** /**
* Workaround for problems with whitespaces in paths in older versions of Boost library * Workaround for problems with whitespaces in paths in older versions of Boost library
@ -116,7 +115,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("resources", bpo::value<std::string>()->default_value("resources"), ("resources", bpo::value<std::string>()->default_value("resources"),
"set resources directory") "set resources directory")
("start", bpo::value<std::string>()->default_value("Beshara"), ("start", bpo::value<std::string>()->default_value(""),
"set initial cell") "set initial cell")
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "") ("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
@ -137,8 +136,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-run", bpo::value<std::string>()->default_value(""), ("script-run", bpo::value<std::string>()->default_value(""),
"select a file containing a list of console commands that is executed on startup") "select a file containing a list of console commands that is executed on startup")
("new-game", bpo::value<bool>()->implicit_value(true) ("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "activate char gen/new game mechanics") ->default_value(false), "skip main menu on game startup")
("fs-strict", bpo::value<bool>()->implicit_value(true) ("fs-strict", bpo::value<bool>()->implicit_value(true)
->default_value(false), "strict file system handling (no case folding)") ->default_value(false), "strict file system handling (no case folding)")
@ -232,7 +231,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// startup-settings // startup-settings
engine.setCell(variables["start"].as<std::string>()); engine.setCell(variables["start"].as<std::string>());
engine.setNewGame(variables["new-game"].as<bool>()); engine.setSkipMenu (variables["skip-menu"].as<bool>());
// other settings // other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>()); engine.setSoundUsage(!variables["no-sound"].as<bool>());

View file

@ -11,13 +11,14 @@
#include "mechanicsmanager.hpp" #include "mechanicsmanager.hpp"
#include "inputmanager.hpp" #include "inputmanager.hpp"
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include "statemanager.hpp"
MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment *MWBase::Environment::sThis = 0;
bool MWBase::Environment::sExit = false;
MWBase::Environment::Environment() MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0) mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0),
mStateManager (0)
{ {
assert (!sThis); assert (!sThis);
sThis = this; sThis = this;
@ -69,6 +70,11 @@ void MWBase::Environment::setInputManager (InputManager *inputManager)
mInputManager = inputManager; mInputManager = inputManager;
} }
void MWBase::Environment::setStateManager (StateManager *stateManager)
{
mStateManager = stateManager;
}
void MWBase::Environment::setFrameDuration (float duration) void MWBase::Environment::setFrameDuration (float duration)
{ {
mFrameDuration = duration; mFrameDuration = duration;
@ -122,6 +128,12 @@ MWBase::InputManager *MWBase::Environment::getInputManager() const
return mInputManager; return mInputManager;
} }
MWBase::StateManager *MWBase::Environment::getStateManager() const
{
assert (mStateManager);
return mStateManager;
}
float MWBase::Environment::getFrameDuration() const float MWBase::Environment::getFrameDuration() const
{ {
return mFrameDuration; return mFrameDuration;
@ -152,6 +164,9 @@ void MWBase::Environment::cleanup()
delete mInputManager; delete mInputManager;
mInputManager = 0; mInputManager = 0;
delete mStateManager;
mStateManager = 0;
} }
const MWBase::Environment& MWBase::Environment::get() const MWBase::Environment& MWBase::Environment::get()

View file

@ -11,6 +11,7 @@ namespace MWBase
class MechanicsManager; class MechanicsManager;
class InputManager; class InputManager;
class WindowManager; class WindowManager;
class StateManager;
/// \brief Central hub for mw-subsystems /// \brief Central hub for mw-subsystems
/// ///
@ -30,10 +31,9 @@ namespace MWBase
DialogueManager *mDialogueManager; DialogueManager *mDialogueManager;
Journal *mJournal; Journal *mJournal;
InputManager *mInputManager; InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration; float mFrameDuration;
static bool sExit;
Environment (const Environment&); Environment (const Environment&);
///< not implemented ///< not implemented
@ -46,9 +46,6 @@ namespace MWBase
~Environment(); ~Environment();
static void setRequestExit () { sExit = true; }
static bool getRequestExit () { return sExit; }
void setWorld (World *world); void setWorld (World *world);
void setSoundManager (SoundManager *soundManager); void setSoundManager (SoundManager *soundManager);
@ -65,6 +62,8 @@ namespace MWBase
void setInputManager (InputManager *inputManager); void setInputManager (InputManager *inputManager);
void setStateManager (StateManager *stateManager);
void setFrameDuration (float duration); void setFrameDuration (float duration);
///< Set length of current frame in seconds. ///< Set length of current frame in seconds.
@ -84,6 +83,8 @@ namespace MWBase
InputManager *getInputManager() const; InputManager *getInputManager() const;
StateManager *getStateManager() const;
float getFrameDuration() const; float getFrameDuration() const;
void cleanup(); void cleanup();

View file

@ -5,10 +5,18 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <libs/platform/stdint.h>
#include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/journalentry.hpp"
#include "../mwdialogue/topic.hpp" #include "../mwdialogue/topic.hpp"
#include "../mwdialogue/quest.hpp" #include "../mwdialogue/quest.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace MWBase namespace MWBase
{ {
/// \brief Interface for the player's journal (implemented in MWDialogue) /// \brief Interface for the player's journal (implemented in MWDialogue)
@ -46,7 +54,7 @@ namespace MWBase
virtual int getJournalIndex (const std::string& id) const = 0; virtual int getJournalIndex (const std::string& id) const = 0;
///< Get the journal index. ///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId) = 0; virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0;
virtual TEntryIter begin() const = 0; virtual TEntryIter begin() const = 0;
///< Iterator pointing to the begin of the main journal. ///< Iterator pointing to the begin of the main journal.
@ -69,6 +77,12 @@ namespace MWBase
virtual TTopicIter topicEnd() const = 0; virtual TTopicIter topicEnd() const = 0;
///< Iterator pointing past the last topic. ///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };
} }

View file

@ -136,33 +136,35 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state. ///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not ///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored. /// in the scene should be ignored.
/// ///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop /// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run /// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for ///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored. /// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment) /// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
virtual void toggleAI() = 0; virtual void toggleAI() = 0;
virtual bool isAIActive() = 0; virtual bool isAIActive() = 0;
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0; virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0;
///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor) ///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor)
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0; virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
virtual void playerLoaded() = 0;
}; };
} }

View file

@ -35,8 +35,6 @@ namespace MWBase
virtual ~ScriptManager() {} virtual ~ScriptManager() {}
virtual void resetGlobalScripts() = 0;
virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet) ///< Run the script with the given name (compile first, if not compiled yet)

View file

@ -149,6 +149,8 @@ namespace MWBase
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0; virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0;
virtual void clear() = 0;
}; };
} }

View file

@ -0,0 +1,81 @@
#ifndef GAME_MWSTATE_STATEMANAGER_H
#define GAME_MWSTATE_STATEMANAGER_H
#include <vector>
#include <string>
namespace MWState
{
struct Slot;
class Character;
}
namespace MWBase
{
/// \brief Interface for game state manager (implemented in MWState)
class StateManager
{
public:
enum State
{
State_NoGame,
State_Ended,
State_Running
};
typedef std::vector<MWState::Character>::const_iterator CharacterIterator;
private:
StateManager (const StateManager&);
///< not implemented
StateManager& operator= (const StateManager&);
///< not implemented
public:
StateManager() {}
virtual ~StateManager() {}
virtual void requestQuit() = 0;
virtual bool hasQuitRequest() const = 0;
virtual void askLoadRecent() = 0;
virtual State getState() const = 0;
virtual void newGame (bool bypass = false) = 0;
///< Start a new game.
///
/// \param bypass Skip new game mechanics.
virtual void endGame() = 0;
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
///
/// \note Slot must belong to the current character.
virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
///< Load a saved game file from \a slot.
///
/// \note \a slot must belong to \a character.
virtual MWState::Character *getCurrentCharacter (bool create = true) = 0;
///< \param create Create a new character, if there is no current character.
virtual CharacterIterator characterBegin() = 0;
///< Any call to SaveGame and getCurrentCharacter can invalidate the returned
/// iterator.
virtual CharacterIterator characterEnd() = 0;
virtual void update (float duration) = 0;
};
}
#endif

View file

@ -33,6 +33,8 @@ namespace OEngine
namespace ESM namespace ESM
{ {
struct Class; struct Class;
class ESMReader;
class ESMWriter;
} }
namespace MWWorld namespace MWWorld
@ -279,12 +281,19 @@ namespace MWBase
virtual const Translation::Storage& getTranslationDataStorage() const = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0;
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
virtual Loading::Listener* getLoadingScreen() = 0; virtual Loading::Listener* getLoadingScreen() = 0;
/// Should the cursor be visible? /// Should the cursor be visible?
virtual bool getCursorVisible() = 0; virtual bool getCursorVisible() = 0;
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };
} }

View file

@ -2,6 +2,7 @@
#define GAME_MWBASE_WORLD_H #define GAME_MWBASE_WORLD_H
#include <vector> #include <vector>
#include <map>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -30,12 +31,14 @@ namespace OEngine
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
class ESMWriter;
struct Position; struct Position;
struct Cell; struct Cell;
struct Class; struct Class;
struct Potion; struct Potion;
struct Spell; struct Spell;
struct NPC; struct NPC;
struct CellId;
} }
namespace MWRender namespace MWRender
@ -94,13 +97,26 @@ namespace MWBase
virtual void startNewGame() = 0; virtual void startNewGame() = 0;
virtual void clear() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 0;
virtual OEngine::Render::Fader* getFader() = 0; virtual OEngine::Render::Fader* getFader() = 0;
///< \ŧodo remove this function. Rendering details should not be exposed. ///< \todo remove this function. Rendering details should not be exposed.
virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;
virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;
virtual void useDeathCamera() = 0;
virtual void setWaterHeight(const float height) = 0; virtual void setWaterHeight(const float height) = 0;
virtual void toggleWater() = 0; virtual void toggleWater() = 0;
@ -139,16 +155,26 @@ namespace MWBase
virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0;
///< see MWRender::LocalMap::isPositionExplored ///< see MWRender::LocalMap::isPositionExplored
virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; virtual void setGlobalInt (const std::string& name, int value) = 0;
///< Set value independently from real type.
virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; virtual void setGlobalFloat (const std::string& name, float value) = 0;
///< Set value independently from real type.
virtual int getGlobalInt (const std::string& name) const = 0;
///< Get value independently from real type.
virtual float getGlobalFloat (const std::string& name) const = 0;
///< Get value independently from real type.
virtual char getGlobalVariableType (const std::string& name) const = 0; virtual char getGlobalVariableType (const std::string& name) const = 0;
///< Return ' ', if there is no global variable with this name. ///< Return ' ', if there is no global variable with this name.
virtual std::vector<std::string> getGlobals () const = 0; virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0;
///< Return name of the cell.
virtual std::string getCurrentCellName() const = 0; ///
/// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name.
virtual void removeRefScript (MWWorld::RefData *ref) = 0; virtual void removeRefScript (MWWorld::RefData *ref) = 0;
//< Remove the script attached to ref from mLocalScripts //< Remove the script attached to ref from mLocalScripts
@ -185,8 +211,12 @@ namespace MWBase
virtual void setDay (int day) = 0; virtual void setDay (int day) = 0;
///< Set in-game time day. ///< Set in-game time day.
virtual int getDay() = 0; virtual int getDay() const = 0;
virtual int getMonth() = 0; virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0; virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp. ///< Return current in-game time stamp.
@ -215,6 +245,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0; virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell. ///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell. ///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -336,7 +368,7 @@ namespace MWBase
virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
///Is the head of the creature underwater? ///Is the head of the creature underwater?
virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0; virtual void togglePOV() = 0;
@ -384,6 +416,7 @@ namespace MWBase
virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0; virtual void stopVideo() = 0;
virtual void frameStarted (float dt, bool paused) = 0; virtual void frameStarted (float dt, bool paused) = 0;
virtual void screenshot (Ogre::Image& image, int w, int h) = 0;
/// Find default position inside exterior cell specified by name /// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise /// \return false if exterior with given name not exists, true otherwise
@ -428,6 +461,8 @@ namespace MWBase
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0; const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night? // Are we in an exterior or pseudo-exterior cell and it's night?

View file

@ -366,12 +366,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell);
} }
float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref =
ptr.get<ESM::Armor>(); ptr.get<ESM::Armor>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -79,7 +79,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
}; };

View file

@ -189,12 +189,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell);
} }
float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Book> *ref = MWWorld::LiveCellRef<ESM::Book> *ref =
ptr.get<ESM::Book>(); ptr.get<ESM::Book>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -58,7 +58,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;

View file

@ -279,12 +279,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell);
} }
float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Clothing> *ref = MWWorld::LiveCellRef<ESM::Clothing> *ref =
ptr.get<ESM::Clothing>(); ptr.get<ESM::Clothing>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -71,7 +71,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;

View file

@ -2,6 +2,7 @@
#include "container.hpp" #include "container.hpp"
#include <components/esm/loadcont.hpp> #include <components/esm/loadcont.hpp>
#include <components/esm/containerstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -258,4 +259,26 @@ namespace MWClass
return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell); return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell);
} }
void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);
}
void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory);
}
} }

View file

@ -54,6 +54,14 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const; virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object ///< Unlock object
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
static void registerSelf(); static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;

View file

@ -2,6 +2,7 @@
#include "creature.hpp" #include "creature.hpp"
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp>
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/magiceffects.hpp"
@ -332,7 +333,7 @@ namespace MWClass
if (damage > 0) if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
} }
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -528,8 +529,8 @@ namespace MWClass
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f; moveSpeed = 0.0f;
else if(mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
world->isLevitationEnabled()) world->isLevitationEnabled()))
{ {
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
@ -758,6 +759,28 @@ namespace MWClass
return 0; return 0;
} }
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
readState (state2.mInventory);
}
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
writeState (state2.mInventory);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect; const ESM::GameSetting *Creature::fEncumberedMoveEffect;

View file

@ -91,7 +91,7 @@ namespace MWClass
virtual bool isEssential (const MWWorld::Ptr& ptr) const; virtual bool isEssential (const MWWorld::Ptr& ptr) const;
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const; virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const; virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
@ -125,6 +125,14 @@ namespace MWClass
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -2,6 +2,7 @@
#include "light.hpp" #include "light.hpp"
#include <components/esm/loadligh.hpp> #include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -269,4 +270,24 @@ namespace MWClass
} }
return std::make_pair(1,""); return std::make_pair(1,"");
} }
void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::LightState& state2 = dynamic_cast<const ESM::LightState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
}
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::LightState& state2 = dynamic_cast<ESM::LightState&> (state);
ensureCustomData (ptr);
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
}
} }

View file

@ -71,6 +71,14 @@ namespace MWClass
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -155,11 +155,8 @@ namespace MWClass
int count = ptr.getRefData().getCount(); int count = ptr.getRefData().getCount();
bool gold = isGold(ptr); bool gold = isGold(ptr);
if (gold)
if (gold && ptr.getCellRef().mGoldValue != 1) count *= getValue(ptr);
count = ptr.getCellRef().mGoldValue;
else if (gold)
count *= ref->mBase->mData.mValue;
std::string countString; std::string countString;
if (!gold) if (!gold)
@ -204,7 +201,7 @@ namespace MWClass
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
if (isGold(ptr)) { if (isGold(ptr)) {
int goldAmount = ptr.getRefData().getCount(); int goldAmount = getValue(ptr) * ptr.getRefData().getCount();
std::string base = "Gold_001"; std::string base = "Gold_001";
if (goldAmount >= 100) if (goldAmount >= 100)
@ -223,6 +220,7 @@ namespace MWClass
newRef.getPtr().get<ESM::Miscellaneous>(); newRef.getPtr().get<ESM::Miscellaneous>();
newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell);
newPtr.getCellRef().mGoldValue = goldAmount; newPtr.getCellRef().mGoldValue = goldAmount;
newPtr.getRefData().setCount(1);
} else { } else {
MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = MWWorld::LiveCellRef<ESM::Miscellaneous> *ref =
ptr.get<ESM::Miscellaneous>(); ptr.get<ESM::Miscellaneous>();

View file

@ -7,6 +7,7 @@
#include <components/esm/loadmgef.hpp> #include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -536,7 +537,7 @@ namespace MWClass
weapon.getCellRef().mCharge = weapmaxhealth; weapon.getCellRef().mCharge = weapmaxhealth;
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
} }
if (!MWBase::Environment::get().getWorld()->getGodModeState()) if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1, weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge);
@ -603,10 +604,7 @@ namespace MWClass
{ {
MWMechanics::CastSpell cast(ptr, victim); MWMechanics::CastSpell cast(ptr, victim);
cast.mHitPosition = hitPosition; cast.mHitPosition = hitPosition;
bool success = cast.cast(weapon); cast.cast(weapon);
if (ptr.getRefData().getHandle() == "player" && success)
skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3);
} }
} }
@ -726,6 +724,9 @@ namespace MWClass
if (armorref.mCharge == 0) if (armorref.mCharge == 0)
inv.unequipItem(armor, ptr); inv.unequipItem(armor, ptr);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, get(armor).getEquipmentSkill(armor), 0);
switch(get(armor).getEquipmentSkill(armor)) switch(get(armor).getEquipmentSkill(armor))
{ {
case ESM::Skill::LightArmor: case ESM::Skill::LightArmor:
@ -739,6 +740,8 @@ namespace MWClass
break; break;
} }
} }
else if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
} }
} }
@ -999,7 +1002,7 @@ namespace MWClass
return ref->mBase->mFlags & ESM::NPC::Essential; return ref->mBase->mFlags & ESM::NPC::Essential;
} }
void Npc::registerSelf() void Npc::registerSelf()
{ {
boost::shared_ptr<Class> instance (new Npc); boost::shared_ptr<Class> instance (new Npc);
@ -1268,6 +1271,28 @@ namespace MWClass
return 0; return 0;
} }
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
readState (state2.mInventory);
}
void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
writeState (state2.mInventory);
}
const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect; const ESM::GameSetting *Npc::fEncumberedMoveEffect;

View file

@ -137,7 +137,7 @@ namespace MWClass
///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable) ///< Is \a ptr essential? (i.e. may losing \a ptr make the game unwinnable)
virtual int getServices (const MWWorld::Ptr& actor) const; virtual int getServices (const MWWorld::Ptr& actor) const;
virtual bool isPersistent (const MWWorld::Ptr& ptr) const; virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const; virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
@ -158,6 +158,14 @@ namespace MWClass
virtual bool isNpc() const { virtual bool isNpc() const {
return true; return true;
} }
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -429,12 +429,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell);
} }
float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Weapon> *ref = MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>(); ptr.get<ESM::Weapon>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -84,7 +84,7 @@ namespace MWClass
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
}; };
} }

View file

@ -286,7 +286,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript); executeScript (info->mResultScript);
@ -451,7 +451,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript); executeScript (info->mResultScript);
} }
} }

View file

@ -171,7 +171,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
// internally all globals are float :( // internally all globals are float :(
return select.selectCompare ( return select.selectCompare (
MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat); MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName()));
case SelectWrapper::Function_Local: case SelectWrapper::Function_Local:
{ {

View file

@ -3,6 +3,8 @@
#include <stdexcept> #include <stdexcept>
#include <components/esm/journalentry.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -10,23 +12,55 @@
namespace MWDialogue namespace MWDialogue
{ {
JournalEntry::JournalEntry() {} Entry::Entry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) Entry::Entry (const std::string& topic, const std::string& infoId)
: mTopic (topic), mInfoId (infoId) : mInfoId (infoId)
{}
std::string JournalEntry::getText (const MWWorld::ESMStore& store) const
{ {
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
store.get<ESM::Dialogue>().find (mTopic); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin()); for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId) if (iter->mId == mInfoId)
return iter->mResponse; {
/// \todo text replacement
mText = iter->mResponse;
return;
}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic);
}
Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {}
std::string Entry::getText() const
{
return mText;
}
void Entry::write (ESM::JournalEntry& entry) const
{
entry.mInfo = mInfoId;
entry.mText = mText;
entry.mActorName = mActorName;
}
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: Entry (topic, infoId), mTopic (topic)
{}
JournalEntry::JournalEntry (const ESM::JournalEntry& record)
: Entry (record), mTopic (record.mTopic)
{}
void JournalEntry::write (ESM::JournalEntry& entry) const
{
Entry::write (entry);
entry.mTopic = mTopic;
} }
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)
@ -49,6 +83,7 @@ namespace MWDialogue
throw std::runtime_error ("unknown journal index for topic " + topic); throw std::runtime_error ("unknown journal index for topic " + topic);
} }
StampedJournalEntry::StampedJournalEntry() StampedJournalEntry::StampedJournalEntry()
: mDay (0), mMonth (0), mDayOfMonth (0) : mDay (0), mMonth (0), mDayOfMonth (0)
{} {}
@ -58,11 +93,24 @@ namespace MWDialogue
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) : JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{} {}
StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)
: JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth),
mDayOfMonth (record.mDayOfMonth)
{}
void StampedJournalEntry::write (ESM::JournalEntry& entry) const
{
JournalEntry::write (entry);
entry.mDay = mDay;
entry.mMonth = mMonth;
entry.mDayOfMonth = mDayOfMonth;
}
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index)
{ {
int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong; int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed");
int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month");
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day");
return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth);
} }

View file

@ -3,24 +3,45 @@
#include <string> #include <string>
namespace MWWorld namespace ESM
{ {
struct ESMStore; struct JournalEntry;
} }
namespace MWDialogue namespace MWDialogue
{ {
/// \brief A quest or dialogue entry /// \brief Basic quest/dialogue/topic entry
struct JournalEntry struct Entry
{
std::string mInfoId;
std::string mText;
std::string mActorName; // optional
Entry();
Entry (const std::string& topic, const std::string& infoId);
Entry (const ESM::JournalEntry& record);
std::string getText() const;
void write (ESM::JournalEntry& entry) const;
};
/// \brief A dialogue entry
///
/// Same as entry, but store TopicID
struct JournalEntry : public Entry
{ {
std::string mTopic; std::string mTopic;
std::string mInfoId;
JournalEntry(); JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId); JournalEntry (const std::string& topic, const std::string& infoId);
std::string getText (const MWWorld::ESMStore& store) const; JournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static JournalEntry makeFromQuest (const std::string& topic, int index); static JournalEntry makeFromQuest (const std::string& topic, int index);
@ -39,6 +60,10 @@ namespace MWDialogue
StampedJournalEntry (const std::string& topic, const std::string& infoId, StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth); int day, int month, int dayOfMonth);
StampedJournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static StampedJournalEntry makeFromQuest (const std::string& topic, int index); static StampedJournalEntry makeFromQuest (const std::string& topic, int index);
}; };
} }

View file

@ -1,6 +1,13 @@
#include "journalimp.hpp" #include "journalimp.hpp"
#include <iterator>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/queststate.hpp>
#include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -26,6 +33,38 @@ namespace MWDialogue
return iter->second; return iter->second;
} }
Topic& Journal::getTopic (const std::string& id)
{
TTopicContainer::iterator iter = mTopics.find (id);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (id, Topic (id)));
iter = result.first;
}
return iter->second;
}
bool Journal::isThere (const std::string& topicId, const std::string& infoId) const
{
if (const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search (topicId))
{
if (infoId.empty())
return true;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == infoId)
return true;
}
return false;
}
Journal::Journal() Journal::Journal()
{} {}
@ -64,19 +103,13 @@ namespace MWDialogue
quest.setIndex (index); quest.setIndex (index);
} }
void Journal::addTopic (const std::string& topicId, const std::string& infoId) void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName)
{ {
TTopicContainer::iterator iter = mTopics.find (topicId); Topic& topic = getTopic (topicId);
if (iter==mTopics.end()) JournalEntry entry(topicId, infoId);
{ entry.mActorName = actorName;
std::pair<TTopicContainer::iterator, bool> result topic.addEntry (entry);
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
iter = result.first;
}
iter->second.addEntry (JournalEntry (topicId, infoId));
} }
int Journal::getJournalIndex (const std::string& id) const int Journal::getJournalIndex (const std::string& id) const
@ -118,4 +151,106 @@ namespace MWDialogue
{ {
return mTopics.end(); return mTopics.end();
} }
int Journal::countSavedGameRecords() const
{
int count = static_cast<int> (mQuests.size());
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
count += std::distance (mJournal.begin(), mJournal.end());
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
return count;
}
void Journal::write (ESM::ESMWriter& writer) const
{
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
{
const Quest& quest = iter->second;
ESM::QuestState state;
quest.write (state);
writer.startRecord (ESM::REC_QUES);
state.save (writer);
writer.endRecord (ESM::REC_QUES);
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Quest;
entry.mTopic = quest.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Journal;
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
{
const Topic& topic = iter->second;
for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Topic;
entry.mTopic = topic.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
}
void Journal::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_JOUR)
{
ESM::JournalEntry record;
record.load (reader);
if (isThere (record.mTopic, record.mInfo))
switch (record.mType)
{
case ESM::JournalEntry::Type_Quest:
getQuest (record.mTopic).insertEntry (record);
break;
case ESM::JournalEntry::Type_Journal:
mJournal.push_back (record);
break;
case ESM::JournalEntry::Type_Topic:
getTopic (record.mTopic).insertEntry (record);
break;
}
}
else if (type==ESM::REC_QUES)
{
ESM::QuestState record;
record.load (reader);
if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record));
}
}
} }

View file

@ -15,8 +15,14 @@ namespace MWDialogue
TQuestContainer mQuests; TQuestContainer mQuests;
TTopicContainer mTopics; TTopicContainer mTopics;
private:
Quest& getQuest (const std::string& id); Quest& getQuest (const std::string& id);
Topic& getTopic (const std::string& id);
bool isThere (const std::string& topicId, const std::string& infoId = "") const;
public: public:
Journal(); Journal();
@ -32,7 +38,7 @@ namespace MWDialogue
virtual int getJournalIndex (const std::string& id) const; virtual int getJournalIndex (const std::string& id) const;
///< Get the journal index. ///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId); virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName);
virtual TEntryIter begin() const; virtual TEntryIter begin() const;
///< Iterator pointing to the begin of the main journal. ///< Iterator pointing to the begin of the main journal.
@ -55,6 +61,12 @@ namespace MWDialogue
virtual TTopicIter topicEnd() const; virtual TTopicIter topicEnd() const;
///< Iterator pointing past the last topic. ///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
}; };
} }

View file

@ -1,6 +1,8 @@
#include "quest.hpp" #include "quest.hpp"
#include <components/esm/queststate.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -16,7 +18,11 @@ namespace MWDialogue
: Topic (topic), mIndex (0), mFinished (false) : Topic (topic), mIndex (0), mFinished (false)
{} {}
const std::string Quest::getName() const Quest::Quest (const ESM::QuestState& state)
: Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0)
{}
std::string Quest::getName() const
{ {
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
@ -82,12 +88,20 @@ namespace MWDialogue
if (index==-1) if (index==-1)
throw std::runtime_error ("unknown journal entry for topic " + mTopic); throw std::runtime_error ("unknown journal entry for topic " + mTopic);
setIndex (index); if (index > mIndex)
setIndex (index);
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
if (*iter==entry.mInfoId) if (iter->mInfoId==entry.mInfoId)
return; return;
mEntries.push_back (entry.mInfoId); mEntries.push_back (entry); // we want slicing here
}
void Quest::write (ESM::QuestState& state) const
{
state.mTopic = getTopic();
state.mState = mIndex;
state.mFinished = mFinished;
} }
} }

View file

@ -3,9 +3,14 @@
#include "topic.hpp" #include "topic.hpp"
namespace ESM
{
struct QuestState;
}
namespace MWDialogue namespace MWDialogue
{ {
/// \brief A quest in progress or a compelted quest /// \brief A quest in progress or a completed quest
class Quest : public Topic class Quest : public Topic
{ {
int mIndex; int mIndex;
@ -17,13 +22,15 @@ namespace MWDialogue
Quest (const std::string& topic); Quest (const std::string& topic);
const std::string getName() const; Quest (const ESM::QuestState& state);
virtual std::string getName() const;
///< May be an empty string ///< May be an empty string
int getIndex() const; int getIndex() const;
void setIndex (int index); void setIndex (int index);
///< Calling this function with a non-existant index while throw an exception. ///< Calling this function with a non-existent index will throw an exception.
bool isFinished() const; bool isFinished() const;
@ -31,6 +38,8 @@ namespace MWDialogue
///< Add entry and adjust index accordingly. ///< Add entry and adjust index accordingly.
/// ///
/// \note Redundant entries are ignored, but the index is still adjusted. /// \note Redundant entries are ignored, but the index is still adjusted.
void write (ESM::QuestState& state) const;
}; };
} }

View file

@ -1,6 +1,9 @@
#include "topic.hpp" #include "topic.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
namespace MWDialogue namespace MWDialogue
@ -9,7 +12,8 @@ namespace MWDialogue
{} {}
Topic::Topic (const std::string& topic) Topic::Topic (const std::string& topic)
: mTopic (topic) : mTopic (topic), mName (
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic)->mId)
{} {}
Topic::~Topic() Topic::~Topic()
@ -20,11 +24,29 @@ namespace MWDialogue
if (entry.mTopic!=mTopic) if (entry.mTopic!=mTopic)
throw std::runtime_error ("topic does not match: " + mTopic); throw std::runtime_error ("topic does not match: " + mTopic);
for (TEntryIter iter = begin(); iter!=end(); ++iter) // bail out if we already have heard this
if (*iter==entry.mInfoId) for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)
{
if (it->mInfoId == entry.mInfoId)
return; return;
}
mEntries.push_back (entry.mInfoId); mEntries.push_back (entry); // we want slicing here
}
void Topic::insertEntry (const ESM::JournalEntry& entry)
{
mEntries.push_back (entry);
}
std::string Topic::getTopic() const
{
return mTopic;
}
std::string Topic::getName() const
{
return mName;
} }
Topic::TEntryIter Topic::begin() const Topic::TEntryIter Topic::begin() const

View file

@ -6,6 +6,11 @@
#include "journalentry.hpp" #include "journalentry.hpp"
namespace ESM
{
struct JournalEntry;
}
namespace MWDialogue namespace MWDialogue
{ {
/// \brief Collection of seen responses for a topic /// \brief Collection of seen responses for a topic
@ -13,13 +18,14 @@ namespace MWDialogue
{ {
public: public:
typedef std::vector<std::string> TEntryContainer; typedef std::vector<Entry> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter; typedef TEntryContainer::const_iterator TEntryIter;
protected: protected:
std::string mTopic; std::string mTopic;
TEntryContainer mEntries; // info-IDs std::string mName;
TEntryContainer mEntries;
public: public:
@ -34,7 +40,13 @@ namespace MWDialogue
/// ///
/// \note Redundant entries are ignored. /// \note Redundant entries are ignored.
std::string const & getName () const { return mTopic; } void insertEntry (const ESM::JournalEntry& entry);
///< Add entry without checking for redundant entries or modifying the state of the
/// topic otherwise
std::string getTopic() const;
virtual std::string getName() const;
TEntryIter begin() const; TEntryIter begin() const;
///< Iterator pointing to the begin of the journal for this topic. ///< Iterator pointing to the begin of the journal for this topic.

View file

@ -33,8 +33,7 @@ namespace MWGui
getWidget(mBirthList, "BirthsignList"); getWidget(mBirthList, "BirthsignList");
mBirthList->setScrollVisible(true); mBirthList->setScrollVisible(true);
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept);
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
MyGUI::Button* backButton; MyGUI::Button* backButton;
@ -97,6 +96,14 @@ namespace MWGui
eventDone(this); eventDone(this);
} }
void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)
{
onSelectBirth(_sender, _index);
if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void BirthDialog::onBackClicked(MyGUI::Widget* _sender) void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
{ {
eventBack(); eventBack();

View file

@ -38,6 +38,7 @@ namespace MWGui
protected: protected:
void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); void onSelectBirth(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t index);
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender);

View file

@ -10,7 +10,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp" #include "../mwworld/fallback.hpp"
@ -47,9 +47,8 @@ namespace
void updatePlayerHealth() void updatePlayerHealth()
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player);
npcStats.updateHealth();
creatureStats.updateHealth();
} }
} }

View file

@ -82,8 +82,7 @@ namespace MWGui
getWidget(mClassList, "ClassList"); getWidget(mClassList, "ClassList");
mClassList->setScrollVisible(true); mClassList->setScrollVisible(true);
mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept);
mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
getWidget(mClassImage, "ClassImage"); getWidget(mClassImage, "ClassImage");
@ -152,6 +151,14 @@ namespace MWGui
eventBack(); eventBack();
} }
void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index)
{
onSelectClass(_sender, _index);
if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)
{ {
if (_index == MyGUI::ITEM_NONE) if (_index == MyGUI::ITEM_NONE)

View file

@ -111,6 +111,7 @@ namespace MWGui
protected: protected:
void onSelectClass(MyGUI::ListBox* _sender, size_t _index); void onSelectClass(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender);

View file

@ -235,7 +235,7 @@ namespace MWGui
mItemView->setModel (mSortModel); mItemView->setModel (mSortModel);
MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model. // or we end up using a possibly invalid model.

View file

@ -12,6 +12,8 @@
#include <boost/range/algorithm/copy.hpp> #include <boost/range/algorithm/copy.hpp>
#include <OgreUTFString.h> #include <OgreUTFString.h>
#include <OgreResourceGroupManager.h>
namespace namespace
{ {
int convertFromHex(std::string hex) int convertFromHex(std::string hex)
@ -288,6 +290,16 @@ namespace MWGui
MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox", MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount())); mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
// So if the image isn't found, try appending the size.
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image))
{
std::stringstream str;
str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind("."));
image = str.str();
}
box->setImageTexture("bookart\\" + image); box->setImageTexture("bookart\\" + image);
box->setProperty("NeedMouse", "false"); box->setProperty("NeedMouse", "false");
} }

View file

@ -55,7 +55,7 @@ namespace MWGui
getWidget(mRightPane, "RightPane"); getWidget(mRightPane, "RightPane");
getWidget(mArmorRating, "ArmorRating"); getWidget(mArmorRating, "ArmorRating");
mAvatar->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked); mAvatarImage->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onAvatarClicked);
getWidget(mItemView, "ItemView"); getWidget(mItemView, "ItemView");
mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected); mItemView->eventItemClicked += MyGUI::newDelegate(this, &InventoryWindow::onItemSelected);
@ -76,11 +76,12 @@ namespace MWGui
void InventoryWindow::adjustPanes() void InventoryWindow::adjustPanes()
{ {
const float aspect = 0.5; // fixed aspect ratio for the left pane const float aspect = 0.5; // fixed aspect ratio for the avatar image
mLeftPane->setSize( (mMainWidget->getSize().height-44) * aspect, mMainWidget->getSize().height-44 ); float leftPaneWidth = (mMainWidget->getSize().height-44-mArmorRating->getHeight()) * aspect;
mRightPane->setCoord( mLeftPane->getPosition().left + (mMainWidget->getSize().height-44) * aspect + 4, mLeftPane->setSize( leftPaneWidth, mMainWidget->getSize().height-44 );
mRightPane->setCoord( mLeftPane->getPosition().left + leftPaneWidth + 4,
mRightPane->getPosition().top, mRightPane->getPosition().top,
mMainWidget->getSize().width - 12 - (mMainWidget->getSize().height-44) * aspect - 15, mMainWidget->getSize().width - 12 - leftPaneWidth - 15,
mMainWidget->getSize().height-44 ); mMainWidget->getSize().height-44 );
} }
@ -418,9 +419,9 @@ namespace MWGui
else else
{ {
MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left);
MyGUI::IntPoint relPos = mousePos - mAvatar->getAbsolutePosition (); MyGUI::IntPoint relPos = mousePos - mAvatarImage->getAbsolutePosition ();
int realX = int(float(relPos.left) / float(mAvatar->getSize().width) * 512.f ); int realX = int(float(relPos.left) / float(mAvatarImage->getSize().width) * 512.f );
int realY = int(float(relPos.top) / float(mAvatar->getSize().height) * 1024.f ); int realY = int(float(relPos.top) / float(mAvatarImage->getSize().height) * 1024.f );
MWWorld::Ptr itemSelected = getAvatarSelectedItem (realX, realY); MWWorld::Ptr itemSelected = getAvatarSelectedItem (realX, realY);
if (itemSelected.isEmpty ()) if (itemSelected.isEmpty ())
@ -487,11 +488,18 @@ namespace MWGui
if (mPreviewDirty) if (mPreviewDirty)
{ {
mPreviewDirty = false; mPreviewDirty = false;
MyGUI::IntSize size = mAvatar->getSize(); MyGUI::IntSize size = mAvatarImage->getSize();
mPreview.update (size.width, size.height); mPreview.update (size.width, size.height);
mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024)));
mAvatarImage->setImageTexture("CharacterPreview"); mAvatarImage->setImageTexture("CharacterPreview");
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height)));
mAvatarImage->setImageTile(MyGUI::IntSize(std::min(512, size.width), std::min(1024, size.height)));
mArmorRating->setCaptionWithReplacing ("#{sArmor}: "
+ boost::lexical_cast<std::string>(static_cast<int>(MWWorld::Class::get(mPtr).getArmorRating(mPtr))));
if (mArmorRating->getTextSize().width > mArmorRating->getSize().width)
mArmorRating->setCaptionWithReplacing (boost::lexical_cast<std::string>(static_cast<int>(MWWorld::Class::get(mPtr).getArmorRating(mPtr))));
} }
} }
@ -502,9 +510,6 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells(); MWBase::Environment::get().getWindowManager()->getSpellWindow()->updateSpells();
mPreviewDirty = true; mPreviewDirty = true;
mArmorRating->setCaptionWithReplacing ("#{sArmor}: "
+ boost::lexical_cast<std::string>(static_cast<int>(MWWorld::Class::get(mPtr).getArmorRating(mPtr))));
} }
void InventoryWindow::pickUpObject (MWWorld::Ptr object) void InventoryWindow::pickUpObject (MWWorld::Ptr object)
@ -551,9 +556,4 @@ namespace MWGui
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count);
} }
MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord ()
{
return mAvatar->getAbsoluteCoord ();
}
} }

View file

@ -31,8 +31,6 @@ namespace MWGui
void pickUpObject (MWWorld::Ptr object); void pickUpObject (MWWorld::Ptr object);
MyGUI::IntCoord getAvatarScreenCoord();
MWWorld::Ptr getAvatarSelectedItem(int x, int y); MWWorld::Ptr getAvatarSelectedItem(int x, int y);
void rebuildAvatar() { void rebuildAvatar() {

View file

@ -196,34 +196,6 @@ book JournalBooks::createEmptyJournalBook ()
typesetter->lineBreak (); typesetter->lineBreak ();
typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest.")); typesetter->write (body, to_utf8_span ("You should have gone though the starting quest and got an initial quest."));
BookTypesetter::Style* big = typesetter->createStyle ("", MyGUI::Colour::Black);
BookTypesetter::Style* test = typesetter->createStyle ("MonoFont", MyGUI::Colour::Blue);
typesetter->sectionBreak (20);
typesetter->write (body, to_utf8_span (
"The layout engine doesn't currently support aligning fonts to "
"their baseline within a single line so the following text looks "
"funny. In order to properly implement it, a stupidly simple "
"change is needed in MyGUI to report the where the baseline is for "
"a particular font"
));
typesetter->sectionBreak (20);
typesetter->write (big, to_utf8_span ("big text g"));
typesetter->write (body, to_utf8_span (" проверяем только в дебаге"));
typesetter->write (body, to_utf8_span (" normal g"));
typesetter->write (big, to_utf8_span (" done g"));
typesetter->sectionBreak (20);
typesetter->write (test, to_utf8_span (
"int main (int argc,\n"
" char ** argv)\n"
"{\n"
" print (\"hello world!\\n\");\n"
" return 0;\n"
"}\n"
));
return typesetter->complete (); return typesetter->complete ();
} }

View file

@ -20,8 +20,6 @@ namespace MWGui {
struct JournalViewModelImpl; struct JournalViewModelImpl;
static void injectMonthName (std::ostream & os, int month);
struct JournalViewModelImpl : JournalViewModel struct JournalViewModelImpl : JournalViewModel
{ {
typedef KeywordSearch <std::string, intptr_t> KeywordSearchT; typedef KeywordSearch <std::string, intptr_t> KeywordSearchT;
@ -237,21 +235,21 @@ struct JournalViewModelImpl : JournalViewModel
std::string getText () const std::string getText () const
{ {
return itr->getText(MWBase::Environment::get().getWorld()->getStore()); return itr->getText();
} }
Utf8Span timestamp () const Utf8Span timestamp () const
{ {
if (timestamp_buffer.empty ()) if (timestamp_buffer.empty ())
{ {
std::string dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
std::ostringstream os; std::ostringstream os;
os << itr->mDayOfMonth << ' '; os
<< itr->mDayOfMonth << ' '
injectMonthName (os, itr->mMonth); << MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth)
<< " (" << dayStr << " " << (itr->mDay + 1) << ')';
const std::string& dayStr = MyGUI::LanguageManager::getInstance().replaceTags("#{sDay}");
os << " (" << dayStr << " " << (itr->mDay + 1) << ')';
timestamp_buffer = os.str (); timestamp_buffer = os.str ();
} }
@ -272,7 +270,7 @@ struct JournalViewModelImpl : JournalViewModel
{ {
for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j) for (MWDialogue::Topic::TEntryIter j = quest->begin (); j != quest->end (); ++j)
{ {
if (i->mInfoId == *j) if (i->mInfoId == j->mInfoId)
visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i)); visitor (JournalEntryImpl <MWBase::Journal::TEntryIter> (this, i));
} }
} }
@ -292,9 +290,7 @@ struct JournalViewModelImpl : JournalViewModel
void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const void visitTopicName (TopicId topicId, boost::function <void (Utf8Span)> visitor) const
{ {
MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId); MWDialogue::Topic const & topic = * reinterpret_cast <MWDialogue::Topic const *> (topicId);
// This is to get the correct case for the topic visitor (toUtf8Span (topic.getName()));
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(topic.getName())->mId;
visitor (toUtf8Span (name));
} }
void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const void visitTopicNamesStartingWith (char character, boost::function < void (TopicId , Utf8Span) > visitor) const
@ -306,10 +302,7 @@ struct JournalViewModelImpl : JournalViewModel
if (i->first [0] != std::tolower (character, mLocale)) if (i->first [0] != std::tolower (character, mLocale))
continue; continue;
// This is to get the correct case for the topic visitor (TopicId (&i->second), toUtf8Span (i->second.getName()));
const std::string& name = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find(i->first)->mId;
visitor (TopicId (&i->second), toUtf8Span (name));
} }
} }
@ -318,24 +311,18 @@ struct JournalViewModelImpl : JournalViewModel
{ {
MWDialogue::Topic const & mTopic; MWDialogue::Topic const & mTopic;
mutable std::string source_buffer;
TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) : TopicEntryImpl (JournalViewModelImpl const * model, MWDialogue::Topic const & topic, iterator_t itr) :
BaseEntry (model, itr), mTopic (topic) BaseEntry (model, itr), mTopic (topic)
{} {}
std::string getText () const std::string getText () const
{ {
/// \todo defines are not replaced (%PCName etc). should probably be done elsewhere though since we need the actor return itr->getText();
return mTopic.getEntry (*itr).getText(MWBase::Environment::get().getWorld()->getStore());
} }
Utf8Span source () const Utf8Span source () const
{ {
if (source_buffer.empty ()) return toUtf8Span (itr->mActorName);
source_buffer = "someone";
return toUtf8Span (source_buffer);
} }
}; };
@ -351,38 +338,6 @@ struct JournalViewModelImpl : JournalViewModel
} }
}; };
static void injectMonthName (std::ostream & os, int month)
{
MyGUI::LanguageManager& lm = MyGUI::LanguageManager::getInstance();
if (month == 0)
os << lm.replaceTags ("#{sMonthMorningstar}");
else if (month == 1)
os << lm.replaceTags ("#{sMonthSunsdawn}");
else if (month == 2)
os << lm.replaceTags ("#{sMonthFirstseed}");
else if (month == 3)
os << lm.replaceTags ("#{sMonthRainshand}");
else if (month == 4)
os << lm.replaceTags ("#{sMonthSecondseed}");
else if (month == 5)
os << lm.replaceTags ("#{sMonthMidyear}");
else if (month == 6)
os << lm.replaceTags ("#{sMonthSunsheight}");
else if (month == 7)
os << lm.replaceTags ("#{sMonthLastseed}");
else if (month == 8)
os << lm.replaceTags ("#{sMonthHeartfire}");
else if (month == 9)
os << lm.replaceTags ("#{sMonthFrostfall}");
else if (month == 10)
os << lm.replaceTags ("#{sMonthSunsdusk}");
else if (month == 11)
os << lm.replaceTags ("#{sMonthEveningstar}");
else
os << month;
}
JournalViewModel::Ptr JournalViewModel::create () JournalViewModel::Ptr JournalViewModel::create ()
{ {
return boost::make_shared <JournalViewModelImpl> (); return boost::make_shared <JournalViewModelImpl> ();

View file

@ -155,7 +155,6 @@ namespace MWGui
void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender)
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player);
MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player);
if (mSpentAttributes.size() < 3) if (mSpentAttributes.size() < 3)
@ -165,15 +164,14 @@ namespace MWGui
// increase attributes // increase attributes
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
{ {
MWMechanics::AttributeValue attribute = creatureStats.getAttribute(mSpentAttributes[i]); MWMechanics::AttributeValue attribute = pcStats.getAttribute(mSpentAttributes[i]);
attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i]));
if (attribute.getBase() >= 100) if (attribute.getBase() >= 100)
attribute.setBase(100); attribute.setBase(100);
creatureStats.setAttribute(mSpentAttributes[i], attribute); pcStats.setAttribute(mSpentAttributes[i], attribute);
} }
creatureStats.levelUp();
pcStats.levelUp (); pcStats.levelUp ();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);

View file

@ -1,13 +1,14 @@
#include "mainmenu.hpp" #include "mainmenu.hpp"
#include <OgreRoot.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwstate/character.hpp"
#include "savegamedialog.hpp" #include "savegamedialog.hpp"
@ -16,90 +17,132 @@ namespace MWGui
MainMenu::MainMenu(int w, int h) MainMenu::MainMenu(int w, int h)
: OEngine::GUI::Layout("openmw_mainmenu.layout") : OEngine::GUI::Layout("openmw_mainmenu.layout")
, mButtonBox(0) , mButtonBox(0), mWidth (w), mHeight (h)
, mSaveGameDialog(NULL)
{ {
onResChange(w,h); updateMenu();
}
MainMenu::~MainMenu()
{
delete mSaveGameDialog;
} }
void MainMenu::onResChange(int w, int h) void MainMenu::onResChange(int w, int h)
{ {
setCoord(0,0,w,h); mWidth = w;
mHeight = h;
updateMenu();
}
if (mButtonBox) void MainMenu::setVisible (bool visible)
MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); {
if (visible)
updateMenu();
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); OEngine::GUI::Layout::setVisible (visible);
int curH = 0;
std::vector<std::string> buttons;
buttons.push_back("return");
buttons.push_back("newgame");
//buttons.push_back("loadgame");
//buttons.push_back("savegame");
buttons.push_back("options");
//buttons.push_back("credits");
buttons.push_back("exitgame");
int maxwidth = 0;
mButtons.clear();
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
MyGUI::IntSize requested = button->getRequestedSize();
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
mButtons[*it] = button;
curH += requested.height;
if (requested.width > maxwidth)
maxwidth = requested.width;
}
for (std::map<std::string, MWGui::ImageButton*>::iterator it = mButtons.begin(); it != mButtons.end(); ++it)
{
MyGUI::IntSize requested = it->second->getRequestedSize();
it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height);
}
mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH);
} }
void MainMenu::onButtonClicked(MyGUI::Widget *sender) void MainMenu::onButtonClicked(MyGUI::Widget *sender)
{ {
std::string name = *sender->getUserData<std::string>();
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
if (sender == mButtons["return"]) if (name == "return")
{ {
MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx);
MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu);
} }
else if (sender == mButtons["options"]) else if (name == "options")
MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings);
else if (sender == mButtons["exitgame"]) else if (name == "exitgame")
MWBase::Environment::get().setRequestExit(); MWBase::Environment::get().getStateManager()->requestQuit();
else if (sender == mButtons["newgame"]) else if (name == "newgame")
{ {
MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getStateManager()->newGame();
MWBase::Environment::get().getWindowManager()->setNewGame(true);
MWBase::Environment::get().getDialogueManager()->clear();
MWBase::Environment::get().getJournal()->clear();
} }
else if (sender == mButtons["loadgame"]) else
{ {
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); if (!mSaveGameDialog)
dialog->setLoadOrSave(true); mSaveGameDialog = new SaveGameDialog();
dialog->setVisible(true); if (name == "loadgame")
} mSaveGameDialog->setLoadOrSave(true);
else if (sender == mButtons["savegame"]) else if (name == "savegame")
{ mSaveGameDialog->setLoadOrSave(false);
MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); mSaveGameDialog->setVisible(true);
dialog->setLoadOrSave(false);
dialog->setVisible(true);
} }
} }
void MainMenu::updateMenu()
{
setCoord(0,0, mWidth, mHeight);
if (!mButtonBox)
mButtonBox = mMainWidget->createWidget<MyGUI::Widget>("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default);
int curH = 0;
MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();
std::vector<std::string> buttons;
if (state==MWBase::StateManager::State_Running)
buttons.push_back("return");
buttons.push_back("newgame");
if (MWBase::Environment::get().getStateManager()->characterBegin()!=
MWBase::Environment::get().getStateManager()->characterEnd())
buttons.push_back("loadgame");
if (state==MWBase::StateManager::State_Running &&
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1)
buttons.push_back("savegame");
buttons.push_back("options");
//buttons.push_back("credits");
buttons.push_back("exitgame");
// Create new buttons if needed
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
if (mButtons.find(*it) == mButtons.end())
{
MWGui::ImageButton* button = mButtonBox->createWidget<MWGui::ImageButton>
("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default);
button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds");
button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds");
button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds");
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked);
button->setUserData(std::string(*it));
mButtons[*it] = button;
}
}
// Start by hiding all buttons
int maxwidth = 0;
for (std::map<std::string, MWGui::ImageButton*>::iterator it = mButtons.begin(); it != mButtons.end(); ++it)
{
it->second->setVisible(false);
MyGUI::IntSize requested = it->second->getRequestedSize();
if (requested.width > maxwidth)
maxwidth = requested.width;
}
// Now show and position the ones we want
for (std::vector<std::string>::iterator it = buttons.begin(); it != buttons.end(); ++it)
{
assert(mButtons.find(*it) != mButtons.end());
MWGui::ImageButton* button = mButtons[*it];
button->setVisible(true);
MyGUI::IntSize requested = button->getRequestedSize();
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, requested.height);
curH += requested.height;
}
mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH);
}
} }

View file

@ -5,19 +5,33 @@
namespace MWGui namespace MWGui
{ {
class SaveGameDialog;
class MainMenu : public OEngine::GUI::Layout class MainMenu : public OEngine::GUI::Layout
{ {
public: int mWidth;
MainMenu(int w, int h); int mHeight;
void onResChange(int w, int h); public:
private: MainMenu(int w, int h);
MyGUI::Widget* mButtonBox; ~MainMenu();
std::map<std::string, MWGui::ImageButton*> mButtons; void onResChange(int w, int h);
void onButtonClicked (MyGUI::Widget* sender); virtual void setVisible (bool visible);
private:
MyGUI::Widget* mButtonBox;
std::map<std::string, MWGui::ImageButton*> mButtons;
void onButtonClicked (MyGUI::Widget* sender);
void updateMenu();
SaveGameDialog* mSaveGameDialog;
}; };
} }

View file

@ -373,8 +373,9 @@ namespace MWGui
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
MapWindow::MapWindow(const std::string& cacheDir) MapWindow::MapWindow(DragAndDrop* drag, const std::string& cacheDir)
: MWGui::WindowPinnableBase("openmw_map_window.layout") : WindowPinnableBase("openmw_map_window.layout")
, NoDrop(drag, mMainWidget)
, mGlobal(false) , mGlobal(false)
, mGlobalMap(0) , mGlobalMap(0)
, mGlobalMapRender(0) , mGlobalMapRender(0)
@ -434,7 +435,7 @@ namespace MWGui
static int _counter=0; static int _counter=0;
MyGUI::Button* markerWidget = mGlobalMapImage->createWidget<MyGUI::Button>("ButtonImage", MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter)); widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
markerWidget->setImageResource("DoorMarker"); markerWidget->setImageResource("DoorMarker");
markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipType", "Layout");
@ -499,10 +500,11 @@ namespace MWGui
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight()); mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
for (unsigned int i=0; i<mGlobalMapImage->getChildCount (); ++i) // force markers to foreground
for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i)
{ {
if (mGlobalMapImage->getChildAt (i)->getName().substr(0,4) == "Door") if (mGlobalMapOverlay->getChildAt (i)->getName().substr(0,4) == "Door")
mGlobalMapImage->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker"); mGlobalMapOverlay->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
} }
globalMapUpdatePlayer(); globalMapUpdatePlayer();
@ -573,4 +575,31 @@ namespace MWGui
mGlobalMap->setViewOffset(viewoffs); mGlobalMap->setViewOffset(viewoffs);
} }
void MapWindow::clear()
{
mGlobalMapRender->clear();
while (mEventBoxGlobal->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mEventBoxGlobal->getChildAt(0));
while (mGlobalMapOverlay->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
}
void MapWindow::write(ESM::ESMWriter &writer)
{
mGlobalMapRender->write(writer);
}
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
{
std::vector<std::pair<int, int> > exploredCells;
mGlobalMapRender->readRecord(reader, type, exploredCells);
for (std::vector<std::pair<int, int> >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it)
{
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
if (cell && !cell->mName.empty())
addVisitedLocation(cell->mName, it->first, it->second);
}
}
} }

View file

@ -1,6 +1,8 @@
#ifndef MWGUI_MAPWINDOW_H #ifndef MWGUI_MAPWINDOW_H
#define MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H
#include <libs/platform/stdint.h>
#include "windowpinnablebase.hpp" #include "windowpinnablebase.hpp"
namespace MWRender namespace MWRender
@ -8,6 +10,12 @@ namespace MWRender
class GlobalMap; class GlobalMap;
} }
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace Loading namespace Loading
{ {
class Listener; class Listener;
@ -75,10 +83,10 @@ namespace MWGui
float mLastDirectionY; float mLastDirectionY;
}; };
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop
{ {
public: public:
MapWindow(const std::string& cacheDir); MapWindow(DragAndDrop* drag, const std::string& cacheDir);
virtual ~MapWindow(); virtual ~MapWindow();
void setCellName(const std::string& cellName); void setCellName(const std::string& cellName);
@ -92,6 +100,14 @@ namespace MWGui
virtual void open(); virtual void open();
void onFrame(float dt) { NoDrop::onFrame(dt); }
/// Clear all savegame-specific data
void clear();
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type);
private: private:
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id); void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);

View file

@ -245,11 +245,11 @@ namespace MWGui
} }
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding; mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
MyGUI::IntCoord absCoord; MyGUI::IntPoint absPos;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2; absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2; absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setCoord(absCoord); mMainWidget->setPosition(absPos);
mMainWidget->setSize(mainWidgetSize); mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord; MyGUI::IntCoord messageWidgetCoord;
@ -332,7 +332,7 @@ namespace MWGui
{ {
if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok))
{ {
MyGUI::InputManager::getInstance().setKeyFocusWidget(*button); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(*button);
(*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed); (*button)->eventKeyButtonPressed += MyGUI::newDelegate(this, &InteractiveMessageBox::onKeyPressed);
break; break;
} }

View file

@ -70,8 +70,7 @@ namespace MWGui
setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race")); setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race"));
getWidget(mRaceList, "RaceList"); getWidget(mRaceList, "RaceList");
mRaceList->setScrollVisible(true); mRaceList->setScrollVisible(true);
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onAccept);
mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace); mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus")); setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus"));
@ -241,6 +240,14 @@ namespace MWGui
updateSpellPowers(); updateSpellPowers();
} }
void RaceDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)
{
onSelectRace(_sender, _index);
if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void RaceDialog::getBodyParts (int part, std::vector<std::string>& out) void RaceDialog::getBodyParts (int part, std::vector<std::string>& out)
{ {
out.clear(); out.clear();

View file

@ -67,6 +67,7 @@ namespace MWGui
void onSelectNextHair(MyGUI::Widget* _sender); void onSelectNextHair(MyGUI::Widget* _sender);
void onSelectRace(MyGUI::ListBox* _sender, size_t _index); void onSelectRace(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender);

View file

@ -16,7 +16,7 @@ namespace MWGui
void ReferenceInterface::checkReferenceAvailable() void ReferenceInterface::checkReferenceAvailable()
{ {
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); MWWorld::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell();
// check if player has changed cell, or count of the reference has become 0 // check if player has changed cell, or count of the reference has become 0
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL) if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)

View file

@ -1,12 +1,28 @@
#include "savegamedialog.hpp" #include "savegamedialog.hpp"
#include "widgets.hpp" #include "widgets.hpp"
#include <OgreImage.h>
#include <OgreTextureManager.h>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwstate/character.hpp"
#include "confirmationdialog.hpp"
namespace MWGui namespace MWGui
{ {
SaveGameDialog::SaveGameDialog() SaveGameDialog::SaveGameDialog()
: WindowModal("openmw_savegame_dialog.layout") : WindowModal("openmw_savegame_dialog.layout")
, mSaving(true)
, mCurrentCharacter(NULL)
{ {
getWidget(mScreenshot, "Screenshot"); getWidget(mScreenshot, "Screenshot");
getWidget(mCharacterSelection, "SelectCharacter"); getWidget(mCharacterSelection, "SelectCharacter");
@ -18,21 +34,89 @@ namespace MWGui
getWidget(mSpacer, "Spacer"); getWidget(mSpacer, "Spacer");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);
mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);
}
void SaveGameDialog::onSlotActivated(MyGUI::ListBox *sender, size_t pos)
{
onSlotSelected(sender, pos);
accept();
}
void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender)
{
// This might have previously been a save slot from the list. If so, that is no longer the case
mSaveList->setIndexSelected(MyGUI::ITEM_NONE);
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
}
void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender)
{
accept();
} }
void SaveGameDialog::open() void SaveGameDialog::open()
{ {
WindowModal::open();
mSaveNameEdit->setCaption ("");
if (mSaving)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
center(); center();
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd())
return;
mCurrentCharacter = mgr->getCurrentCharacter (false);
std::string directory =
Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves"));
mCharacterSelection->removeAllItems();
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)
{
if (it->begin()!=it->end())
{
std::stringstream title;
title << it->getSignature().mPlayerName;
title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")";
mCharacterSelection->addItem (title.str());
if (mCurrentCharacter == &*it ||
(!mCurrentCharacter && !mSaving && directory==Misc::StringUtils::lowerCase (
it->begin()->mPath.parent_path().filename().string())))
{
mCurrentCharacter = &*it;
mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1);
}
}
}
fillSaveList();
} }
void SaveGameDialog::setLoadOrSave(bool load) void SaveGameDialog::setLoadOrSave(bool load)
{ {
mSaving = !load;
mSaveNameEdit->setVisible(!load); mSaveNameEdit->setVisible(!load);
mCharacterSelection->setUserString("Hidden", load ? "false" : "true"); mCharacterSelection->setUserString("Hidden", load ? "false" : "true");
mCharacterSelection->setVisible(load); mCharacterSelection->setVisible(load);
mSpacer->setUserString("Hidden", load ? "false" : "true"); mSpacer->setUserString("Hidden", load ? "false" : "true");
if (!load)
{
mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false);
}
center(); center();
} }
@ -41,9 +125,161 @@ namespace MWGui
setVisible(false); setVisible(false);
} }
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) void SaveGameDialog::onConfirmationGiven()
{ {
setVisible(false); accept(true);
} }
void SaveGameDialog::accept(bool reallySure)
{
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
// Get the selected slot, if any
unsigned int i=0;
const MWState::Slot* slot = NULL;
if (mCurrentCharacter)
{
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i)
{
if (i == mSaveList->getIndexSelected())
slot = &*it;
}
}
if (mSaving)
{
// If overwriting an existing slot, ask for confirmation first
if (slot != NULL && !reallySure)
{
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sMessage4}");
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onConfirmationGiven);
dialog->eventCancelClicked.clear();
return;
}
if (mSaveNameEdit->getCaption().empty())
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}");
return;
}
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot);
}
else
{
if (mCurrentCharacter && slot)
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot);
}
setVisible(false);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
}
}
void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender)
{
accept();
}
void SaveGameDialog::onCharacterSelected(MyGUI::ComboBox *sender, size_t pos)
{
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
unsigned int i=0;
const MWState::Character* character = NULL;
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it, ++i)
{
if (i == pos)
character = &*it;
}
assert(character && "Can't find selected character");
mCurrentCharacter = character;
fillSaveList();
}
void SaveGameDialog::fillSaveList()
{
mSaveList->removeAllItems();
if (!mCurrentCharacter)
return;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it)
{
mSaveList->addItem(it->mProfile.mDescription);
}
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
}
void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
{
mInfoText->setCaption("");
mScreenshot->setImageTexture("");
return;
}
if (mSaving)
mSaveNameEdit->setCaption(sender->getItemNameAt(pos));
const MWState::Slot* slot = NULL;
unsigned int i=0;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)
{
if (i == pos)
slot = &*it;
}
assert(slot && "Can't find selected slot");
std::stringstream text;
time_t time = slot->mTimeStamp;
struct tm* timeinfo;
timeinfo = localtime(&time);
text << asctime(timeinfo) << "\n";
text << "Level " << slot->mProfile.mPlayerLevel << "\n";
text << slot->mProfile.mPlayerCell << "\n";
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
int hour = int(slot->mProfile.mInGameTime.mGameHour);
bool pm = hour >= 12;
if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12;
text
<< slot->mProfile.mInGameTime.mDay << " "
<< MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth)
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
mInfoText->setCaptionWithReplacing(text.str());
// Decode screenshot
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
Ogre::Image image;
image.load(stream, "jpg");
const std::string textureName = "@savegame_screenshot";
Ogre::TexturePtr texture;
texture = Ogre::TextureManager::getSingleton().getByName(textureName);
mScreenshot->setImageTexture("");
if (texture.isNull())
{
texture = Ogre::TextureManager::getSingleton().createManual(textureName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
image.getWidth(), image.getHeight(), 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY);
}
texture->unload();
texture->setWidth(image.getWidth());
texture->setHeight(image.getHeight());
texture->loadImage(image);
mScreenshot->setImageTexture(textureName);
}
} }

View file

@ -3,6 +3,11 @@
#include "windowbase.hpp" #include "windowbase.hpp"
namespace MWState
{
class Character;
}
namespace MWGui namespace MWGui
{ {
@ -15,12 +20,22 @@ namespace MWGui
void setLoadOrSave(bool load); void setLoadOrSave(bool load);
private:
void onCancelButtonClicked (MyGUI::Widget* sender); void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
void onSlotActivated (MyGUI::ListBox* sender, size_t pos);
void onEditSelectAccept (MyGUI::EditBox* sender);
void onSaveNameChanged (MyGUI::EditBox* sender);
void onConfirmationGiven();
void accept(bool reallySure=false);
void fillSaveList();
private:
MyGUI::ImageBox* mScreenshot; MyGUI::ImageBox* mScreenshot;
bool mSaving;
MyGUI::ComboBox* mCharacterSelection; MyGUI::ComboBox* mCharacterSelection;
MyGUI::EditBox* mInfoText; MyGUI::EditBox* mInfoText;
@ -30,6 +45,8 @@ namespace MWGui
MyGUI::EditBox* mSaveNameEdit; MyGUI::EditBox* mSaveNameEdit;
MyGUI::Widget* mSpacer; MyGUI::Widget* mSpacer;
const MWState::Character* mCurrentCharacter;
}; };
} }

View file

@ -42,8 +42,9 @@ namespace
namespace MWGui namespace MWGui
{ {
SpellWindow::SpellWindow() SpellWindow::SpellWindow(DragAndDrop* drag)
: WindowPinnableBase("openmw_spell_window.layout") : WindowPinnableBase("openmw_spell_window.layout")
, NoDrop(drag, mMainWidget)
, mHeight(0) , mHeight(0)
, mWidth(0) , mWidth(0)
{ {

View file

@ -7,14 +7,16 @@ namespace MWGui
{ {
class SpellIcons; class SpellIcons;
class SpellWindow : public WindowPinnableBase class SpellWindow : public WindowPinnableBase, public NoDrop
{ {
public: public:
SpellWindow(); SpellWindow(DragAndDrop* drag);
virtual ~SpellWindow(); virtual ~SpellWindow();
void updateSpells(); void updateSpells();
void onFrame(float dt) { NoDrop::onFrame(dt); }
protected: protected:
MyGUI::ScrollView* mSpellView; MyGUI::ScrollView* mSpellView;
MyGUI::Widget* mEffectBox; MyGUI::Widget* mEffectBox;

View file

@ -18,8 +18,9 @@ namespace MWGui
const int StatsWindow::sLineHeight = 18; const int StatsWindow::sLineHeight = 18;
StatsWindow::StatsWindow () StatsWindow::StatsWindow (DragAndDrop* drag)
: WindowPinnableBase("openmw_stats_window.layout") : WindowPinnableBase("openmw_stats_window.layout")
, NoDrop(drag, mMainWidget)
, mSkillView(NULL) , mSkillView(NULL)
, mMajorSkills() , mMajorSkills()
, mMinorSkills() , mMinorSkills()
@ -219,11 +220,13 @@ namespace MWGui
updateSkillArea(); updateSkillArea();
} }
void StatsWindow::onFrame () void StatsWindow::onFrame (float dt)
{ {
if (!mMainWidget->getVisible()) if (!mMainWidget->getVisible())
return; return;
NoDrop::onFrame(dt);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player); const MWMechanics::NpcStats &PCstats = MWWorld::Class::get(player).getNpcStats(player);
@ -231,9 +234,12 @@ namespace MWGui
MyGUI::Widget* levelWidget; MyGUI::Widget* levelWidget;
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
{ {
int max = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iLevelUpTotal")->getInt();
getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); getWidget(levelWidget, i==0 ? "Level_str" : "LevelText");
levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast<std::string>(PCstats.getLevelProgress())); levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast<std::string>(PCstats.getLevelProgress()));
levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast<std::string>(PCstats.getLevelProgress()) + "/10"); levelWidget->setUserString("Range_LevelProgress", boost::lexical_cast<std::string>(max));
levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast<std::string>(PCstats.getLevelProgress()) + "/"
+ boost::lexical_cast<std::string>(max));
} }
setFactions(PCstats.getFactionRanks()); setFactions(PCstats.getFactionRanks());

View file

@ -10,17 +10,17 @@ namespace MWGui
{ {
class WindowManager; class WindowManager;
class StatsWindow : public WindowPinnableBase class StatsWindow : public WindowPinnableBase, public NoDrop
{ {
public: public:
typedef std::map<std::string, int> FactionList; typedef std::map<std::string, int> FactionList;
typedef std::vector<int> SkillList; typedef std::vector<int> SkillList;
StatsWindow(); StatsWindow(DragAndDrop* drag);
/// automatically updates all the data in the stats window, but only if it has changed. /// automatically updates all the data in the stats window, but only if it has changed.
void onFrame(); void onFrame(float dt);
void setBar(const std::string& name, const std::string& tname, int val, int max); void setBar(const std::string& name, const std::string& tname, int val, int max);
void setPlayerName(const std::string& playerName); void setPlayerName(const std::string& playerName);

View file

@ -182,7 +182,7 @@ namespace MWGui
} }
else if (type == "AvatarItemSelection") else if (type == "AvatarItemSelection")
{ {
MyGUI::IntCoord avatarPos = MWBase::Environment::get().getWindowManager()->getInventoryWindow ()->getAvatarScreenCoord (); MyGUI::IntCoord avatarPos = focus->getAbsoluteCoord();
MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top);
int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f );
int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f );

View file

@ -87,49 +87,7 @@ namespace MWGui
onHourSliderChangedPosition(mHourSlider, 0); onHourSliderChangedPosition(mHourSlider, 0);
mHourSlider->setScrollPosition (0); mHourSlider->setScrollPosition (0);
// http://www.uesp.net/wiki/Lore:Calendar std::string month = MWBase::Environment::get().getWorld ()->getMonthName();
std::string month;
int m = MWBase::Environment::get().getWorld ()->getMonth ();
switch (m) {
case 0:
month = "#{sMonthMorningstar}";
break;
case 1:
month = "#{sMonthSunsdawn}";
break;
case 2:
month = "#{sMonthFirstseed}";
break;
case 3:
month = "#{sMonthRainshand}";
break;
case 4:
month = "#{sMonthSecondseed}";
break;
case 5:
month = "#{sMonthMidyear}";
break;
case 6:
month = "#{sMonthSunsheight}";
break;
case 7:
month = "#{sMonthLastseed}";
break;
case 8:
month = "#{sMonthHeartfire}";
break;
case 9:
month = "#{sMonthFrostfall}";
break;
case 10:
month = "#{sMonthSunsdusk}";
break;
case 11:
month = "#{sMonthEveningstar}";
break;
default:
break;
}
int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour (); int hour = MWBase::Environment::get().getWorld ()->getTimeStamp ().getHour ();
bool pm = hour >= 12; bool pm = hour >= 12;
if (hour >= 13) hour -= 12; if (hour >= 13) hour -= 12;
@ -271,7 +229,9 @@ namespace MWGui
const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player); const MWMechanics::NpcStats &pcstats = MWWorld::Class::get(player).getNpcStats(player);
// trigger levelup if possible // trigger levelup if possible
if (mSleeping && pcstats.getLevelProgress () >= 10) const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
if (mSleeping && pcstats.getLevelProgress () >= gmst.find("iLevelUpTotal")->getInt())
{ {
MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup);
} }

View file

@ -1,6 +1,7 @@
#include "windowbase.hpp" #include "windowbase.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "container.hpp"
using namespace MWGui; using namespace MWGui;
@ -50,3 +51,36 @@ void WindowModal::close()
{ {
MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget); MyGUI::InputManager::getInstance ().removeWidgetModal (mMainWidget);
} }
NoDrop::NoDrop(DragAndDrop *drag, MyGUI::Widget *widget)
: mDrag(drag), mWidget(widget), mTransparent(false)
{
}
void NoDrop::onFrame(float dt)
{
MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition();
if (mDrag->mIsOnDragAndDrop)
{
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();
while (focus && focus != mWidget)
focus = focus->getParent();
if (focus == mWidget)
mTransparent = true;
}
if (!mWidget->getAbsoluteCoord().inside(mousePos))
mTransparent = false;
if (mTransparent)
{
mWidget->setNeedMouseFocus(false); // Allow click-through
mWidget->setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5));
}
else
{
mWidget->setNeedMouseFocus(true);
mWidget->setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5));
}
}

View file

@ -11,6 +11,7 @@ namespace MWBase
namespace MWGui namespace MWGui
{ {
class WindowManager; class WindowManager;
class DragAndDrop;
class WindowBase: public OEngine::GUI::Layout class WindowBase: public OEngine::GUI::Layout
{ {
@ -42,6 +43,21 @@ namespace MWGui
virtual void open(); virtual void open();
virtual void close(); virtual void close();
}; };
/// A window that cannot be the target of a drag&drop action.
/// When hovered with a drag item, the window will become transparent and allow click-through.
class NoDrop
{
public:
NoDrop(DragAndDrop* drag, MyGUI::Widget* widget);
void onFrame(float dt);
private:
MyGUI::Widget* mWidget;
DragAndDrop* mDrag;
bool mTransparent;
};
} }
#endif #endif

View file

@ -14,6 +14,7 @@
#include <extern/sdl4ogre/sdlcursormanager.hpp> #include <extern/sdl4ogre/sdlcursormanager.hpp>
#include "../mwbase/inputmanager.hpp" #include "../mwbase/inputmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
@ -200,9 +201,9 @@ namespace MWGui
mRecharge = new Recharge(); mRecharge = new Recharge();
mMenu = new MainMenu(w,h); mMenu = new MainMenu(w,h);
mMap = new MapWindow(""); mMap = new MapWindow(mDragAndDrop, "");
trackWindow(mMap, "map"); trackWindow(mMap, "map");
mStatsWindow = new StatsWindow(); mStatsWindow = new StatsWindow(mDragAndDrop);
trackWindow(mStatsWindow, "stats"); trackWindow(mStatsWindow, "stats");
mConsole = new Console(w,h, mConsoleOnlyScripts); mConsole = new Console(w,h, mConsoleOnlyScripts);
trackWindow(mConsole, "console"); trackWindow(mConsole, "console");
@ -227,7 +228,7 @@ namespace MWGui
mConfirmationDialog = new ConfirmationDialog(); mConfirmationDialog = new ConfirmationDialog();
mAlchemyWindow = new AlchemyWindow(); mAlchemyWindow = new AlchemyWindow();
trackWindow(mAlchemyWindow, "alchemy"); trackWindow(mAlchemyWindow, "alchemy");
mSpellWindow = new SpellWindow(); mSpellWindow = new SpellWindow(mDragAndDrop);
trackWindow(mSpellWindow, "spells"); trackWindow(mSpellWindow, "spells");
mQuickKeysMenu = new QuickKeysMenu(); mQuickKeysMenu = new QuickKeysMenu();
mLevelupDialog = new LevelupDialog(); mLevelupDialog = new LevelupDialog();
@ -699,6 +700,10 @@ namespace MWGui
mToolTips->onFrame(frameDuration); mToolTips->onFrame(frameDuration);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame)
return;
if (mDragAndDrop->mIsOnDragAndDrop) if (mDragAndDrop->mIsOnDragAndDrop)
{ {
assert(mDragAndDrop->mDraggedWidget); assert(mDragAndDrop->mDraggedWidget);
@ -709,7 +714,9 @@ namespace MWGui
mInventoryWindow->onFrame(); mInventoryWindow->onFrame();
mStatsWindow->onFrame(); mStatsWindow->onFrame(frameDuration);
mMap->onFrame(frameDuration);
mSpellWindow->onFrame(frameDuration);
mWaitDialog->onFrame(frameDuration); mWaitDialog->onFrame(frameDuration);
@ -730,31 +737,20 @@ namespace MWGui
mCompanionWindow->onFrame(); mCompanionWindow->onFrame();
} }
void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) void WindowManager::changeCell(MWWorld::CellStore* cell)
{ {
std::string name = MWBase::Environment::get().getWorld()->getCellName (cell);
mMap->setCellName( name );
mHud->setCellName( name );
if (cell->mCell->isExterior()) if (cell->mCell->isExterior())
{ {
std::string name; if (!cell->mCell->mName.empty())
if (cell->mCell->mName != "")
{
name = cell->mCell->mName;
mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ());
}
else
{
const ESM::Region* region =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Region>().search(cell->mCell->mRegion);
if (region)
name = region->mName;
else
name = getGameSettingString("sDefaultCellname", "Wilderness");
}
mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY());
mMap->setCellName( name );
mHud->setCellName( name );
mMap->setCellPrefix("Cell"); mMap->setCellPrefix("Cell");
mHud->setCellPrefix("Cell"); mHud->setCellPrefix("Cell");
mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() );
@ -762,8 +758,6 @@ namespace MWGui
} }
else else
{ {
mMap->setCellName( cell->mCell->mName );
mHud->setCellName( cell->mCell->mName );
mMap->setCellPrefix( cell->mCell->mName ); mMap->setCellPrefix( cell->mCell->mName );
mHud->setCellPrefix( cell->mCell->mName ); mHud->setCellPrefix( cell->mCell->mName );
@ -774,7 +768,6 @@ namespace MWGui
MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos);
mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y); mMap->setGlobalMapPlayerPosition(worldPos.x, worldPos.y);
} }
} }
void WindowManager::setInteriorMapTexture(const int x, const int y) void WindowManager::setInteriorMapTexture(const int x, const int y)
@ -1237,7 +1230,7 @@ namespace MWGui
bool WindowManager::getRestEnabled() bool WindowManager::getRestEnabled()
{ {
//Enable rest dialogue if character creation finished //Enable rest dialogue if character creation finished
if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalVariable ("chargenstate").mFloat==-1) if(mRestAllowed==false && MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
mRestAllowed=true; mRestAllowed=true;
return mRestAllowed; return mRestAllowed;
} }
@ -1390,4 +1383,19 @@ namespace MWGui
Settings::Manager::setFloat(setting + " h", "Windows", h); Settings::Manager::setFloat(setting + " h", "Windows", h);
} }
void WindowManager::clear()
{
mMap->clear();
}
void WindowManager::write(ESM::ESMWriter &writer)
{
mMap->write(writer);
}
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
{
mMap->readRecord(reader, type);
}
} }

View file

@ -105,6 +105,7 @@ namespace MWGui
*/ */
virtual void update(); virtual void update();
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
virtual void setKeyFocusWidget (MyGUI::Widget* widget); virtual void setKeyFocusWidget (MyGUI::Widget* widget);
virtual void setNewGame(bool newgame); virtual void setNewGame(bool newgame);
@ -280,6 +281,12 @@ namespace MWGui
virtual bool getCursorVisible(); virtual bool getCursorVisible();
/// Clear all savegame-specific data
virtual void clear();
virtual void write (ESM::ESMWriter& writer);
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
private: private:
bool mConsoleOnlyScripts; bool mConsoleOnlyScripts;

View file

@ -20,6 +20,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
using namespace ICS; using namespace ICS;
@ -167,7 +168,9 @@ namespace MWInput
switch (action) switch (action)
{ {
case A_GameMenu: case A_GameMenu:
toggleMainMenu (); if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running
&& MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu))
toggleMainMenu ();
break; break;
case A_Screenshot: case A_Screenshot:
screenshot(); screenshot();
@ -248,8 +251,6 @@ namespace MWInput
mInputManager->capture(loading); mInputManager->capture(loading);
// inject some fake mouse movement to force updating MyGUI's widget states // inject some fake mouse movement to force updating MyGUI's widget states
// this shouldn't do any harm since we're moving back to the original position afterwards
MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX+1), int(mMouseY+1), mMouseWheel);
MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel);
// update values of channels (as a result of pressed keys) // update values of channels (as a result of pressed keys)
@ -280,7 +281,9 @@ namespace MWInput
return; return;
// Disable movement in Gui mode // Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; if (MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running)
return;
// Configure player movement according to keyboard input. Actual movement will // Configure player movement according to keyboard input. Actual movement will
@ -574,8 +577,6 @@ namespace MWInput
double x = arg.xrel * mCameraSensitivity * (1.0f/256.f); double x = arg.xrel * mCameraSensitivity * (1.0f/256.f);
double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier; double y = arg.yrel * mCameraSensitivity * (1.0f/256.f) * (mInvertY ? -1 : 1) * mCameraYMultiplier;
float scale = MWBase::Environment::get().getFrameDuration();
if(scale <= 0.0f) scale = 1.0f;
float rot[3]; float rot[3];
rot[0] = -y; rot[0] = -y;
@ -585,8 +586,8 @@ namespace MWInput
// Only actually turn player when we're not in vanity mode // Only actually turn player when we're not in vanity mode
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
{ {
mPlayer->yaw(x/scale); mPlayer->yaw(x);
mPlayer->pitch(-y/scale); mPlayer->pitch(-y);
} }
if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change
@ -615,7 +616,7 @@ namespace MWInput
void InputManager::windowClosed() void InputManager::windowClosed()
{ {
MWBase::Environment::setRequestExit(); MWBase::Environment::get().getStateManager()->requestQuit();
} }
void InputManager::toggleMainMenu() void InputManager::toggleMainMenu()
@ -747,7 +748,8 @@ namespace MWInput
void InputManager::showQuickKeysMenu() void InputManager::showQuickKeysMenu()
{ {
if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()) if (!MWBase::Environment::get().getWindowManager()->isGuiMode ()
&& MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")==-1)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_QuickKeysMenu);
else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu) else if (MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_QuickKeysMenu)
MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu); MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_QuickKeysMenu);

View file

@ -757,7 +757,7 @@ namespace MWMechanics
} }
} }
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore, const MWWorld::Ptr& ignore) void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)
{ {
PtrControllerMap::iterator iter = mActors.begin(); PtrControllerMap::iterator iter = mActors.begin();
while(iter != mActors.end()) while(iter != mActors.end())
@ -774,23 +774,56 @@ namespace MWMechanics
void Actors::update (float duration, bool paused) void Actors::update (float duration, bool paused)
{ {
if (!paused) if(!paused)
{ {
// Reset data from previous frame
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
// Reset last hit object, which is only valid for one frame
// Note, the new hit object for this frame may be set by CharacterController::update -> Animation::runAnimation
// (below)
iter->first.getClass().getCreatureStats(iter->first).setLastHitObject(std::string());
}
// AI and magic effects update
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
{
updateActor(iter->first, duration);
if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, duration, paused);
}
}
// Looping magic VFX update
// Note: we need to do this before any of the animations are updated.
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
iter->second->updateContinuousVfx();
// Animation/movement update
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get(
ESM::MagicEffect::Paralyze).mMagnitude > 0)
iter->second->skipAnim();
iter->second->update(duration);
}
// Kill dead actors
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{ {
const MWWorld::Class &cls = MWWorld::Class::get(iter->first); const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
CreatureStats &stats = cls.getCreatureStats(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first);
stats.setLastHitObject(std::string());
if(!stats.isDead()) if(!stats.isDead())
{ {
if(iter->second->isDead()) if(iter->second->isDead())
iter->second->resurrect(); iter->second->resurrect();
updateActor(iter->first, duration);
if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, duration, paused);
if(!stats.isDead()) if(!stats.isDead())
continue; continue;
} }
@ -799,65 +832,47 @@ namespace MWMechanics
if(iter->first.getRefData().getHandle()=="player" && if(iter->first.getRefData().getHandle()=="player" &&
MWBase::Environment::get().getWorld()->getGodModeState()) MWBase::Environment::get().getWorld()->getGodModeState())
{ {
MWMechanics::DynamicStat<float> stat(stats.getHealth()); MWMechanics::DynamicStat<float> stat (stats.getHealth());
if(stat.getModified()<1) if (stat.getModified()<1)
{ {
stat.setModified(1, 0); stat.setModified(1, 0);
stats.setHealth(stat); stats.setHealth(stat);
} }
stats.resurrect(); stats.resurrect();
continue; continue;
} }
if(iter->second->isDead())
continue;
iter->second->kill();
// Apply soultrap
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
SoulTrap soulTrap (iter->first);
stats.getActiveSpells().visitEffectSources(soulTrap);
}
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0);
// Make sure spell effects with CasterLinked flag are removed // Make sure spell effects with CasterLinked flag are removed
// TODO: would be nice not to do this all the time...
for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) for(PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{ {
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(iter->first.getRefData().getHandle()); spells.purge(iter->first.getRefData().getHandle());
} }
++mDeathCount[cls.getId(iter->first)]; // FIXME: see http://bugs.openmw.org/issues/869
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if(cls.isEssential(iter->first)) if (iter->second->kill())
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); {
++mDeathCount[cls.getId(iter->first)];
} // Apply soultrap
} if (iter->first.getTypeName() == typeid(ESM::Creature).name())
{
SoulTrap soulTrap (iter->first);
stats.getActiveSpells().visitEffectSources(soulTrap);
}
if(!paused) // Reset magic effects and recalculate derived effects
{ // One case where we need this is to make sure bound items are removed upon death
// Note: we need to do this before any of the animations are updated. stats.setMagicEffects(MWMechanics::MagicEffects());
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles), calculateCreatureStatModifiers(iter->first, 0);
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
iter->second->updateContinuousVfx();
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) if(cls.isEssential(iter->first))
{ MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get( }
ESM::MagicEffect::Paralyze).mMagnitude > 0)
iter->second->skipAnim();
iter->second->update(duration);
} }
} }
} }

View file

@ -1,23 +1,22 @@
#include "aicombat.hpp" #include "aicombat.hpp"
#include "aifollow.hpp"
#include "movement.hpp" #include <OgreMath.h>
#include <OgreVector3.h>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/timestamp.hpp" #include "../mwworld/timestamp.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include "character.hpp"
#include "../mwworld/inventorystore.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "steering.hpp"
#include <OgreMath.h> #include "movement.hpp"
#include <OgreVector3.h> #include "character.hpp" // fixme: for getActiveWeapon
namespace namespace
{ {
@ -43,7 +42,9 @@ namespace MWMechanics
mReadyToAttack(false), mReadyToAttack(false),
mStrike(false), mStrike(false),
mCombatMove(false), mCombatMove(false),
mMovement() mRotate(false),
mMovement(),
mTargetAngle(0)
{ {
} }
@ -68,10 +69,16 @@ namespace MWMechanics
mCombatMove = false; mCombatMove = false;
} }
} }
actor.getClass().getMovementSettings(actor) = mMovement; actor.getClass().getMovementSettings(actor) = mMovement;
if (mRotate)
{
if (zTurn(actor, Ogre::Degree(mTargetAngle)))
mRotate = false;
}
//actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mReadyToAttack);
mTimerAttack -= duration; mTimerAttack -= duration;
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
@ -156,17 +163,12 @@ namespace MWMechanics
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
} }
//MWWorld::Class::get(actor).getCreatureStats(actor).setAttackingOrSpell(false);
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
float zAngle;
float rangeMelee; float rangeMelee;
float rangeCloseUp; float rangeCloseUp;
bool distantCombat = false; bool distantCombat = false;
if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_ThowWeapon) if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown)
{ {
rangeMelee = 1000; // TODO: should depend on archer skill rangeMelee = 1000; // TODO: should depend on archer skill
rangeCloseUp = 0; //doesn't needed when attacking from distance rangeCloseUp = 0; //doesn't needed when attacking from distance
@ -189,12 +191,8 @@ namespace MWMechanics
//Melee and Close-up combat //Melee and Close-up combat
vDir.z = 0; vDir.z = 0;
float dirLen = vDir.length(); float dirLen = vDir.length();
zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees(); mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
mRotate = true;
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
//MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
//bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
if (mFollowTarget && distBetween > rangeMelee) if (mFollowTarget && distBetween > rangeMelee)
@ -206,7 +204,10 @@ namespace MWMechanics
{ {
//Melee: stop running and attack //Melee: stop running and attack
mMovement.mPosition[1] = 0; mMovement.mPosition[1] = 0;
chooseBestAttack(weapon, mMovement);
// When attacking with a weapon, choose between slash, thrust or chop
if (actor.getClass().hasInventoryStore(actor))
chooseBestAttack(weapon, mMovement);
if(mMovement.mPosition[0] || mMovement.mPosition[1]) if(mMovement.mPosition[0] || mMovement.mPosition[1])
{ {
@ -237,12 +238,6 @@ namespace MWMechanics
else else
{ {
//target is at far distance: build path to target OR follow target (if previously actor had reached it once) //target is at far distance: build path to target OR follow target (if previously actor had reached it once)
/*
//apply when AIFOLLOW package implementation will be existent
if(mFollowTarget)
actor.getClass().getCreatureStats(actor).getAiSequence().stack(AiFollow(mTarget));*/
mFollowTarget = false; mFollowTarget = false;
buildNewPath(actor); buildNewPath(actor);
@ -252,13 +247,10 @@ namespace MWMechanics
//try shortcut //try shortcut
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget))
zAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees();
else else
zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
mRotate = true;
// TODO: use movement settings instead of rotating directly
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
//mMovement.mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
mMovement.mPosition[1] = 1; mMovement.mPosition[1] = 1;
mReadyToAttack = false; mReadyToAttack = false;
@ -294,6 +286,8 @@ namespace MWMechanics
} }
} }
actor.getClass().getMovementSettings(actor) = mMovement;
return false; return false;
} }
@ -374,7 +368,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement
{ {
if (weapon == NULL) if (weapon == NULL)
{ {
//hand-to-hand and creatures' attacks deal equal damage for each type //hand-to-hand deal equal damage for each type
float roll = static_cast<float>(rand())/RAND_MAX; float roll = static_cast<float>(rand())/RAND_MAX;
if(roll <= 0.333f) //side punch if(roll <= 0.333f) //side punch
{ {

View file

@ -29,16 +29,21 @@ namespace MWMechanics
private: private:
PathFinder mPathFinder; PathFinder mPathFinder;
//controls duration of the actual strike // controls duration of the actual strike
float mTimerAttack; float mTimerAttack;
float mTimerReact; float mTimerReact;
//controls duration of the sideway & forward moves // controls duration of the sideway & forward moves
//when mCombatMove is true // when mCombatMove is true
float mTimerCombatMove; float mTimerCombatMove;
// the z rotation angle (degrees) we want to reach
// used every frame when mRotate is true
float mTargetAngle;
bool mReadyToAttack, mStrike; bool mReadyToAttack, mStrike;
bool mFollowTarget; bool mFollowTarget;
bool mCombatMove; bool mCombatMove;
bool mRotate;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
MWWorld::Ptr mTarget; MWWorld::Ptr mTarget;

View file

@ -8,6 +8,8 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "steering.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
@ -33,7 +35,7 @@ namespace MWMechanics
{ {
mMaxDist = 470; mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long // The CS Help File states that if a duration is given, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mDuration = 0;
@ -52,7 +54,7 @@ namespace MWMechanics
{ {
mMaxDist = 470; mMaxDist = 470;
// The CS Help File states that if a duration is givin, the AI package will run for that long // The CS Help File states that if a duration is given, the AI package will run for that long
// BUT if a location is givin, it "trumps" the duration so it will simply escort to that location. // BUT if a location is givin, it "trumps" the duration so it will simply escort to that location.
if(mX != 0 || mY != 0 || mZ != 0) if(mX != 0 || mY != 0 || mZ != 0)
mDuration = 0; mDuration = 0;
@ -89,25 +91,23 @@ namespace MWMechanics
if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX)
{ {
int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort. // Check if actor is near the border of an inactive cell. If so, pause walking.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200)) 2.0 - 200))
{ {
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return false;
} }
} }
if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY)
{ {
int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY);
// Check if actor is near the border of an inactive cell. If so, disable AiEscort. // Check if actor is near the border of an inactive cell. If so, pause walking.
// FIXME: This *should* pause the AiEscort package instead of terminating it.
if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200)) 2.0 - 200))
{ {
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
return true; return false;
} }
} }
@ -151,8 +151,7 @@ namespace MWMechanics
if(distanceBetweenResult <= mMaxDist * mMaxDist) if(distanceBetweenResult <= mMaxDist * mMaxDist)
{ {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly zTurn(actor, Ogre::Degree(zAngle));
MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
mMaxDist = 470; mMaxDist = 470;
} }

View file

@ -6,15 +6,17 @@
#include "movement.hpp" #include "movement.hpp"
#include <OgreMath.h> #include <OgreMath.h>
#include "steering.hpp"
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
{ {
} }
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
{ {
} }
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
{ {
@ -45,14 +47,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
} }
} }
ESM::Pathgrid::Point dest; ESM::Pathgrid::Point dest;
dest.mX = target.getRefData().getPosition().pos[0]; dest.mX = target.getRefData().getPosition().pos[0];
dest.mY = target.getRefData().getPosition().pos[1]; dest.mY = target.getRefData().getPosition().pos[1];
dest.mZ = target.getRefData().getPosition().pos[2]; dest.mZ = target.getRefData().getPosition().pos[2];
ESM::Pathgrid::Point start; ESM::Pathgrid::Point start;
start.mX = pos.pos[0]; start.mX = pos.pos[0];
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; start.mZ = pos.pos[2];
if(mPathFinder.getPath().empty()) if(mPathFinder.getPath().empty())
@ -88,18 +90,14 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
{ {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
//MWBase::Environment::get().getWorld()->rotateObject(actor, 0, 0, zAngle, false); }
MWWorld::Class::get(actor).getMovementSettings(actor).mRotation[2] = 10*(Ogre::Degree(zAngle).valueRadians()-pos.rot[2]);
//std::cout << Ogre::Degree(zAngle).valueDegrees()-Ogre::Radian(actor.getRefData().getPosition().rot[2]).valueDegrees() << " "<< pos.rot[2] << " " << zAngle << "\n"; if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
//MWWorld::Class::get(actor).get < 100*100)
} actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
< 100*100)
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
else else
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
return false; return false;
} }
@ -109,12 +107,12 @@ std::string MWMechanics::AiFollow::getFollowedActor()
return mActorId; return mActorId;
} }
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
{ {
return new AiFollow(*this); return new AiFollow(*this);
} }
int MWMechanics::AiFollow::getTypeId() const int MWMechanics::AiFollow::getTypeId() const
{ {
return TypeIdFollow; return TypeIdFollow;
} }

View file

@ -23,7 +23,7 @@ void MWMechanics::AiSequence::copy (const AiSequence& sequence)
mPackages.push_back ((*iter)->clone()); mPackages.push_back ((*iter)->clone());
} }
MWMechanics::AiSequence::AiSequence() : mDone (false) {} MWMechanics::AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {}
MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false) MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false)
{ {
@ -84,6 +84,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
{ {
if (!mPackages.empty()) if (!mPackages.empty())
{ {
mLastAiPackage = mPackages.front()->getTypeId();
if (mPackages.front()->execute (actor,duration)) if (mPackages.front()->execute (actor,duration))
{ {
delete *mPackages.begin(); delete *mPackages.begin();
@ -91,7 +92,9 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
mDone = true; mDone = true;
} }
else else
{
mDone = false; mDone = false;
}
} }
} }
} }

View file

@ -23,6 +23,9 @@ namespace MWMechanics
void copy (const AiSequence& sequence); void copy (const AiSequence& sequence);
// The type of AI package that ran last
int mLastAiPackage;
public: public:
AiSequence(); AiSequence();
@ -36,6 +39,10 @@ namespace MWMechanics
int getTypeId() const; int getTypeId() const;
///< @see enum AiPackage::TypeId ///< @see enum AiPackage::TypeId
int getLastRunTypeId() const { return mLastAiPackage; }
///< Get the typeid of the Ai package that ran last, NOT the currently "active" Ai package that will be run in the next frame.
/// This difference is important when an Ai package has just finished and been removed.
bool getCombatTarget (std::string &targetActorId) const; bool getCombatTarget (std::string &targetActorId) const;
///< Return true and assign target if combat package is currently ///< Return true and assign target if combat package is currently
/// active, return false otherwise /// active, return false otherwise

View file

@ -1,11 +1,12 @@
#include "aitravel.hpp" #include "aitravel.hpp"
#include "movement.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "steering.hpp"
#include "movement.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
@ -86,9 +87,7 @@ namespace MWMechanics
return true; return true;
} }
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
// TODO: use movement settings instead of rotating directly
world->rotateObject(actor, 0, 0, zAngle, false);
movement.mPosition[1] = 1; movement.mPosition[1] = 1;
return false; return false;

View file

@ -11,6 +11,8 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include <OgreVector3.h> #include <OgreVector3.h>
#include "steering.hpp"
namespace namespace
{ {
float sgn(float a) float sgn(float a)
@ -68,6 +70,7 @@ namespace MWMechanics
bool AiWander::execute (const MWWorld::Ptr& actor,float duration) bool AiWander::execute (const MWWorld::Ptr& actor,float duration)
{ {
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
if(mDuration) if(mDuration)
{ {
@ -188,15 +191,18 @@ namespace MWMechanics
mIdleNow = true; mIdleNow = true;
// Play idle voiced dialogue entries randomly // Play idle voiced dialogue entries randomly
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
float chance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat(); if (hello > 0)
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99] {
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float chance = store.get<ESM::GameSetting>().find("fVoiceIdleOdds")->getFloat();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // Don't bother if the player is out of hearing range
if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500)
// Don't bother if the player is out of hearing range MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) }
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
} }
} }
@ -205,7 +211,7 @@ namespace MWMechanics
// Play a random voice greeting if the player gets too close // Play a random voice greeting if the player gets too close
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
float hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
float helloDistance = hello; float helloDistance = hello;
int iGreetDistanceMultiplier = store.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt(); int iGreetDistanceMultiplier = store.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->getInt();
helloDistance *= iGreetDistanceMultiplier; helloDistance *= iGreetDistanceMultiplier;
@ -281,11 +287,6 @@ namespace MWMechanics
if(mWalking) if(mWalking)
{ {
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
// TODO: use movement settings instead of rotating directly
world->rotateObject(actor, 0, 0, zAngle, false);
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{ {
stopWalking(actor); stopWalking(actor);
@ -293,6 +294,12 @@ namespace MWMechanics
mWalking = false; mWalking = false;
mChooseAction = true; mChooseAction = true;
} }
else
{
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
}
} }
return false; return false;

View file

@ -32,6 +32,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -118,7 +119,7 @@ static const struct WeaponInfo {
{ WeapType_TwoWide, "2w", "weapontwowide" }, { WeapType_TwoWide, "2w", "weapontwowide" },
{ WeapType_BowAndArrow, "1h", "bowandarrow" }, { WeapType_BowAndArrow, "1h", "bowandarrow" },
{ WeapType_Crossbow, "crossbow", "crossbow" }, { WeapType_Crossbow, "crossbow", "crossbow" },
{ WeapType_ThowWeapon, "1h", "throwweapon" }, { WeapType_Thrown, "1h", "throwweapon" },
{ WeapType_PickProbe, "1h", "pickprobe" }, { WeapType_PickProbe, "1h", "pickprobe" },
{ WeapType_Spell, "spell", "spellcast" }, { WeapType_Spell, "spell", "spellcast" },
}; };
@ -156,7 +157,14 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
if(mHitState == CharState_None) if(mHitState == CharState_None)
{ {
if(knockdown) if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0)
{
mHitState = CharState_KnockOut;
mCurrentHit = "knockout";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, false, 1, "start", "stop", 0.0f, ~0ul);
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
}
else if(knockdown)
{ {
mHitState = CharState_KnockDown; mHitState = CharState_KnockDown;
mCurrentHit = "knockdown"; mCurrentHit = "knockdown";
@ -186,6 +194,12 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mPtr.getClass().getCreatureStats(mPtr).setBlock(false); mPtr.getClass().getCreatureStats(mPtr).setBlock(false);
mHitState = CharState_None; mHitState = CharState_None;
} }
else if (mHitState == CharState_KnockOut && mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() > 0)
{
mHitState = CharState_KnockDown;
mAnimation->disable(mCurrentHit);
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::Group_All, true, 1, "loop stop", "stop", 0.0f, 0);
}
} }
const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType));
@ -301,12 +315,24 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
float vel, speedmult = 1.0f; float vel, speedmult = 1.0f;
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f)
speedmult = mMovementSpeed / vel; speedmult = mMovementSpeed / vel;
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
else if (mMovementSpeed > 0.0f)
// The first person anims don't have any velocity to calculate a speed multiplier from.
// We use the third person velocities instead.
// FIXME: should be pulled from the actual animation, but it is not presently loaded.
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
mMovementAnimVelocity = vel;
} }
else mMovementAnimVelocity = 0.0f;
} }
} }
@ -367,7 +393,7 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
*weaptype = WeapType_Crossbow; *weaptype = WeapType_Crossbow;
break; break;
case ESM::Weapon::MarksmanThrown: case ESM::Weapon::MarksmanThrown:
*weaptype = WeapType_ThowWeapon; *weaptype = WeapType_Thrown;
break; break;
} }
} }
@ -403,6 +429,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mIdleState(CharState_None) , mIdleState(CharState_None)
, mMovementState(CharState_None) , mMovementState(CharState_None)
, mMovementSpeed(0.0f) , mMovementSpeed(0.0f)
, mMovementAnimVelocity(0.0f)
, mDeathState(CharState_None) , mDeathState(CharState_None)
, mHitState(CharState_None) , mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing) , mUpperBodyState(UpperCharState_Nothing)
@ -479,7 +506,14 @@ bool CharacterController::updateCreatureState()
{ {
MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); MWBase::Environment::get().getWorld()->breakInvisibility(mPtr);
determineAttackType(); // These are unique animations and not linked to movement type. Just pick one randomly.
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 3; // [0, 2]
if (roll == 0)
mCurrentWeapon = "attack1";
else if (roll == 1)
mCurrentWeapon = "attack2";
else
mCurrentWeapon = "attack3";
mAnimation->play(mCurrentWeapon, Priority_Weapon, mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_All, true, MWRender::Animation::Group_All, true,
@ -527,6 +561,7 @@ bool CharacterController::updateWeaponState()
{ {
getWeaponGroup(weaptype, weapgroup); getWeaponGroup(weaptype, weapgroup);
mAnimation->showWeapons(false); mAnimation->showWeapons(false);
mAnimation->setWeaponGroup(weapgroup);
mAnimation->play(weapgroup, Priority_Weapon, mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true, MWRender::Animation::Group_UpperBody, true,
@ -581,6 +616,19 @@ bool CharacterController::updateWeaponState()
if(isWeapon) if(isWeapon)
weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed; weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
// Cancel attack if we no longer have ammunition
bool ammunition = true;
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (mWeaponType == WeapType_Crossbow)
ammunition = (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt);
else if (mWeaponType == WeapType_BowAndArrow)
ammunition = (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Arrow);
if (!ammunition && mUpperBodyState > UpperCharState_WeapEquiped)
{
mAnimation->disable(mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
}
float complete; float complete;
bool animPlaying; bool animPlaying;
if(stats.getAttackingOrSpell()) if(stats.getAttackingOrSpell())
@ -685,14 +733,15 @@ bool CharacterController::updateWeaponState()
if(item.getRefData().getCount()) if(item.getRefData().getCount())
MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item);
} }
else else if (ammunition)
{ {
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow ||
mWeaponType == WeapType_ThowWeapon) mWeaponType == WeapType_Thrown)
mAttackType = "shoot"; mAttackType = "shoot";
else else
{ {
if(isWeapon && Settings::Manager::getBool("best attack", "Game")) if(isWeapon && mPtr.getRefData().getHandle() == "player" &&
Settings::Manager::getBool("best attack", "Game"))
mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase); mAttackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
else else
determineAttackType(); determineAttackType();
@ -751,12 +800,59 @@ bool CharacterController::updateWeaponState()
} }
} }
mAnimation->setPitchFactor(0.f);
if (mWeaponType == WeapType_BowAndArrow || mWeaponType == WeapType_Thrown)
{
switch (mUpperBodyState)
{
case UpperCharState_StartToMinAttack:
mAnimation->setPitchFactor(complete);
break;
case UpperCharState_MinAttackToMaxAttack:
case UpperCharState_MaxAttackToMinHit:
case UpperCharState_MinHitToHit:
mAnimation->setPitchFactor(1.f);
break;
case UpperCharState_FollowStartToFollowStop:
if (animPlaying)
mAnimation->setPitchFactor(1.f-complete);
break;
default:
break;
}
}
else if (mWeaponType == WeapType_Crossbow)
{
switch (mUpperBodyState)
{
case UpperCharState_EquipingWeap:
mAnimation->setPitchFactor(complete);
break;
case UpperCharState_UnEquipingWeap:
mAnimation->setPitchFactor(1.f-complete);
break;
case UpperCharState_WeapEquiped:
case UpperCharState_StartToMinAttack:
case UpperCharState_MinAttackToMaxAttack:
case UpperCharState_MaxAttackToMinHit:
case UpperCharState_MinHitToHit:
case UpperCharState_FollowStartToFollowStop:
mAnimation->setPitchFactor(1.f);
break;
default:
break;
}
}
if(!animPlaying) if(!animPlaying)
{ {
if(mUpperBodyState == UpperCharState_EquipingWeap || if(mUpperBodyState == UpperCharState_EquipingWeap ||
mUpperBodyState == UpperCharState_FollowStartToFollowStop || mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
mUpperBodyState == UpperCharState_CastingSpell) mUpperBodyState == UpperCharState_CastingSpell)
{ {
if (ammunition && mWeaponType == WeapType_Crossbow)
mAnimation->attachArrow();
mUpperBodyState = UpperCharState_WeapEquiped; mUpperBodyState = UpperCharState_WeapEquiped;
//don't allow to continue playing hit animation on UpperBody after actor had attacked during it //don't allow to continue playing hit animation on UpperBody after actor had attacked during it
if(mHitState == CharState_Hit) if(mHitState == CharState_Hit)
@ -1136,18 +1232,17 @@ void CharacterController::update(float duration)
if (!mSkipAnim) if (!mSkipAnim)
{ {
if(mHitState != CharState_KnockDown) rot *= Ogre::Math::RadiansToDegrees(1.0f);
if(mHitState != CharState_KnockDown && mHitState != CharState_KnockOut)
{ {
rot *= duration * Ogre::Math::RadiansToDegrees(1.0f);
world->rotateObject(mPtr, rot.x, rot.y, rot.z, true); world->rotateObject(mPtr, rot.x, rot.y, rot.z, true);
} }
else //avoid z-rotating for knockdown else //avoid z-rotating for knockdown
world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true);
// all actual movement in 3rd person controlled by animations, except for jump // always control actual movement by animation unless this:
// !mAnimation->hasAnimation("death1") identifies 1st person mode // FIXME: actor falling/landing should be controlled by physics engine
if(mJumpState != JumpState_None || vec.z > 0 if(mMovementAnimVelocity == 0.0f && (vec.length() > 0.0f || mJumpState != JumpState_None))
|| (mPtr.getRefData().getHandle() == "player" && !mAnimation->hasAnimation("death1")))
{ {
world->queueMovement(mPtr, vec); world->queueMovement(mPtr, vec);
} }
@ -1158,7 +1253,6 @@ void CharacterController::update(float duration)
} }
else if(cls.getCreatureStats(mPtr).isDead()) else if(cls.getCreatureStats(mPtr).isDead())
{ {
MWBase::Environment::get().getWorld()->enableActorCollision(mPtr, false);
world->queueMovement(mPtr, Ogre::Vector3(0.0f)); world->queueMovement(mPtr, Ogre::Vector3(0.0f));
} }
@ -1260,10 +1354,17 @@ void CharacterController::forceStateUpdate()
} }
} }
void CharacterController::kill() bool CharacterController::kill()
{ {
if(mDeathState != CharState_None) if( isDead() )
return; {
//player's death animation is over
if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) )
{
MWBase::Environment::get().getStateManager()->askLoadRecent();
}
return false;
}
playRandomDeath(); playRandomDeath();
@ -1274,6 +1375,8 @@ void CharacterController::kill()
mIdleState = CharState_None; mIdleState = CharState_None;
mCurrentIdle.clear(); mCurrentIdle.clear();
return true;
} }
void CharacterController::resurrect() void CharacterController::resurrect()
@ -1338,15 +1441,6 @@ void CharacterController::determineAttackType()
else else
mAttackType = "chop"; mAttackType = "chop";
} }
else
{
if (move[0] && !move[1]) //sideway
mCurrentWeapon = "attack2";
else if (move[1]) //forward
mCurrentWeapon = "attack3";
else
mCurrentWeapon = "attack1";
}
} }
} }

View file

@ -93,6 +93,7 @@ enum CharacterState {
CharState_Hit, CharState_Hit,
CharState_KnockDown, CharState_KnockDown,
CharState_KnockOut,
CharState_Block CharState_Block
}; };
@ -105,7 +106,7 @@ enum WeaponType {
WeapType_TwoWide, WeapType_TwoWide,
WeapType_BowAndArrow, WeapType_BowAndArrow,
WeapType_Crossbow, WeapType_Crossbow,
WeapType_ThowWeapon, WeapType_Thrown,
WeapType_PickProbe, WeapType_PickProbe,
WeapType_Spell WeapType_Spell
@ -144,6 +145,7 @@ class CharacterController
CharacterState mMovementState; CharacterState mMovementState;
std::string mCurrentMovement; std::string mCurrentMovement;
float mMovementSpeed; float mMovementSpeed;
float mMovementAnimVelocity;
CharacterState mDeathState; CharacterState mDeathState;
std::string mCurrentDeath; std::string mCurrentDeath;
@ -198,7 +200,7 @@ public:
void skipAnim(); void skipAnim();
bool isAnimPlaying(const std::string &groupName); bool isAnimPlaying(const std::string &groupName);
void kill(); bool kill();
void resurrect(); void resurrect();
bool isDead() const bool isDead() const
{ return mDeathState != CharState_None; } { return mDeathState != CharState_None; }

View file

@ -10,7 +10,7 @@
namespace MWMechanics namespace MWMechanics
{ {
CreatureStats::CreatureStats() CreatureStats::CreatureStats()
: mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false), mTalkedTo (false), mAlarmed (false),
mAttacked (false), mHostile (false), mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackingOrSpell(false),
@ -22,35 +22,6 @@ namespace MWMechanics
mAiSettings[i] = 0; mAiSettings[i] = 0;
} }
float CreatureStats::getLevelHealthBonus () const
{
return mLevelHealthBonus;
}
void CreatureStats::levelUp()
{
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
// "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance"
mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat();
updateHealth();
mLevel++;
}
void CreatureStats::updateHealth()
{
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(static_cast<int> (0.5 * (strength + endurance)) + mLevelHealthBonus);
}
const AiSequence& CreatureStats::getAiSequence() const const AiSequence& CreatureStats::getAiSequence() const
{ {
return mAiSequence; return mAiSequence;
@ -208,9 +179,6 @@ namespace MWMechanics
mDynamic[index] = value; mDynamic[index] = value;
if (index == 2 && value.getCurrent() < 0)
setKnockedDown(true);
if (index==0 && mDynamic[index].getCurrent()<1) if (index==0 && mDynamic[index].getCurrent()<1)
{ {
if (!mDead) if (!mDead)

View file

@ -22,13 +22,11 @@ namespace MWMechanics
DrawState_ mDrawState; DrawState_ mDrawState;
AttributeValue mAttributes[8]; AttributeValue mAttributes[8];
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
int mLevel;
Spells mSpells; Spells mSpells;
ActiveSpells mActiveSpells; ActiveSpells mActiveSpells;
MagicEffects mMagicEffects; MagicEffects mMagicEffects;
Stat<int> mAiSettings[4]; Stat<int> mAiSettings[4];
AiSequence mAiSequence; AiSequence mAiSequence;
float mLevelHealthBonus;
bool mDead; bool mDead;
bool mDied; bool mDied;
int mFriendlyHits; int mFriendlyHits;
@ -54,6 +52,7 @@ namespace MWMechanics
protected: protected:
bool mIsWerewolf; bool mIsWerewolf;
AttributeValue mWerewolfAttributes[8]; AttributeValue mWerewolfAttributes[8];
int mLevel;
public: public:
CreatureStats(); CreatureStats();
@ -142,14 +141,6 @@ namespace MWMechanics
float getFatigueTerm() const; float getFatigueTerm() const;
///< Return effective fatigue ///< Return effective fatigue
float getLevelHealthBonus() const;
void levelUp();
void updateHealth();
///< Calculate health based on endurance and strength.
/// Called at character creation and at level up.
bool isDead() const; bool isDead() const;
bool hasDied() const; bool hasDied() const;

View file

@ -250,7 +250,10 @@ namespace MWMechanics
{ {
if (itemEmpty()) if (itemEmpty())
return 0; return 0;
return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
return mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get<ESM::GameSetting>().find("fEnchantmentMult")->getFloat();
} }
bool Enchanting::soulEmpty() const bool Enchanting::soulEmpty() const
{ {
@ -274,22 +277,18 @@ namespace MWMechanics
float Enchanting::getEnchantChance() const float Enchanting::getEnchantChance() const
{ {
/*
Formula from http://www.uesp.net/wiki/Morrowind:Enchant
*/
const CreatureStats& creatureStats = MWWorld::Class::get (mEnchanter).getCreatureStats (mEnchanter);
const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter); const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter);
float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() + float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() +
(0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) (0.25 * npcStats.getAttribute (ESM::Attribute::Intelligence).getModified())
+ (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + (0.125 * npcStats.getAttribute (ESM::Attribute::Luck).getModified()));
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
float chance2 = 7.5 / (gmst.find("fEnchantmentChanceMult")->getFloat() * ((mCastStyle == ESM::Enchantment::ConstantEffect) ?
gmst.find("fEnchantmentConstantChanceMult")->getFloat() : 1 ))
* getEnchantPoints();
float chance2 = 2.5 * getEnchantPoints();
if(mCastStyle==ESM::Enchantment::ConstantEffect)
{
float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat();
chance2 /= constantChance;
}
return (chance1-chance2); return (chance1-chance2);
} }

View file

@ -760,6 +760,14 @@ namespace MWMechanics
return mAI; return mAI;
} }
void MechanicsManager::playerLoaded()
{
mUpdatePlayer = true;
mClassSelected = true;
mRaceSelected = true;
mAI = true;
}
bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed)
{ {
MWWorld::Ptr victim; MWWorld::Ptr victim;

View file

@ -123,22 +123,24 @@ namespace MWMechanics
/// @return was it illegal, and someone saw you doing it? /// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed);
virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
virtual void skipAnimation(const MWWorld::Ptr& ptr); virtual void skipAnimation(const MWWorld::Ptr& ptr);
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName); virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment) /// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr); virtual void updateMagicEffects (const MWWorld::Ptr& ptr);
virtual void toggleAI(); virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects);
virtual bool isAIActive();
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects); virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor);
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor); virtual void toggleAI();
virtual bool isAIActive();
virtual void playerLoaded();
}; };
} }

View file

@ -30,6 +30,7 @@ MWMechanics::NpcStats::NpcStats()
, mProfit(0) , mProfit(0)
, mTimeToStartDrowning(20.0) , mTimeToStartDrowning(20.0)
, mLastDrowningHit(0) , mLastDrowningHit(0)
, mLevelHealthBonus(0)
{ {
mSkillIncreases.resize (ESM::Attribute::Length, 0); mSkillIncreases.resize (ESM::Attribute::Length, 0);
} }
@ -189,22 +190,31 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
base += 1; base += 1;
// if this is a major or minor skill of the class, increase level progress const MWWorld::Store<ESM::GameSetting> &gmst =
bool levelProgress = false; MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
for (int i=0; i<2; ++i)
for (int j=0; j<5; ++j) // is this a minor or major skill?
int increase = gmst.find("iLevelupMiscMultAttriubte")->getInt(); // Note: GMST has a typo
for (int k=0; k<5; ++k)
{
if (class_.mData.mSkills[k][0] == skillIndex)
{ {
int skill = class_.mData.mSkills[j][i]; mLevelProgress += gmst.find("iLevelUpMinorMult")->getInt();
if (skill == skillIndex) increase = gmst.find("iLevelUpMajorMultAttribute")->getInt();
levelProgress = true;
} }
}
for (int k=0; k<5; ++k)
{
if (class_.mData.mSkills[k][1] == skillIndex)
{
mLevelProgress += gmst.find("iLevelUpMajorMult")->getInt();
increase = gmst.find("iLevelUpMinorMultAttribute")->getInt();
}
}
mLevelProgress += levelProgress;
// check the attribute this skill belongs to
const ESM::Skill* skill = const ESM::Skill* skill =
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex); MWBase::Environment::get().getWorld ()->getStore ().get<ESM::Skill>().find(skillIndex);
++mSkillIncreases[skill->mData.mAttribute]; mSkillIncreases[skill->mData.mAttribute] += increase;
// Play sound & skill progress notification // Play sound & skill progress notification
/// \todo check if character is the player, if levelling is ever implemented for NPCs /// \todo check if character is the player, if levelling is ever implemented for NPCs
@ -216,7 +226,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
% static_cast<int> (base); % static_cast<int> (base);
MWBase::Environment::get().getWindowManager ()->messageBox(message.str()); MWBase::Environment::get().getWindowManager ()->messageBox(message.str());
if (mLevelProgress >= 10) if (mLevelProgress >= gmst.find("iLevelUpTotal")->getInt())
{ {
// levelup is possible now // levelup is possible now
MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}"); MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}");
@ -237,22 +247,43 @@ void MWMechanics::NpcStats::levelUp()
mLevelProgress -= 10; mLevelProgress -= 10;
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0; mSkillIncreases[i] = 0;
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
// "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance"
mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat();
updateHealth();
setLevel(getLevel()+1);
}
void MWMechanics::NpcStats::updateHealth()
{
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(static_cast<int> (0.5 * (strength + endurance)) + mLevelHealthBonus);
} }
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const
{ {
// Source: http://www.uesp.net/wiki/Morrowind:Level#How_to_Level_Up
int num = mSkillIncreases[attribute]; int num = mSkillIncreases[attribute];
if (num <= 1)
if (num == 0)
return 1; return 1;
else if (num <= 4)
return 2; num = std::min(10, num);
else if (num <= 7)
return 3; // iLevelUp01Mult - iLevelUp10Mult
else if (num <= 9) std::stringstream gmst;
return 4; gmst << "iLevelUp" << std::setfill('0') << std::setw(2) << num << "Mult";
else
return 5; return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(gmst.str())->getInt();
} }
void MWMechanics::NpcStats::flagAsUsed (const std::string& id) void MWMechanics::NpcStats::flagAsUsed (const std::string& id)

View file

@ -51,6 +51,8 @@ namespace MWMechanics
/// time since last hit from drowning /// time since last hit from drowning
float mLastDrowningHit; float mLastDrowningHit;
float mLevelHealthBonus;
public: public:
NpcStats(); NpcStats();
@ -98,6 +100,10 @@ namespace MWMechanics
void levelUp(); void levelUp();
void updateHealth();
///< Calculate health based on endurance and strength.
/// Called at character creation and at level up.
void flagAsUsed (const std::string& id); void flagAsUsed (const std::string& id);
bool hasBeenUsed (const std::string& id) const; bool hasBeenUsed (const std::string& id) const;

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