Merge remote-tracking branch 'refs/remotes/master/master' into NonTableFields

Conflicts:
	apps/opencs/CMakeLists.txt
test
Marek Kochanowicz 11 years ago
commit cdac934315

@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 30)
set(OPENMW_VERSION_MINOR 31)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
@ -325,12 +325,14 @@ else ()
add_definitions(-DOGRE_PLUGIN_DEBUG_SUFFIX="_d")
endif()
add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}")
add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}")
if (APPLE AND OPENMW_OSX_DEPLOYMENT)
# make it empty so plugin loading code can check this and try to find plugins inside app bundle
add_definitions(-DOGRE_PLUGIN_DIR="")
else()
if (NOT DEFINED ${OGRE_PLUGIN_DIR})
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL})
endif()
add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}")
endif()

@ -249,6 +249,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " Global: '" << ref.mGlobalVariable << "'" << std::endl;
std::cout << " Faction: '" << ref.mFaction << "'" << std::endl;
std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl;
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";

@ -513,7 +513,7 @@ void Record<ESM::Cell>::print()
else
std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl;
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
std::cout << " NAM0: " << mData.mNAM0 << std::endl;
std::cout << " RefId counter: " << mData.mRefIdCounter << std::endl;
}

@ -61,8 +61,13 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator
)
opencs_units (view/render
@ -75,12 +80,10 @@ opencs_units_noqt (view/render
lightingbright object cell
)
opencs_units_noqt (view/world
subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator
opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton
)
opencs_units (view/tools
reportsubview
)

@ -977,13 +977,13 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mFactIndex;
return record.get().mFactionRank;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mFactIndex = data.toInt();
record2.mFactionRank = data.toInt();
record.setModified (record2);
}

@ -81,6 +81,7 @@ QWidget *CSVDoc::StartupDialogue::createTools()
config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
config->setIcon (QIcon (":startup/configure"));
config->setToolTip ("Open user settings");
layout->addWidget (config);

@ -3,6 +3,7 @@
#include <QEvent>
#include <QResizeEvent>
#include <QTimer>
#include <QShortcut>
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
@ -11,7 +12,7 @@
#include <OgreSceneNode.h>
#include <OgreViewport.h>
#include "../world/scenetoolmode.hpp"
#include "../widget/scenetoolmode.hpp"
#include "navigation.hpp"
#include "lighting.hpp"
@ -51,16 +52,34 @@ namespace CSVRender
QTimer *timer = new QTimer (this);
connect (timer, SIGNAL (timeout()), this, SLOT (update()));
timer->start (20); /// \todo make this configurable
timer->start (20); ///< \todo make this configurable
/// \todo make shortcut configurable
QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest()));
}
CSVWorld::SceneToolMode *SceneWidget::makeLightingSelector (CSVWorld::SceneToolbar *parent)
CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent)
{
CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent);
tool->addButton (":door.png", "day"); /// \todo replace icons
tool->addButton (":GMST.png", "night");
tool->addButton (":Info.png", "bright");
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode");
/// \todo replace icons
tool->addButton (":door.png", "day",
"Day"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Strong directional light source/lir>"
"<li>This mode closely resembles day time in-game</li></ul>");
tool->addButton (":GMST.png", "night",
"Night"
"<ul><li>Cell specific ambient in interiors</li>"
"<li>Low ambient in exteriors</li>"
"<li>Weak directional light source</li>"
"<li>This mode closely resembles night time in-game</li></ul>");
tool->addButton (":Info.png", "bright",
"Bright"
"<ul><li>Maximum ambient</li>"
"<li>Strong directional light source</li></ul>");
connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectLightingMode (const std::string&)));
@ -347,6 +366,9 @@ namespace CSVRender
mLighting = lighting;
mLighting->activate (mSceneMgr, mHasDefaultAmbient ? &mDefaultAmbient : 0);
if (mWindow)
mWindow->update();
}
void SceneWidget::selectLightingMode (const std::string& mode)

@ -16,7 +16,7 @@ namespace Ogre
class RenderWindow;
}
namespace CSVWorld
namespace CSVWidget
{
class SceneToolMode;
class SceneToolbar;
@ -38,7 +38,7 @@ namespace CSVRender
QPaintEngine* paintEngine() const;
CSVWorld::SceneToolMode *makeLightingSelector (CSVWorld::SceneToolbar *parent);
CSVWidget::SceneToolMode *makeLightingSelector (CSVWidget::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that
/// is the responsibility of the calling function.
@ -111,6 +111,10 @@ namespace CSVRender
void update();
void selectLightingMode (const std::string& mode);
signals:
void focusToolbarRequest();
};
}

@ -7,8 +7,9 @@
#include <QtGui/qevent.h>
#include "../world/scenetoolmode.hpp"
#include <apps/opencs/model/world/universalid.hpp>
#include "../../model/world/universalid.hpp"
#include "../widget/scenetoolmode.hpp"
CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (parent), mDocument(document)
@ -53,14 +54,39 @@ void CSVRender::WorldspaceWidget::selectDefaultNavigationMode()
setNavigation (&m1st);
}
CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
CSVWorld::SceneToolbar *parent)
CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
CSVWidget::SceneToolbar *parent)
{
CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent);
tool->addButton (":door.png", "1st"); /// \todo replace icons
tool->addButton (":GMST.png", "free");
tool->addButton (":Info.png", "orbit");
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Camera Mode");
/// \todo replace icons
/// \todo consider user-defined button-mapping
tool->addButton (":door.png", "1st",
"First Person"
"<ul><li>Mouse-Look while holding the left button</li>"
"<li>WASD movement keys</li>"
"<li>Mouse wheel moves the camera forawrd/backward</li>"
"<li>Stafing (also vertically) by holding the left mouse button and control</li>"
"<li>Camera is held upright</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
tool->addButton (":GMST.png", "free",
"Free Camera"
"<ul><li>Mouse-Look while holding the left button</li>"
"<li>Stafing (also vertically) via WASD or by holding the left mouse button and control</li>"
"<li>Mouse wheel moves the camera forawrd/backward</li>"
"<li>Roll camera with Q and E keys</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
tool->addButton (":Info.png", "orbit",
"Orbiting Camera"
"<ul><li>Always facing the centre point</li>"
"<li>Rotate around the centre point via WASD or by moving the mouse while holding the left button</li>"
"<li>Mouse wheel moves camera away or towards centre point but can not pass through it</li>"
"<li>Roll camera with Q and E keys</li>"
"<li>Stafing (also vertically) by holding the left mouse button and control (includes relocation of the centre point)</li>"
"<li>Hold shift to speed up movement</li>"
"</ul>");
connect (tool, SIGNAL (modeChanged (const std::string&)),
this, SLOT (selectNavigationMode (const std::string&)));

@ -13,7 +13,7 @@ namespace CSMWorld
{
class UniversalId;
}
namespace CSVWorld
namespace CSVWidget
{
class SceneToolMode;
class SceneToolbar;
@ -49,7 +49,7 @@ namespace CSVRender
WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0);
CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent);
CSVWidget::SceneToolMode *makeNavigationSelector (CSVWidget::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that
/// is the responsibility of the calling function.

@ -0,0 +1,83 @@
#include "pushbutton.hpp"
#include <QMouseEvent>
#include <QKeyEvent>
void CSVWidget::PushButton::setExtendedToolTip (const QString& text)
{
QString tooltip = text;
if (tooltip.isEmpty())
tooltip = "(Tool tip not implemented yet)";
switch (mType)
{
case Type_TopMode:
tooltip +=
"<p>(left click to change mode)";
break;
case Type_Mode:
tooltip +=
"<p>(left click to activate,"
"<br>shift-left click to activate and keep panel open)";
break;
}
setToolTip (tooltip);
}
void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event)
{
if (event->key()!=Qt::Key_Shift)
mKeepOpen = false;
QPushButton::keyPressEvent (event);
}
void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event)
{
if (event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter)
{
mKeepOpen = event->modifiers() & Qt::ShiftModifier;
emit clicked();
}
QPushButton::keyReleaseEvent (event);
}
void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event)
{
mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier);
QPushButton::mouseReleaseEvent (event);
}
CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip,
QWidget *parent)
: QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
{
setCheckable (type==Type_Mode);
setExtendedToolTip (tooltip);
}
CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent)
: QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
{
setCheckable (type==Type_Mode);
setExtendedToolTip (tooltip);
}
bool CSVWidget::PushButton::hasKeepOpen() const
{
return mKeepOpen;
}
QString CSVWidget::PushButton::getBaseToolTip() const
{
return mToolTip;
}

@ -0,0 +1,55 @@
#ifndef CSV_WIDGET_PUSHBUTTON_H
#define CSV_WIDGET_PUSHBUTTON_H
#include <QPushButton>
namespace CSVWidget
{
class PushButton : public QPushButton
{
Q_OBJECT
public:
enum Type
{
Type_TopMode, // top level button for mode selector panel
Type_Mode // mode button
};
private:
bool mKeepOpen;
Type mType;
QString mToolTip;
private:
void setExtendedToolTip (const QString& text);
protected:
virtual void keyPressEvent (QKeyEvent *event);
virtual void keyReleaseEvent (QKeyEvent *event);
virtual void mouseReleaseEvent (QMouseEvent *event);
public:
/// \param push Do not maintain a toggle state
PushButton (const QIcon& icon, Type type, const QString& tooltip = "",
QWidget *parent = 0);
/// \param push Do not maintain a toggle state
PushButton (Type type, const QString& tooltip = "",
QWidget *parent = 0);
bool hasKeepOpen() const;
/// Return tooltip used at construction (without any button-specific modifications)
QString getBaseToolTip() const;
};
}
#endif

@ -3,7 +3,8 @@
#include "scenetoolbar.hpp"
CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent)
CSVWidget::SceneTool::SceneTool (SceneToolbar *parent)
: PushButton (PushButton::Type_TopMode, "", parent)
{
setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
setIconSize (QSize (parent->getIconSize(), parent->getIconSize()));
@ -12,7 +13,7 @@ CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent)
connect (this, SIGNAL (clicked()), this, SLOT (openRequest()));
}
void CSVWorld::SceneTool::openRequest()
void CSVWidget::SceneTool::openRequest()
{
showPanel (parentWidget()->mapToGlobal (pos()));
}

@ -1,14 +1,14 @@
#ifndef CSV_WORLD_SCENETOOL_H
#define CSV_WORLD_SCENETOOL_H
#ifndef CSV_WIDGET_SCENETOOL_H
#define CSV_WIDGET_SCENETOOL_H
#include <QPushButton>
#include "pushbutton.hpp"
namespace CSVWorld
namespace CSVWidget
{
class SceneToolbar;
///< \brief Tool base class
class SceneTool : public QPushButton
class SceneTool : public PushButton
{
Q_OBJECT

@ -0,0 +1,47 @@
#include "scenetoolbar.hpp"
#include <QVBoxLayout>
#include <QShortcut>
#include "scenetool.hpp"
void CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event)
{
QWidget::focusInEvent (event);
if (mLayout->count())
dynamic_cast<QWidgetItem&> (*mLayout->itemAt (0)).widget()->setFocus();
}
CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
{
setFixedWidth (mButtonSize);
mLayout = new QVBoxLayout (this);
mLayout->setAlignment (Qt::AlignTop);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
setLayout (mLayout);
/// \todo make shortcut configurable
QShortcut *focusScene = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
connect (focusScene, SIGNAL (activated()), this, SIGNAL (focusSceneRequest()));
}
void CSVWidget::SceneToolbar::addTool (SceneTool *tool)
{
mLayout->addWidget (tool, 0, Qt::AlignTop);
}
int CSVWidget::SceneToolbar::getButtonSize() const
{
return mButtonSize;
}
int CSVWidget::SceneToolbar::getIconSize() const
{
return mIconSize;
}

@ -1,11 +1,11 @@
#ifndef CSV_WORLD_SCENETOOLBAR_H
#define CSV_WORLD_SCENETOOLBAR_H
#ifndef CSV_WIDGET_SCENETOOLBAR_H
#define CSV_WIDGET_SCENETOOLBAR_H
#include <QWidget>
class QVBoxLayout;
namespace CSVWorld
namespace CSVWidget
{
class SceneTool;
@ -17,6 +17,10 @@ namespace CSVWorld
int mButtonSize;
int mIconSize;
protected:
virtual void focusInEvent (QFocusEvent *event);
public:
SceneToolbar (int buttonSize, QWidget *parent = 0);
@ -26,6 +30,10 @@ namespace CSVWorld
int getButtonSize() const;
int getIconSize() const;
signals:
void focusSceneRequest();
};
}

@ -0,0 +1,86 @@
#include "scenetoolmode.hpp"
#include <QHBoxLayout>
#include <QFrame>
#include <QSignalMapper>
#include "scenetoolbar.hpp"
#include "pushbutton.hpp"
void CSVWidget::SceneToolMode::adjustToolTip (const PushButton *activeMode)
{
QString toolTip = mToolTip;
toolTip += "<p>Currently selected: " + activeMode->getBaseToolTip();
toolTip += "<p>(left click to change mode)";
setToolTip (toolTip);
}
CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip)
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()),
mToolTip (toolTip), mFirst (0)
{
mPanel = new QFrame (this, Qt::Popup);
mLayout = new QHBoxLayout (mPanel);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
mPanel->setLayout (mLayout);
}
void CSVWidget::SceneToolMode::showPanel (const QPoint& position)
{
mPanel->move (position);
mPanel->show();
if (mFirst)
mFirst->setFocus (Qt::OtherFocusReason);
}
void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id,
const QString& tooltip)
{
PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode,
tooltip, mPanel);
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setIconSize (QSize (mIconSize, mIconSize));
button->setFixedSize (mButtonSize, mButtonSize);
mLayout->addWidget (button);
mButtons.insert (std::make_pair (button, id));
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
if (mButtons.size()==1)
{
mFirst = button;
setIcon (button->icon());
button->setChecked (true);
adjustToolTip (button);
}
}
void CSVWidget::SceneToolMode::selected()
{
std::map<PushButton *, std::string>::const_iterator iter =
mButtons.find (dynamic_cast<PushButton *> (sender()));
if (iter!=mButtons.end())
{
if (!iter->first->hasKeepOpen())
mPanel->hide();
for (std::map<PushButton *, std::string>::const_iterator iter2 = mButtons.begin();
iter2!=mButtons.end(); ++iter2)
iter2->first->setChecked (iter2==iter);
setIcon (iter->first->icon());
adjustToolTip (iter->first);
emit modeChanged (iter->second);
}
}

@ -1,5 +1,5 @@
#ifndef CSV_WORLD_SCENETOOL_MODE_H
#define CSV_WORLD_SCENETOOL_MODE_H
#ifndef CSV_WIDGET_SCENETOOL_MODE_H
#define CSV_WIDGET_SCENETOOL_MODE_H
#include "scenetool.hpp"
@ -7,9 +7,10 @@
class QHBoxLayout;
namespace CSVWorld
namespace CSVWidget
{
class SceneToolbar;
class PushButton;
///< \brief Mode selector tool
class SceneToolMode : public SceneTool
@ -18,17 +19,22 @@ namespace CSVWorld
QWidget *mPanel;
QHBoxLayout *mLayout;
std::map<QPushButton *, std::string> mButtons; // widget, id
std::map<PushButton *, std::string> mButtons; // widget, id
int mButtonSize;
int mIconSize;
QString mToolTip;
PushButton *mFirst;
void adjustToolTip (const PushButton *activeMode);
public:
SceneToolMode (SceneToolbar *parent);
SceneToolMode (SceneToolbar *parent, const QString& toolTip);
virtual void showPanel (const QPoint& position);
void addButton (const std::string& icon, const std::string& id);
void addButton (const std::string& icon, const std::string& id,
const QString& tooltip = "");
signals:

@ -471,25 +471,32 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
QHBoxLayout *buttonsLayout = new QHBoxLayout;
QToolButton* prevButton = new QToolButton(mainWidget);
prevButton->setIcon(QIcon(":/go-previous.png"));
prevButton->setToolTip ("Switch to previous record");
QToolButton* nextButton = new QToolButton(mainWidget);
nextButton->setIcon(QIcon(":/go-next.png"));
nextButton->setToolTip ("Switch to next record");
buttonsLayout->addWidget(prevButton, 0);
buttonsLayout->addWidget(nextButton, 1);
buttonsLayout->addStretch(2);
QToolButton* cloneButton = new QToolButton(mainWidget);
cloneButton->setIcon(QIcon(":/edit-clone.png"));
cloneButton->setToolTip ("Clone record");
QToolButton* addButton = new QToolButton(mainWidget);
addButton->setIcon(QIcon(":/add.png"));
addButton->setToolTip ("Add new record");
QToolButton* deleteButton = new QToolButton(mainWidget);
deleteButton->setIcon(QIcon(":/edit-delete.png"));
deleteButton->setToolTip ("Delete record");
QToolButton* revertButton = new QToolButton(mainWidget);
revertButton->setIcon(QIcon(":/edit-undo.png"));
revertButton->setToolTip ("Revert record");
if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview)
{
QToolButton* previewButton = new QToolButton(mainWidget);
previewButton->setIcon(QIcon(":/edit-preview.png"));
previewButton->setToolTip ("Open a preview of this record");
buttonsLayout->addWidget(previewButton);
connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview()));
}
@ -498,6 +505,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
{
QToolButton* viewButton = new QToolButton(mainWidget);
viewButton->setIcon(QIcon(":/cell.png"));
viewButton->setToolTip ("Open a scene view of the cell this record is located in");
buttonsLayout->addWidget(viewButton);
connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord()));
}

@ -5,8 +5,8 @@
#include "../render/previewwidget.hpp"
#include "scenetoolbar.hpp"
#include "scenetoolmode.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetoolmode.hpp"
CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id)
@ -28,9 +28,9 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
else
mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this);
SceneToolbar *toolbar = new SceneToolbar (48+6, this);
CSVWidget::SceneToolbar *toolbar = new CSVWidget::SceneToolbar (48+6, this);
SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar);
CSVWidget::SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar);
toolbar->addTool (lightingTool);
layout->addWidget (toolbar, 0);
@ -46,6 +46,8 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)),
this, SLOT (referenceableIdChanged (const std::string&)));
connect (mScene, SIGNAL (focusToolbarRequest()), toolbar, SLOT (setFocus()));
connect (toolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));
}
void CSVWorld::PreviewSubView::setEditLock (bool locked) {}

@ -17,9 +17,11 @@
#include "../render/pagedworldspacewidget.hpp"
#include "../render/unpagedworldspacewidget.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetoolmode.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
#include "scenetoolmode.hpp"
CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id), mLayout(new QHBoxLayout), mDocument(document), mScene(NULL), mToolbar(NULL)
@ -95,18 +97,18 @@ void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget*
this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&)));
}
CSVWorld::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type)
CSVWidget::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type)
{
CSVWorld::SceneToolbar* toolbar = new SceneToolbar (48+6, this);
CSVWidget::SceneToolbar* toolbar = new CSVWidget::SceneToolbar (48+6, this);
SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar);
CSVWidget::SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar);
toolbar->addTool (navigationTool);
SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar);
CSVWidget::SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar);
toolbar->addTool (lightingTool);
/* Add buttons specific to the type. For now no need for it.
*
*
switch (type)
{
case widget_Paged:
@ -188,7 +190,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
{
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
SceneToolbar* toolbar = NULL;
CSVWidget::SceneToolbar* toolbar = NULL;
switch (mScene->getDropRequirements(CSVRender::WorldspaceWidget::getDropType(data)))
{
@ -217,7 +219,7 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI
}
}
void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWorld::SceneToolbar* toolbar)
void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar)
{
assert(mLayout);
@ -236,8 +238,12 @@ void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceW
mScene = widget;
mToolbar = toolbar;
connect (mScene, SIGNAL (focusToolbarRequest()), mToolbar, SLOT (setFocus()));
connect (mToolbar, SIGNAL (focusSceneRequest()), mScene, SLOT (setFocus()));
mLayout->addWidget (mToolbar, 0);
mLayout->addWidget (mScene, 1);
mScene->selectDefaultNavigationMode();
setFocusProxy (mScene);
}

@ -4,7 +4,6 @@
#include <QHBoxLayout>
#include "../doc/subview.hpp"
#include "scenetoolbar.hpp"
class QModelIndex;
@ -25,6 +24,11 @@ namespace CSVRender
class UnpagedWorldspaceWidget;
}
namespace CSVWidget
{
class SceneToolbar;
}
namespace CSVWorld
{
class Table;
@ -39,7 +43,7 @@ namespace CSVWorld
CSVRender::WorldspaceWidget *mScene;
QHBoxLayout* mLayout;
CSMDoc::Document& mDocument;
SceneToolbar* mToolbar;
CSVWidget::SceneToolbar* mToolbar;
public:
@ -59,14 +63,15 @@ namespace CSVWorld
void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget);
void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, SceneToolbar* toolbar);
void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, CSVWidget::SceneToolbar* toolbar);
enum widgetType
{
widget_Paged,
widget_Unpaged
};
SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);
CSVWidget::SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);
private slots:

@ -1,34 +0,0 @@
#include "scenetoolbar.hpp"
#include <QVBoxLayout>
#include "scenetool.hpp"
CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
{
setFixedWidth (mButtonSize);
mLayout = new QVBoxLayout (this);
mLayout->setAlignment (Qt::AlignTop);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
setLayout (mLayout);
}
void CSVWorld::SceneToolbar::addTool (SceneTool *tool)
{
mLayout->addWidget (tool, 0, Qt::AlignTop);
}
int CSVWorld::SceneToolbar::getButtonSize() const
{
return mButtonSize;
}
int CSVWorld::SceneToolbar::getIconSize() const
{
return mIconSize;
}

@ -1,57 +0,0 @@
#include "scenetoolmode.hpp"
#include <QHBoxLayout>
#include <QFrame>
#include <QSignalMapper>
#include "scenetoolbar.hpp"
CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent)
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize())
{
mPanel = new QFrame (this, Qt::Popup);
mLayout = new QHBoxLayout (mPanel);
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
mPanel->setLayout (mLayout);
}
void CSVWorld::SceneToolMode::showPanel (const QPoint& position)
{
mPanel->move (position);
mPanel->show();
}
void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::string& id)
{
QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel);
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setIconSize (QSize (mIconSize, mIconSize));
button->setFixedSize (mButtonSize, mButtonSize);
mLayout->addWidget (button);
mButtons.insert (std::make_pair (button, id));
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
if (mButtons.size()==1)
setIcon (button->icon());
}
void CSVWorld::SceneToolMode::selected()
{
std::map<QPushButton *, std::string>::const_iterator iter =
mButtons.find (dynamic_cast<QPushButton *> (sender()));
if (iter!=mButtons.end())
{
mPanel->hide();
setIcon (iter->first->icon());
emit modeChanged (iter->second);
}
}

@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling
)
add_openmw_dir (mwstate

@ -196,6 +196,10 @@ namespace MWBase
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
/// Usually done once a frame, but can be invoked manually in time-critical situations.
/// This will increase the death count for any actors that were killed.
virtual void killDeadActors() = 0;
};
}

@ -398,8 +398,9 @@ namespace MWBase
/// open or close a non-teleport door (depending on current state)
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
/// open or close a non-teleport door as specified
virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0;
/// update movement state of a non-teleport door as specified
/// @param state see MWClass::setDoorState
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object

@ -97,8 +97,7 @@ namespace MWClass
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}
info.text = text;

@ -127,8 +127,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}
info.text = text;

@ -247,8 +247,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -139,8 +139,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -193,8 +193,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -235,8 +235,7 @@ namespace MWClass
text += "\n#{sTrapped}";
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -9,6 +9,7 @@
#include "../mwmechanics/movement.hpp"
#include "../mwmechanics/disease.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -254,6 +255,23 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
// Weapon health is reduced by 1 even if the attack misses
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
if(weaphashealth)
{
int weaphealth = weapon.getClass().getItemHealth(weapon);
if (!MWBase::Environment::get().getWorld()->getGodModeState())
{
weaphealth -= std::min(1, weaphealth);
weapon.getCellRef().setCharge(weaphealth);
}
// Weapon broken? unequip it
if (weapon.getCellRef().getCharge() == 0)
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
}
return;
}
@ -290,13 +308,13 @@ namespace MWClass
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
if(weaphashealth)
{
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
int weaphealth = weapon.getClass().getItemHealth(weapon);
weaponDamage *= float(weaphealth) / weapmaxhealth;
damage *= float(weaphealth) / weapmaxhealth;
if (!MWBase::Environment::get().getWorld()->getGodModeState())
{
@ -311,8 +329,6 @@ namespace MWClass
if (weapon.getCellRef().getCharge() == 0)
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
}
damage += weaponDamage;
}
// Apply "On hit" enchanted weapons
@ -330,6 +346,8 @@ namespace MWClass
}
}
MWMechanics::applyElementalShields(ptr, victim);
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
@ -376,6 +394,9 @@ namespace MWClass
if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if (damage < 0.001f)
damage = 0;
if (damage > 0.f)
{
// Check for knockdown
@ -391,8 +412,13 @@ namespace MWClass
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
damage = std::max(1.f, damage);
if(ishealth)
{
if (!attacker.isEmpty())
damage = scaleDamage(damage, attacker, ptr);
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
@ -618,8 +644,8 @@ namespace MWClass
float Creature::getArmorRating (const MWWorld::Ptr& ptr) const
{
/// \todo add Shield magic effect magnitude here, controlled by a GMST (Vanilla vs MCP behaviour)
return 0.f;
// Note this is currently unused. Creatures do not use armor mitigation.
return getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
}
float Creature::getCapacity (const MWWorld::Ptr& ptr) const

@ -62,7 +62,7 @@ namespace MWClass
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
if (customData.mDoorState > 0)
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false);
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
}
}
}
@ -250,8 +250,10 @@ namespace MWClass
text += "\n#{sTrapped}";
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}
info.text = text;
return info;

@ -147,8 +147,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -187,8 +187,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -141,8 +141,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -182,8 +182,7 @@ namespace MWClass
}
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -22,6 +22,8 @@
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/disease.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/autocalcspell.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
@ -53,6 +55,24 @@ namespace
return new NpcCustomData (*this);
}
int is_even(double d) {
double int_part;
modf(d / 2.0, &int_part);
return 2.0 * int_part == d;
}
int round_ieee_754(double d) {
double i = floor(d);
d -= i;
if(d < 0.5)
return i;
if(d > 0.5)
return i + 1.0;
if(is_even(i))
return i;
return i + 1.0;
}
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
{
// race bonus
@ -108,8 +128,9 @@ namespace
}
modifierSum += add;
}
creatureStats.setAttribute(attribute, std::min(creatureStats.getAttribute(attribute).getBase()
+ static_cast<int>((level-1) * modifierSum+0.5), 100) );
creatureStats.setAttribute(attribute, std::min(
round_ieee_754(creatureStats.getAttribute(attribute).getBase()
+ (level-1) * modifierSum), 100) );
}
// initial health
@ -193,18 +214,6 @@ namespace
majorMultiplier = 1.0f;
break;
}
if (class_->mData.mSkills[k][1] == skillIndex)
{
// Major skill -> add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_Autocalc
&& MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex)
npcStats.getSpells().add(it->mId);
}
}
}
// is this skill in the same Specialization as the class?
@ -217,12 +226,25 @@ namespace
npcStats.getSkill(skillIndex).setBase(
std::min(
npcStats.getSkill(skillIndex).getBase()
round_ieee_754(
npcStats.getSkill(skillIndex).getBase()
+ 5
+ raceBonus
+ specBonus
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100));
+(int(level)-1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0
}
int skills[ESM::Skill::Length];
for (int i=0; i<ESM::Skill::Length; ++i)
skills[i] = npcStats.getSkill(i).getBase();
int attributes[ESM::Attribute::Length];
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
std::vector<std::string> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
for (std::vector<std::string>::iterator it = spells.begin(); it != spells.end(); ++it)
npcStats.getSpells().add(*it);
}
}
@ -258,6 +280,7 @@ namespace MWClass
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
gmst.fDamageStrengthBase = store.find("fDamageStrengthBase");
gmst.fDamageStrengthMult = store.find("fDamageStrengthMult");
gmst.fCombatArmorMinMult = store.find("fCombatArmorMinMult");
inited = true;
}
@ -506,6 +529,24 @@ namespace MWClass
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
// Weapon health is reduced by 1 even if the attack misses
const bool weaphashealth = !weapon.isEmpty() && weapon.getClass().hasItemHealth(weapon);
if(weaphashealth)
{
int weaphealth = weapon.getClass().getItemHealth(weapon);
if (!MWBase::Environment::get().getWorld()->getGodModeState())
{
weaphealth -= std::min(1, weaphealth);
weapon.getCellRef().setCharge(weaphealth);
}
// Weapon broken? unequip it
if (weaphealth == 0)
weapon = *inv.unequipItem(weapon, ptr);
}
return;
}
@ -560,8 +601,8 @@ namespace MWClass
damage = stats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
healthdmg = (otherstats.getFatigue().getCurrent() < 1.0f)
|| (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0);
healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0)
|| otherstats.getKnockedDown();
if(stats.isWerewolf())
{
healthdmg = true;
@ -583,15 +624,21 @@ namespace MWClass
sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f);
}
if(ptr.getRefData().getHandle() == "player")
{
skillUsageSucceeded(ptr, weapskill, 0);
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(!detected)
{
damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
const MWMechanics::AiSequence& seq = victim.getClass().getCreatureStats(victim).getAiSequence();
bool unaware = !seq.isInCombat()
&& !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(unaware)
{
damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
}
if (othercls.getCreatureStats(victim).getKnockedDown())
damage *= store.find("fCombatKODamageMult")->getFloat();
@ -609,6 +656,8 @@ namespace MWClass
}
}
MWMechanics::applyElementalShields(ptr, victim);
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
@ -631,6 +680,14 @@ namespace MWClass
!MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
if (!attacker.isEmpty() && attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(ptr)
&& !ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat(attacker))
{
// Attacker is in combat with us, but we are not in combat with the attacker yet. Time to fight back.
// Note: accidental or collateral damage attacks are ignored.
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
}
bool wasDead = getCreatureStats(ptr).isDead();
getCreatureStats(ptr).setAttacked(true);
@ -658,6 +715,9 @@ namespace MWClass
if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if (damage < 0.001f)
damage = 0;
if(damage > 0.0f)
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
@ -686,7 +746,7 @@ namespace MWClass
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage
if(damage > 0 && ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage
{
// Hit percentages:
// cuirass = 30%
@ -706,9 +766,12 @@ namespace MWClass
};
int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)];
float damagediff = damage;
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
damagediff -= damage;
float unmitigatedDamage = damage;
float x = damage / (damage + getArmorRating(ptr));
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
int damageDiff = unmitigatedDamage - damage;
if (damage < 1)
damage = 1;
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
@ -716,13 +779,13 @@ namespace MWClass
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
{
int armorhealth = armor.getClass().getItemHealth(armor);
armorhealth -= std::min(std::max(1, (int)damagediff),
armorhealth -= std::min(std::max(1, damageDiff),
armorhealth);
armor.getCellRef().setCharge(armorhealth);
// Armor broken? unequip it
if (armorhealth == 0)
inv.unequipItem(armor, ptr);
armor = *inv.unequipItem(armor, ptr);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
@ -747,6 +810,9 @@ namespace MWClass
if(ishealth)
{
if (!attacker.isEmpty())
damage = scaleDamage(damage, attacker, ptr);
if(damage > 0.0f)
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
@ -827,6 +893,7 @@ namespace MWClass
if(ptr.getRefData().getHandle() == "player")
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(actor));
// Werewolfs can't activate NPCs
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
@ -837,6 +904,7 @@ namespace MWClass
return action;
}
if(getCreatureStats(ptr).isDead())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(ptr.getClass().getCreatureStats(ptr).isHostile())
@ -844,8 +912,10 @@ namespace MWClass
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
// Can't talk to werewolfs
if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf())
return boost::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
}
MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)

@ -40,6 +40,7 @@ namespace MWClass
const ESM::GameSetting *iKnockDownOddsBase;
const ESM::GameSetting *fDamageStrengthBase;
const ESM::GameSetting *fDamageStrengthMult;
const ESM::GameSetting *fCombatArmorMinMult;
};
static const GMST& getGmst();

@ -151,8 +151,7 @@ namespace MWClass
info.isPotion = true;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -140,8 +140,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -144,8 +144,7 @@ namespace MWClass
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -351,8 +351,7 @@ namespace MWClass
info.remainingEnchantCharge = ptr.getCellRef().getEnchantmentCharge();
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getFaction(), "Faction");
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
}

@ -266,7 +266,7 @@ namespace MWDialogue
}
catch (const std::exception& error)
{
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what();
std::cerr << std::string ("Dialogue error: An exception has been thrown: ") + error.what() << std::endl;
}
}
}
@ -505,9 +505,10 @@ namespace MWDialogue
void DialogueManager::askQuestion (const std::string& question, int choice)
{
mIsInChoice = true;
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->addChoice(question, choice);
mIsInChoice = true;
}
void DialogueManager::goodbye()

@ -76,8 +76,18 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
}
else if (info.mData.mRank != -1)
{
// if there is a rank condition, but the NPC is not in a faction, always fail
return false;
if (isCreature)
return false;
// Rank requirement, but no faction given. Use the actor's faction, if there is one.
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
if (!stats.getFactionRanks().size())
return false;
// check rank
if (stats.getFactionRanks().begin()->second < info.mData.mRank)
return false;
}
// Gender

@ -153,6 +153,13 @@ void CompanionWindow::onReferenceUnavailable()
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
}
void CompanionWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
mModel = NULL;
mSortModel = NULL;
}
}

@ -20,6 +20,8 @@ namespace MWGui
virtual void exit();
virtual void resetReference();
void open(const MWWorld::Ptr& npc);
void onFrame ();

@ -195,15 +195,6 @@ namespace MWGui
{
if (mPtr.getTypeName() == typeid(ESM::Container).name())
{
// check that we don't exceed container capacity
MWWorld::Ptr item = mDragAndDrop->mItem.mBase;
float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount;
if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}");
return;
}
// check container organic flag
MWWorld::LiveCellRef<ESM::Container>* ref = mPtr.get<ESM::Container>();
if (ref->mBase->mFlags & ESM::Container::Organic)
@ -212,6 +203,15 @@ namespace MWGui
messageBox("#{sContentsMessage2}");
return;
}
// check that we don't exceed container capacity
MWWorld::Ptr item = mDragAndDrop->mItem.mBase;
float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount;
if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}");
return;
}
}
mDragAndDrop->drop(mModel, mItemView);
@ -258,6 +258,14 @@ namespace MWGui
onTakeAllButtonClicked(mTakeButton);
}
void ContainerWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
mModel = NULL;
mSortModel = NULL;
}
void ContainerWindow::close()
{
WindowBase::close();

@ -54,6 +54,8 @@ namespace MWGui
void open(const MWWorld::Ptr& container, bool loot=false);
virtual void close();
virtual void resetReference();
virtual void exit();
private:

@ -273,8 +273,13 @@ namespace MWGui
{
if ((!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice())
&& !mGoodbye)
return;
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
{
// in choice, not allowed to escape, but give access to main menu to allow loading other saves
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx);
}
else
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
}
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)

@ -260,21 +260,28 @@ namespace MWGui
// More hacks! The french game uses several win1252 characters that are not included
// in the cp437 encoding of the font. Fall back to similar available characters.
// Same for U+2013
std::map<int, int> additional;
additional[39] = 0x2019; // apostrophe
additional[45] = 0x2013; // dash
if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437)
if (mEncoding == ToUTF8::CP437)
{
MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", additional[i]);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "
+ MyGUI::utility::toString(h));
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
std::multimap<int, int> additional;
additional.insert(std::make_pair(39, 0x2019)); // apostrophe
additional.insert(std::make_pair(45, 0x2013)); // dash
additional.insert(std::make_pair(34, 0x201D)); // right double quotation mark
additional.insert(std::make_pair(34, 0x201C)); // left double quotation mark
for (std::multimap<int, int>::iterator it = additional.begin(); it != additional.end(); ++it)
{
if (it->first != i)
continue;
MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", it->second);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "
+ MyGUI::utility::toString(h));
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
}
}
// ASCII vertical bar, use this as text input cursor

@ -92,6 +92,7 @@ namespace MWGui
, mSpellVisible(true)
, mWorldMouseOver(false)
, mEnemyHealthTimer(-1)
, mEnemyActorId(-1)
, mIsDrowning(false)
, mWeaponSpellTimer(0.f)
, mDrowningFlashTheta(0.f)
@ -609,7 +610,10 @@ namespace MWGui
void HUD::updateEnemyHealthBar()
{
MWMechanics::CreatureStats& stats = mEnemy.getClass().getCreatureStats(mEnemy);
MWWorld::Ptr enemy = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mEnemyActorId);
if (enemy.isEmpty())
return;
MWMechanics::CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
mEnemyHealth->setProgressRange(100);
// Health is usually cast to int before displaying. Actors die whenever they are < 1 health.
// Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)
@ -620,7 +624,7 @@ namespace MWGui
{
mSpellIcons->updateWidgets(mEffectBox, true);
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
if (mEnemyActorId != -1 && mEnemyHealth->getVisible())
{
updateEnemyHealthBar();
}
@ -634,7 +638,7 @@ namespace MWGui
void HUD::setEnemy(const MWWorld::Ptr &enemy)
{
mEnemy = enemy;
mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();
mEnemyHealthTimer = 5;
if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
@ -644,7 +648,7 @@ namespace MWGui
void HUD::resetEnemy()
{
mEnemy = MWWorld::Ptr();
mEnemyActorId = -1;
mEnemyHealthTimer = -1;
}

@ -104,7 +104,7 @@ namespace MWGui
SpellIcons* mSpellIcons;
MWWorld::Ptr mEnemy;
int mEnemyActorId;
float mEnemyHealthTimer;
bool mIsDrowning;

@ -140,26 +140,28 @@ void ItemView::onMouseWheel(MyGUI::Widget *_sender, int _rel)
void ItemView::setSize(const MyGUI::IntSize &_value)
{
bool changed = (_value.width != getWidth() || _value.height != getHeight());
Base::setSize(_value);
update();
if (changed)
update();
}
void ItemView::setSize(int _width, int _height)
{
Base::setSize(_width, _height);
update();
setSize(MyGUI::IntSize(_width, _height));
}
void ItemView::setCoord(const MyGUI::IntCoord &_value)
{
bool changed = (_value.width != getWidth() || _value.height != getHeight());
Base::setCoord(_value);
update();
if (changed)
update();
}
void ItemView::setCoord(int _left, int _top, int _width, int _height)
{
Base::setCoord(_left, _top, _width, _height);
update();
setCoord(MyGUI::IntCoord(_left, _top, _width, _height));
}
void ItemView::registerComponents()

@ -259,7 +259,7 @@ struct JournalViewModelImpl : JournalViewModel
os
<< itr->mDayOfMonth << ' '
<< MWBase::Environment::get().getWorld()->getMonthName (itr->mMonth)
<< " (" << dayStr << " " << (itr->mDay + 1) << ')';
<< " (" << dayStr << " " << (itr->mDay) << ')';
timestamp_buffer = os.str ();
}

@ -16,9 +16,10 @@
namespace MWGui
{
const unsigned int LevelupDialog::sMaxCoins = 3;
LevelupDialog::LevelupDialog()
: WindowBase("openmw_levelup_dialog.layout")
: WindowBase("openmw_levelup_dialog.layout"),
mCoinCount(sMaxCoins)
{
getWidget(mOkButton, "OkButton");
getWidget(mClassImage, "ClassImage");
@ -46,12 +47,10 @@ namespace MWGui
mAttributeMultipliers.push_back(t);
}
int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5;
for (int i=0; i<3; ++i)
for (unsigned int i = 0; i < mCoinCount; ++i)
{
MyGUI::ImageBox* image = mMainWidget->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(curX,250,16,16), MyGUI::Align::Default);
MyGUI::ImageBox* image = mCoinBox->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(0,0,16,16), MyGUI::Align::Default);
image->setImageTexture ("icons\\tx_goldicon.dds");
curX += 24+2;
mCoins.push_back(image);
}
@ -61,15 +60,15 @@ namespace MWGui
void LevelupDialog::setAttributeValues()
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player);
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);
for (int i=0; i<8; ++i)
for (int i = 0; i < 8; ++i)
{
int val = creatureStats.getAttribute (i).getBase ();
int val = creatureStats.getAttribute(i).getBase();
if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end())
{
val += pcStats.getLevelupAttributeMultiplier (i);
val += pcStats.getLevelupAttributeMultiplier(i);
}
if (val >= 100)
@ -80,20 +79,21 @@ namespace MWGui
}
void LevelupDialog::resetCoins ()
void LevelupDialog::resetCoins()
{
int curX = 0;
for (int i=0; i<3; ++i)
const int coinSpacing = 10;
int curX = mCoinBox->getWidth()/2 - (coinSpacing*(mCoinCount - 1) + 16*mCoinCount)/2;
for (unsigned int i=0; i<mCoinCount; ++i)
{
MyGUI::ImageBox* image = mCoins[i];
image->detachFromWidget();
image->attachToWidget(mCoinBox);
image->setCoord(MyGUI::IntCoord(curX,0,16,16));
curX += 24+2;
curX += 16+coinSpacing;
}
}
void LevelupDialog::assignCoins ()
void LevelupDialog::assignCoins()
{
resetCoins();
for (unsigned int i=0; i<mSpentAttributes.size(); ++i)
@ -118,13 +118,8 @@ namespace MWGui
{
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats (player);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats (player);
mSpentAttributes.clear();
resetCoins();
setAttributeValues();
MWMechanics::CreatureStats& creatureStats = player.getClass().getCreatureStats(player);
MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player);
const ESM::NPC *playerData = player.get<ESM::NPC>()->mBase;
@ -132,82 +127,111 @@ namespace MWGui
const ESM::Class *cls =
world->getStore().get<ESM::Class>().find(playerData->mClass);
// Vanilla uses thief.dds for custom classes. A player with a custom class
// doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as
// "$dynamic0". This check should resolve bug #1260.
// Choosing Stealth specialization and Speed/Agility as attributes.
if(world->getStore().get<ESM::Class>().isDynamic(cls->mId))
{
// Vanilla uses thief.dds for custom classes.
// Choosing Stealth specialization and Speed/Agility as attributes, if possible. Otherwise fall back to first class found.
MWWorld::SharedIterator<ESM::Class> it = world->getStore().get<ESM::Class>().begin();
for(; it != world->getStore().get<ESM::Class>().end(); it++)
{
if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3)
break;
}
mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds");
if (it == world->getStore().get<ESM::Class>().end())
it = world->getStore().get<ESM::Class>().begin();
if (it != world->getStore().get<ESM::Class>().end())
cls = &*it;
}
else
mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds");
mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds");
int level = creatureStats.getLevel ()+1;
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast<std::string>(level));
std::string levelupdescription;
if(level>20)
if(level > 20)
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
else
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+boost::lexical_cast<std::string>(level));
mLevelDescription->setCaption (levelupdescription);
for (int i=0; i<8; ++i)
unsigned int availableAttributes = 0;
for (int i = 0; i < 8; ++i)
{
MyGUI::TextBox* text = mAttributeMultipliers[i];
int mult = pcStats.getLevelupAttributeMultiplier (i);
text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult));
if (pcStats.getAttribute(i).getBase() < 100)
{
mAttributes[i]->setEnabled(true);
availableAttributes++;
int mult = pcStats.getLevelupAttributeMultiplier (i);
text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast<std::string>(mult));
}
else
{
mAttributes[i]->setEnabled(false);
text->setCaption("");
}
}
mCoinCount = std::min(sMaxCoins, availableAttributes);
for (unsigned int i = 0; i < sMaxCoins; i++)
{
if (i < mCoinCount)
mCoins[i]->attachToWidget(mCoinBox);
else
mCoins[i]->detachFromWidget();
}
mSpentAttributes.clear();
resetCoins();
setAttributeValues();
center();
}
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::NpcStats& pcStats = player.getClass().getNpcStats (player);
if (mSpentAttributes.size() < 3)
MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}");
if (mSpentAttributes.size() < mCoinCount)
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage36}");
else
{
// increase attributes
for (int i=0; i<3; ++i)
for (unsigned int i = 0; i < mCoinCount; ++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)
attribute.setBase(100);
pcStats.setAttribute(mSpentAttributes[i], attribute);
}
pcStats.levelUp ();
pcStats.levelUp();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Levelup);
}
}
void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender)
void LevelupDialog::onAttributeClicked(MyGUI::Widget *sender)
{
int attribute = *sender->getUserData<int>();
std::vector<int>::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute);
if (found != mSpentAttributes.end())
mSpentAttributes.erase (found);
mSpentAttributes.erase(found);
else
{
if (mSpentAttributes.size() == 3)
mSpentAttributes[2] = attribute;
if (mSpentAttributes.size() == mCoinCount)
mSpentAttributes[mCoinCount - 1] = attribute;
else
mSpentAttributes.push_back(attribute);
}

@ -28,8 +28,11 @@ namespace MWGui
std::vector<int> mSpentAttributes;
void onOkButtonClicked (MyGUI::Widget* sender);
void onAttributeClicked (MyGUI::Widget* sender);
unsigned int mCoinCount;
static const unsigned int sMaxCoins;
void onOkButtonClicked(MyGUI::Widget* sender);
void onAttributeClicked(MyGUI::Widget* sender);
void assignCoins();
void resetCoins();

@ -117,15 +117,18 @@ void MerchantRepair::exit()
void MerchantRepair::onRepairButtonClick(MyGUI::Widget *sender)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// repair
MWWorld::Ptr item = *sender->getUserData<MWWorld::Ptr>();
item.getCellRef().setCharge(item.getClass().getItemMaxHealth(item));
player.getClass().getContainerStore(player).restack(item);
MWBase::Environment::get().getSoundManager()->playSound("Repair",1,1);
int price = boost::lexical_cast<int>(sender->getUserString("Price"));
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price, player);
startRepair(mActor);

@ -165,6 +165,8 @@ void Recharge::onItemClicked(MyGUI::Widget *sender)
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
player.getClass().getContainerStore(player).restack(item);
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
}

@ -157,6 +157,8 @@ namespace MWGui
{
configureWidgets(mMainWidget);
setTitle("#{sOptions}");
getWidget(mOkButton, "OkButton");
getWidget(mResolutionList, "ResolutionList");
getWidget(mFullscreenButton, "FullscreenButton");
@ -174,6 +176,7 @@ namespace MWGui
getWidget(mControlsBox, "ControlsBox");
getWidget(mResetControlsButton, "ResetControlsButton");
getWidget(mRefractionButton, "RefractionButton");
getWidget(mDifficultySlider, "DifficultySlider");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled);
@ -227,6 +230,10 @@ namespace MWGui
MyGUI::TextBox* fovText;
getWidget(fovText, "FovText");
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("field of view", "General"))) + ")");
MyGUI::TextBox* diffText;
getWidget(diffText, "DifficultyText");
diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("difficulty", "Game"))) + ")");
}
void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)
@ -406,6 +413,12 @@ namespace MWGui
getWidget(fovText, "FovText");
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(value)) + ")");
}
if (scroller == mDifficultySlider)
{
MyGUI::TextBox* diffText;
getWidget(diffText, "DifficultyText");
diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast<std::string>(int(value)) + ")");
}
}
else
{

@ -30,6 +30,7 @@ namespace MWGui
MyGUI::Button* mVSyncButton;
MyGUI::Button* mFPSButton;
MyGUI::ScrollBar* mFOVSlider;
MyGUI::ScrollBar* mDifficultySlider;
MyGUI::ScrollBar* mAnisotropySlider;
MyGUI::ComboBox* mTextureFilteringButton;
MyGUI::TextBox* mAnisotropyLabel;

@ -321,6 +321,11 @@ namespace MWGui
skillValueWidget->_setWidgetState(state);
skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);
// resize dynamically according to text size
int textWidthPlusMargin = skillValueWidget->getTextSize().width + 12;
skillValueWidget->setCoord(coord2.left + coord2.width - textWidthPlusMargin, coord2.top, textWidthPlusMargin, coord2.height);
skillNameWidget->setSize(skillNameWidget->getSize() + MyGUI::IntSize(coord2.width - textWidthPlusMargin, 0));
mSkillWidgets.push_back(skillNameWidget);
mSkillWidgets.push_back(skillValueWidget);

@ -547,6 +547,17 @@ namespace MWGui
return " (" + boost::lexical_cast<std::string>(value) + ")";
}
std::string ToolTips::getCellRefString(const MWWorld::CellRef& cellref)
{
std::string ret;
ret += getMiscString(cellref.getOwner(), "Owner");
ret += getMiscString(cellref.getFaction(), "Faction");
if (cellref.getFactionRank() > 0)
ret += getValueString(cellref.getFactionRank(), "Rank");
ret += getMiscString(cellref.getGlobalVariable(), "Global");
return ret;
}
bool ToolTips::toggleFullHelp()
{
mFullHelp = !mFullHelp;

@ -66,6 +66,9 @@ namespace MWGui
static std::string getCountString(const int value);
///< @return blank string if count is 1, or else " (value)"
static std::string getCellRefString(const MWWorld::CellRef& cellref);
///< Returns a string containing debug tooltip information about the given cellref.
// these do not create an actual tooltip, but they fill in the data that is required so the tooltip
// system knows what to show in case this widget is hovered
static void createSkillToolTip(MyGUI::Widget* widget, int skillId);

@ -531,4 +531,12 @@ namespace MWGui
sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp());
}
}
void TradeWindow::resetReference()
{
ReferenceInterface::resetReference();
mItemView->setModel(NULL);
mTradeModel = NULL;
mSortModel = NULL;
}
}

@ -37,6 +37,7 @@ namespace MWGui
virtual void exit();
virtual void resetReference();
private:
ItemView* mItemView;

@ -268,7 +268,7 @@ namespace MWGui
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
trackWindow(mCompanionWindow, "companion");
mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Windows");
mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Overlay");
mHud->setVisible(mHudEnabled);
@ -1529,6 +1529,8 @@ namespace MWGui
mCompanionWindow->resetReference();
mConsole->resetReference();
mSelectedSpell.clear();
mGuiModes.clear();
MWBase::Environment::get().getInputManager()->changeInputMode(false);
updateVisible();

@ -686,14 +686,6 @@ namespace MWInput
return;
}
if(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue) { //Give access to the main menu when at a choice in dialogue
if(MWBase::Environment::get().getDialogueManager()->isInChoice()) {
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx);
return;
}
}
if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) //No open GUIs, open up the MainMenu
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
@ -707,11 +699,13 @@ namespace MWInput
}
void InputManager::quickLoad() {
MWBase::Environment::get().getStateManager()->quickLoad();
if (!MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getStateManager()->quickLoad();
}
void InputManager::quickSave() {
MWBase::Environment::get().getStateManager()->quickSave();
if (!MyGUI::InputManager::getInstance().isModalAny())
MWBase::Environment::get().getStateManager()->quickSave();
}
void InputManager::toggleSpell()
{

@ -184,7 +184,7 @@ namespace MWMechanics
calculateCreatureStatModifiers (ptr, duration);
// fatigue restoration
calculateRestoration(ptr, duration, false);
calculateRestoration(ptr, duration);
}
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool againstPlayer)
@ -293,7 +293,7 @@ namespace MWMechanics
creatureStats.setFatigue(fatigue);
}
void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep)
void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, bool sleep)
{
if (ptr.getClass().getCreatureStats(ptr).isDead())
return;
@ -332,9 +332,29 @@ namespace MWMechanics
x *= fEndFatigueMult * endurance;
DynamicStat<float> fatigue = stats.getFatigue();
fatigue.setCurrent (fatigue.getCurrent() + duration * x);
fatigue.setCurrent (fatigue.getCurrent() + 3600 * x);
stats.setFatigue (fatigue);
}
void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration)
{
if (ptr.getClass().getCreatureStats(ptr).isDead())
return;
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
// restore fatigue
float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat ();
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat ();
float x = fFatigueReturnBase + fFatigueReturnMult * endurance;
DynamicStat<float> fatigue = stats.getFatigue();
fatigue.setCurrent (fatigue.getCurrent() + duration * x);
stats.setFatigue (fatigue);
}
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
@ -840,7 +860,7 @@ namespace MWMechanics
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
{
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt();
// Force dialogue on sight if bounty is greater than the cutoff
// In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
if ( player.getClass().getNpcStats(player).getBounty() >= cutoff
@ -848,7 +868,11 @@ namespace MWMechanics
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
{
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
static int iCrimeThresholdMultiplier = esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->getInt();
if (player.getClass().getNpcStats(player).getBounty() >= cutoff * iCrimeThresholdMultiplier)
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player);
else
creatureStats.getAiSequence().stack(AiPursue(player), ptr);
creatureStats.setAlarmed(true);
npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
}
@ -1029,10 +1053,6 @@ namespace MWMechanics
iter->second->update(duration);
}
// Kill dead actors, update some variables
int hostilesCount = 0; // need to know this to play Battle music
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
@ -1048,68 +1068,23 @@ namespace MWMechanics
stats.setKnockedDownOneFrame(false);
stats.setKnockedDownOverOneFrame(true);
}
}
if(!stats.isDead())
{
if (stats.isHostile()) hostilesCount++;
if(iter->second->isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->resurrect();
}
if(!stats.isDead())
continue;
}
// If it's the player and God Mode is turned on, keep it alive
if (iter->first.getRefData().getHandle()=="player" &&
MWBase::Environment::get().getWorld()->getGodModeState())
{
MWMechanics::DynamicStat<float> stat (stats.getHealth());
int hostilesCount = 0; // need to know this to play Battle music
if (stat.getModified()<1)
{
stat.setModified(1, 0);
stats.setHealth(stat);
}
stats.resurrect();
continue;
}
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
if (iter->second->kill())
if(!stats.isDead())
{
++mDeathCount[cls.getId(iter->first)];
// Make sure spell effects with CasterLinked flag are removed
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(stats.getActorId());
}
// 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());
stats.getActiveSpells().clear();
calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if (cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
if (stats.isHostile()) hostilesCount++;
}
}
killDeadActors();
// check if we still have any player enemies to switch music
static bool isBattleMusic = false;
@ -1184,10 +1159,78 @@ namespace MWMechanics
}
}
}
void Actors::killDeadActors()
{
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
if(!stats.isDead())
{
if(iter->second->isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->resurrect();
}
if(!stats.isDead())
continue;
}
// If it's the player and God Mode is turned on, keep it alive
if (iter->first.getRefData().getHandle()=="player" &&
MWBase::Environment::get().getWorld()->getGodModeState())
{
MWMechanics::DynamicStat<float> stat (stats.getHealth());
if (stat.getModified()<1)
{
stat.setModified(1, 0);
stats.setHealth(stat);
}
stats.resurrect();
continue;
}
if (iter->second->kill())
{
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
// Make sure spell effects with CasterLinked flag are removed
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(stats.getActorId());
}
// 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());
stats.getActiveSpells().clear();
calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if (cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
}
}
}
void Actors::restoreDynamicStats(bool sleep)
{
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
calculateRestoration(iter->first, 3600, sleep);
restoreDynamicStats(iter->first, sleep);
}
int Actors::getHoursToRest(const MWWorld::Ptr &ptr) const
@ -1281,12 +1324,8 @@ namespace MWMechanics
{
const MWWorld::Class &cls = iter->getClass();
CreatureStats &stats = cls.getCreatureStats(*iter);
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
{
MWMechanics::AiCombat* package = static_cast<MWMechanics::AiCombat*>(stats.getAiSequence().getActivePackage());
if(package->getTarget() == actor)
list.push_front(*iter);
}
if (!stats.isDead() && stats.getAiSequence().isInCombat(actor))
list.push_front(*iter);
}
return list;
}

@ -36,7 +36,7 @@ namespace MWMechanics
void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration);
void calculateNpcStatModifiers (const MWWorld::Ptr& ptr);
void calculateRestoration (const MWWorld::Ptr& ptr, float duration, bool sleep);
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
void updateDrowning (const MWWorld::Ptr& ptr, float duration);
@ -90,12 +90,17 @@ namespace MWMechanics
void restoreDynamicStats(bool sleep);
///< If the player is sleeping, this should be called every hour.
void restoreDynamicStats(const MWWorld::Ptr& actor, bool sleep);
int getHoursToRest(const MWWorld::Ptr& ptr) const;
///< Calculate how many hours the given actor needs to rest in order to be fully healed
int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID.
///@see MechanicsManager::killDeadActors
void killDeadActors ();
void forceStateUpdate(const MWWorld::Ptr &ptr);
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);

@ -265,7 +265,7 @@ namespace MWMechanics
const ESM::Weapon *weapon = NULL;
MWMechanics::WeaponType weaptype;
float weapRange;
float weapRange = 1.0f;
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
@ -300,7 +300,7 @@ namespace MWMechanics
else //is creature
{
weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit)
}
float rangeAttack;

@ -72,6 +72,30 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
return true;
}
bool AiSequence::isInCombat() const
{
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
return true;
}
return false;
}
bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
{
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it)
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{
const AiCombat *combat = static_cast<const AiCombat *>(*it);
if (combat->getTarget() == actor)
return true;
}
}
return false;
}
bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const
{
bool firstCombatFound = false;

@ -63,6 +63,12 @@ namespace MWMechanics
/// Return true and assign target if combat package is currently active, return false otherwise
bool getCombatTarget (MWWorld::Ptr &targetActor) const;
/// Is there any combat package?
bool isInCombat () const;
/// Are we in combat with this particular actor?
bool isInCombat (const MWWorld::Ptr& actor) const;
bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;
///< Function assumes that actor can have only 1 target apart player

@ -0,0 +1,232 @@
#include "autocalcspell.hpp"
#include <climits>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWMechanics
{
struct SchoolCaps
{
int mCount;
int mLimit;
bool mReachedLimit;
int mMinCost;
std::string mWeakestSpell;
};
std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)
{
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
static int iAutoSpellSchoolMax[6];
static bool init = false;
if (!init)
{
for (int i=0; i<6; ++i)
{
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt();
}
init = true;
}
std::map<int, SchoolCaps> schoolCaps;
for (int i=0; i<6; ++i)
{
SchoolCaps caps;
caps.mCount = 0;
caps.mLimit = iAutoSpellSchoolMax[i];
caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0;
caps.mMinCost = INT_MAX;
caps.mWeakestSpell.clear();
schoolCaps[i] = caps;
}
std::vector<std::string> selectedSpells;
const MWWorld::Store<ESM::Spell> &spells =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
// Store must preserve the record ordering as it was in the content files.
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
continue;
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt();
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
continue;
int school;
float skillTerm;
calcWeakestSchool(spell, actorSkills, school, skillTerm);
assert(school >= 0 && school < 6);
SchoolCaps& cap = schoolCaps[school];
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
continue;
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat();
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
continue;
selectedSpells.push_back(spell->mId);
if (cap.mReachedLimit)
{
std::vector<std::string>::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell);
if (found != selectedSpells.end())
selectedSpells.erase(found);
cap.mMinCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = spells.find(*weakIt);
//int testSchool;
//float dummySkillTerm;
//calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm);
// Note: if there are multiple spells with the same cost, we pick the first one we found.
// So the algorithm depends on the iteration order of the outer loop.
if (
// There is a huge bug here. It is not checked that weakestSpell is of the correct school.
// As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school
// already erased it, and so the number of spells would often exceed the sum of limits.
// This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested.
//testSchool == school &&
testSpell->mData.mCost < cap.mMinCost)
{
cap.mMinCost = testSpell->mData.mCost;
cap.mWeakestSpell = testSpell->mId;
}
}
}
else
{
cap.mCount += 1;
if (cap.mCount == cap.mLimit)
cap.mReachedLimit = true;
if (spell->mData.mCost < cap.mMinCost)
{
cap.mWeakestSpell = spell->mId;
cap.mMinCost = spell->mData.mCost;
}
}
}
return selectedSpells;
}
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
{
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->getInt();
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
{
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
return false;
}
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
{
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
return false;
}
}
return true;
}
ESM::Skill::SkillEnum mapSchoolToSkill(int school)
{
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
schoolSkillMap[0] = ESM::Skill::Alteration;
schoolSkillMap[1] = ESM::Skill::Conjuration;
schoolSkillMap[3] = ESM::Skill::Illusion;
schoolSkillMap[2] = ESM::Skill::Destruction;
schoolSkillMap[4] = ESM::Skill::Mysticism;
schoolSkillMap[5] = ESM::Skill::Restoration;
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
return schoolSkillMap[school];
}
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
{
float minChance = FLT_MAX;
const ESM::EffectList& effects = spell->mEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
{
const ESM::ENAMstruct& effect = *it;
float x = effect.mDuration;
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
x = std::max(1.f, x);
x *= 0.1f * magicEffect->mData.mBaseCost;
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;
if (effect.mRange == ESM::RT_Target)
x *= 1.5f;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
x *= fEffectCostMult;
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
if (s - x < minChance)
{
minChance = s - x;
effectiveSchool = magicEffect->mData.mSchool;
skillTerm = s;
}
}
}
float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool)
{
if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f;
if (spell->mData.mFlags & ESM::Spell::F_Always)
return 100.f;
float skillTerm;
if (effectiveSchool != -1)
skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)];
else
calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck];
return castChance;
}
}

@ -0,0 +1,31 @@
#ifndef OPENMW_AUTOCALCSPELL_H
#define OPENMW_AUTOCALCSPELL_H
#include <cfloat>
#include <set>
#include <components/esm/loadspel.hpp>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadrace.hpp>
namespace MWMechanics
{
/// Contains algorithm for calculating an NPC's spells based on stats
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes
std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
// Helpers
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);
ESM::Skill::SkillEnum mapSchoolToSkill(int school);
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);
float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);
}
#endif

@ -361,9 +361,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
* beginning. */
int mode = ((movement == mCurrentMovement) ? 2 : 1);
mMovementAnimationControlled = true;
mAnimation->disable(mCurrentMovement);
mCurrentMovement = movement;
mMovementAnimVelocity = 0.0f;
if(!mCurrentMovement.empty())
{
float vel, speedmult = 1.0f;
@ -383,16 +384,18 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(anim)) > 1.0f)
{
mMovementAnimVelocity = 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);
mMovementAnimationControlled = false;
}
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
}
@ -506,6 +509,7 @@ void CharacterController::playDeath(float startpoint, CharacterState death)
mJumpState = JumpState_None;
mAnimation->disable(mCurrentJump);
mCurrentJump = "";
mMovementAnimationControlled = true;
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", startpoint, 0);
@ -547,7 +551,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mIdleState(CharState_None)
, mMovementState(CharState_None)
, mMovementSpeed(0.0f)
, mMovementAnimVelocity(0.0f)
, mMovementAnimationControlled(true)
, mDeathState(CharState_None)
, mHitState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing)
@ -570,10 +574,14 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
if (cls.hasInventoryStore(mPtr))
{
getActiveWeapon(cls.getCreatureStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
if (mWeaponType != WeapType_None)
{
getWeaponGroup(mWeaponType, mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
getWeaponGroup(mWeaponType, mCurrentWeapon);
}
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
{
mAnimation->showWeapons(true);
mAnimation->setWeaponGroup(mCurrentWeapon);
}
@ -599,6 +607,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
if(mDeathState == CharState_None)
refreshCurrentAnims(mIdleState, mMovementState, true);
mAnimation->runAnimation(0.f);
}
CharacterController::~CharacterController()
@ -1241,6 +1251,7 @@ void CharacterController::update(float duration)
if (inwater || flying)
cls.getCreatureStats(mPtr).land();
bool inJump = true;
if(!onground && !flying && !inwater)
{
// In the air (either getting up —ascending part of jump— or falling).
@ -1330,6 +1341,8 @@ void CharacterController::update(float duration)
mJumpState = JumpState_None;
vec.z = 0.0f;
inJump = false;
if(std::abs(vec.x/2.0f) > std::abs(vec.y))
{
if(vec.x > 0.0f)
@ -1391,6 +1404,8 @@ void CharacterController::update(float duration)
forcestateupdate = updateCreatureState() || forcestateupdate;
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
if (inJump)
mMovementAnimationControlled = false;
if (!mSkipAnim)
{
@ -1402,7 +1417,7 @@ void CharacterController::update(float duration)
else //avoid z-rotating for knockdown
world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true);
if (mMovementAnimVelocity == 0)
if (!mMovementAnimationControlled)
world->queueMovement(mPtr, vec);
}
else
@ -1446,7 +1461,7 @@ void CharacterController::update(float duration)
}
// Update movement
if(mMovementAnimVelocity > 0)
if(mMovementAnimationControlled && mPtr.getClass().isActor())
world->queueMovement(mPtr, moved);
}
else if (mAnimation)

@ -147,7 +147,7 @@ class CharacterController
CharacterState mMovementState;
std::string mCurrentMovement;
float mMovementSpeed;
float mMovementAnimVelocity;
bool mMovementAnimationControlled;
CharacterState mDeathState;
std::string mCurrentDeath;

@ -10,6 +10,7 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
@ -54,11 +55,24 @@ namespace MWMechanics
if (!blocker.getClass().hasInventoryStore(blocker))
return false;
if (blocker.getClass().getCreatureStats(blocker).getKnockedDown()
|| blocker.getClass().getCreatureStats(blocker).getHitRecovery())
MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker);
if (blockerStats.getKnockedDown() // Used for both knockout or knockdown
|| blockerStats.getHitRecovery()
|| blockerStats.getMagicEffects().get(ESM::MagicEffect::Paralyze).mMagnitude > 0)
return false;
// Don't block when in spellcasting state (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Spell)
return false;
MWWorld::InventoryStore& inv = blocker.getClass().getInventoryStore(blocker);
// Don't block when in hand-to-hand combat (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Weapon &&
inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end())
return false;
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end() || shield->getTypeName() != typeid(ESM::Armor).name())
return false;
@ -72,17 +86,6 @@ namespace MWMechanics
if (angle.valueDegrees() > gmst.find("fCombatBlockRightAngle")->getFloat())
return false;
MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker);
// Don't block when in spellcasting state (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Spell)
return false;
// Don't block when in hand-to-hand combat (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Weapon &&
inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end())
return false;
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified()
@ -144,11 +147,7 @@ namespace MWMechanics
float resistance = std::min(100.f, stats.getMagicEffects().get(ESM::MagicEffect::ResistNormalWeapons).mMagnitude
- stats.getMagicEffects().get(ESM::MagicEffect::WeaknessToNormalWeapons).mMagnitude);
float multiplier = 0;
if (resistance >= 0)
multiplier = 1 - resistance / 100.f;
else
multiplier = -(resistance-100) / 100.f;
float multiplier = 1.f - resistance / 100.f;
if (!(weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver
|| weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Magical))
@ -210,16 +209,10 @@ namespace MWMechanics
damage *= fDamageStrengthBase +
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
if(attacker.getRefData().getHandle() == "player")
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
if(!detected)
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
if (victim.getClass().getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat();
@ -256,4 +249,50 @@ namespace MWMechanics
return hitchance;
}
void applyElementalShields(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim)
{
for (int i=0; i<3; ++i)
{
float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).mMagnitude;
if (!magnitude)
continue;
CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
float saveTerm = attacker.getClass().getSkill(attacker, ESM::Skill::Destruction)
+ 0.2f * attackerStats.getAttribute(ESM::Attribute::Willpower).getModified()
+ 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified();
int fatigueMax = attackerStats.getFatigue().getModified();
int fatigueCurrent = attackerStats.getFatigue().getCurrent();
float normalisedFatigue = fatigueMax==0 ? 1 : std::max (0.0f, static_cast<float> (fatigueCurrent)/fatigueMax);
saveTerm *= 1.25f * normalisedFatigue;
float roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
float x = std::max(0.f, saveTerm - roll);
int element = ESM::MagicEffect::FireDamage;
if (i == 1)
element = ESM::MagicEffect::ShockDamage;
if (i == 2)
element = ESM::MagicEffect::FrostDamage;
float elementResistance = MWMechanics::getEffectResistanceAttribute(element, &attackerStats.getMagicEffects());
x = std::min(100.f, x + elementResistance);
static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fElementalShieldMult")->getFloat();
x = fElementalShieldMult * magnitude * (1.f - 0.01f * x);
// Note swapped victim and attacker, since the attacker takes the damage here.
x = scaleDamage(x, victim, attacker);
MWMechanics::DynamicStat<float> health = attackerStats.getHealth();
health.setCurrent(health.getCurrent() - x);
attackerStats.setHealth(health);
}
}
}

@ -19,6 +19,9 @@ void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MW
/// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value
float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue);
/// Applies damage to attacker based on the victim's elemental shields.
void applyElementalShields(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
}
#endif

@ -8,6 +8,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
namespace MWMechanics
{
@ -191,6 +192,12 @@ namespace MWMechanics
mDied = true;
mDead = true;
if (mDied)
// Must increase death count immediately. There are scripts that use getDeadCount as reaction to onDeath
// and rely on the increased value.
// Would be more appropriate to use a killActor(actor) function, but we don't have access to the Ptr in CreatureStats.
MWBase::Environment::get().getMechanicsManager()->killDeadActors();
}
}

@ -208,7 +208,8 @@ namespace MWMechanics
float getEvasion() const;
void setKnockedDown(bool value);
///Returns true for the entire duration of the actor being knocked down
/// Returns true for the entire duration of the actor being knocked down or knocked out,
/// including transition animations (falling down & standing up)
bool getKnockedDown() const;
void setKnockedDownOneFrame(bool value);
///Returns true only for the first frame of the actor being knocked out; used for "onKnockedOut" command

@ -0,0 +1,38 @@
#include "difficultyscaling.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp"
#include <components/settings/settings.hpp>
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim)
{
const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// [-100, 100]
int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDifficultyMult")->getFloat();
float difficultyTerm = 0.01f * difficultySetting;
float x = 0;
if (victim == player)
{
if (difficultyTerm > 0)
x = fDifficultyMult * difficultyTerm;
else
x = difficultyTerm / fDifficultyMult;
}
else if (attacker == player)
{
if (difficultyTerm > 0)
x = -difficultyTerm / fDifficultyMult;
else
x = fDifficultyMult * (-difficultyTerm);
}
damage *= 1 + x;
return damage;
}

@ -0,0 +1,12 @@
#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
namespace MWWorld
{
class Ptr;
}
/// Scales damage dealt to an actor based on difficulty setting
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
#endif

@ -14,9 +14,11 @@ namespace MWMechanics
{
/// Call when \a actor has got in contact with \a carrier (e.g. hit by him, or loots him)
/// @param actor The actor that will potentially catch diseases. Currently only the player can catch diseases.
/// @param carrier The disease carrier.
inline void diseaseContact (MWWorld::Ptr actor, MWWorld::Ptr carrier)
{
if (!carrier.getClass().isActor())
if (!carrier.getClass().isActor() || actor != MWBase::Environment::get().getWorld()->getPlayerPtr())
return;
float fDiseaseXferChance =
@ -27,25 +29,47 @@ namespace MWMechanics
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(it->first);
if (spell->mData.mType == ESM::Spell::ST_Disease
|| spell->mData.mType == ESM::Spell::ST_Blight)
if (actor.getClass().getCreatureStats(actor).getSpells().hasSpell(spell->mId))
continue;
bool hasCorprusEffect = false;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
{
float roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (roll < fDiseaseXferChance)
if (effectIt->mEffectID == ESM::MagicEffect::Corprus)
{
// Contracted disease!
actor.getClass().getCreatureStats(actor).getSpells().add(it->first);
if (actor.getRefData().getHandle() == "player")
{
std::string msg = "sMagicContractDisease";
msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->getString();
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, spell->mName);
MWBase::Environment::get().getWindowManager()->messageBox(msg);
}
hasCorprusEffect = true;
break;
}
}
float resist = 0.f;
if (hasCorprusEffect)
resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCorprusDisease).mMagnitude
- actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCorprusDisease).mMagnitude);
else if (spell->mData.mType == ESM::Spell::ST_Disease)
resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistCommonDisease).mMagnitude
- actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToCommonDisease).mMagnitude);
else if (spell->mData.mType == ESM::Spell::ST_Blight)
resist = 1.f - 0.01 * (actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::ResistBlightDisease).mMagnitude
- actor.getClass().getCreatureStats(actor).getMagicEffects().get(ESM::MagicEffect::WeaknessToBlightDisease).mMagnitude);
else
continue;
int x = fDiseaseXferChance * 100 * resist;
float roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 10000; // [0, 9999]
if (roll < x)
{
// Contracted disease!
actor.getClass().getCreatureStats(actor).getSpells().add(it->first);
std::string msg = "sMagicContractDisease";
msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->getString();
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, spell->mName);
MWBase::Environment::get().getWindowManager()->messageBox(msg);
}
}
}
}

@ -19,6 +19,7 @@
#include <OgreSceneNode.h>
#include "spellcasting.hpp"
#include "autocalcspell.hpp"
namespace
{
@ -33,10 +34,19 @@ namespace
if (!faction.empty() && ptr.getClass().isNpc())
{
const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();
if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end())
std::map<std::string, int>::const_iterator found = factions.find(Misc::StringUtils::lowerCase(faction));
if (found == factions.end()
|| found->second < item.getCellRef().getFactionRank())
isFactionOwned = true;
}
const std::string& globalVariable = item.getCellRef().getGlobalVariable();
if (!globalVariable.empty() && MWBase::Environment::get().getWorld()->getGlobalInt(Misc::StringUtils::lowerCase(globalVariable)) == 1)
{
isOwned = false;
isFactionOwned = false;
}
if (!item.getCellRef().getOwner().empty())
victim = MWBase::Environment::get().getWorld()->searchPtr(item.getCellRef().getOwner(), true);
@ -155,19 +165,6 @@ namespace MWMechanics
npcStats.getSkill (index).setBase (
npcStats.getSkill (index).getBase() + bonus);
}
if (i==1)
{
// Major skill - add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_PCStart
&& spellSchoolToSkill(getSpellSchool(&*it, ptr)) == index)
creatureStats.getSpells().add(it->mId);
}
}
}
}
@ -190,6 +187,87 @@ namespace MWMechanics
}
}
// F_PCStart spells
static const float fPCbaseMagickaMult = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->getFloat();
float baseMagicka = fPCbaseMagickaMult * creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase();
bool reachedLimit = false;
const ESM::Spell* weakestSpell = NULL;
int minCost = INT_MAX;
std::vector<std::string> selectedSpells;
const ESM::Race* race = NULL;
if (mRaceSelected)
race = esmStore.get<ESM::Race>().find(player->mRace);
int skills[ESM::Skill::Length];
for (int i=0; i<ESM::Skill::Length; ++i)
skills[i] = npcStats.getSkill(i).getBase();
int attributes[ESM::Attribute::Length];
for (int i=0; i<ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
const MWWorld::Store<ESM::Spell> &spells =
esmStore.get<ESM::Spell>();
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
{
const ESM::Spell* spell = &*iter;
if (spell->mData.mType != ESM::Spell::ST_Spell)
continue;
if (!(spell->mData.mFlags & ESM::Spell::F_PCStart))
continue;
if (reachedLimit && spell->mData.mCost <= minCost)
continue;
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
continue;
if (baseMagicka < spell->mData.mCost)
continue;
static const float fAutoPCSpellChance = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->getFloat();
if (calcAutoCastChance(spell, skills, attributes, -1) < fAutoPCSpellChance)
continue;
if (!attrSkillCheck(spell, skills, attributes))
continue;
selectedSpells.push_back(spell->mId);
if (reachedLimit)
{
std::vector<std::string>::iterator it = std::find(selectedSpells.begin(), selectedSpells.end(), weakestSpell->mId);
if (it != selectedSpells.end())
selectedSpells.erase(it);
minCost = INT_MAX;
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
{
const ESM::Spell* testSpell = esmStore.get<ESM::Spell>().find(*weakIt);
if (testSpell->mData.mCost < minCost)
{
minCost = testSpell->mData.mCost;
weakestSpell = testSpell;
}
}
}
else
{
if (spell->mData.mCost < minCost)
{
weakestSpell = spell;
minCost = weakestSpell->mData.mCost;
}
static const unsigned int iAutoPCSpellMax = esmStore.get<ESM::GameSetting>().find("iAutoPCSpellMax")->getInt();
if (selectedSpells.size() == iAutoPCSpellMax)
reachedLimit = true;
}
}
for (std::vector<std::string>::iterator it = selectedSpells.begin(); it != selectedSpells.end(); ++it)
creatureStats.getSpells().add(*it);
// forced update and current value adjustments
mActors.updateActor (ptr, 0);
@ -584,6 +662,10 @@ namespace MWMechanics
return mActors.countDeaths (id);
}
void MechanicsManager::killDeadActors()
{
mActors.killDeadActors();
}
void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type,
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange)

@ -160,6 +160,10 @@ namespace MWMechanics
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false);
/// Usually done once a frame, but can be invoked manually in time-critical situations.
/// This will increase the death count for any actors that were killed.
virtual void killDeadActors();
};
}

@ -57,10 +57,13 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair));
itemToRepair.getCellRef().setCharge(charge);
// attempt to re-stack item, in case it was fully repaired
MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair);
// set the OnPCRepair variable on the item's script
std::string script = itemToRepair.getClass().getScript(itemToRepair);
std::string script = stacked->getClass().getScript(itemToRepair);
if(script != "")
itemToRepair.getRefData().getLocals().setVarByInt(script, "onpcrepair", 1);
stacked->getRefData().getLocals().setVarByInt(script, "onpcrepair", 1);
// increase skill
player.getClass().skillUsageSucceeded(player, ESM::Skill::Armorer, 0);

@ -92,7 +92,7 @@ namespace MWMechanics
x *= 0.1 * magicEffect->mData.mBaseCost;
x *= 0.5 * (it->mMagnMin + it->mMagnMax);
x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
if (magicEffect->mData.mFlags & ESM::MagicEffect::CastTarget)
if (it->mRange == ESM::RT_Target)
x *= 1.5;
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fEffectCostMult")->getFloat();
@ -148,6 +148,27 @@ namespace MWMechanics
return school;
}
float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects)
{
short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);
float resistance = 0;
if (resistanceEffect != -1)
resistance += actorEffects->get(resistanceEffect).mMagnitude;
if (weaknessEffect != -1)
resistance -= actorEffects->get(weaknessEffect).mMagnitude;
if (effectId == ESM::MagicEffect::FireDamage)
resistance += actorEffects->get(ESM::MagicEffect::FireShield).mMagnitude;
if (effectId == ESM::MagicEffect::ShockDamage)
resistance += actorEffects->get(ESM::MagicEffect::LightningShield).mMagnitude;
if (effectId == ESM::MagicEffect::FrostDamage)
resistance += actorEffects->get(ESM::MagicEffect::FrostShield).mMagnitude;
return resistance;
}
float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
const ESM::Spell* spell, const MagicEffects* effects)
{
@ -163,16 +184,7 @@ namespace MWMechanics
float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);
float resistance = 0;
if (resistanceEffect != -1)
resistance += magicEffects->get(resistanceEffect).mMagnitude;
if (weaknessEffect != -1)
resistance -= magicEffects->get(weaknessEffect).mMagnitude;
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
@ -484,7 +496,11 @@ namespace MWMechanics
if (effectId == ESM::MagicEffect::Lock)
{
if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude
{
if (caster.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
target.getCellRef().setLockLevel(magnitude);
}
}
else if (effectId == ESM::MagicEffect::Open)
{
@ -492,12 +508,14 @@ namespace MWMechanics
{
if (target.getCellRef().getLockLevel() > 0)
{
//Door not already unlocked
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
if (!caster.isEmpty() && caster.getClass().isActor())
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
if (caster.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}");
}
target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); //unlocks the door
target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel()));
}
else
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f);

@ -35,6 +35,11 @@ namespace MWMechanics
int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
/// Get the resistance attribute against an effect for a given actor. This will add together
/// ResistX and Weakness to X effects relevant against the given effect.
float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects);
/// Get the effective resistance against an effect casted by the given actor in the given spell (optional).
/// @return >=100 for fully resisted. can also return negative value for damage amplification.
/// @param effects Override the actor's current magicEffects. Useful if there are effects currently
/// being applied (but not applied yet) that should also be considered.

@ -149,6 +149,8 @@ namespace MWRender
mViewModeToggleQueued = true;
return;
}
else
mViewModeToggleQueued = false;
mFirstPersonView = !mFirstPersonView;
processViewChange();

@ -340,7 +340,9 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
sFogOfWarResolution, sFogOfWarResolution,
0,
PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY);
TU_DYNAMIC_WRITE_ONLY,
this // ManualResourceLoader required if the texture contents are lost (due to lost devices nonsense that can occur with D3D)
);
}
return tex;
@ -457,6 +459,30 @@ bool LocalMap::isPositionExplored (float nX, float nY, int x, int y, bool interi
return alpha < 200;
}
void LocalMap::loadResource(Ogre::Resource* resource)
{
std::string resourceName = resource->getName();
size_t pos = resourceName.find("_fog");
if (pos != std::string::npos)
resourceName = resourceName.substr(0, pos);
if (mBuffers.find(resourceName) == mBuffers.end())
{
// create a buffer to use for dynamic operations
std::vector<uint32> buffer;
// initialize to (0, 0, 0, 1)
buffer.resize(sFogOfWarResolution*sFogOfWarResolution, 0xFF000000);
mBuffers[resourceName] = buffer;
}
std::vector<uint32>& buffer = mBuffers[resourceName];
Ogre::Texture* tex = dynamic_cast<Ogre::Texture*>(resource);
tex->createInternalResources();
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
tex->getBuffer()->unlock();
}
void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation)
{
if (sFogOfWarSkip != 0)

@ -5,6 +5,7 @@
#include <OgreAxisAlignedBox.h>
#include <OgreColourValue.h>
#include <OgreResource.h>
namespace MWWorld
{
@ -23,12 +24,14 @@ namespace MWRender
///
/// \brief Local map rendering
///
class LocalMap
class LocalMap : public Ogre::ManualResourceLoader
{
public:
LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering);
~LocalMap();
virtual void loadResource(Ogre::Resource* resource);
/**
* Clear all savegame-specific data (i.e. fog of war textures)
*/

@ -1088,7 +1088,7 @@ public:
void close() { }
bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height)
bool update()
{ return false; }
};

@ -147,6 +147,14 @@ namespace MWScript
}
ptr.getClass().lock (ptr, lockLevel);
// Instantly reset door to closed state
// This is done when using Lock in scripts, but not when using Lock spells.
if (ptr.getTypeName() == typeid(ESM::Door).name())
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, 0);
MWBase::Environment::get().getWorld()->localRotateObject(ptr, 0, 0, 0);
}
}
};

@ -86,16 +86,24 @@ namespace MWScript
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees();
MWWorld::LocalRotation localRot = ptr.getRefData().getLocalRotation();
if (axis == "x")
{
localRot.rot[0] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az);
}
else if (axis == "y")
{
localRot.rot[1] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az);
}
else if (axis == "z")
{
localRot.rot[2] = 0;
ptr.getRefData().setLocalRotation(localRot);
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
}
else

@ -88,6 +88,16 @@ namespace MWWorld
return mCellRef.mOwner;
}
std::string CellRef::getGlobalVariable() const
{
return mCellRef.mGlobalVariable;
}
int CellRef::getFactionRank() const
{
return mCellRef.mFactionRank;
}
void CellRef::setOwner(const std::string &owner)
{
if (owner != mCellRef.mOwner)

@ -61,6 +61,11 @@ namespace MWWorld
std::string getOwner() const;
void setOwner(const std::string& owner);
// Name of a global variable. If the global variable is set to '1', using the object is temporarily allowed
// even if it has an Owner field.
// Used by bed rent scripts to allow the player to use the bed for the duration of the rent.
std::string getGlobalVariable() const;
// ID of creature trapped in this soul gem
std::string getSoul() const;
void setSoul(const std::string& soul);
@ -70,6 +75,9 @@ namespace MWWorld
std::string getFaction() const;
void setFaction (const std::string& faction);
// PC faction rank required to use the item. Sometimes is -1, which means "any rank".
int getFactionRank() const;
// Lock level for doors and containers
// Positive for a locked door. 0 for a door that was never locked.
// For an unlocked door, it is set to -(previous locklevel)

@ -141,6 +141,34 @@ void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container)
remove(ptr, ptr.getRefData().getCount()-1, container);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)
{
MWWorld::ContainerStoreIterator retval = end();
for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
{
if (item == *iter)
{
retval = iter;
break;
}
}
if (retval == end())
throw std::runtime_error("item is not from this container");
for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter)
{
if (stacks(*iter, item))
{
iter->getRefData().setCount(iter->getRefData().getCount() + item.getRefData().getCount());
item.getRefData().setCount(0);
retval = iter;
break;
}
}
return retval;
}
bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2)
{
const MWWorld::Class& cls1 = ptr1.getClass();

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

Loading…
Cancel
Save