Merge branch 'master' into preview

This commit is contained in:
Marc Zinnschlag 2014-03-21 08:12:06 +01:00
commit 2128d3008c
93 changed files with 2005 additions and 707 deletions

View file

@ -73,6 +73,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE)
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
# Apps and tools # Apps and tools
option(BUILD_BSATOOL "build BSA extractor" OFF) option(BUILD_BSATOOL "build BSA extractor" OFF)
option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_ESMTOOL "build ESM inspector" ON)
@ -463,7 +465,6 @@ if(WIN32)
INSTALL(FILES INSTALL(FILES
"${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/readme.txt"
"${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/GPL3.txt"
"${OpenMW_SOURCE_DIR}/OFL.txt"
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg"

93
OFL.txt
View file

@ -1,93 +0,0 @@
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View file

@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
scenetoolmode infocreator scriptedit previewsubview scenetoolmode infocreator scriptedit dialoguesubview previewsubview
) )
opencs_units (view/render opencs_units (view/render
@ -73,8 +73,7 @@ opencs_units_noqt (view/render
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world
dialoguesubview subviews subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator dialoguecreator scripthighlighter idvalidator dialoguecreator
) )

View file

@ -18,3 +18,8 @@ std::string CSMWorld::ColumnBase::getTitle() const
{ {
return Columns::getName (static_cast<Columns::ColumnId> (mColumnId)); return Columns::getName (static_cast<Columns::ColumnId> (mColumnId));
} }
int CSMWorld::ColumnBase::getId() const
{
return mColumnId;
}

View file

@ -28,6 +28,7 @@ namespace CSMWorld
{ {
Display_None, //Do not use Display_None, //Do not use
Display_String, Display_String,
Display_LongString,
//CONCRETE TYPES STARTS HERE //CONCRETE TYPES STARTS HERE
Display_Skill, Display_Skill,
@ -105,6 +106,8 @@ namespace CSMWorld
///< Can this column be edited directly by the user? ///< Can this column be edited directly by the user?
virtual std::string getTitle() const; virtual std::string getTitle() const;
virtual int getId() const;
}; };
template<typename ESXRecordT> template<typename ESXRecordT>

View file

@ -201,7 +201,7 @@ namespace CSMWorld
struct DescriptionColumn : public Column<ESXRecordT> struct DescriptionColumn : public Column<ESXRecordT>
{ {
DescriptionColumn() DescriptionColumn()
: Column<ESXRecordT> (Columns::ColumnId_Description, ColumnBase::Display_String) : Column<ESXRecordT> (Columns::ColumnId_Description, ColumnBase::Display_LongString)
{} {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
@ -833,7 +833,7 @@ namespace CSMWorld
virtual bool isUserEditable() const virtual bool isUserEditable() const
{ {
return false; return true;
} }
}; };
@ -1113,7 +1113,7 @@ namespace CSMWorld
virtual bool isUserEditable() const virtual bool isUserEditable() const
{ {
return false; return true;
} }
}; };
@ -1379,7 +1379,7 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct QuestDescriptionColumn : public Column<ESXRecordT> struct QuestDescriptionColumn : public Column<ESXRecordT>
{ {
QuestDescriptionColumn() : Column<ESXRecordT> (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {} QuestDescriptionColumn() : Column<ESXRecordT> (Columns::ColumnId_QuestDescription, ColumnBase::Display_LongString) {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {
@ -1559,7 +1559,7 @@ namespace CSMWorld
template<typename ESXRecordT> template<typename ESXRecordT>
struct ResponseColumn : public Column<ESXRecordT> struct ResponseColumn : public Column<ESXRecordT>
{ {
ResponseColumn() : Column<ESXRecordT> (Columns::ColumnId_Response, ColumnBase::Display_String) {} ResponseColumn() : Column<ESXRecordT> (Columns::ColumnId_Response, ColumnBase::Display_LongString) {}
virtual QVariant get (const Record<ESXRecordT>& record) const virtual QVariant get (const Record<ESXRecordT>& record) const
{ {

View file

@ -236,3 +236,8 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint);
} }
int CSMWorld::IdTable::getColumnId(int column) const
{
return mIdCollection->getColumn(column).getId();
}

View file

@ -106,6 +106,8 @@ namespace CSMWorld
std::pair<UniversalId, std::string> view (int row) const; std::pair<UniversalId, std::string> view (int row) const;
///< Return the UniversalId and the hint for viewing \a row. If viewing is not ///< Return the UniversalId and the hint for viewing \a row. If viewing is not
/// supported by this table, return (UniversalId::Type_None, ""). /// supported by this table, return (UniversalId::Type_None, "").
int getColumnId(int column) const;
}; };
} }

View file

@ -444,3 +444,8 @@ CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::U
return CSMWorld::ColumnBase::Display_None; return CSMWorld::ColumnBase::Display_None;
} }
} }
const CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const
{
return &mDocument;
}

View file

@ -48,6 +48,8 @@ namespace CSMWorld
UniversalId returnMatching(UniversalId::Type type) const; UniversalId returnMatching(UniversalId::Type type) const;
const CSMDoc::Document* getDocumentPtr() const;
UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const;
static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type);

View file

@ -19,3 +19,9 @@ void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QSt
void CSVDoc::SubView::setStatusBar (bool show) {} void CSVDoc::SubView::setStatusBar (bool show) {}
void CSVDoc::SubView::useHint (const std::string& hint) {} void CSVDoc::SubView::useHint (const std::string& hint) {}
void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id)
{
mUniversalId = id;
setWindowTitle (mUniversalId.toString().c_str());
}

View file

@ -27,6 +27,8 @@ namespace CSVDoc
// not implemented // not implemented
SubView (const SubView&); SubView (const SubView&);
SubView& operator= (SubView&); SubView& operator= (SubView&);
protected:
void setUniversalId(const CSMWorld::UniversalId& id);
public: public:

View file

@ -316,8 +316,16 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin
/// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
SubView *view = mSubViewFactory.makeSubView (id, *mDocument); const std::vector<CSMWorld::UniversalId::Type> referenceables(CSMWorld::UniversalId::listReferenceableTypes());
SubView *view = NULL;
if(std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end())
{
view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument);
} else
{
view = mSubViewFactory.makeSubView (id, *mDocument);
}
assert(view);
if (!hint.empty()) if (!hint.empty())
view->useHint (hint); view->useHint (hint);

View file

@ -1,98 +1,663 @@
#include "dialoguesubview.hpp" #include "dialoguesubview.hpp"
#include <utility>
#include <memory>
#include <QGridLayout> #include <QGridLayout>
#include <QLabel> #include <QLabel>
#include <QSize>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QSpinBox> #include <QSpinBox>
#include <QLineEdit> #include <QLineEdit>
#include <QEvent>
#include <QDataWidgetMapper> #include <QDataWidgetMapper>
#include <QCheckBox>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QComboBox>
#include <QScrollArea>
#include <QPushButton>
#include <QToolButton>
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/record.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/commands.hpp"
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, #include "recordstatusdelegate.hpp"
bool createAndDelete) #include "util.hpp"
: SubView (id) #include "tablebottombox.hpp"
/*
==============================NotEditableSubDelegate==========================================
*/
CSVWorld::NotEditableSubDelegate::NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent) :
QAbstractItemDelegate(parent),
mTable(table)
{}
void CSVWorld::NotEditableSubDelegate::setEditorData (QLabel* editor, const QModelIndex& index) const
{ {
QWidget *widget = new QWidget (this); QVariant v = index.data(Qt::EditRole);
if (!v.isValid())
setWidget (widget);
QGridLayout *layout = new QGridLayout;
widget->setLayout (layout);
QAbstractItemModel *model = document.getData().getTableModel (id);
int columns = model->columnCount();
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel (model);
for (int i=0; i<columns; ++i)
{ {
int flags = model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); v = index.data(Qt::DisplayRole);
if (!v.isValid())
if (flags & CSMWorld::ColumnBase::Flag_Dialogue)
{ {
layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); return;
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>
(model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
QWidget *widget = 0;
if (model->flags (model->index (0, i)) & Qt::ItemIsEditable)
{
switch (display)
{
case CSMWorld::ColumnBase::Display_String:
layout->addWidget (widget = new QLineEdit, i, 1);
break;
case CSMWorld::ColumnBase::Display_Integer:
/// \todo configure widget properly (range)
layout->addWidget (widget = new QSpinBox, i, 1);
break;
case CSMWorld::ColumnBase::Display_Float:
/// \todo configure widget properly (range, format?)
layout->addWidget (widget = new QDoubleSpinBox, i, 1);
break;
default: break; // silence warnings for other times for now
}
}
else
{
switch (display)
{
case CSMWorld::ColumnBase::Display_String:
case CSMWorld::ColumnBase::Display_Integer:
case CSMWorld::ColumnBase::Display_Float:
layout->addWidget (widget = new QLabel, i, 1);
break;
default: break; // silence warnings for other times for now
}
}
if (widget)
mWidgetMapper->addMapping (widget, i);
} }
} }
mWidgetMapper->setCurrentModelIndex ( if (QVariant::String == v.type())
dynamic_cast<CSMWorld::IdTable&> (*model).getModelIndex (id.getId(), 0)); {
editor->setText(v.toString());
} else //else we are facing enums
{
int data = v.toInt();
std::vector<std::string> enumNames (CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mTable->getColumnId (index.column()))));
editor->setText(QString::fromUtf8(enumNames.at(data).c_str()));
}
}
void CSVWorld::NotEditableSubDelegate::setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const
{
//not editable widgets will not save model data
}
void CSVWorld::NotEditableSubDelegate::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
//does nothing
}
QSize CSVWorld::NotEditableSubDelegate::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QSize();
}
QWidget* CSVWorld::NotEditableSubDelegate::createEditor (QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex& index,
CSMWorld::ColumnBase::Display display) const
{
return new QLabel(parent);
}
/*
==============================DialogueDelegateDispatcherProxy==========================================
*/
CSVWorld::DialogueDelegateDispatcherProxy::refWrapper::refWrapper(const QModelIndex& index) :
mIndex(index)
{}
CSVWorld::DialogueDelegateDispatcherProxy::DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display) :
mEditor(editor),
mDisplay(display),
mIndexWrapper(NULL)
{
}
void CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited()
{
if (mIndexWrapper.get())
{
emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay);
}
}
void CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index)
{
mIndexWrapper.reset(new refWrapper(index));
}
QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const
{
return mEditor;
}
void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>& data, const CSMDoc::Document* document)
{
QLineEdit* lineEdit = qobject_cast<QLineEdit*>(mEditor);
{
if (!lineEdit or !mIndexWrapper.get())
{
return;
}
}
for (unsigned i = 0; i < data.size(); ++i)
{
if (mDisplay == CSMWorld::TableMimeData::convertEnums(data[i].getType()))
{
emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document);
emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay);
break;
}
}
}
/*
==============================DialogueDelegateDispatcher==========================================
*/
CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack) :
mParent(parent),
mTable(table),
mUndoStack(undoStack),
mNotEditableDelegate(table, parent)
{
}
CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display)
{
CommandDelegate *delegate = NULL;
std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
if (delegateIt == mDelegates.end())
{
delegate = CommandDelegateFactoryCollection::get().makeDelegate (
display, mUndoStack, mParent);
mDelegates.insert(std::make_pair<int, CommandDelegate*>(display, delegate));
} else
{
delegate = delegateIt->second;
}
return delegate;
}
void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display)
{
setModelData(editor, mTable, index, display);
}
void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QModelIndex& index) const
{
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>
(mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
QLabel* label = qobject_cast<QLabel*>(editor);
if(label)
{
mNotEditableDelegate.setEditorData(label, index);
return;
}
std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
if (delegateIt != mDelegates.end())
{
delegateIt->second->setEditorData(editor, index, true);
}
for (unsigned i = 0; i < mProxys.size(); ++i)
{
if (mProxys[i]->getEditor() == editor)
{
mProxys[i]->setIndex(index);
}
}
}
void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const
{
std::map<int, CommandDelegate*>::const_iterator delegateIt(mDelegates.find(display));
if (delegateIt != mDelegates.end())
{
delegateIt->second->setModelData(editor, model, index);
}
}
void CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
//Does nothing
}
QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const
{
return QSize(); //silencing warning, otherwise does nothing
}
QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index)
{
QVariant variant = index.data();
if (!variant.isValid())
{
variant = index.data(Qt::DisplayRole);
if (!variant.isValid())
{
return NULL;
}
}
QWidget* editor = NULL;
if (! (mTable->flags (index) & Qt::ItemIsEditable))
{
return mNotEditableDelegate.createEditor(qobject_cast<QWidget*>(mParent), QStyleOptionViewItem(), index, display);
}
std::map<int, CommandDelegate*>::iterator delegateIt(mDelegates.find(display));
if (delegateIt != mDelegates.end())
{
editor = delegateIt->second->createEditor(qobject_cast<QWidget*>(mParent), QStyleOptionViewItem(), index, display);
DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display);
bool skip = false;
if (qobject_cast<DropLineEdit*>(editor))
{
connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited()));
connect(editor, SIGNAL(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*)),
proxy, SLOT(tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>&, const CSMDoc::Document*)));
connect(proxy, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)),
this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
skip = true;
}
if(!skip && qobject_cast<QCheckBox*>(editor))
{
connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited()));
skip = true;
}
if(!skip && qobject_cast<QPlainTextEdit*>(editor))
{
connect(editor, SIGNAL(textChanged()), proxy, SLOT(editorDataCommited()));
skip = true;
}
if(!skip && qobject_cast<QComboBox*>(editor))
{
connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited()));
skip = true;
}
if(!skip && qobject_cast<QAbstractSpinBox*>(editor))
{
connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited()));
skip = true;
}
connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)));
mProxys.push_back(proxy); //deleted in the destructor
}
return editor;
}
CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
{
for (unsigned i = 0; i < mProxys.size(); ++i)
{
delete mProxys[i]; //unique_ptr could be handy
}
}
/*
=============================================================EditWidget=====================================================
*/
CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, QUndoStack& undoStack, bool createAndDelete) :
mDispatcher(this, table, undoStack),
QScrollArea(parent),
mWidgetMapper(NULL),
mMainWidget(NULL),
mUndoStack(undoStack),
mTable(table)
{
remake (row);
connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
}
void CSVWorld::EditWidget::remake(int row)
{
if (mMainWidget)
{
delete mMainWidget;
}
mMainWidget = new QWidget (this);
//not sure if widget mapper can handle deleting the widgets that were mapped
if (mWidgetMapper)
{
delete mWidgetMapper;
}
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable);
mWidgetMapper->setItemDelegate(&mDispatcher);
QFrame* line = new QFrame(mMainWidget);
line->setObjectName(QString::fromUtf8("line"));
line->setGeometry(QRect(320, 150, 118, 3));
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget);
QGridLayout *unlockedLayout = new QGridLayout();
QGridLayout *lockedLayout = new QGridLayout();
mainLayout->addLayout(lockedLayout, 0);
mainLayout->addWidget(line, 1);
mainLayout->addLayout(unlockedLayout, 2);
mainLayout->addStretch(1);
int unlocked = 0;
int locked = 0;
const int columns = mTable->columnCount();
for (int i=0; i<columns; ++i)
{
int flags = mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
if (flags & CSMWorld::ColumnBase::Flag_Dialogue)
{
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>
(mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
mDispatcher.makeDelegate(display);
QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (row, i)));
if (editor)
{
mWidgetMapper->addMapping (editor, i);
QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget);
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable))
{
lockedLayout->addWidget (label, locked, 0);
lockedLayout->addWidget (editor, locked, 1);
++locked;
} else
{
unlockedLayout->addWidget (label, unlocked, 0);
unlockedLayout->addWidget (editor, unlocked, 1);
++unlocked;
}
}
}
}
mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0));
this->setMinimumWidth(325); //TODO find better way to set the width or make it customizable
this->setWidget(mMainWidget);
this->setWidgetResizable(true);
}
/*
==============================DialogueSubView==========================================
*/
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory, bool sorting) :
SubView (id),
mEditWidget(0),
mMainLayout(NULL),
mUndoStack(document.getUndoStack()),
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mRow (-1),
mLocked(false),
mDocument(document)
{
connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&)));
mRow = mTable->getModelIndex (id.getId(), 0).row();
QWidget *mainWidget = new QWidget(this);
QHBoxLayout *buttonsLayout = new QHBoxLayout;
QToolButton* prevButton = new QToolButton(mainWidget);
prevButton->setIcon(QIcon(":/go-previous.png"));
QToolButton* nextButton = new QToolButton(mainWidget);
nextButton->setIcon(QIcon(":/go-next.png"));
buttonsLayout->addWidget(prevButton, 0);
buttonsLayout->addWidget(nextButton, 1);
buttonsLayout->addStretch(2);
QToolButton* cloneButton = new QToolButton(mainWidget);
cloneButton->setIcon(QIcon(":/edit-clone.png"));
QToolButton* addButton = new QToolButton(mainWidget);
addButton->setIcon(QIcon(":/add.png"));
QToolButton* deleteButton = new QToolButton(mainWidget);
deleteButton->setIcon(QIcon(":/edit-delete.png"));
QToolButton* revertButton = new QToolButton(mainWidget);
revertButton->setIcon(QIcon(":/edit-undo.png"));
if (mTable->hasPreview())
{
QToolButton* previewButton = new QToolButton(mainWidget);
previewButton->setIcon(QIcon(":/edit-preview.png"));
buttonsLayout->addWidget(previewButton);
connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview()));
}
if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None)
{
QToolButton* viewButton = new QToolButton(mainWidget);
viewButton->setIcon(QIcon(":/cell.png"));
buttonsLayout->addWidget(viewButton);
connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord()));
}
buttonsLayout->addWidget(cloneButton);
buttonsLayout->addWidget(addButton);
buttonsLayout->addWidget(deleteButton);
buttonsLayout->addWidget(revertButton);
connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId()));
connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId()));
connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest()));
connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord()));
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteRecord()));
mMainLayout = new QVBoxLayout(mainWidget);
mEditWidget = new EditWidget(mainWidget, mRow, mTable, mUndoStack, false);
connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)),
this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)));
mMainLayout->addWidget(mEditWidget);
mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
mMainLayout->addWidget (mBottom =
new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this));
mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&)));
connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest()));
if(!mBottom->canCreateAndDelete())
{
cloneButton->setDisabled(true);
addButton->setDisabled(true);
deleteButton->setDisabled(true);
}
dataChanged(mTable->index(mRow, 0));
mMainLayout->addLayout(buttonsLayout);
setWidget(mainWidget);
}
void CSVWorld::DialogueSubView::prevId()
{
int newRow = mRow - 1;
if (newRow < 0)
{
return;
}
while (newRow >= 0)
{
QModelIndex newIndex(mTable->index(newRow, 0));
if (!newIndex.isValid())
{
return;
}
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (newRow, 1)).toInt());
if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased))
{
mEditWidget->remake(newRow);
setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()),
mTable->data (mTable->index (newRow, 0)).toString().toStdString()));
mRow = newRow;
mEditWidget->setDisabled(mLocked);
return;
}
--newRow;
}
}
void CSVWorld::DialogueSubView::nextId()
{
int newRow = mRow + 1;
if (newRow >= mTable->rowCount())
{
return;
}
while (newRow < mTable->rowCount())
{
QModelIndex newIndex(mTable->index(newRow, 0));
if (!newIndex.isValid())
{
return;
}
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (newRow, 1)).toInt());
if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased))
{
mEditWidget->remake(newRow);
setUniversalId(CSMWorld::UniversalId (static_cast<CSMWorld::UniversalId::Type> (mTable->data (mTable->index (newRow, 2)).toInt()),
mTable->data (mTable->index (newRow, 0)).toString().toStdString()));
mRow = newRow;
mEditWidget->setDisabled(mLocked);
return;
}
++newRow;
}
} }
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
mLocked = locked;
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt());
if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)
{
mEditWidget->setDisabled(true);
} else
{
mEditWidget->setDisabled(mLocked);
}
}
void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index)
{
if (index.row() == mRow)
{
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt());
if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)
{
mEditWidget->setDisabled(true);
} else
{
mEditWidget->setDisabled(mLocked);
}
}
}
void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor,
const QModelIndex& index,
const CSMWorld::UniversalId& id,
const CSMDoc::Document* document)
{
if (document == &mDocument)
{
qobject_cast<DropLineEdit*>(editor)->setText(id.getId().c_str());
}
}
void CSVWorld::DialogueSubView::revertRecord()
{
int rows = mTable->rowCount();
if (!mLocked && mTable->columnCount() > 0 && mRow < mTable->rowCount() )
{
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mTable->data (mTable->index (mRow, 1)).toInt());
if (state!=CSMWorld::RecordBase::State_BaseOnly)
{
mUndoStack.push(new CSMWorld::RevertCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString()));
}
if (rows != mTable->rowCount())
{
if (mTable->rowCount() == 0)
{
mEditWidget->setDisabled(true); //closing the editor is other option
return;
}
if (mRow >= mTable->rowCount())
{
prevId();
} else {
dataChanged(mTable->index(mRow, 0));
}
}
}
}
void CSVWorld::DialogueSubView::deleteRecord()
{
int rows = mTable->rowCount();
//easier than disabling the button
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt());
bool deledetedOrErased = (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased);
if (!mLocked &&
mTable->columnCount() > 0 &&
!deledetedOrErased &&
mRow < rows &&
mBottom->canCreateAndDelete())
{
mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString()));
if (rows != mTable->rowCount())
{
if (mTable->rowCount() == 0)
{
mEditWidget->setDisabled(true); //closing the editor is other option
return;
}
if (mRow >= mTable->rowCount())
{
prevId();
} else {
dataChanged(mTable->index(mRow, 0));
}
}
}
}
void CSVWorld::DialogueSubView::requestFocus (const std::string& id)
{
mRow = mTable->getModelIndex (id, 0).row();
mEditWidget->remake(mRow);
}
void CSVWorld::DialogueSubView::cloneRequest ()
{
mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toStdString(),
static_cast<CSMWorld::UniversalId::Type>(mTable->data(mTable->index(mRow, 2)).toInt()));
}
void CSVWorld::DialogueSubView::showPreview ()
{
if (mTable->hasPreview() && mRow < mTable->rowCount())
{
emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toStdString()), "");
}
}
void CSVWorld::DialogueSubView::viewRecord()
{
if (mRow < mTable->rowCount())
{
std::pair<CSMWorld::UniversalId, std::string> params = mTable->view (mRow);
if (params.first.getType()!=CSMWorld::UniversalId::Type_None)
emit focusId (params.first, params.second);
}
} }

View file

@ -1,9 +1,25 @@
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H #ifndef CSV_WORLD_DIALOGUESUBVIEW_H
#define CSV_WORLD_DIALOGUESUBVIEW_H #define CSV_WORLD_DIALOGUESUBVIEW_H
#include <map>
#include <memory>
#include <QAbstractItemDelegate>
#include <QScrollArea>
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
#include "../../model/world/columnbase.hpp"
class QDataWidgetMapper; class QDataWidgetMapper;
class QSize;
class QEvent;
class QLabel;
class QVBoxLayout;
namespace CSMWorld
{
class IdTable;
}
namespace CSMDoc namespace CSMDoc
{ {
@ -12,15 +28,181 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class DialogueSubView : public CSVDoc::SubView class CommandDelegate;
class CreatorFactoryBase;
class TableBottomBox;
class NotEditableSubDelegate : public QAbstractItemDelegate
{ {
const CSMWorld::IdTable* mTable;
public:
NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent = 0);
virtual void setEditorData (QLabel* editor, const QModelIndex& index) const;
virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const;
virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
///< does nothing
virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const;
///< does nothing
virtual QWidget *createEditor (QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex& index,
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const;
};
//this can't be nested into the DialogueDelegateDispatcher, because it needs to emit signals
class DialogueDelegateDispatcherProxy : public QObject
{
Q_OBJECT
class refWrapper
{
public:
refWrapper(const QModelIndex& index);
const QModelIndex& mIndex;
};
QWidget* mEditor;
CSMWorld::ColumnBase::Display mDisplay;
std::auto_ptr<refWrapper> mIndexWrapper;
public:
DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display);
QWidget* getEditor() const;
public slots:
void editorDataCommited();
void setIndex(const QModelIndex& index);
void tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>& data, const CSMDoc::Document* document);
signals:
void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display);
void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id,
const CSMDoc::Document* document);
};
class DialogueDelegateDispatcher : public QAbstractItemDelegate
{
Q_OBJECT
std::map<int, CommandDelegate*> mDelegates;
QObject* mParent;
CSMWorld::IdTable* mTable;
QUndoStack& mUndoStack;
NotEditableSubDelegate mNotEditableDelegate;
std::vector<DialogueDelegateDispatcherProxy*> mProxys; //once we move to the C++11 we should use unique_ptr
public:
DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack);
~DialogueDelegateDispatcher();
CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display);
QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index);
///< will return null if delegate is not present, parent of the widget is same as for dispatcher itself
virtual void setEditorData (QWidget* editor, const QModelIndex& index) const;
virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const;
virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
///< does nothing
virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const;
///< does nothing
private slots:
void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display);
signals:
void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id,
const CSMDoc::Document* document);
};
class EditWidget : public QScrollArea
{
Q_OBJECT
QDataWidgetMapper *mWidgetMapper; QDataWidgetMapper *mWidgetMapper;
DialogueDelegateDispatcher mDispatcher;
QWidget* mMainWidget;
CSMWorld::IdTable* mTable;
QUndoStack& mUndoStack;
public: public:
DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, QUndoStack& undoStack, bool createAndDelete = false);
void remake(int row);
signals:
void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id,
const CSMDoc::Document* document);
};
class DialogueSubView : public CSVDoc::SubView
{
Q_OBJECT
EditWidget* mEditWidget;
QVBoxLayout* mMainLayout;
CSMWorld::IdTable* mTable;
QUndoStack& mUndoStack;
int mRow;
bool mLocked;
const CSMDoc::Document& mDocument;
TableBottomBox* mBottom;
public:
DialogueSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory,
bool sorting = false);
virtual void setEditLock (bool locked); virtual void setEditLock (bool locked);
private slots:
void nextId();
void prevId();
void showPreview();
void viewRecord();
void revertRecord();
void deleteRecord();
void cloneRequest();
void dataChanged(const QModelIndex & index);
///\brief we need to care for deleting currently edited record
void tableMimeDataDropped(QWidget* editor, const QModelIndex& index,
const CSMWorld::UniversalId& id,
const CSMDoc::Document* document);
void requestFocus (const std::string& id);
}; };
} }

View file

@ -41,10 +41,18 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector<std::pair<int, QString>
} }
QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent,
const QModelIndex& index) const const QStyleOptionViewItem& option,
const QModelIndex& index) const
{ {
if (!index.data().isValid()) return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_None);
//overloading virtual functions is HARD
}
QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option,
const QModelIndex& index, CSMWorld::ColumnBase::Display display) const
{
if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid())
return 0; return 0;
QComboBox *comboBox = new QComboBox (parent); QComboBox *comboBox = new QComboBox (parent);
@ -56,11 +64,22 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio
return comboBox; return comboBox;
} }
void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const
{ {
if (QComboBox *comboBox = dynamic_cast<QComboBox *> (editor)) if (QComboBox *comboBox = dynamic_cast<QComboBox *> (editor))
{ {
int value = index.data (Qt::EditRole).toInt(); QVariant data = index.data (Qt::EditRole);
if (tryDisplay && !data.isValid())
{
data = index.data (Qt::DisplayRole);
if (!data.isValid())
{
return;
}
}
int value = data.toInt();
std::size_t size = mValues.size(); std::size_t size = mValues.size();

View file

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <QString> #include <QString>
#include <QStyledItemDelegate>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
@ -31,10 +32,16 @@ namespace CSVWorld
EnumDelegate (const std::vector<std::pair<int, QString> >& values, EnumDelegate (const std::vector<std::pair<int, QString> >& values,
QUndoStack& undoStack, QObject *parent); QUndoStack& undoStack, QObject *parent);
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, virtual QWidget *createEditor(QWidget *parent,
const QModelIndex& index) const; const QStyleOptionViewItem& option,
const QModelIndex& index) const;
virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; virtual QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex& index,
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const;
virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const;
virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, virtual void paint (QPainter *painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const; const QModelIndex& index) const;

View file

@ -80,5 +80,61 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>); manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>);
//edit subviews
manager.add (CSMWorld::UniversalId::Type_Region,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Spell,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Referenceable,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Birthsign,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Global,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Gmst,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Race,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Class,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Reference,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Cell,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<CellCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Filter,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Sound,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Faction,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Skill,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_JournalInfo,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<InfoCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_TopicInfo,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<InfoCreator> >(false));
manager.add (CSMWorld::UniversalId::Type_Topic,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, TopicCreatorFactory> (false));
manager.add (CSMWorld::UniversalId::Type_Journal,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, JournalCreatorFactory> (false));
//preview
manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory<PreviewSubView>); manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory<PreviewSubView>);
} }

View file

@ -442,6 +442,7 @@ void CSVWorld::Table::previewRecord()
emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , ""); emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , "");
} }
} }
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
{ {
int columns = mModel->columnCount(); int columns = mModel->columnCount();
@ -533,27 +534,14 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event)
drag->setMimeData (mime); drag->setMimeData (mime);
drag->setPixmap (QString::fromStdString (mime->getIcon())); drag->setPixmap (QString::fromStdString (mime->getIcon()));
drag->exec(Qt::CopyAction);
Qt::DropActions action = Qt::IgnoreAction;
switch (QApplication::keyboardModifiers())
{
case Qt::ControlModifier:
action = Qt::CopyAction;
break;
case Qt::ShiftModifier:
action = Qt::MoveAction;
break;
}
drag->exec(action);
} }
} }
void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event)
{ {
event->acceptProposedAction(); event->acceptProposedAction();
} }
void CSVWorld::Table::dropEvent(QDropEvent *event) void CSVWorld::Table::dropEvent(QDropEvent *event)
@ -585,7 +573,7 @@ void CSVWorld::Table::dropEvent(QDropEvent *event)
void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event)
{ {
event->accept(); event->accept();
} }
std::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const std::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const

View file

@ -4,8 +4,18 @@
#include <stdexcept> #include <stdexcept>
#include <QUndoStack> #include <QUndoStack>
#include <QMetaProperty>
#include <QStyledItemDelegate>
#include <QLineEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QCheckBox>
#include <QPlainTextEdit>
#include <QEvent>
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/tablemimedata.hpp"
CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model)
: mModel (model) : mModel (model)
@ -117,10 +127,56 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode
} }
QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const const QModelIndex& index, CSMWorld::ColumnBase::Display display) const
{ {
if (!index.data().isValid()) QVariant variant = index.data();
return 0; if (!variant.isValid())
{
variant = index.data(Qt::DisplayRole);
if (!variant.isValid())
{
return 0;
}
}
if (display != CSMWorld::ColumnBase::Display_None)
{
if (variant.type() == QVariant::Color)
{
return new QLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Integer)
{
return new QSpinBox(parent);
}
if (display == CSMWorld::ColumnBase::Display_Var)
{
return new QLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Float)
{
return new QDoubleSpinBox(parent);
}
if (display == CSMWorld::ColumnBase::Display_LongString)
{
return new QTextEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_String ||
display == CSMWorld::ColumnBase::Display_Skill ||
display == CSMWorld::ColumnBase::Display_Script ||
display == CSMWorld::ColumnBase::Display_Race ||
display == CSMWorld::ColumnBase::Display_Class ||
display == CSMWorld::ColumnBase::Display_Faction ||
display == CSMWorld::ColumnBase::Display_Miscellaneous ||
display == CSMWorld::ColumnBase::Display_Sound)
{
return new DropLineEdit(parent);
}
if (display == CSMWorld::ColumnBase::Display_Boolean)
{
return new QCheckBox(parent);
}
}
return QStyledItemDelegate::createEditor (parent, option, index); return QStyledItemDelegate::createEditor (parent, option, index);
} }
@ -141,3 +197,66 @@ bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName,
{ {
return false; return false;
} }
void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const
{
QVariant v = index.data(Qt::EditRole);
if (tryDisplay)
{
if (!v.isValid())
{
v = index.data(Qt::DisplayRole);
if (!v.isValid())
{
return;
}
}
QPlainTextEdit* plainTextEdit = qobject_cast<QPlainTextEdit*>(editor);
if(plainTextEdit) //for some reason it is easier to brake the loop here
{
if(plainTextEdit->toPlainText() == v.toString())
{
return;
}
}
}
QByteArray n = editor->metaObject()->userProperty().name();
if (n == "dateTime") {
if (editor->inherits("QTimeEdit"))
n = "time";
else if (editor->inherits("QDateEdit"))
n = "date";
}
if (!n.isEmpty()) {
if (!v.isValid())
v = QVariant(editor->property(n).userType(), (const void *)0);
editor->setProperty(n, v);
}
}
CSVWorld::DropLineEdit::DropLineEdit(QWidget* parent) :
QLineEdit(parent)
{
setAcceptDrops(true);
}
void CSVWorld::DropLineEdit::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void CSVWorld::DropLineEdit::dragMoveEvent(QDragMoveEvent *event)
{
event->accept();
}
void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event)
{
const CSMWorld::TableMimeData* data(dynamic_cast<const CSMWorld::TableMimeData*>(event->mimeData()));
emit tableMimeDataDropped(data->getData(), data->getDocumentPtr());
//WIP
}

View file

@ -5,11 +5,19 @@
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#include <QLineEdit>
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/doc/document.hpp"
class QUndoStack; class QUndoStack;
namespace CSMWorld
{
class TableMimeData;
class UniversalId;
}
namespace CSVWorld namespace CSVWorld
{ {
///< \brief Getting the data out of an editor widget ///< \brief Getting the data out of an editor widget
@ -79,6 +87,24 @@ namespace CSVWorld
}; };
class DropLineEdit : public QLineEdit
{
Q_OBJECT
public:
DropLineEdit(QWidget *parent);
private:
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
signals:
void tableMimeDataDropped(const std::vector<CSMWorld::UniversalId>& data, const CSMDoc::Document* document);
};
///< \brief Use commands instead of manipulating the model directly ///< \brief Use commands instead of manipulating the model directly
class CommandDelegate : public QStyledItemDelegate class CommandDelegate : public QStyledItemDelegate
{ {
@ -101,8 +127,10 @@ namespace CSVWorld
virtual void setModelData (QWidget *editor, QAbstractItemModel *model, virtual void setModelData (QWidget *editor, QAbstractItemModel *model,
const QModelIndex& index) const; const QModelIndex& index) const;
virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, virtual QWidget *createEditor (QWidget *parent,
const QModelIndex& index) const; const QStyleOptionViewItem& option,
const QModelIndex& index,
CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const;
void setEditLock (bool locked); void setEditLock (bool locked);
@ -111,6 +139,9 @@ namespace CSVWorld
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
///< \return Does column require update? ///< \return Does column require update?
virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const;
private slots: private slots:
virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {} virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {}

View file

@ -15,7 +15,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst effectmanager terrainstorage renderconst effectmanager weaponanimation
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -44,7 +44,7 @@ add_openmw_dir (mwscript
locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions
guiextensions soundextensions skyextensions statsextensions containerextensions guiextensions soundextensions skyextensions statsextensions containerextensions
aiextensions controlextensions extensions globalscripts ref dialogueextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions
animationextensions transformationextensions consoleextensions userextensions locals animationextensions transformationextensions consoleextensions userextensions
) )
add_openmw_dir (mwsound add_openmw_dir (mwsound

View file

@ -359,7 +359,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create the world // Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride)); mActivationDistanceOverride, mCellName));
MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer()); input->setPlayer(&mEnvironment.getWorld()->getPlayer());
@ -395,31 +395,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer(); mechanics->buildPlayer();
window->updatePlayer(); window->updatePlayer();
// load cell
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos);
}
else {
world->findInteriorPosition(mCellName, pos);
world->changeToInteriorCell (mCellName, pos);
}
}
else
{
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event;
event.timeSinceLastEvent = 0;
event.timeSinceLastFrame = 0;
frameRenderingQueued(event);
mOgre->getRoot()->addFrameListener (this); mOgre->getRoot()->addFrameListener (this);
// scripts // scripts
@ -457,15 +432,15 @@ void OMW::Engine::go()
// Play some good 'ol tunes // Play some good 'ol tunes
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu // start in main menu
if (!mSkipMenu) if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else else
MWBase::Environment::get().getStateManager()->newGame (true); MWBase::Environment::get().getStateManager()->newGame (true);
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// Start the main rendering loop // Start the main rendering loop
while (!mEnvironment.get().getStateManager()->hasQuitRequest()) while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();

View file

@ -20,6 +20,9 @@ namespace MWBase
InputManager() {} InputManager() {}
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual ~InputManager() {} virtual ~InputManager() {}
virtual void update(float dt, bool loading) = 0; virtual void update(float dt, bool loading) = 0;

View file

@ -101,7 +101,8 @@ namespace MWBase
virtual ~World() {} virtual ~World() {}
virtual void startNewGame() = 0; virtual void startNewGame (bool bypass) = 0;
///< \param bypass Bypass regular game start.
virtual void clear() = 0; virtual void clear() = 0;

View file

@ -28,16 +28,16 @@
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct ContainerCustomData : public MWWorld::CustomData
{ {
MWWorld::ContainerStore mContainerStore; MWWorld::ContainerStore mContainerStore;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
MWWorld::CustomData *CustomData::clone() const MWWorld::CustomData *ContainerCustomData::clone() const
{ {
return new CustomData (*this); return new ContainerCustomData (*this);
} }
} }
@ -47,7 +47,7 @@ namespace MWClass
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CustomData> data (new CustomData); std::auto_ptr<ContainerCustomData> data (new ContainerCustomData);
MWWorld::LiveCellRef<ESM::Container> *ref = MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>(); ptr.get<ESM::Container>();
@ -174,7 +174,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore; return dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
} }
std::string Container::getScript (const MWWorld::Ptr& ptr) const std::string Container::getScript (const MWWorld::Ptr& ptr) const
@ -267,7 +267,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore. dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory); readState (state2.mInventory);
} }
@ -278,7 +278,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore. dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory); writeState (state2.mInventory);
} }
} }

View file

@ -37,7 +37,7 @@
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct CreatureCustomData : public MWWorld::CustomData
{ {
MWMechanics::CreatureStats mCreatureStats; MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
@ -45,13 +45,13 @@ namespace
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
CustomData() : mContainerStore(0) {} CreatureCustomData() : mContainerStore(0) {}
virtual ~CustomData() { delete mContainerStore; } virtual ~CreatureCustomData() { delete mContainerStore; }
}; };
MWWorld::CustomData *CustomData::clone() const MWWorld::CustomData *CreatureCustomData::clone() const
{ {
CustomData* cloned = new CustomData (*this); CreatureCustomData* cloned = new CreatureCustomData (*this);
cloned->mContainerStore = mContainerStore->clone(); cloned->mContainerStore = mContainerStore->clone();
return cloned; return cloned;
} }
@ -63,7 +63,7 @@ namespace MWClass
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CustomData> data (new CustomData); std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
static bool inited = false; static bool inited = false;
if(!inited) if(!inited)
@ -192,7 +192,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats; return dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
} }
@ -456,7 +456,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return *dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore; return *dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
} }
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const
@ -525,7 +525,7 @@ namespace MWClass
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f; moveSpeed = 0.0f;
else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
world->isLevitationEnabled())) world->isLevitationEnabled()))
{ {
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
@ -559,7 +559,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement; return dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mMovement;
} }
Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const
@ -678,7 +678,15 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Creature>().insert(*ref), &cell); return MWWorld::Ptr(&cell.get<ESM::Creature>().insert(*ref), &cell);
} }
bool Creature::isFlying(const MWWorld::Ptr &ptr) const bool Creature::isBipedal(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Bipedal;
}
bool Creature::canFly(const MWWorld::Ptr &ptr) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
@ -686,6 +694,22 @@ namespace MWClass
return ref->mBase->mFlags & ESM::Creature::Flies; return ref->mBase->mFlags & ESM::Creature::Flies;
} }
bool Creature::canSwim(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Swims;
}
bool Creature::canWalk(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Walks;
}
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
{ {
if(name == "left") if(name == "left")
@ -762,7 +786,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()); CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
customData.mContainerStore->readState (state2.mInventory); customData.mContainerStore->readState (state2.mInventory);
customData.mCreatureStats.readState (state2.mCreatureStats); customData.mCreatureStats.readState (state2.mCreatureStats);
@ -776,7 +800,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()); CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
customData.mContainerStore->writeState (state2.mInventory); customData.mContainerStore->writeState (state2.mInventory);
customData.mCreatureStats.writeState (state2.mCreatureStats); customData.mCreatureStats.writeState (state2.mCreatureStats);

View file

@ -124,7 +124,10 @@ namespace MWClass
return true; return true;
} }
virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual bool isBipedal (const MWWorld::Ptr &ptr) const;
virtual bool canFly (const MWWorld::Ptr &ptr) const;
virtual bool canSwim (const MWWorld::Ptr &ptr) const;
virtual bool canWalk (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;

View file

@ -9,15 +9,15 @@
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct CreatureLevListCustomData : public MWWorld::CustomData
{ {
// TODO: save the creature we spawned here // TODO: save the creature we spawned here
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
MWWorld::CustomData *CustomData::clone() const MWWorld::CustomData *CreatureLevListCustomData::clone() const
{ {
return new CustomData (*this); return new CreatureLevListCustomData (*this);
} }
} }
@ -44,7 +44,7 @@ namespace MWClass
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CustomData> data (new CustomData); std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>(); ptr.get<ESM::CreatureLevList>();

View file

@ -26,12 +26,12 @@
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct LightCustomData : public MWWorld::CustomData
{ {
float mTime; float mTime;
///< Time remaining ///< Time remaining
CustomData(MWWorld::Ptr ptr) LightCustomData(MWWorld::Ptr ptr)
{ {
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
mTime = ref->mBase->mData.mTime; mTime = ref->mBase->mData.mTime;
@ -40,7 +40,7 @@ namespace
virtual MWWorld::CustomData *clone() const virtual MWWorld::CustomData *clone() const
{ {
return new CustomData (*this); return new LightCustomData (*this);
} }
}; };
} }
@ -210,7 +210,7 @@ namespace MWClass
{ {
ensureCustomData(ptr); ensureCustomData(ptr);
float &timeRemaining = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime; float &timeRemaining = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
timeRemaining = duration; timeRemaining = duration;
} }
@ -218,7 +218,7 @@ namespace MWClass
{ {
ensureCustomData(ptr); ensureCustomData(ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime; return dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
} }
MWWorld::Ptr MWWorld::Ptr
@ -233,7 +233,7 @@ namespace MWClass
void Light::ensureCustomData (const MWWorld::Ptr& ptr) const void Light::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
ptr.getRefData().setCustomData(new CustomData(ptr)); ptr.getRefData().setCustomData(new LightCustomData(ptr));
} }
bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const
@ -278,7 +278,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime; dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
} }
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -288,6 +288,6 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime; state2.mTime = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
} }
} }

View file

@ -39,7 +39,7 @@
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct NpcCustomData : public MWWorld::CustomData
{ {
MWMechanics::NpcStats mNpcStats; MWMechanics::NpcStats mNpcStats;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
@ -48,9 +48,9 @@ namespace
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
MWWorld::CustomData *CustomData::clone() const MWWorld::CustomData *NpcCustomData::clone() const
{ {
return new CustomData (*this); return new NpcCustomData (*this);
} }
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
@ -262,7 +262,7 @@ namespace MWClass
} }
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CustomData> data(new CustomData); std::auto_ptr<NpcCustomData> data(new NpcCustomData);
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -436,14 +436,14 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats; return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
} }
MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats; return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
} }
@ -673,12 +673,7 @@ namespace MWClass
else else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(object.isEmpty()) if(ishealth)
{
if(ishealth)
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
}
else if(ishealth)
{ {
// Hit percentages: // Hit percentages:
// cuirass = 30% // cuirass = 30%
@ -824,7 +819,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore; return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
} }
MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr)
@ -832,7 +827,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore; return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
} }
std::string Npc::getScript (const MWWorld::Ptr& ptr) const std::string Npc::getScript (const MWWorld::Ptr& ptr) const
@ -846,7 +841,7 @@ namespace MWClass
float Npc::getSpeed(const MWWorld::Ptr& ptr) const float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
@ -901,7 +896,7 @@ namespace MWClass
float Npc::getJump(const MWWorld::Ptr &ptr) const float Npc::getJump(const MWWorld::Ptr &ptr) const
{ {
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
fJumpEncumbranceMultiplier->getFloat() * fJumpEncumbranceMultiplier->getFloat() *
@ -940,7 +935,7 @@ namespace MWClass
if (fallHeight >= fallDistanceMin) if (fallHeight >= fallDistanceMin)
{ {
const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat();
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat();
@ -965,7 +960,7 @@ namespace MWClass
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement; return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mMovement;
} }
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
@ -1271,7 +1266,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()); NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
customData.mInventoryStore.readState (state2.mInventory); customData.mInventoryStore.readState (state2.mInventory);
customData.mNpcStats.readState (state2.mNpcStats); customData.mNpcStats.readState (state2.mNpcStats);
@ -1285,7 +1280,7 @@ namespace MWClass
ensureCustomData (ptr); ensureCustomData (ptr);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()); NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
customData.mInventoryStore.writeState (state2.mInventory); customData.mInventoryStore.writeState (state2.mInventory);
customData.mNpcStats.writeState (state2.mNpcStats); customData.mNpcStats.writeState (state2.mNpcStats);

View file

@ -26,16 +26,6 @@ namespace
return path; return path;
} }
std::string getCountString(const int count)
{
if (count == 1)
return "";
if (count > 9999)
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
else
return boost::lexical_cast<std::string>(count);
}
} }
namespace MWGui namespace MWGui
@ -226,7 +216,7 @@ namespace MWGui
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);
text->setTextShadow(true); text->setTextShadow(true);
text->setTextShadowColour(MyGUI::Colour(0,0,0)); text->setTextShadowColour(MyGUI::Colour(0,0,0));
text->setCaption(getCountString(ingredient->getUserData<MWWorld::Ptr>()->getRefData().getCount())); text->setCaption(ItemView::getCountString(ingredient->getUserData<MWWorld::Ptr>()->getRefData().getCount()));
} }
mItemView->update(); mItemView->update();

View file

@ -23,19 +23,6 @@
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "pickpocketitemmodel.hpp" #include "pickpocketitemmodel.hpp"
namespace
{
std::string getCountString(const int count)
{
if (count == 1)
return "";
if (count > 9999)
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
else
return boost::lexical_cast<std::string>(count);
}
}
namespace MWGui namespace MWGui
{ {
@ -79,7 +66,7 @@ namespace MWGui
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);
text->setTextShadow(true); text->setTextShadow(true);
text->setTextShadowColour(MyGUI::Colour(0,0,0)); text->setTextShadowColour(MyGUI::Colour(0,0,0));
text->setCaption(getCountString(count)); text->setCaption(ItemView::getCountString(count));
sourceView->update(); sourceView->update();

View file

@ -23,18 +23,7 @@
#include "travelwindow.hpp" #include "travelwindow.hpp"
#include "bookpage.hpp" #include "bookpage.hpp"
#include "journalbooks.hpp" // to_utf8_span
namespace
{
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
typedef MWGui::BookTypesetter::Utf8Point point;
point begin = reinterpret_cast <point> (text);
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
}
namespace MWGui namespace MWGui
{ {

View file

@ -604,15 +604,22 @@ namespace MWGui
mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());
} }
void HUD::updateEnemyHealthBar()
{
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
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 :)
mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100);
}
void HUD::update() void HUD::update()
{ {
mSpellIcons->updateWidgets(mEffectBox, true); mSpellIcons->updateWidgets(mEffectBox, true);
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
{ {
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); updateEnemyHealthBar();
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
} }
if (mIsDrowning) if (mIsDrowning)
@ -629,9 +636,7 @@ namespace MWGui
if (!mEnemyHealth->getVisible()) if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
mEnemyHealth->setVisible(true); mEnemyHealth->setVisible(true);
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); updateEnemyHealthBar();
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
} }
} }

View file

@ -1,3 +1,6 @@
#ifndef OPENMW_GAME_MWGUI_HUD_H
#define OPENMW_GAME_MWGUI_HUD_H
#include "mapwindow.hpp" #include "mapwindow.hpp"
#include "../mwmechanics/stat.hpp" #include "../mwmechanics/stat.hpp"
@ -112,6 +115,10 @@ namespace MWGui
void onMagicClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender);
void onMapClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender);
void updateEnemyHealthBar();
void updatePositions(); void updatePositions();
}; };
} }
#endif

View file

@ -162,6 +162,7 @@ namespace MWGui
} }
const ItemStack& item = mTradeModel->getItem(index); const ItemStack& item = mTradeModel->getItem(index);
std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase);
MWWorld::Ptr object = item.mBase; MWWorld::Ptr object = item.mBase;
int count = item.mCount; int count = item.mCount;
@ -170,6 +171,7 @@ namespace MWGui
if (item.mBase.getCellRef().mRefID.size() > 6 if (item.mBase.getCellRef().mRefID.size() > 6
&& item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_")
{ {
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}");
return; return;
} }
@ -213,6 +215,7 @@ namespace MWGui
int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices(); int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices();
if (!MWWorld::Class::get(object).canSell(object, services)) if (!MWWorld::Class::get(object).canSell(object, services))
{ {
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
MWBase::Environment::get().getWindowManager()-> MWBase::Environment::get().getWindowManager()->
messageBox("#{sBarterDialog4}"); messageBox("#{sBarterDialog4}");
return; return;
@ -514,6 +517,9 @@ namespace MWGui
void InventoryWindow::pickUpObject (MWWorld::Ptr object) void InventoryWindow::pickUpObject (MWWorld::Ptr object)
{ {
// If the inventory is not yet enabled, don't pick anything up
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
return;
// make sure the object is of a type that can be picked up // make sure the object is of a type that can be picked up
std::string type = object.getTypeName(); std::string type = object.getTypeName();
if ( (type != typeid(ESM::Apparatus).name()) if ( (type != typeid(ESM::Apparatus).name())

View file

@ -1,3 +1,6 @@
#ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H
#define OPENMW_GAME_MWGUI_ITEMSELECTION_H
#include "container.hpp" #include "container.hpp"
namespace MWGui namespace MWGui
@ -32,3 +35,5 @@ namespace MWGui
}; };
} }
#endif

View file

@ -13,23 +13,19 @@
#include "itemmodel.hpp" #include "itemmodel.hpp"
namespace
{
std::string getCountString(const int count)
{
if (count == 1)
return "";
if (count > 9999)
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
else
return boost::lexical_cast<std::string>(count);
}
}
namespace MWGui namespace MWGui
{ {
std::string ItemView::getCountString(int count)
{
if (count == 1)
return "";
if (count > 9999)
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
else
return boost::lexical_cast<std::string>(count);
}
ItemView::ItemView() ItemView::ItemView()
: mModel(NULL) : mModel(NULL)
, mScrollView(NULL) , mScrollView(NULL)

View file

@ -30,6 +30,8 @@ namespace MWGui
void update(); void update();
static std::string getCountString(int count);
private: private:
virtual void initialiseOverride(); virtual void initialiseOverride();

View file

@ -2,15 +2,6 @@
namespace namespace
{ {
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
typedef MWGui::BookTypesetter::Utf8Point point;
point begin = reinterpret_cast <point> (text);
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f); const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f); const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f); const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
@ -178,6 +169,15 @@ namespace
namespace MWGui namespace MWGui
{ {
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
{
typedef MWGui::BookTypesetter::Utf8Point point;
point begin = reinterpret_cast <point> (text);
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
}
typedef TypesetBook::Ptr book; typedef TypesetBook::Ptr book;
JournalBooks::JournalBooks (JournalViewModel::Ptr model) : JournalBooks::JournalBooks (JournalViewModel::Ptr model) :

View file

@ -6,6 +6,8 @@
namespace MWGui namespace MWGui
{ {
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text);
struct JournalBooks struct JournalBooks
{ {
typedef TypesetBook::Ptr Book; typedef TypesetBook::Ptr Book;

View file

@ -1,5 +1,7 @@
#include "mainmenu.hpp" #include "mainmenu.hpp"
#include <components/version/version.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -20,6 +22,22 @@ namespace MWGui
, mButtonBox(0), mWidth (w), mHeight (h) , mButtonBox(0), mWidth (w), mHeight (h)
, mSaveGameDialog(NULL) , mSaveGameDialog(NULL)
{ {
getWidget(mVersionText, "VersionText");
std::stringstream sstream;
sstream << "OpenMW version: " << OPENMW_VERSION;
// adding info about git hash if availible
std::string rev = OPENMW_VERSION_COMMITHASH;
std::string tag = OPENMW_VERSION_TAGHASH;
if (!rev.empty() && !tag.empty())
{
rev = rev.substr(0,10);
sstream << "\nrevision: " << rev;
}
std::string output = sstream.str();
mVersionText->setCaption(output);
updateMenu(); updateMenu();
} }

View file

@ -1,3 +1,6 @@
#ifndef OPENMW_GAME_MWGUI_MAINMENU_H
#define OPENMW_GAME_MWGUI_MAINMENU_H
#include <openengine/gui/layout.hpp> #include <openengine/gui/layout.hpp>
#include "imagebutton.hpp" #include "imagebutton.hpp"
@ -24,6 +27,7 @@ namespace MWGui
private: private:
MyGUI::Widget* mButtonBox; MyGUI::Widget* mButtonBox;
MyGUI::TextBox* mVersionText;
std::map<std::string, MWGui::ImageButton*> mButtons; std::map<std::string, MWGui::ImageButton*> mButtons;
@ -35,3 +39,5 @@ namespace MWGui
}; };
} }
#endif

View file

@ -19,28 +19,8 @@
#include "windowmanagerimp.hpp" #include "windowmanagerimp.hpp"
#include "itemselection.hpp" #include "itemselection.hpp"
#include "spellwindow.hpp"
namespace
{
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
{
int cmp = left.getClass().getName(left).compare(
right.getClass().getName(right));
return cmp < 0;
}
bool sortSpells(const std::string& left, const std::string& right)
{
const MWWorld::Store<ESM::Spell> &spells =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
const ESM::Spell* a = spells.find(left);
const ESM::Spell* b = spells.find(right);
int cmp = a->mName.compare(b->mName);
return cmp < 0;
}
}
namespace MWGui namespace MWGui
{ {

View file

@ -41,9 +41,13 @@ namespace MWGui
getWidget(mPreviewImage, "PreviewImage"); getWidget(mPreviewImage, "PreviewImage");
getWidget(mHeadRotate, "HeadRotate"); getWidget(mHeadRotate, "HeadRotate");
mHeadRotate->setScrollRange(50);
mHeadRotate->setScrollPosition(25); // Mouse wheel step is hardcoded to 50 in MyGUI 3.2 ("FIXME").
mHeadRotate->setScrollViewPage(10); // Give other steps the same value to accomodate.
mHeadRotate->setScrollRange(1000);
mHeadRotate->setScrollPosition(500);
mHeadRotate->setScrollViewPage(50);
mHeadRotate->setScrollPage(50);
mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
// Set up next/previous buttons // Set up next/previous buttons
@ -171,9 +175,9 @@ namespace MWGui
eventBack(); eventBack();
} }
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position)
{ {
float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2;
float diff = angle - mCurrentAngle; float diff = angle - mCurrentAngle;
mPreview->update (diff); mPreview->update (diff);
mPreviewDirty = true; mPreviewDirty = true;

View file

@ -18,8 +18,16 @@
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "confirmationdialog.hpp" #include "confirmationdialog.hpp"
namespace namespace MWGui
{ {
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
{
int cmp = left.getClass().getName(left).compare(
right.getClass().getName(right));
return cmp < 0;
}
bool sortSpells(const std::string& left, const std::string& right) bool sortSpells(const std::string& left, const std::string& right)
{ {
const MWWorld::Store<ESM::Spell> &spells = const MWWorld::Store<ESM::Spell> &spells =
@ -32,16 +40,6 @@ namespace
return cmp < 0; return cmp < 0;
} }
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
{
int cmp = MWWorld::Class::get(left).getName(left).compare(
MWWorld::Class::get(right).getName(right));
return cmp < 0;
}
}
namespace MWGui
{
SpellWindow::SpellWindow(DragAndDrop* drag) SpellWindow::SpellWindow(DragAndDrop* drag)
: WindowPinnableBase("openmw_spell_window.layout") : WindowPinnableBase("openmw_spell_window.layout")
, NoDrop(drag, mMainWidget) , NoDrop(drag, mMainWidget)

View file

@ -2,11 +2,16 @@
#define MWGUI_SPELLWINDOW_H #define MWGUI_SPELLWINDOW_H
#include "windowpinnablebase.hpp" #include "windowpinnablebase.hpp"
#include "../mwworld/ptr.hpp"
namespace MWGui namespace MWGui
{ {
class SpellIcons; class SpellIcons;
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right);
bool sortSpells(const std::string& left, const std::string& right);
class SpellWindow : public WindowPinnableBase, public NoDrop class SpellWindow : public WindowPinnableBase, public NoDrop
{ {
public: public:

View file

@ -143,6 +143,13 @@ namespace MWInput
mControlSwitch["vanitymode"] = true; mControlSwitch["vanitymode"] = true;
} }
void InputManager::clear()
{
// Enable all controls
for (std::map<std::string, bool>::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it)
it->second = true;
}
InputManager::~InputManager() InputManager::~InputManager()
{ {
mInputBinder->save (mUserFile); mInputBinder->save (mUserFile);

View file

@ -65,6 +65,9 @@ namespace MWInput
virtual ~InputManager(); virtual ~InputManager();
/// Clear all savegame-specific data
virtual void clear();
virtual void update(float dt, bool loading); virtual void update(float dt, bool loading);
void setPlayer (MWWorld::Player* player) { mPlayer = player; } void setPlayer (MWWorld::Player* player) { mPlayer = player; }

View file

@ -10,16 +10,6 @@
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
namespace
{
float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
}
MWMechanics::AiActivate::AiActivate(const std::string &objectId) MWMechanics::AiActivate::AiActivate(const std::string &objectId)
: mObjectId(objectId) : mObjectId(objectId)
{ {
@ -38,7 +28,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX) if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{ {
int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking. //check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
@ -49,7 +39,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
} }
if(cell->mData.mY != player.getCell()->getCell()->mData.mY) if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{ {
int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking. //check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))

View file

@ -11,16 +11,6 @@
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
namespace
{
float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
}
/* /*
TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0.
TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z.
@ -91,7 +81,7 @@ namespace MWMechanics
if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX) if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX)
{ {
int sideX = sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX); int sideX = PathFinder::sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX);
// Check if actor is near the border of an inactive cell. If so, pause walking. // Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
2.0 - 200)) 2.0 - 200))
@ -102,7 +92,7 @@ namespace MWMechanics
} }
if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY) if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY)
{ {
int sideY = sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY); int sideY = PathFinder::sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY);
// Check if actor is near the border of an inactive cell. If so, pause walking. // Check if actor is near the border of an inactive cell. If so, pause walking.
if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
2.0 - 200)) 2.0 - 200))

View file

@ -9,16 +9,6 @@
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
namespace
{
float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
}
namespace MWMechanics namespace MWMechanics
{ {
AiTravel::AiTravel(float x, float y, float z) AiTravel::AiTravel(float x, float y, float z)
@ -43,7 +33,7 @@ namespace MWMechanics
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->getCell()->mData.mX) if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
{ {
int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking. //check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
@ -54,7 +44,7 @@ namespace MWMechanics
} }
if(cell->mData.mY != player.getCell()->getCell()->mData.mY) if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
{ {
int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking. //check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))

View file

@ -15,18 +15,13 @@
#include "steering.hpp" #include "steering.hpp"
#include "movement.hpp" #include "movement.hpp"
namespace
{
float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
}
namespace MWMechanics namespace MWMechanics
{ {
// NOTE: determined empirically but probably need further tweaking
static const int COUNT_BEFORE_STUCK = 20;
static const int COUNT_BEFORE_RESET = 200;
static const int COUNT_EVADE = 7;
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat): AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
, mCellX(std::numeric_limits<int>::max()) , mCellX(std::numeric_limits<int>::max())
@ -36,6 +31,11 @@ namespace MWMechanics
, mX(0) , mX(0)
, mY(0) , mY(0)
, mZ(0) , mZ(0)
, mPrevX(0)
, mPrevY(0)
, mWalkState(State_Norm)
, mStuckCount(0)
, mEvadeCount(0)
, mSaidGreeting(false) , mSaidGreeting(false)
{ {
for(unsigned short counter = 0; counter < mIdle.size(); counter++) for(unsigned short counter = 0; counter < mIdle.size(); counter++)
@ -298,9 +298,91 @@ namespace MWMechanics
} }
else else
{ {
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); /* 1 n
* State_Norm <---> State_CheckStuck --> State_Evade
* ^ ^ | ^ | ^ | |
* | | | | | | | |
* | +---+ +---+ +---+ | m
* | any < n < m |
* +--------------------------------------------+
*/
bool samePosition = (abs(pos.pos[0] - mPrevX) < 1) && (abs(pos.pos[1] - mPrevY) < 1);
switch(mWalkState)
{
case State_Norm:
{
if(!samePosition)
break;
else
mWalkState = State_CheckStuck;
}
/* FALL THROUGH */
case State_CheckStuck:
{
if(!samePosition)
{
mWalkState = State_Norm;
// to do this properly need yet another variable, simply don't clear for now
//mStuckCount = 0;
break;
}
else
{
// consider stuck only if position unchanges consecutively
if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0)
mWalkState = State_Evade;
// NOTE: mStuckCount is purposely not cleared here
else
break; // still in the same state, but counter got incremented
}
}
/* FALL THROUGH */
case State_Evade:
{
if(mEvadeCount++ < COUNT_EVADE)
break;
else
{
mWalkState = State_Norm; // tried to evade, assume all is ok and start again
// NOTE: mStuckCount is purposely not cleared here
mEvadeCount = 0;
}
}
/* NO DEFAULT CASE */
}
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; if(mWalkState == State_Evade)
{
//std::cout << "Stuck \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
// diagonal should have same animation as walk forward
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.01f;
// change the angle a bit, too
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
}
else
{
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
}
if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
{
//std::cout << "Reset \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
mWalkState = State_Norm;
mStuckCount = 0;
stopWalking(actor);
mMoveNow = false;
mWalking = false;
mChooseAction = true;
}
// update position
ESM::Position updatedPos = actor.getRefData().getPosition();
mPrevX = updatedPos.pos[0];
mPrevY = updatedPos.pos[1];
} }
} }

View file

@ -43,6 +43,21 @@ namespace MWMechanics
float mXCell; float mXCell;
float mYCell; float mYCell;
// for checking if we're stuck (but don't check Z axis)
float mPrevX;
float mPrevY;
enum WalkState
{
State_Norm,
State_CheckStuck,
State_Evade
};
WalkState mWalkState;
int mStuckCount;
int mEvadeCount;
bool mStoredAvailableNodes; bool mStoredAvailableNodes;
bool mChooseAction; bool mChooseAction;
bool mIdleNow; bool mIdleNow;

View file

@ -37,13 +37,6 @@ namespace
return sqrt(x * x + y * y + z * z); return sqrt(x * x + y * y + z * z);
} }
static float sgn(Ogre::Radian a)
{
if(a.valueRadians() > 0)
return 1.0;
return -1.0;
}
int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z)
{ {
if(!grid || grid->mPoints.empty()) if(!grid || grid->mPoints.empty())

View file

@ -4,6 +4,8 @@
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
#include <list> #include <list>
#include <OgreMath.h>
namespace MWWorld namespace MWWorld
{ {
class CellStore; class CellStore;
@ -16,6 +18,20 @@ namespace MWMechanics
public: public:
PathFinder(); PathFinder();
static float sgn(Ogre::Radian a)
{
if(a.valueRadians() > 0)
return 1.0;
return -1.0;
}
static float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
void clearPath(); void clearPath();
void buildPathgridGraph(const ESM::Pathgrid* pathGrid); void buildPathgridGraph(const ESM::Pathgrid* pathGrid);

View file

@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
updateParts(); updateParts();
} }
mWeaponAnimationTime = Ogre::SharedPtr<WeaponAnimationTime>(new WeaponAnimationTime(this));
} }
void CreatureWeaponAnimation::showWeapons(bool showWeapon) void CreatureWeaponAnimation::showWeapons(bool showWeapon)
@ -110,6 +112,20 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0,
!item.getClass().getEnchantment(item).empty(), &glowColor); !item.getClass().getEnchantment(item).empty(), &glowColor);
// Crossbows start out with a bolt attached
if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
item.getTypeName() == typeid(ESM::Weapon).name() &&
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
attachArrow();
else
mAmmunition.setNull();
}
else
mAmmunition.setNull();
if(scene->mSkelBase) if(scene->mSkelBase)
{ {
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton(); Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
updateSkeletonInstance(mSkelBase->getSkeleton(), skel); updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
} }
// TODO:
// type == ESM::PRT_Weapon should get an animation source based on the current offset
// of the weapon attack animation (from its beginning, or start marker?)
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin()); std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin());
for(;ctrl != scene->mControllers.end();ctrl++) for(;ctrl != scene->mControllers.end();ctrl++)
{ {
if(ctrl->getSource().isNull()) if(ctrl->getSource().isNull())
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime())); {
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
ctrl->setSource(mWeaponAnimationTime);
else
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
}
} }
} }
void CreatureWeaponAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
{
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
setRenderProperties(object, RV_Actors, RQG_Main, RQG_Alpha, 0,
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
}
void CreatureWeaponAnimation::attachArrow()
{
WeaponAnimation::attachArrow(mPtr);
}
void CreatureWeaponAnimation::releaseArrow()
{
WeaponAnimation::releaseArrow(mPtr);
}
Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration)
{
Ogre::Vector3 ret = Animation::runAnimation(duration);
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton());
return ret;
}
} }

View file

@ -2,6 +2,7 @@
#define GAME_RENDER_CREATUREANIMATION_H #define GAME_RENDER_CREATUREANIMATION_H
#include "animation.hpp" #include "animation.hpp"
#include "weaponanimation.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
namespace MWWorld namespace MWWorld
@ -21,7 +22,7 @@ namespace MWRender
// For creatures with weapons and shields // For creatures with weapons and shields
// Animation is already virtual anyway, so might as well make a separate class. // Animation is already virtual anyway, so might as well make a separate class.
// Most creatures don't need weapons/shields, so this will save some memory. // Most creatures don't need weapons/shields, so this will save some memory.
class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
{ {
public: public:
CreatureWeaponAnimation(const MWWorld::Ptr& ptr); CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
@ -36,11 +37,29 @@ namespace MWRender
void updatePart(NifOgre::ObjectScenePtr& scene, int slot); void updatePart(NifOgre::ObjectScenePtr& scene, int slot);
virtual void attachArrow();
virtual void releaseArrow();
virtual Ogre::Vector3 runAnimation(float duration);
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
/// to indicate the facing orientation of the character.
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
// WeaponAnimation
virtual NifOgre::ObjectScenePtr getWeapon() { return mWeapon; }
virtual void showWeapon(bool show) { showWeapons(show); }
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
private: private:
NifOgre::ObjectScenePtr mWeapon; NifOgre::ObjectScenePtr mWeapon;
NifOgre::ObjectScenePtr mShield; NifOgre::ObjectScenePtr mShield;
bool mShowWeapons; bool mShowWeapons;
bool mShowCarriedLeft; bool mShowCarriedLeft;
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
}; };
} }

View file

@ -76,27 +76,6 @@ float HeadAnimationTime::getValue() const
return 1; return 1;
} }
float WeaponAnimationTime::getValue() const
{
if (mWeaponGroup.empty())
return 0;
float current = mAnimation->getCurrentTime(mWeaponGroup);
if (current == -1)
return 0;
return current - mStartTime;
}
void WeaponAnimationTime::setGroup(const std::string &group)
{
mWeaponGroup = group;
mStartTime = mAnimation->getStartTime(mWeaponGroup);
}
void WeaponAnimationTime::updateStartTime()
{
setGroup(mWeaponGroup);
}
static NpcAnimation::PartBoneMap createPartListMap() static NpcAnimation::PartBoneMap createPartListMap()
{ {
NpcAnimation::PartBoneMap result; NpcAnimation::PartBoneMap result;
@ -147,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
mShowCarriedLeft(true), mShowCarriedLeft(true),
mFirstPersonOffset(0.f, 0.f, 0.f), mFirstPersonOffset(0.f, 0.f, 0.f),
mAlpha(1.f), mAlpha(1.f),
mNpcType(Type_Normal), mNpcType(Type_Normal)
mPitchFactor(0)
{ {
mNpc = mPtr.get<ESM::NPC>()->mBase; mNpc = mPtr.get<ESM::NPC>()->mBase;
@ -538,14 +516,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
// updateSkeletonInstance, below, touches the hands. // updateSkeletonInstance, below, touches the hands.
node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
} }
else if (mPitchFactor > 0) else
{ {
// In third person mode we may still need pitch for ranged weapon targeting // In third person mode we may still need pitch for ranged weapon targeting
float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
Ogre::Node *node = baseinst->getBone("Bip01 Spine2");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
node = baseinst->getBone("Bip01 Spine1");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
} }
mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
@ -695,13 +669,14 @@ void NpcAnimation::showWeapons(bool showWeapon)
{ {
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon != inv.end()) // special case for weapons if(weapon != inv.end())
{ {
Ogre::Vector3 glowColor = getEnchantmentColor(*weapon); Ogre::Vector3 glowColor = getEnchantmentColor(*weapon);
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
// Crossbows start out with a bolt attached
if (weapon->getTypeName() == typeid(ESM::Weapon).name() && if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{ {
@ -743,113 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show)
removeIndividualPart(ESM::PRT_Shield); removeIndividualPart(ESM::PRT_Shield);
} }
void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
{
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
std::for_each(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot));
std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot));
}
void NpcAnimation::attachArrow() void NpcAnimation::attachArrow()
{ {
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); WeaponAnimation::attachArrow(mPtr);
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
showWeapons(true);
else
{
NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon];
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
std::string model = ammo->getClass().getModel(*ammo);
mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model);
Ogre::Vector3 glowColor = getEnchantmentColor(*ammo);
setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
!ammo->getClass().getEnchantment(*ammo).empty(), &glowColor);
std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
}
} }
void NpcAnimation::releaseArrow() void NpcAnimation::releaseArrow()
{ {
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); WeaponAnimation::releaseArrow(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weapon == inv.end())
return;
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::CreatureStats& attackerStats = mPtr.getClass().getCreatureStats(mPtr);
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
const float normalizedEncumbrance = mPtr.getClass().getEncumbrance(mPtr) / mPtr.getClass().getCapacity(mPtr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon->isEmpty())
fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
attackerStats.setFatigue(fatigue);
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
{
// Thrown weapons get detached now
NifOgre::ObjectScenePtr objects = mObjectParts[ESM::PRT_Weapon];
Ogre::Vector3 launchPos(0,0,0);
if (objects->mSkelBase)
{
launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition();
}
else if (objects->mEntities.size())
{
objects->mEntities[0]->getParentNode()->needUpdate(true);
launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition();
}
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) *
mPtr.getClass().getCreatureStats(mPtr).getAttackStrength();
MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, speed);
showWeapons(false);
inv.remove(*weapon, 1, mPtr);
}
else
{
// With bows and crossbows only the used arrow/bolt gets detached
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
Ogre::Vector3 launchPos(0,0,0);
if (mAmmunition->mSkelBase)
{
launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition();
}
else if (mAmmunition->mEntities.size())
{
mAmmunition->mEntities[0]->getParentNode()->needUpdate(true);
launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition();
}
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * mPtr.getClass().getCreatureStats(mPtr).getAttackStrength();
MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, speed);
inv.remove(*ammo, 1, mPtr);
mAmmunition.setNull();
}
} }
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)

View file

@ -5,6 +5,8 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "weaponanimation.hpp"
namespace ESM namespace ESM
{ {
struct NPC; struct NPC;
@ -25,24 +27,7 @@ public:
{ } { }
}; };
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real> class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
{
private:
Animation* mAnimation;
std::string mWeaponGroup;
float mStartTime;
public:
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
void setGroup(const std::string& group);
void updateStartTime();
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value)
{ }
};
class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener
{ {
public: public:
virtual void equipmentChanged() { updateParts(); } virtual void equipmentChanged() { updateParts(); }
@ -91,7 +76,6 @@ private:
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime; Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
float mAlpha; float mAlpha;
float mPitchFactor;
void updateNpcBase(); void updateNpcBase();
@ -138,7 +122,10 @@ public:
virtual void attachArrow(); virtual void attachArrow();
virtual void releaseArrow(); virtual void releaseArrow();
NifOgre::ObjectScenePtr mAmmunition; // WeaponAnimation
virtual NifOgre::ObjectScenePtr getWeapon() { return mObjectParts[ESM::PRT_Weapon]; }
virtual void showWeapon(bool show) { showWeapons(show); }
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
void setViewMode(ViewMode viewMode); void setViewMode(ViewMode viewMode);

View file

@ -1073,7 +1073,7 @@ float RenderingManager::getCameraDistance() const
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale)
{ {
mEffectManager->addEffect(model, "", worldPosition, scale); mEffectManager->addEffect(model, texture, worldPosition, scale);
} }
} // namespace } // namespace

View file

@ -0,0 +1,159 @@
#include "weaponanimation.hpp"
#include <OgreEntity.h>
#include <OgreBone.h>
#include <OgreSceneNode.h>
#include <OgreSkeletonInstance.h>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "animation.hpp"
namespace MWRender
{
float WeaponAnimationTime::getValue() const
{
if (mWeaponGroup.empty())
return 0;
float current = mAnimation->getCurrentTime(mWeaponGroup);
if (current == -1)
return 0;
return current - mStartTime;
}
void WeaponAnimationTime::setGroup(const std::string &group)
{
mWeaponGroup = group;
mStartTime = mAnimation->getStartTime(mWeaponGroup);
}
void WeaponAnimationTime::updateStartTime()
{
setGroup(mWeaponGroup);
}
void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
{
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
showWeapon(true);
else
{
NifOgre::ObjectScenePtr weapon = getWeapon();
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
std::string model = ammo->getClass().getModel(*ammo);
mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", weapon->mSkelBase->getParentSceneNode(), model);
configureAddedObject(mAmmunition, *ammo, MWWorld::InventoryStore::Slot_Ammunition);
}
}
void WeaponAnimation::releaseArrow(MWWorld::Ptr actor)
{
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weapon == inv.end())
return;
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor);
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon->isEmpty())
fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
attackerStats.setFatigue(fatigue);
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
{
// Thrown weapons get detached now
NifOgre::ObjectScenePtr objects = getWeapon();
Ogre::Vector3 launchPos(0,0,0);
if (objects->mSkelBase)
{
launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition();
}
else if (objects->mEntities.size())
{
objects->mEntities[0]->getParentNode()->needUpdate(true);
launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition();
}
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) *
actor.getClass().getCreatureStats(actor).getAttackStrength();
MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed);
showWeapon(false);
inv.remove(*weapon, 1, actor);
}
else
{
// With bows and crossbows only the used arrow/bolt gets detached
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo == inv.end())
return;
Ogre::Vector3 launchPos(0,0,0);
if (mAmmunition->mSkelBase)
{
launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition();
}
else if (mAmmunition->mEntities.size())
{
mAmmunition->mEntities[0]->getParentNode()->needUpdate(true);
launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition();
}
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * actor.getClass().getCreatureStats(actor).getAttackStrength();
MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed);
inv.remove(*ammo, 1, actor);
mAmmunition.setNull();
}
}
void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel)
{
if (mPitchFactor == 0)
return;
float pitch = xrot * mPitchFactor;
Ogre::Node *node = skel->getBone("Bip01 Spine2");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
node = skel->getBone("Bip01 Spine1");
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
}
}

View file

@ -0,0 +1,56 @@
#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H
#define OPENMW_MWRENDER_WEAPONANIMATION_H
#include <OgreController.h>
#include <components/nifogre/ogrenifloader.hpp>
#include "../mwworld/ptr.hpp"
namespace MWRender
{
class Animation;
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
{
private:
Animation* mAnimation;
std::string mWeaponGroup;
float mStartTime;
public:
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
void setGroup(const std::string& group);
void updateStartTime();
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value)
{ }
};
/// Handles attach & release of projectiles for ranged weapons
class WeaponAnimation
{
public:
WeaponAnimation() : mPitchFactor(0) {}
virtual void attachArrow(MWWorld::Ptr actor);
virtual void releaseArrow(MWWorld::Ptr actor);
protected:
NifOgre::ObjectScenePtr mAmmunition;
virtual NifOgre::ObjectScenePtr getWeapon() = 0;
virtual void showWeapon(bool show) = 0;
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) = 0;
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
/// to indicate the facing orientation of the character, for ranged weapon aiming.
float mPitchFactor;
void pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel);
};
}
#endif

View file

@ -121,7 +121,7 @@ namespace MWScript
std::string itemName; std::string itemName;
for (MWWorld::ContainerStoreIterator iter(store.begin()); iter != store.end(); ++iter) for (MWWorld::ContainerStoreIterator iter(store.begin()); iter != store.end(); ++iter)
if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item))
itemName = iter->getClass().getName(*iter); itemName = iter->getClass().getName(*iter);
int numRemoved = store.remove(item, count, ptr); int numRemoved = store.remove(item, count, ptr);
@ -165,7 +165,7 @@ namespace MWScript
MWWorld::ContainerStoreIterator it = invStore.begin(); MWWorld::ContainerStoreIterator it = invStore.begin();
for (; it != invStore.end(); ++it) for (; it != invStore.end(); ++it)
{ {
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) if (::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
break; break;
} }
if (it == invStore.end()) if (it == invStore.end())
@ -268,7 +268,7 @@ namespace MWScript
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{ {
MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); MWWorld::ContainerStoreIterator it = invStore.getSlot (slot);
if (it != invStore.end() && Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
{ {
runtime.push(1); runtime.push(1);
return; return;
@ -295,7 +295,7 @@ namespace MWScript
it != invStore.end(); ++it) it != invStore.end(); ++it)
{ {
if (Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name)) if (::Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name))
{ {
runtime.push(1); runtime.push(1);
return; return;

View file

@ -24,7 +24,7 @@ namespace MWScript
void GlobalScripts::addScript (const std::string& name) void GlobalScripts::addScript (const std::string& name)
{ {
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (Misc::StringUtils::lowerCase (name)); mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end()) if (iter==mScripts.end())
{ {
@ -44,7 +44,7 @@ namespace MWScript
void GlobalScripts::removeScript (const std::string& name) void GlobalScripts::removeScript (const std::string& name)
{ {
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (Misc::StringUtils::lowerCase (name)); mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter!=mScripts.end()) if (iter!=mScripts.end())
iter->second.first = false; iter->second.first = false;
@ -53,7 +53,7 @@ namespace MWScript
bool GlobalScripts::isRunning (const std::string& name) const bool GlobalScripts::isRunning (const std::string& name) const
{ {
std::map<std::string, std::pair<bool, Locals> >::const_iterator iter = std::map<std::string, std::pair<bool, Locals> >::const_iterator iter =
mScripts.find (Misc::StringUtils::lowerCase (name)); mScripts.find (::Misc::StringUtils::lowerCase (name));
if (iter==mScripts.end()) if (iter==mScripts.end())
return false; return false;
@ -151,7 +151,7 @@ namespace MWScript
Locals& GlobalScripts::getLocals (const std::string& name) Locals& GlobalScripts::getLocals (const std::string& name)
{ {
std::string name2 = Misc::StringUtils::lowerCase (name); std::string name2 = ::Misc::StringUtils::lowerCase (name);
std::map<std::string, std::pair<bool, Locals> >::iterator iter = std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (name2); mScripts.find (name2);

View file

@ -113,7 +113,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); std::string cell = (runtime.getStringLiteral (runtime[0].mInteger));
Misc::StringUtils::toLower(cell); ::Misc::StringUtils::toLower(cell);
runtime.pop(); runtime.pop();
// "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well."
@ -126,7 +126,7 @@ namespace MWScript
for (; it != cells.extEnd(); ++it) for (; it != cells.extEnd(); ++it)
{ {
std::string name = it->mName; std::string name = it->mName;
Misc::StringUtils::toLower(name); ::Misc::StringUtils::toLower(name);
if (name.find(cell) != std::string::npos) if (name.find(cell) != std::string::npos)
MWBase::Environment::get().getWindowManager()->addVisitedLocation ( MWBase::Environment::get().getWindowManager()->addVisitedLocation (
it->mName, it->mName,

View file

@ -540,7 +540,7 @@ namespace MWScript
factionID = runtime.getStringLiteral (runtime[0].mInteger); factionID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
} }
Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -569,7 +569,7 @@ namespace MWScript
factionID = runtime.getStringLiteral (runtime[0].mInteger); factionID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
} }
Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -602,7 +602,7 @@ namespace MWScript
factionID = runtime.getStringLiteral (runtime[0].mInteger); factionID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
} }
Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -640,7 +640,7 @@ namespace MWScript
factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first;
} }
} }
Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="") if(factionID!="")
{ {
@ -742,7 +742,7 @@ namespace MWScript
if (factionId.empty()) if (factionId.empty())
throw std::runtime_error ("failed to determine faction"); throw std::runtime_error ("failed to determine faction");
Misc::StringUtils::toLower (factionId); ::Misc::StringUtils::toLower (factionId);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
runtime.push ( runtime.push (
@ -778,7 +778,7 @@ namespace MWScript
if (factionId.empty()) if (factionId.empty())
throw std::runtime_error ("failed to determine faction"); throw std::runtime_error ("failed to determine faction");
Misc::StringUtils::toLower (factionId); ::Misc::StringUtils::toLower (factionId);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value);
@ -813,7 +813,7 @@ namespace MWScript
if (factionId.empty()) if (factionId.empty())
throw std::runtime_error ("failed to determine faction"); throw std::runtime_error ("failed to determine faction");
Misc::StringUtils::toLower (factionId); ::Misc::StringUtils::toLower (factionId);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId,
@ -858,11 +858,11 @@ namespace MWScript
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
std::string race = runtime.getStringLiteral(runtime[0].mInteger); std::string race = runtime.getStringLiteral(runtime[0].mInteger);
Misc::StringUtils::toLower(race); ::Misc::StringUtils::toLower(race);
runtime.pop(); runtime.pop();
std::string npcRace = ptr.get<ESM::NPC>()->mBase->mRace; std::string npcRace = ptr.get<ESM::NPC>()->mBase->mRace;
Misc::StringUtils::toLower(npcRace); ::Misc::StringUtils::toLower(npcRace);
runtime.push (npcRace == race); runtime.push (npcRace == race);
} }
@ -906,7 +906,7 @@ namespace MWScript
factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first;
} }
} }
Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="") if(factionID!="")
{ {

View file

@ -99,7 +99,7 @@ namespace MWScript
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
} }
else else
throw std::runtime_error ("invalid ration axis: " + axis); throw std::runtime_error ("invalid rotation axis: " + axis);
} }
}; };
@ -128,7 +128,7 @@ namespace MWScript
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()); runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees());
} }
else else
throw std::runtime_error ("invalid ration axis: " + axis); throw std::runtime_error ("invalid rotation axis: " + axis);
} }
}; };
@ -157,7 +157,7 @@ namespace MWScript
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees());
} }
else else
throw std::runtime_error ("invalid ration axis: " + axis); throw std::runtime_error ("invalid rotation axis: " + axis);
} }
}; };
@ -186,7 +186,7 @@ namespace MWScript
runtime.push(ptr.getRefData().getPosition().pos[2]); runtime.push(ptr.getRefData().getPosition().pos[2]);
} }
else else
throw std::runtime_error ("invalid rotation axis: " + axis); throw std::runtime_error ("invalid axis: " + axis);
} }
}; };

View file

@ -11,9 +11,10 @@
namespace MWSound namespace MWSound
{ {
static void fail(const std::string &msg) void FFmpeg_Decoder::fail(const std::string &msg)
{ throw std::runtime_error("FFmpeg exception: "+msg); } {
throw std::runtime_error("FFmpeg exception: "+msg);
}
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{ {

View file

@ -61,6 +61,8 @@ namespace MWSound
virtual void rewind(); virtual void rewind();
virtual size_t getSampleOffset(); virtual size_t getSampleOffset();
void fail(const std::string &msg);
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs);

View file

@ -20,6 +20,7 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/scriptmanager.hpp" #include "../mwbase/scriptmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/inputmanager.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -39,6 +40,7 @@ void MWState::StateManager::cleanup (bool force)
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getWorld()->clear();
MWBase::Environment::get().getWindowManager()->clear(); MWBase::Environment::get().getWindowManager()->clear();
MWBase::Environment::get().getInputManager()->clear();
mState = State_NoGame; mState = State_NoGame;
mCharacterManager.clearCurrentCharacter(); mCharacterManager.clearCurrentCharacter();
@ -123,11 +125,10 @@ void MWState::StateManager::newGame (bool bypass)
{ {
cleanup(); cleanup();
MWBase::Environment::get().getWorld()->startNewGame (bypass);
if (!bypass) if (!bypass)
{
MWBase::Environment::get().getWorld()->startNewGame();
MWBase::Environment::get().getWindowManager()->setNewGame (true); MWBase::Environment::get().getWindowManager()->setNewGame (true);
}
else else
MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1);

View file

@ -363,7 +363,22 @@ namespace MWWorld
return newPtr; return newPtr;
} }
bool Class::isFlying(const Ptr &ptr) const bool Class::isBipedal(const Ptr &ptr) const
{
return false;
}
bool Class::canFly(const Ptr &ptr) const
{
return false;
}
bool Class::canSwim(const Ptr &ptr) const
{
return false;
}
bool Class::canWalk(const Ptr &ptr) const
{ {
return false; return false;
} }

View file

@ -307,7 +307,10 @@ namespace MWWorld
return false; return false;
} }
virtual bool isFlying(const MWWorld::Ptr& ptr) const; virtual bool isBipedal(const MWWorld::Ptr& ptr) const;
virtual bool canFly(const MWWorld::Ptr& ptr) const;
virtual bool canSwim(const MWWorld::Ptr& ptr) const;
virtual bool canWalk(const MWWorld::Ptr& ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;

View file

@ -130,31 +130,52 @@ namespace MWWorld
position.z += halfExtents.z; position.z += halfExtents.z;
waterlevel -= halfExtents.z * 0.5; waterlevel -= halfExtents.z * 0.5;
/*
* A 3/4 submerged example
*
* +---+
* | |
* | | <- (original waterlevel)
* | |
* | | <- position <- waterlevel
* | |
* | |
* | |
* +---+ <- (original position)
*/
OEngine::Physic::ActorTracer tracer; OEngine::Physic::ActorTracer tracer;
bool wasOnGround = false; bool wasOnGround = false;
bool isOnGround = false; bool isOnGround = false;
Ogre::Vector3 inertia(0.0f); Ogre::Vector3 inertia(0.0f);
Ogre::Vector3 velocity; Ogre::Vector3 velocity;
if(position.z < waterlevel || isFlying)
bool canWalk = ptr.getClass().canWalk(ptr);
bool isBipedal = ptr.getClass().isBipedal(ptr);
bool isNpc = ptr.getClass().isNpc();
if(position.z < waterlevel || isFlying) // under water by 3/4 or can fly
{ {
// TODO: Shouldn't water have higher drag in calculating velocity?
velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)*
Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement;
} }
else else
{ {
velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement;
// not in water nor can fly, so need to deal with gravity
if(!physicActor->getOnGround()) if(!physicActor->getOnGround())
{ {
// If falling, add part of the incoming velocity with the current inertia // If falling, add part of the incoming velocity with the current inertia
velocity = velocity*time + physicActor->getInertialForce(); velocity = velocity * time + physicActor->getInertialForce();
} }
inertia = velocity; inertia = velocity; // REM velocity is for z axis only in this code block
if(!(movement.z > 0.0f)) if(!(movement.z > 0.0f))
{ {
wasOnGround = physicActor->getOnGround(); wasOnGround = physicActor->getOnGround();
tracer.doTrace(colobj, position, position-Ogre::Vector3(0,0,2), engine); // TODO: Find out if there is a significance with the value 2 used here
tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine);
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
isOnGround = true; isOnGround = true;
} }
@ -163,24 +184,38 @@ namespace MWWorld
if(isOnGround) if(isOnGround)
{ {
// if we're on the ground, don't try to fall // if we're on the ground, don't try to fall
velocity.z = std::max(0.0f, velocity.z); velocity.z = std::max(0.0f, velocity.z); // NOTE: two different velocity assignments above
} }
Ogre::Vector3 newPosition = position; Ogre::Vector3 newPosition = position;
/*
* A loop to find newPosition using tracer, if successful different from the starting position.
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
* The initial velocity was set earlier (see above).
*/
float remainingTime = time; float remainingTime = time;
for(int iterations = 0;iterations < sMaxIterations && remainingTime > 0.01f;++iterations) for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations)
{ {
Ogre::Vector3 nextpos = newPosition + velocity*remainingTime; Ogre::Vector3 nextpos = newPosition + velocity * remainingTime;
if(newPosition.z < waterlevel && !isFlying && // If not able to fly, walk or bipedal don't allow to move out of water
nextpos.z > waterlevel && newPosition.z <= waterlevel) // TODO: this if condition may not work for large creatures or situations
// where the creature gets above the waterline for some reason
if(newPosition.z < waterlevel && // started 3/4 under water
!isFlying && // can't fly
!canWalk && // can't walk
!isBipedal && // not bipedal (assume bipedals can walk)
!isNpc && // FIXME: shouldn't really need this
nextpos.z > waterlevel && // but about to go above water
newPosition.z <= waterlevel)
{ {
const Ogre::Vector3 down(0,0,-1); const Ogre::Vector3 down(0,0,-1);
Ogre::Real movelen = velocity.normalise(); Ogre::Real movelen = velocity.normalise();
Ogre::Vector3 reflectdir = velocity.reflect(down); Ogre::Vector3 reflectdir = velocity.reflect(down);
reflectdir.normalise(); reflectdir.normalise();
velocity = slide(reflectdir, down)*movelen; velocity = slide(reflectdir, down)*movelen;
continue; // NOTE: remainingTime is unchanged before the loop continues
continue; // velocity updated, calculate nextpos again
} }
// trace to where character would go if there were no obstructions // trace to where character would go if there were no obstructions
@ -189,13 +224,14 @@ namespace MWWorld
// check for obstructions // check for obstructions
if(tracer.mFraction >= 1.0f) if(tracer.mFraction >= 1.0f)
{ {
newPosition = tracer.mEndPos; newPosition = tracer.mEndPos; // ok to move, so set newPosition
remainingTime *= (1.0f-tracer.mFraction); remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it?
break; break;
} }
// We hit something. Try to step up onto it. // We hit something. Try to step up onto it.
if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) // NOTE: May need to stop slaughterfish step out of the water.
if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine))
isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity
else else
{ {
@ -214,7 +250,7 @@ namespace MWWorld
if(isOnGround || wasOnGround) if(isOnGround || wasOnGround)
{ {
tracer.doTrace(colobj, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+2.0f), engine); tracer.doTrace(colobj, newPosition, newPosition - Ogre::Vector3(0,0,sStepSize+2.0f), engine);
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
{ {
newPosition.z = tracer.mEndPos.z + 1.0f; newPosition.z = tracer.mEndPos.z + 1.0f;
@ -236,7 +272,7 @@ namespace MWWorld
} }
physicActor->setOnGround(isOnGround); physicActor->setOnGround(isOnGround);
newPosition.z -= halfExtents.z; newPosition.z -= halfExtents.z; // remove what was added at the beggining
return newPosition; return newPosition;
} }
}; };

View file

@ -121,13 +121,15 @@ namespace MWWorld
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::vector<std::string>& contentFiles, const std::vector<std::string>& contentFiles,
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride) ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
int activationDistanceOverride, const std::string& startCell)
: mPlayer (0), mLocalScripts (mStore), : mPlayer (0), mLocalScripts (mStore),
mSky (true), mCells (mStore, mEsm), mSky (true), mCells (mStore, mEsm),
mActivationDistanceOverride (mActivationDistanceOverride), mActivationDistanceOverride (activationDistanceOverride),
mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(true), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(true),
mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles), mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles),
mGoToJail(false) mGoToJail(false),
mStartCell (startCell)
{ {
mPhysics = new PhysicsSystem(renderer); mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine(); mPhysEngine = mPhysics->getEngine();
@ -168,7 +170,7 @@ namespace MWWorld
mWorldScene = new Scene(*mRendering, mPhysics); mWorldScene = new Scene(*mRendering, mPhysics);
} }
void World::startNewGame() void World::startNewGame (bool bypass)
{ {
mGoToJail = false; mGoToJail = false;
mLevitationEnabled = true; mLevitationEnabled = true;
@ -181,23 +183,44 @@ namespace MWWorld
MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getWindowManager()->updatePlayer();
// FIXME: this will add cell 0,0 as visible on the global map if (bypass && !mStartCell.empty())
ESM::Position pos; {
const int cellSize = 8192; ESM::Position pos;
pos.pos[0] = cellSize/2;
pos.pos[1] = cellSize/2;
pos.pos[2] = 0;
pos.rot[0] = 0;
pos.rot[1] = 0;
pos.rot[2] = 0;
mWorldScene->changeToExteriorCell(pos);
// FIXME: should be set to 1, but the sound manager won't pause newly started sounds if (findExteriorPosition (mStartCell, pos))
mPlayIntro = 2; {
changeToExteriorCell (pos);
}
else
{
findInteriorPosition (mStartCell, pos);
changeToInteriorCell (mStartCell, pos);
}
}
else
{
/// \todo if !bypass, do not add player location to global map for the duration of one
/// frame
ESM::Position pos;
const int cellSize = 8192;
pos.pos[0] = cellSize/2;
pos.pos[1] = cellSize/2;
pos.pos[2] = 0;
pos.rot[0] = 0;
pos.rot[1] = 0;
pos.rot[2] = 0;
mWorldScene->changeToExteriorCell(pos);
}
// set new game mark if (!bypass)
mGlobalVariables["chargenstate"].setInteger (1); {
mGlobalVariables["pcrace"].setInteger (3); // FIXME: should be set to 1, but the sound manager won't pause newly started sounds
mPlayIntro = 2;
// set new game mark
mGlobalVariables["chargenstate"].setInteger (1);
mGlobalVariables["pcrace"].setInteger (3);
}
// we don't want old weather to persist on a new game // we don't want old weather to persist on a new game
delete mWeatherManager; delete mWeatherManager;
@ -1626,7 +1649,7 @@ namespace MWWorld
if (ptr.getClass().getCreatureStats(ptr).isDead()) if (ptr.getClass().getCreatureStats(ptr).isDead())
return false; return false;
if (ptr.getClass().isFlying(ptr)) if (ptr.getClass().canFly(ptr))
return true; return true;
const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);

View file

@ -120,6 +120,9 @@ namespace MWWorld
std::map<MWWorld::Ptr, MagicBoltState> mMagicBolts; std::map<MWWorld::Ptr, MagicBoltState> mMagicBolts;
std::map<MWWorld::Ptr, ProjectileState> mProjectiles; std::map<MWWorld::Ptr, ProjectileState> mProjectiles;
std::string mStartCell;
void updateWeather(float duration); void updateWeather(float duration);
int getDaysPerMonth (int month) const; int getDaysPerMonth (int month) const;
@ -179,11 +182,13 @@ namespace MWWorld
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::vector<std::string>& contentFiles, const std::vector<std::string>& contentFiles,
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride); ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
int activationDistanceOverride, const std::string& startCell);
virtual ~World(); virtual ~World();
virtual void startNewGame(); virtual void startNewGame (bool bypass);
///< \param bypass Bypass regular game start.
virtual void clear(); virtual void clear();

View file

@ -1,26 +1,75 @@
function(enable_unity_build UB_SUFFIX SOURCE_VARIABLE_NAME)
set(files ${SOURCE_VARIABLE_NAME})
# Generate a unique filename for the unity build translation unit
set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${UB_SUFFIX}.cpp)
# Exclude all translation units from compilation
set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true)
# Open the ub file
FILE(WRITE ${unit_build_file} "// Unity Build generated by CMake\n")
# Add include statement for each translation unit
foreach(source_file ${files} )
FILE( APPEND ${unit_build_file} "#include <${source_file}>\n")
endforeach(source_file)
# Complement list of translation units with the name of ub
set(${SOURCE_VARIABLE_NAME} ${${SOURCE_VARIABLE_NAME}} ${unit_build_file} PARENT_SCOPE)
endfunction(enable_unity_build)
macro (add_openmw_dir dir) macro (add_openmw_dir dir)
set (files) set (files)
foreach (u ${ARGN}) set (cppfiles)
file (GLOB ALL "${dir}/${u}.[ch]pp") foreach (u ${ARGN})
foreach (f ${ALL})
list (APPEND files "${f}") # Add cpp and hpp to OPENMW_FILES
list (APPEND OPENMW_FILES "${f}") file (GLOB ALL "${dir}/${u}.[ch]pp")
endforeach (f) foreach (f ${ALL})
endforeach (u) list (APPEND files "${f}")
source_group ("apps\\openmw\\${dir}" FILES ${files}) list (APPEND OPENMW_FILES "${f}")
endforeach (f)
# Add cpp to unity build
file (GLOB ALL "${dir}/${u}.cpp")
foreach (f ${ALL})
list (APPEND cppfiles "${f}")
endforeach (f)
endforeach (u)
if (OPENMW_UNITY_BUILD)
enable_unity_build(${dir} "${cppfiles}")
list (APPEND OPENMW_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp)
endif()
source_group ("apps\\openmw\\${dir}" FILES ${files})
endmacro (add_openmw_dir) endmacro (add_openmw_dir)
macro (add_component_dir dir) macro (add_component_dir dir)
set (files) set (files)
foreach (u ${ARGN}) set (cppfiles)
file (GLOB ALL "${dir}/${u}.[ch]pp")
foreach (f ${ALL}) foreach (u ${ARGN})
list (APPEND files "${f}") file (GLOB ALL "${dir}/${u}.[ch]pp")
list (APPEND COMPONENT_FILES "${f}")
endforeach (f) foreach (f ${ALL})
endforeach (u) list (APPEND files "${f}")
source_group ("components\\${dir}" FILES ${files}) list (APPEND COMPONENT_FILES "${f}")
endforeach (f)
# Add cpp to unity build
file (GLOB ALL "${dir}/${u}.cpp")
foreach (f ${ALL})
list (APPEND cppfiles "${f}")
endforeach (f)
endforeach (u)
if (OPENMW_UNITY_BUILD)
enable_unity_build(${dir} "${cppfiles}")
list (APPEND COMPONENT_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp)
endif()
source_group ("components\\${dir}" FILES ${files})
endmacro (add_component_dir) endmacro (add_component_dir)
macro (add_component_qt_dir dir) macro (add_component_qt_dir dir)

View file

@ -111,7 +111,7 @@ void BSAFile::readHeader()
fail("Directory information larger than entire archive"); fail("Directory information larger than entire archive");
// Read the offset info into a temporary buffer // Read the offset info into a temporary buffer
vector<uint32_t> offsets(3*filenum); std::vector<uint32_t> offsets(3*filenum);
input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum); input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
// Read the string table // Read the string table

View file

@ -18,7 +18,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke
{ {
if (mState==State_Name) if (mState==State_Name)
{ {
std::string name2 = Misc::StringUtils::lowerCase (name); std::string name2 = ::Misc::StringUtils::lowerCase (name);
char type = mLocals.getType (name2); char type = mLocals.getType (name2);

View file

@ -50,7 +50,7 @@ namespace Interpreter
return literalBlock+offset; return literalBlock+offset;
} }
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context) void Runtime::configure (const Type_Code *code, int codeSize, Context& context)
{ {
clear(); clear();

View file

@ -282,9 +282,6 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
size_t wantedLod = 0; size_t wantedLod = 0;
float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); float cellWorldSize = mTerrain->getStorage()->getCellWorldSize();
if (!mTerrain->getDistantLandEnabled() && dist > cellWorldSize)
return true;
if (dist > cellWorldSize*64) if (dist > cellWorldSize*64)
wantedLod = 6; wantedLod = 6;
else if (dist > cellWorldSize*32) else if (dist > cellWorldSize*32)
@ -357,6 +354,7 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
if (!childrenLoaded) if (!childrenLoaded)
{ {
mChunk->setVisible(true);
// Make sure child scene nodes are detached until all children are loaded // Make sure child scene nodes are detached until all children are loaded
mSceneNode->removeAllChildren(); mSceneNode->removeAllChildren();
} }
@ -391,6 +389,8 @@ void QuadTreeNode::load(const LoadResponseData &data)
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data);
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
mChunk->setCastShadows(true); mChunk->setCastShadows(true);
if (!mTerrain->getDistantLandEnabled())
mChunk->setRenderingDistance(8192);
mSceneNode->attachObject(mChunk); mSceneNode->attachObject(mChunk);
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
@ -437,7 +437,7 @@ void QuadTreeNode::unload(bool recursive)
if (recursive && hasChildren()) if (recursive && hasChildren())
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mChildren[i]->unload(); mChildren[i]->unload(true);
} }
} }

View file

@ -82,12 +82,14 @@ Sandy Carter (bwrsandman) - Arch Linux
Public Relations and Translations: Public Relations and Translations:
Alex McKibben (WeirdSexy) - Podcaster
Artem Kotsynyak (greye) - Russian News Writer Artem Kotsynyak (greye) - Russian News Writer
Jim Clauwaert (Zedd) - Public Outreach
Julien Voisin (jvoisin/ap0) - French News Writer Julien Voisin (jvoisin/ap0) - French News Writer
Lukasz Gromanowski (lgro) - English News Writer
Mickey Lyle (raevol) - Release Manager Mickey Lyle (raevol) - Release Manager
Pithorn - Chinese News Writer Pithorn - Chinese News Writer
sir_herrbatka - English/Polish News Writer sir_herrbatka - Polish News Writer
Alex McKibben (WeirdSexy) - Podcaster
Website: Website:

View file

@ -2,5 +2,11 @@
<MyGUI type="Layout"> <MyGUI type="Layout">
<!-- The entire screen --> <!-- The entire screen -->
<Widget type="Widget" layer="Windows" position="0 0 300 300" name="_Main" /> <Widget type="Widget" layer="Windows" position="0 0 300 300" name="_Main" >
<Widget type="TextBox" skin="SandText" position="0 250 300 50" align="Bottom" name="VersionText">
<Property key="TextAlign" value="Center"/>
<Property key="TextShadow" value="true"/>
<Property key="TextShadowColour" value="0 0 0"/>
</Widget>
</Widget>
</MyGUI> </MyGUI>

BIN
files/opencs/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

BIN
files/opencs/edit-clone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

BIN
files/opencs/edit-undo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

BIN
files/opencs/go-next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

View file

@ -57,6 +57,13 @@
<file>static.png</file> <file>static.png</file>
<file>weapon.png</file> <file>weapon.png</file>
<file>multitype.png</file> <file>multitype.png</file>
<file>go-next.png</file>
<file>go-previous.png</file>
<file>edit-delete.png</file>
<file>edit-undo.png</file>
<file>edit-preview.png</file>
<file>edit-clone.png</file>
<file>add.png</file>
<file alias="startup/create-addon">raster/startup/big/create-addon.png</file> <file alias="startup/create-addon">raster/startup/big/create-addon.png</file>
<file alias="startup/create-game">raster/startup/big/new-game.png</file> <file alias="startup/create-game">raster/startup/big/new-game.png</file>
<file alias="startup/edit-content">raster/startup/big/edit-content.png</file> <file alias="startup/edit-content">raster/startup/big/edit-content.png</file>

View file

@ -8,7 +8,6 @@ License: GPL (see GPL3.txt for more information)
Website: http://www.openmw.org Website: http://www.openmw.org
Font Licenses: Font Licenses:
EBGaramond-Regular.ttf: OFL (see OFL.txt for more information)
DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information)