mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 20:53:50 +00:00
Merge remote-tracking branch 'refs/remotes/master/master' into NonTableFields
Conflicts: apps/opencs/CMakeLists.txt
This commit is contained in:
commit
cdac934315
135 changed files with 2109 additions and 827 deletions
|
@ -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);
|
||||
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Lighting Mode");
|
||||
|
||||
tool->addButton (":door.png", "day"); /// \todo replace icons
|
||||
tool->addButton (":GMST.png", "night");
|
||||
tool->addButton (":Info.png", "bright");
|
||||
/// \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);
|
||||
CSVWidget::SceneToolMode *tool = new CSVWidget::SceneToolMode (parent, "Camera Mode");
|
||||
|
||||
tool->addButton (":door.png", "1st"); /// \todo replace icons
|
||||
tool->addButton (":GMST.png", "free");
|
||||
tool->addButton (":Info.png", "orbit");
|
||||
/// \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.
|
||||
|
||||
|
|
83
apps/opencs/view/widget/pushbutton.cpp
Normal file
83
apps/opencs/view/widget/pushbutton.cpp
Normal file
|
@ -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;
|
||||
}
|
55
apps/opencs/view/widget/pushbutton.hpp
Normal file
55
apps/opencs/view/widget/pushbutton.hpp
Normal file
|
@ -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
|
||||
|
47
apps/opencs/view/widget/scenetoolbar.cpp
Normal file
47
apps/opencs/view/widget/scenetoolbar.cpp
Normal file
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
86
apps/opencs/view/widget/scenetoolmode.cpp
Normal file
86
apps/opencs/view/widget/scenetoolmode.cpp
Normal file
|
@ -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,14 +97,14 @@ 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.
|
||||
|
@ -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;
|
||||
|
@ -331,10 +331,30 @@ namespace MWMechanics
|
|||
float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance);
|
||||
x *= fEndFatigueMult * endurance;
|
||||
|
||||
DynamicStat<float> fatigue = stats.getFatigue();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
CreatureStats &stats = cls.getCreatureStats(iter->first);
|
||||
|
||||
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());
|
||||
|
||||
if (stat.getModified()<1)
|
||||
{
|
||||
stat.setModified(1, 0);
|
||||
stats.setHealth(stat);
|
||||
}
|
||||
stats.resurrect();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->second->kill())
|
||||
{
|
||||
++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}");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
232
apps/openmw/mwmechanics/autocalcspell.cpp
Normal file
232
apps/openmw/mwmechanics/autocalcspell.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
31
apps/openmw/mwmechanics/autocalcspell.hpp
Normal file
31
apps/openmw/mwmechanics/autocalcspell.hpp
Normal file
|
@ -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)
|
||||
{
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||
}
|
||||
|
||||
if(mWeaponType != WeapType_None && mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
|
||||
{
|
||||
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
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
|
||||
|
|
38
apps/openmw/mwmechanics/difficultyscaling.cpp
Normal file
38
apps/openmw/mwmechanics/difficultyscaling.cpp
Normal file
|
@ -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;
|
||||
}
|
12
apps/openmw/mwmechanics/difficultyscaling.hpp
Normal file
12
apps/openmw/mwmechanics/difficultyscaling.hpp
Normal file
|
@ -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)
|
||||
{
|
||||
float roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (roll < fDiseaseXferChance)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (effectIt->mEffectID == ESM::MagicEffect::Corprus)
|
||||
{
|
||||
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…
Reference in a new issue