Update to upstream/master. Resolve merge conflicts in MWWorld::Store

This commit is contained in:
Stanislav Bas 2015-07-12 12:01:18 +03:00
commit 2ed182b144
72 changed files with 2329 additions and 1570 deletions

View file

@ -18,8 +18,8 @@ addons:
name: "OpenMW/openmw" name: "OpenMW/openmw"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de notification_email: scrawl@baseoftrash.de
build_command_prepend: "cmake ." build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE"
build_command: "make -j3" build_command: "make"
branch_pattern: coverity_scan branch_pattern: coverity_scan
matrix: matrix:
include: include:

View file

@ -10,9 +10,10 @@ fi
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
echo "yes" | sudo apt-add-repository ppa:openmw/openmw echo "yes" | sudo apt-add-repository ppa:openmw/openmw
echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -qq libgtest-dev google-mock sudo apt-get install -qq libgtest-dev google-mock
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev sudo apt-get install -qq libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-thread1.55-dev
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavresample-dev
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04 sudo apt-get install -qq cmake-data #workaround for broken osgqt cmake script in ubuntu 12.04

View file

@ -1,5 +1,7 @@
#include "convertplayer.hpp" #include "convertplayer.hpp"
#include <components/misc/stringops.hpp>
namespace ESSImport namespace ESSImport
{ {

View file

@ -166,7 +166,9 @@ namespace ESSImport
if (i >= file2.mRecords.size()) if (i >= file2.mRecords.size())
{ {
std::ios::fmtflags f(std::cout.flags());
std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl; std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl;
std::cout.flags(f);
return; return;
} }
@ -174,7 +176,9 @@ namespace ESSImport
if (rec.mName != rec2.mName) if (rec.mName != rec2.mName)
{ {
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl; std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
std::cout.flags(f);
return; // TODO: try to recover return; // TODO: try to recover
} }
@ -185,7 +189,9 @@ namespace ESSImport
if (j >= rec2.mSubrecords.size()) if (j >= rec2.mSubrecords.size())
{ {
std::ios::fmtflags f(std::cout.flags());
std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl; std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl;
std::cout.flags(f);
return; return;
} }
@ -193,8 +199,10 @@ namespace ESSImport
if (sub.mName != sub2.mName) if (sub.mName != sub2.mName)
{ {
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset
<< " (2) 0x" << sub2.mFileOffset << std::endl; << " (2) 0x" << sub2.mFileOffset << std::endl;
std::cout.flags(f);
break; // TODO: try to recover break; // TODO: try to recover
} }
@ -203,6 +211,8 @@ namespace ESSImport
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
continue; continue;
std::ios::fmtflags f(std::cout.flags());
std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset
<< " (2) 0x" << sub2.mFileOffset << std::endl; << " (2) 0x" << sub2.mFileOffset << std::endl;
@ -235,6 +245,7 @@ namespace ESSImport
std::cout << "\033[0m"; std::cout << "\033[0m";
} }
std::cout << std::endl; std::cout << std::endl;
std::cout.flags(f);
} }
} }
} }
@ -319,7 +330,11 @@ namespace ESSImport
else else
{ {
if (unknownRecords.insert(n.val).second) if (unknownRecords.insert(n.val).second)
{
std::ios::fmtflags f(std::cerr.flags());
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
std::cerr.flags(f);
}
esm.skipRecord(); esm.skipRecord();
} }

View file

@ -49,6 +49,10 @@ namespace ESSImport
std::map<std::string, ESM::NPC> mNpcs; std::map<std::string, ESM::NPC> mNpcs;
Context() Context()
: mDay(0)
, mMonth(0)
, mYear(0)
, mHour(0.f)
{ {
mPlayer.mAutoMove = 0; mPlayer.mAutoMove = 0;
ESM::CellId playerCellId; ESM::CellId playerCellId;

View file

@ -64,7 +64,7 @@ opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview cellcreator referenceablecreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar dialoguespinbox recordbuttonbar tableeditidaction
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world

View file

@ -92,7 +92,7 @@ namespace CSMWorld
{ ColumnId_Trainer, "Trainer" }, { ColumnId_Trainer, "Trainer" },
{ ColumnId_Spellmaking, "Spellmaking" }, { ColumnId_Spellmaking, "Spellmaking" },
{ ColumnId_EnchantingService, "Enchanting Service" }, { ColumnId_EnchantingService, "Enchanting Service" },
{ ColumnId_RepairService, "Repair Serivce" }, { ColumnId_RepairService, "Repair Service" },
{ ColumnId_ApparatusType, "Apparatus Type" }, { ColumnId_ApparatusType, "Apparatus Type" },
{ ColumnId_ArmorType, "Armor Type" }, { ColumnId_ArmorType, "Armor Type" },
{ ColumnId_Health, "Health" }, { ColumnId_Health, "Health" },

View file

@ -161,8 +161,19 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const
{ {
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getColumnsCount( const ColumnBase &nestedColumn = Collection<ESXRecordT, IdAccessorT>::getColumn(column);
Collection<ESXRecordT, IdAccessorT>::getRecord(row)); int numRecords = Collection<ESXRecordT, IdAccessorT>::getSize();
if (row >= 0 && row < numRecords)
{
const Record<ESXRecordT>& record = Collection<ESXRecordT, IdAccessorT>::getRecord(row);
return getAdapter(nestedColumn).getColumnsCount(record);
}
else
{
// If the row is invalid (or there no records), retrieve the column count using a blank record
const Record<ESXRecordT> record;
return getAdapter(nestedColumn).getColumnsCount(record);
}
} }
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>

View file

@ -11,6 +11,7 @@
#include <QPainter> #include <QPainter>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QSortFilterProxyModel>
#include "../../model/tools/reportmodel.hpp" #include "../../model/tools/reportmodel.hpp"
@ -23,7 +24,7 @@ namespace CSVTools
public: public:
RichTextDelegate (QObject *parent = 0); RichTextDelegate (QObject *parent = 0);
virtual void paint(QPainter *painter, const QStyleOptionViewItem& option, virtual void paint(QPainter *painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const; const QModelIndex& index) const;
}; };
@ -63,7 +64,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
for (QModelIndexList::const_iterator iter (selectedRows.begin()); for (QModelIndexList::const_iterator iter (selectedRows.begin());
iter!=selectedRows.end(); ++iter) iter!=selectedRows.end(); ++iter)
{ {
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString(); QString hint = mProxyModel->data (mProxyModel->index (iter->row(), 2)).toString();
if (!hint.isEmpty() && hint[0]=='R') if (!hint.isEmpty() && hint[0]=='R')
{ {
@ -78,7 +79,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
if (mRefreshAction) if (mRefreshAction)
menu.addAction (mRefreshAction); menu.addAction (mRefreshAction);
menu.exec (event->globalPos()); menu.exec (event->globalPos());
} }
@ -106,14 +107,14 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event)
event->accept(); event->accept();
return; return;
} }
switch (iter->second) switch (iter->second)
{ {
case Action_None: case Action_None:
event->accept(); event->accept();
break; break;
case Action_Edit: case Action_Edit:
event->accept(); event->accept();
@ -152,7 +153,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionMode (QAbstractItemView::ExtendedSelection);
setModel (mModel); mProxyModel = new QSortFilterProxyModel (this);
mProxyModel->setSourceModel (mModel);
setModel (mProxyModel);
setColumnHidden (2, true); setColumnHidden (2, true);
mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0, mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0,
@ -162,7 +166,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
if (richTextDescription) if (richTextDescription)
setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this)); setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this));
mShowAction = new QAction (tr ("Show"), this); mShowAction = new QAction (tr ("Show"), this);
connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection())); connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection()));
addAction (mShowAction); addAction (mShowAction);
@ -182,10 +186,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
connect (mRefreshAction, SIGNAL (triggered()), this, SIGNAL (refreshRequest())); connect (mRefreshAction, SIGNAL (triggered()), this, SIGNAL (refreshRequest()));
addAction (mRefreshAction); addAction (mRefreshAction);
} }
mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit)); mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit));
mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove)); mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove));
mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove)); mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove));
} }
std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const
@ -197,7 +201,7 @@ std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() co
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
{ {
ids.push_back (mModel->getUniversalId (iter->row())); ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row()));
} }
return ids; return ids;
@ -234,7 +238,7 @@ void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStrin
mDoubleClickActions[modifiers] = action; mDoubleClickActions[modifiers] = action;
return; return;
} }
} }
std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
@ -245,13 +249,22 @@ std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<int> rows;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
{ {
QString hint = mModel->data (mModel->index (iter->row(), 2)).toString(); rows.push_back (mProxyModel->mapToSource (*iter).row());
}
std::sort (rows.begin(), rows.end());
for (std::vector<int>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{
QString hint = mModel->data (mModel->index (*iter, 2)).toString();
if (!hint.isEmpty() && hint[0]=='R') if (!hint.isEmpty() && hint[0]=='R')
indices.push_back (iter->row()); indices.push_back (*iter);
} }
} }
else else
@ -272,25 +285,35 @@ void CSVTools::ReportTable::flagAsReplaced (int index)
{ {
mModel->flagAsReplaced (index); mModel->flagAsReplaced (index);
} }
void CSVTools::ReportTable::showSelection() void CSVTools::ReportTable::showSelection()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
emit editRequest (mModel->getUniversalId (iter->row()), mModel->getHint (iter->row())); {
int row = mProxyModel->mapToSource (*iter).row();
emit editRequest (mModel->getUniversalId (row), mModel->getHint (row));
}
} }
void CSVTools::ReportTable::removeSelection() void CSVTools::ReportTable::removeSelection()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
std::reverse (selectedRows.begin(), selectedRows.end()); std::vector<int> rows;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); for (QModelIndexList::iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter) ++iter)
mModel->removeRows (iter->row(), 1); {
rows.push_back (mProxyModel->mapToSource (*iter).row());
}
std::sort (rows.begin(), rows.end());
for (std::vector<int>::const_reverse_iterator iter (rows.rbegin()); iter!=rows.rend(); ++iter)
mProxyModel->removeRows (*iter, 1);
selectionModel()->clear(); selectionModel()->clear();
} }

View file

@ -6,6 +6,7 @@
#include "../world/dragrecordtable.hpp" #include "../world/dragrecordtable.hpp"
class QAction; class QAction;
class QSortFilterProxyModel;
namespace CSMTools namespace CSMTools
{ {
@ -30,7 +31,8 @@ namespace CSVTools
Action_Remove, Action_Remove,
Action_EditAndRemove Action_EditAndRemove
}; };
QSortFilterProxyModel *mProxyModel;
CSMTools::ReportModel *mModel; CSMTools::ReportModel *mModel;
CSVWorld::CommandDelegate *mIdTypeDelegate; CSVWorld::CommandDelegate *mIdTypeDelegate;
QAction *mShowAction; QAction *mShowAction;
@ -63,11 +65,14 @@ namespace CSVTools
void clear(); void clear();
// Return indices of rows that are suitable for replacement. /// Return indices of rows that are suitable for replacement.
// ///
// \param selection Only list selected rows. /// \param selection Only list selected rows.
///
/// \return rows in the original model
std::vector<int> getReplaceIndices (bool selection) const; std::vector<int> getReplaceIndices (bool selection) const;
/// \param index row in the original model
void flagAsReplaced (int index); void flagAsReplaced (int index);
private slots: private slots:
@ -78,8 +83,8 @@ namespace CSVTools
public slots: public slots:
void stateChanged (int state, CSMDoc::Document *document); void stateChanged (int state, CSMDoc::Document *document);
signals: signals:
void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);

View file

@ -19,6 +19,7 @@
#include <QComboBox> #include <QComboBox>
#include <QHeaderView> #include <QHeaderView>
#include <QScrollBar> #include <QScrollBar>
#include <QMenu>
#include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
@ -314,10 +315,141 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher()
} }
} }
CSVWorld::IdContextMenu::IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display)
: QObject(widget),
mWidget(widget),
mIdType(CSMWorld::TableMimeData::convertEnums(display))
{
Q_ASSERT(mWidget != NULL);
Q_ASSERT(CSMWorld::ColumnBase::isId(display));
Q_ASSERT(mIdType != CSMWorld::UniversalId::Type_None);
mWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(mWidget,
SIGNAL(customContextMenuRequested(const QPoint &)),
this,
SLOT(showContextMenu(const QPoint &)));
mEditIdAction = new QAction(this);
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editIdRequest()));
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
if (lineEdit != NULL)
{
mContextMenu = lineEdit->createStandardContextMenu();
}
else
{
mContextMenu = new QMenu(mWidget);
}
}
void CSVWorld::IdContextMenu::excludeId(const std::string &id)
{
mExcludedIds.insert(id);
}
QString CSVWorld::IdContextMenu::getWidgetValue() const
{
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(mWidget);
QLabel *label = qobject_cast<QLabel *>(mWidget);
QString value = "";
if (lineEdit != NULL)
{
value = lineEdit->text();
}
else if (label != NULL)
{
value = label->text();
}
return value;
}
void CSVWorld::IdContextMenu::addEditIdActionToMenu(const QString &text)
{
mEditIdAction->setText(text);
if (mContextMenu->actions().isEmpty())
{
mContextMenu->addAction(mEditIdAction);
}
else if (mContextMenu->actions().first() != mEditIdAction)
{
QAction *action = mContextMenu->actions().first();
mContextMenu->insertAction(action, mEditIdAction);
mContextMenu->insertSeparator(action);
}
}
void CSVWorld::IdContextMenu::removeEditIdActionFromMenu()
{
if (mContextMenu->actions().isEmpty())
{
return;
}
if (mContextMenu->actions().first() == mEditIdAction)
{
mContextMenu->removeAction(mEditIdAction);
if (!mContextMenu->actions().isEmpty() && mContextMenu->actions().first()->isSeparator())
{
mContextMenu->removeAction(mContextMenu->actions().first());
}
}
}
void CSVWorld::IdContextMenu::showContextMenu(const QPoint &pos)
{
QString value = getWidgetValue();
bool isExcludedId = mExcludedIds.find(value.toUtf8().constData()) != mExcludedIds.end();
if (!value.isEmpty() && !isExcludedId)
{
addEditIdActionToMenu("Edit '" + value + "'");
}
else
{
removeEditIdActionFromMenu();
}
if (!mContextMenu->actions().isEmpty())
{
mContextMenu->exec(mWidget->mapToGlobal(pos));
}
}
void CSVWorld::IdContextMenu::editIdRequest()
{
CSMWorld::UniversalId editId(mIdType, getWidgetValue().toUtf8().constData());
emit editIdRequest(editId, "");
}
/* /*
=============================================================EditWidget===================================================== =============================================================EditWidget=====================================================
*/ */
void CSVWorld::EditWidget::createEditorContextMenu(QWidget *editor,
CSMWorld::ColumnBase::Display display,
int currentRow) const
{
Q_ASSERT(editor != NULL);
if (CSMWorld::ColumnBase::isId(display) &&
CSMWorld::TableMimeData::convertEnums(display) != CSMWorld::UniversalId::Type_None)
{
int idColumn = mTable->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
QString id = mTable->data(mTable->index(currentRow, idColumn)).toString();
IdContextMenu *menu = new IdContextMenu(editor, display);
// Current ID is already opened, so no need to create Edit 'ID' action for it
menu->excludeId(id.toUtf8().constData());
connect(menu,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));
}
}
CSVWorld::EditWidget::~EditWidget() CSVWorld::EditWidget::~EditWidget()
{ {
for (unsigned i = 0; i < mNestedModels.size(); ++i) for (unsigned i = 0; i < mNestedModels.size(); ++i)
@ -455,6 +587,11 @@ void CSVWorld::EditWidget::remake(int row)
tablesLayout->addWidget(label); tablesLayout->addWidget(label);
tablesLayout->addWidget(table); tablesLayout->addWidget(table);
connect(table,
SIGNAL(editRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)));
} }
else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List)) else if (!(flags & CSMWorld::ColumnBase::Flag_Dialogue_List))
{ {
@ -488,6 +625,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false); editor->setEnabled(false);
label->setEnabled(false); label->setEnabled(false);
} }
createEditorContextMenu(editor, display, row);
} }
} }
else else
@ -539,6 +678,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false); editor->setEnabled(false);
label->setEnabled(false); label->setEnabled(false);
} }
createEditorContextMenu(editor, display, row);
} }
} }
mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i)));
@ -607,6 +748,11 @@ CSVWorld::SimpleDialogueSubView::SimpleDialogueSubView (const CSMWorld::Universa
mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
dataChanged(mTable->getModelIndex (getUniversalId().getId(), 0)); dataChanged(mTable->getModelIndex (getUniversalId().getId(), 0));
connect(mEditWidget,
SIGNAL(editIdRequest(const CSMWorld::UniversalId &, const std::string &)),
this,
SIGNAL(focusId(const CSMWorld::UniversalId &, const std::string &)));
} }
void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked) void CSVWorld::SimpleDialogueSubView::setEditLock (bool locked)

View file

@ -1,6 +1,7 @@
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H #ifndef CSV_WORLD_DIALOGUESUBVIEW_H
#define CSV_WORLD_DIALOGUESUBVIEW_H #define CSV_WORLD_DIALOGUESUBVIEW_H
#include <set>
#include <map> #include <map>
#include <memory> #include <memory>
@ -11,12 +12,14 @@
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/commanddispatcher.hpp" #include "../../model/world/commanddispatcher.hpp"
#include "../../model/world/universalid.hpp"
class QDataWidgetMapper; class QDataWidgetMapper;
class QSize; class QSize;
class QEvent; class QEvent;
class QLabel; class QLabel;
class QVBoxLayout; class QVBoxLayout;
class QMenu;
namespace CSMWorld namespace CSMWorld
{ {
@ -149,6 +152,36 @@ namespace CSVWorld
CSMWorld::ColumnBase::Display display); CSMWorld::ColumnBase::Display display);
}; };
/// A context menu with "Edit 'ID'" action for editors in the dialogue subview
class IdContextMenu : public QObject
{
Q_OBJECT
QWidget *mWidget;
CSMWorld::UniversalId::Type mIdType;
std::set<std::string> mExcludedIds;
///< A list of IDs that should not have the Edit 'ID' action.
QMenu *mContextMenu;
QAction *mEditIdAction;
QString getWidgetValue() const;
void addEditIdActionToMenu(const QString &text);
void removeEditIdActionFromMenu();
public:
IdContextMenu(QWidget *widget, CSMWorld::ColumnBase::Display display);
void excludeId(const std::string &id);
private slots:
void showContextMenu(const QPoint &pos);
void editIdRequest();
signals:
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
};
class EditWidget : public QScrollArea class EditWidget : public QScrollArea
{ {
Q_OBJECT Q_OBJECT
@ -162,6 +195,9 @@ namespace CSVWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor std::vector<CSMWorld::NestedTableProxyModel*> mNestedModels; //Plain, raw C pointers, deleted in the dtor
void createEditorContextMenu(QWidget *editor,
CSMWorld::ColumnBase::Display display,
int currentRow) const;
public: public:
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
@ -171,6 +207,9 @@ namespace CSVWorld
virtual ~EditWidget(); virtual ~EditWidget();
void remake(int row); void remake(int row);
signals:
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
}; };
class SimpleDialogueSubView : public CSVDoc::SubView class SimpleDialogueSubView : public CSVDoc::SubView

View file

@ -1,15 +1,18 @@
#include "nestedtable.hpp" #include "nestedtable.hpp"
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "util.hpp"
#include <QHeaderView> #include <QHeaderView>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QMenu> #include <QMenu>
#include <QDebug> #include <QDebug>
#include "../../model/world/nestedtableproxymodel.hpp"
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp"
CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
CSMWorld::UniversalId id, CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model, CSMWorld::NestedTableProxyModel* model,
@ -55,6 +58,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
connect(mRemoveRowAction, SIGNAL(triggered()), connect(mRemoveRowAction, SIGNAL(triggered()),
this, SLOT(removeRowActionTriggered())); this, SLOT(removeRowActionTriggered()));
mEditIdAction = new TableEditIdAction(*this, this);
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editCell()));
} }
std::vector<CSMWorld::UniversalId> CSVWorld::NestedTable::getDraggedRecords() const std::vector<CSMWorld::UniversalId> CSVWorld::NestedTable::getDraggedRecords() const
@ -69,6 +75,15 @@ void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event)
QMenu menu(this); QMenu menu(this);
int currentRow = rowAt(event->y());
int currentColumn = columnAt(event->x());
if (mEditIdAction->isValidIdCell(currentRow, currentColumn))
{
mEditIdAction->setCell(currentRow, currentColumn);
menu.addAction(mEditIdAction);
menu.addSeparator();
}
if (selectionModel()->selectedRows().size() == 1) if (selectionModel()->selectedRows().size() == 1)
menu.addAction(mRemoveRowAction); menu.addAction(mRemoveRowAction);
@ -92,3 +107,8 @@ void CSVWorld::NestedTable::addNewRowActionTriggered()
selectionModel()->selectedRows().size(), selectionModel()->selectedRows().size(),
mModel->getParentColumn())); mModel->getParentColumn()));
} }
void CSVWorld::NestedTable::editCell()
{
emit editRequest(mEditIdAction->getCurrentId(), "");
}

View file

@ -22,12 +22,15 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class TableEditIdAction;
class NestedTable : public DragRecordTable class NestedTable : public DragRecordTable
{ {
Q_OBJECT Q_OBJECT
QAction *mAddNewRowAction; QAction *mAddNewRowAction;
QAction *mRemoveRowAction; QAction *mRemoveRowAction;
TableEditIdAction *mEditIdAction;
CSMWorld::NestedTableProxyModel* mModel; CSMWorld::NestedTableProxyModel* mModel;
CSMWorld::CommandDispatcher *mDispatcher; CSMWorld::CommandDispatcher *mDispatcher;
@ -46,6 +49,11 @@ namespace CSVWorld
void removeRowActionTriggered(); void removeRowActionTriggered();
void addNewRowActionTriggered(); void addNewRowActionTriggered();
void editCell();
signals:
void editRequest(const CSMWorld::UniversalId &id, const std::string &hint);
}; };
} }

View file

@ -276,11 +276,11 @@ void CSVWorld::ScriptEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
if(textCursor().hasSelection()) if(textCursor().hasSelection())
{ {
QString str = textCursor().selection().toPlainText(); QString str = textCursor().selection().toPlainText();
int selectedLines = str.count("\n")+1; int offset = str.count("\n");
if(textCursor().position() < textCursor().anchor()) if(textCursor().position() < textCursor().anchor())
endBlock += selectedLines; endBlock += offset;
else else
startBlock -= selectedLines; startBlock -= offset;
} }
painter.setBackgroundMode(Qt::OpaqueMode); painter.setBackgroundMode(Qt::OpaqueMode);
QFont font = painter.font(); QFont font = painter.font();

View file

@ -27,6 +27,7 @@
#include "../../model/settings/usersettings.hpp" #include "../../model/settings/usersettings.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp" #include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
@ -58,33 +59,13 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
/// \todo add menu items for select all and clear selection /// \todo add menu items for select all and clear selection
int currentRow = rowAt(event->y());
int currentColumn = columnAt(event->x());
if (mEditIdAction->isValidIdCell(currentRow, currentColumn))
{ {
// Request UniversalId editing from table columns. mEditIdAction->setCell(currentRow, currentColumn);
menu.addAction(mEditIdAction);
int currRow = rowAt( event->y() ), menu.addSeparator();
currCol = columnAt( event->x() );
currRow = mProxyModel->mapToSource(mProxyModel->index( currRow, 0 )).row();
CSMWorld::ColumnBase::Display colDisplay =
static_cast<CSMWorld::ColumnBase::Display>(
mModel->headerData(
currCol,
Qt::Horizontal,
CSMWorld::ColumnBase::Role_Display ).toInt());
QString cellData = mModel->data(mModel->index( currRow, currCol )).toString();
CSMWorld::UniversalId::Type colType = CSMWorld::TableMimeData::convertEnums( colDisplay );
if ( !cellData.isEmpty()
&& colType != CSMWorld::UniversalId::Type_None )
{
mEditCellAction->setText(tr("Edit '").append(cellData).append("'"));
menu.addAction( mEditCellAction );
mEditCellId = CSMWorld::UniversalId( colType, cellData.toUtf8().constData() );
}
} }
if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) if (!mEditLock && !(mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant))
@ -363,10 +344,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord()));
addAction (mMoveDownAction); addAction (mMoveDownAction);
mEditCellAction = new QAction( tr("Edit Cell"), this );
connect( mEditCellAction, SIGNAL(triggered()), this, SLOT(editCell()) );
addAction( mEditCellAction );
mViewAction = new QAction (tr ("View"), this); mViewAction = new QAction (tr ("View"), this);
connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));
addAction (mViewAction); addAction (mViewAction);
@ -387,6 +364,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert())); connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction); addAction (mExtendedRevertAction);
mEditIdAction = new TableEditIdAction (*this, this);
connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell()));
addAction (mEditIdAction);
connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); this, SLOT (tableSizeUpdate()));
@ -522,7 +503,7 @@ void CSVWorld::Table::moveDownRecord()
void CSVWorld::Table::editCell() void CSVWorld::Table::editCell()
{ {
emit editRequest( mEditCellId, std::string() ); emit editRequest(mEditIdAction->getCurrentId(), "");
} }
void CSVWorld::Table::viewRecord() void CSVWorld::Table::viewRecord()

View file

@ -30,6 +30,7 @@ namespace CSMWorld
namespace CSVWorld namespace CSVWorld
{ {
class CommandDelegate; class CommandDelegate;
class TableEditIdAction;
///< Table widget ///< Table widget
class Table : public DragRecordTable class Table : public DragRecordTable
@ -57,15 +58,14 @@ namespace CSVWorld
QAction *mMoveUpAction; QAction *mMoveUpAction;
QAction *mMoveDownAction; QAction *mMoveDownAction;
QAction *mViewAction; QAction *mViewAction;
QAction *mEditCellAction;
QAction *mPreviewAction; QAction *mPreviewAction;
QAction *mExtendedDeleteAction; QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction; QAction *mExtendedRevertAction;
TableEditIdAction *mEditIdAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTableBase *mModel; CSMWorld::IdTableBase *mModel;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher; CSMWorld::CommandDispatcher *mDispatcher;
CSMWorld::UniversalId mEditCellId;
std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions; std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;
bool mJumpToAddedRecord; bool mJumpToAddedRecord;
bool mUnselectAfterJump; bool mUnselectAfterJump;

View file

@ -0,0 +1,49 @@
#include "tableeditidaction.hpp"
#include <QTableView>
#include "../../model/world/tablemimedata.hpp"
CSVWorld::TableEditIdAction::CellData CSVWorld::TableEditIdAction::getCellData(int row, int column) const
{
QModelIndex index = mTable.model()->index(row, column);
if (index.isValid())
{
QVariant display = mTable.model()->data(index, CSMWorld::ColumnBase::Role_Display);
QString value = mTable.model()->data(index).toString();
return std::make_pair(static_cast<CSMWorld::ColumnBase::Display>(display.toInt()), value);
}
return std::make_pair(CSMWorld::ColumnBase::Display_None, "");
}
CSVWorld::TableEditIdAction::TableEditIdAction(const QTableView &table, QWidget *parent)
: QAction(parent),
mTable(table),
mCurrentId(CSMWorld::UniversalId::Type_None)
{}
void CSVWorld::TableEditIdAction::setCell(int row, int column)
{
CellData data = getCellData(row, column);
CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);
if (idType != CSMWorld::UniversalId::Type_None)
{
mCurrentId = CSMWorld::UniversalId(idType, data.second.toUtf8().constData());
setText("Edit '" + data.second + "'");
}
}
CSMWorld::UniversalId CSVWorld::TableEditIdAction::getCurrentId() const
{
return mCurrentId;
}
bool CSVWorld::TableEditIdAction::isValidIdCell(int row, int column) const
{
CellData data = getCellData(row, column);
CSMWorld::UniversalId::Type idType = CSMWorld::TableMimeData::convertEnums(data.first);
return CSMWorld::ColumnBase::isId(data.first) &&
idType != CSMWorld::UniversalId::Type_None &&
!data.second.isEmpty();
}

View file

@ -0,0 +1,31 @@
#ifndef CSVWORLD_TABLEEDITIDACTION_HPP
#define CSVWORLD_TABLEEDITIDACTION_HPP
#include <QAction>
#include "../../model/world/columnbase.hpp"
#include "../../model/world/universalid.hpp"
class QTableView;
namespace CSVWorld
{
class TableEditIdAction : public QAction
{
const QTableView &mTable;
CSMWorld::UniversalId mCurrentId;
typedef std::pair<CSMWorld::ColumnBase::Display, QString> CellData;
CellData getCellData(int row, int column) const;
public:
TableEditIdAction(const QTableView &table, QWidget *parent = 0);
void setCell(int row, int column);
CSMWorld::UniversalId getCurrentId() const;
bool isValidIdCell(int row, int column) const;
};
}
#endif

View file

@ -14,7 +14,9 @@
#if defined(_WIN32) #if defined(_WIN32)
// For OutputDebugString // For OutputDebugString
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h> #include <windows.h>
// makes __argc and __argv available on windows // makes __argc and __argv available on windows
#include <cstdlib> #include <cstdlib>

View file

@ -10,6 +10,7 @@
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp> #include <components/esm/loadinfo.hpp>
#include <components/esm/dialoguestate.hpp> #include <components/esm/dialoguestate.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/compiler/exception.hpp> #include <components/compiler/exception.hpp>
#include <components/compiler/errorhandler.hpp> #include <components/compiler/errorhandler.hpp>

View file

@ -203,6 +203,8 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
return false; // script does not have a variable of this name. return false; // script does not have a variable of this name.
int index = localDefs.getIndex (name); int index = localDefs.getIndex (name);
if (index < 0)
return false; // shouldn't happen, we checked that variable has a type above, so must exist
const MWScript::Locals& locals = mActor.getRefData().getLocals(); const MWScript::Locals& locals = mActor.getRefData().getLocals();

View file

@ -10,10 +10,14 @@
#include <osg/Texture2D> #include <osg/Texture2D>
#include <components/misc/stringops.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/misc/stringops.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"

View file

@ -12,6 +12,7 @@
#include <MyGUI_FactoryManager.h> #include <MyGUI_FactoryManager.h>
#include <components/esm/globalmap.hpp> #include <components/esm/globalmap.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/myguiplatform/myguitexture.hpp> #include <components/myguiplatform/myguitexture.hpp>

View file

@ -72,6 +72,11 @@ namespace MWGui
{ {
MarkerUserData(MWRender::LocalMap* map) MarkerUserData(MWRender::LocalMap* map)
: mLocalMapRender(map) : mLocalMapRender(map)
, interior(false)
, cellX(0)
, cellY(0)
, nX(0.f)
, nY(0.f)
{ {
} }

View file

@ -5,6 +5,7 @@
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <components/esm/esmwriter.hpp>
#include <components/esm/quickkeys.hpp> #include <components/esm/quickkeys.hpp>
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"

View file

@ -22,6 +22,9 @@
#include <components/sdlutil/sdlcursormanager.hpp> #include <components/sdlutil/sdlcursormanager.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/fontloader/fontloader.hpp> #include <components/fontloader/fontloader.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
@ -893,6 +896,9 @@ namespace MWGui
void WindowManager::updateMap() void WindowManager::updateMap()
{ {
if (!mLocalMapRender)
return;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3(); osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3();

View file

@ -4,7 +4,10 @@
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/esmreader.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"

View file

@ -31,8 +31,6 @@ namespace
//chooses an attack depending on probability to avoid uniformity //chooses an attack depending on probability to avoid uniformity
ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations)[2]);
osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos, osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos,
float duration, int weapType, float strength); float duration, int weapType, float strength);
@ -83,7 +81,7 @@ namespace MWMechanics
/// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive. /// \brief This class holds the variables AiCombat needs which are deleted if the package becomes inactive.
struct AiCombatStorage : AiTemporaryBase struct AiCombatStorage : AiTemporaryBase
{ {
float mTimerAttack; float mAttackCooldown;
float mTimerReact; float mTimerReact;
float mTimerCombatMove; float mTimerCombatMove;
bool mReadyToAttack; bool mReadyToAttack;
@ -95,15 +93,13 @@ namespace MWMechanics
boost::shared_ptr<Action> mCurrentAction; boost::shared_ptr<Action> mCurrentAction;
float mActionCooldown; float mActionCooldown;
float mStrength; float mStrength;
float mMinMaxAttackDuration[3][2];
bool mMinMaxAttackDurationInitialised;
bool mForceNoShortcut; bool mForceNoShortcut;
ESM::Position mShortcutFailPos; ESM::Position mShortcutFailPos;
osg::Vec3f mLastActorPos; osg::Vec3f mLastActorPos;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
AiCombatStorage(): AiCombatStorage():
mTimerAttack(0), mAttackCooldown(0),
mTimerReact(0), mTimerReact(0),
mTimerCombatMove(0), mTimerCombatMove(0),
mReadyToAttack(false), mReadyToAttack(false),
@ -115,7 +111,6 @@ namespace MWMechanics
mCurrentAction(), mCurrentAction(),
mActionCooldown(0), mActionCooldown(0),
mStrength(), mStrength(),
mMinMaxAttackDurationInitialised(false),
mForceNoShortcut(false), mForceNoShortcut(false),
mLastActorPos(0,0,0), mLastActorPos(0,0,0),
mMovement(){} mMovement(){}
@ -233,41 +228,12 @@ namespace MWMechanics
{ {
if(smoothTurn(actor, osg::DegreesToRadians(movement.mRotation[0]), 0)) movement.mRotation[0] = 0; if(smoothTurn(actor, osg::DegreesToRadians(movement.mRotation[0]), 0)) movement.mRotation[0] = 0;
} }
float attacksPeriod = 1.0f;
ESM::Weapon::AttackType attackType;
bool& attack = storage.mAttack; bool& attack = storage.mAttack;
bool& readyToAttack = storage.mReadyToAttack; bool& readyToAttack = storage.mReadyToAttack;
float& timerAttack = storage.mTimerAttack;
bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised;
float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration;
if(readyToAttack)
{
if (!minMaxAttackDurationInitialised) if (attack && (characterController.getAttackStrength() >= storage.mStrength || characterController.readyToPrepareAttack()))
{
// TODO: this must be updated when a different weapon is equipped
// TODO: it would be much easier to ask the CharacterController about the current % completion of the weapon wind-up animation
getMinMaxAttackDuration(actor, minMaxAttackDuration);
minMaxAttackDurationInitialised = true;
}
if (timerAttack < 0) attack = false;
timerAttack -= duration;
}
else
{
timerAttack = -attacksPeriod;
attack = false; attack = false;
}
characterController.setAttackingOrSpell(attack); characterController.setAttackingOrSpell(attack);
@ -300,10 +266,6 @@ namespace MWMechanics
currentCell = actor.getCell(); currentCell = actor.getCell();
} }
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(actor);
if (!anim) // shouldn't happen
return false;
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
if (actionCooldown > 0) if (actionCooldown > 0)
@ -312,11 +274,7 @@ namespace MWMechanics
float rangeAttack = 0; float rangeAttack = 0;
float rangeFollow = 0; float rangeFollow = 0;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction; boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
// TODO: upperBodyReady() works fine for checking if we can start an attack, if (characterController.readyToPrepareAttack())
// but doesn't work properly for checking if the attack is finished (as things like hit recovery or knockdown also play on the upper body)
// Only a minor problem, but can mess with the actionCooldown timer.
// To fix this the AI needs to be brought closer to the CharacterController, so we can simply check if a weapon animation is playing.
if (anim->upperBodyReady())
{ {
currentAction = prepareNextAction(actor, target); currentAction = prepareNextAction(actor, target);
actionCooldown = currentAction->getActionCooldown(); actionCooldown = currentAction->getActionCooldown();
@ -333,9 +291,6 @@ namespace MWMechanics
// Get weapon characteristics // Get weapon characteristics
if (actorClass.hasInventoryStore(actor)) if (actorClass.hasInventoryStore(actor))
{ {
// TODO: Check equipped weapon and equip a different one if we can't attack with it
// (e.g. no ammunition, or wrong type of ammunition equipped, etc. autoEquip is not very smart in this regard))
//Get weapon speed and range //Get weapon speed and range
MWWorld::ContainerStoreIterator weaponSlot = MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype); MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype);
@ -383,34 +338,38 @@ namespace MWMechanics
} }
float& strength = storage.mStrength; float& strength = storage.mStrength;
// start new attack // start new attack
if(readyToAttack) if(readyToAttack && characterController.readyToStartAttack())
{ {
if(timerAttack <= -attacksPeriod) if (storage.mAttackCooldown <= 0)
{ {
attack = true; // attack starts just now attack = true; // attack starts just now
characterController.setAttackingOrSpell(attack);
if (!distantCombat) attackType = chooseBestAttack(weapon, movement); if (!distantCombat)
else attackType = ESM::Weapon::AT_Chop; // cause it's =0 chooseBestAttack(weapon, movement);
strength = Misc::Rng::rollClosedProbability(); strength = Misc::Rng::rollClosedProbability();
// Note: may be 0 for some animations const MWWorld::ESMStore &store = world->getStore();
timerAttack = minMaxAttackDuration[attackType][0] +
(minMaxAttackDuration[attackType][1] - minMaxAttackDuration[attackType][0]) * strength;
//say a provoking combat phrase //say a provoking combat phrase
if (actor.getClass().isNpc()) if (actor.getClass().isNpc())
{ {
const MWWorld::ESMStore &store = world->getStore();
int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt(); int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
if (Misc::Rng::roll0to99() < chance) if (Misc::Rng::roll0to99() < chance)
{ {
MWBase::Environment::get().getDialogueManager()->say(actor, "attack"); MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
} }
} }
float baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayCreature")->getFloat();
if (actor.getClass().isNpc())
baseDelay = store.get<ESM::GameSetting>().find("fCombatDelayNPC")->getFloat();
storage.mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9);
} }
else
storage.mAttackCooldown -= tReaction;
} }
@ -618,57 +577,6 @@ namespace MWMechanics
readyToAttack = false; readyToAttack = false;
} }
if(!isStuck && distToTarget > rangeAttack && !distantCombat)
{
//special run attack; it shouldn't affect melee combat tactics
if(actorClass.getMovementSettings(actor).mPosition[1] == 1)
{
/* check if actor can overcome the distance = distToTarget - attackerWeapRange
less than in time of swinging with weapon (t_swing), then start attacking
*/
float speed1 = actorClass.getSpeed(actor);
float speed2 = target.getClass().getSpeed(target);
if(target.getClass().getMovementSettings(target).mPosition[0] == 0
&& target.getClass().getMovementSettings(target).mPosition[1] == 0)
speed2 = 0;
float s1 = distToTarget - weapRange;
float t = s1/speed1;
float s2 = speed2 * t;
float t_swing =
minMaxAttackDuration[ESM::Weapon::AT_Thrust][0] +
(minMaxAttackDuration[ESM::Weapon::AT_Thrust][1] - minMaxAttackDuration[ESM::Weapon::AT_Thrust][0]) * Misc::Rng::rollClosedProbability();
if (t + s2/speed1 <= t_swing)
{
readyToAttack = true;
if(timerAttack <= -attacksPeriod)
{
timerAttack = t_swing;
attack = true;
}
}
}
}
// TODO: Add a parameter to vary DURATION_SAME_SPOT?
if((distToTarget > rangeAttack || followTarget) &&
mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
{
// probably walking into another NPC TODO: untested in combat situation
// TODO: diagonal should have same animation as walk forward
// but doesn't seem to do that?
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
// change the angle a bit, too
if(mPathFinder.isPathConstructed())
zTurn(actor, osg::DegreesToRadians(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
if(followTarget)
followTarget = false;
// FIXME: can fool actors to stay behind doors, etc.
// Related to Bug#1102 and to some degree #1155 as well
}
return false; return false;
} }
@ -794,70 +702,6 @@ ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics:
return attackType; return attackType;
} }
void getMinMaxAttackDuration(const MWWorld::Ptr& actor, float (*fMinMaxDurations)[2])
{
if (!actor.getClass().hasInventoryStore(actor)) // creatures
{
fMinMaxDurations[0][0] = fMinMaxDurations[0][1] = 0.1f;
fMinMaxDurations[1][0] = fMinMaxDurations[1][1] = 0.1f;
fMinMaxDurations[2][0] = fMinMaxDurations[2][1] = 0.1f;
return;
}
// get weapon information: type and speed
const ESM::Weapon *weapon = NULL;
MWMechanics::WeaponType weaptype = MWMechanics::WeapType_None;
MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(actor.getClass().getCreatureStats(actor), actor.getClass().getInventoryStore(actor), &weaptype);
float weapSpeed;
if (weaptype != MWMechanics::WeapType_HandToHand
&& weaptype != MWMechanics::WeapType_Spell
&& weaptype != MWMechanics::WeapType_None)
{
weapon = weaponSlot->get<ESM::Weapon>()->mBase;
weapSpeed = weapon->mData.mSpeed;
}
else weapSpeed = 1.0f;
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(actor);
std::string weapGroup;
MWMechanics::getWeaponGroup(weaptype, weapGroup);
weapGroup = weapGroup + ": ";
bool bRangedWeap = (weaptype >= MWMechanics::WeapType_BowAndArrow && weaptype <= MWMechanics::WeapType_Thrown);
const char *attackType[] = {"chop ", "slash ", "thrust ", "shoot "};
std::string textKey = "start";
std::string textKey2;
// get durations for each attack type
for (int i = 0; i < (bRangedWeap ? 1 : 3); i++)
{
float start1 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey);
if (start1 < 0)
{
fMinMaxDurations[i][0] = fMinMaxDurations[i][1] = 0.1f;
continue;
}
textKey2 = "min attack";
float start2 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2);
fMinMaxDurations[i][0] = (start2 - start1) / weapSpeed;
textKey2 = "max attack";
start1 = anim->getTextKeyTime(weapGroup + (bRangedWeap ? attackType[3] : attackType[i]) + textKey2);
fMinMaxDurations[i][1] = fMinMaxDurations[i][0] + (start1 - start2) / weapSpeed;
}
}
osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos, osg::Vec3f AimDirToMovingTarget(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const osg::Vec3f& vLastTargetPos,
float duration, int weapType, float strength) float duration, int weapType, float strength)
{ {

View file

@ -184,6 +184,11 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
const ESM::Position &targetPos = target.getRefData().getPosition(); const ESM::Position &targetPos = target.getRefData().getPosition();
float distTo = (targetPos.asVec3() - vActorPos).length(); float distTo = (targetPos.asVec3() - vActorPos).length();
// Small threshold for changing target
if (it == mPackages.begin())
distTo = std::max(0.f, distTo - 50.f);
if (distTo < nearestDist) if (distTo < nearestDist)
{ {
nearestDist = distTo; nearestDist = distTo;

View file

@ -1,5 +1,7 @@
#include "aiwander.hpp" #include "aiwander.hpp"
#include <cfloat>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
@ -28,6 +30,12 @@ namespace MWMechanics
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
static const int GREETING_SHOULD_END = 10; static const int GREETING_SHOULD_END = 10;
// to prevent overcrowding
static const int DESTINATION_TOLERANCE = 64;
// distance must be long enough that NPC will need to move to get there.
static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2;
const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] =
{ {
std::string("idle2"), std::string("idle2"),
@ -214,7 +222,7 @@ namespace MWMechanics
// Are we there yet? // Are we there yet?
bool& chooseAction = storage.mChooseAction; bool& chooseAction = storage.mChooseAction;
if(walking && if(walking &&
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], 64.f)) storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
moveNow = false; moveNow = false;
@ -267,6 +275,7 @@ namespace MWMechanics
moveNow = false; moveNow = false;
walking = false; walking = false;
chooseAction = true; chooseAction = true;
mStuckCount = 0;
} }
//#endif //#endif
} }
@ -410,7 +419,7 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false); storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false);
if(storage.mPathFinder.isPathConstructed()) if(storage.mPathFinder.isPathConstructed())
{ {
@ -497,30 +506,17 @@ namespace MWMechanics
{ {
assert(mAllowedNodes.size()); assert(mAllowedNodes.size());
unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size());
// NOTE: initially constructed with local (i.e. cell) co-ordinates
// convert dest to use world co-ordinates
ESM::Pathgrid::Point dest(mAllowedNodes[randNode]); ESM::Pathgrid::Point dest(mAllowedNodes[randNode]);
if (currentCell->getCell()->isExterior()) ToWorldCoordinates(dest, currentCell->getCell());
{
dest.mX += currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE;
dest.mY += currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE;
}
// actor position is already in world co-ordinates // actor position is already in world co-ordinates
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// don't take shortcuts for wandering // don't take shortcuts for wandering
storage.mPathFinder.buildPath(start, dest, actor.getCell(), false); storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false);
if(storage.mPathFinder.isPathConstructed()) if(storage.mPathFinder.isPathConstructed())
{ {
// buildPath inserts dest in case it is not a pathgraph point
// index which is a duplicate for AiWander. However below code
// does not work since getPath() returns a copy of path not a
// reference
//if(storage.mPathFinder.getPathSize() > 1)
//storage.mPathFinder.getPath().pop_back();
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node): // Remove this node as an option and add back the previously used node (stops NPC from picking the same node):
ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
mAllowedNodes.erase(mAllowedNodes.begin() + randNode); mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
@ -543,6 +539,15 @@ namespace MWMechanics
return false; // AiWander package not yet completed return false; // AiWander package not yet completed
} }
void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell)
{
if (cell->isExterior())
{
point.mX += cell->mData.mX * ESM::Land::REAL_SIZE;
point.mY += cell->mData.mY * ESM::Land::REAL_SIZE;
}
}
void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes, void AiWander::trimAllowedNodes(std::vector<ESM::Pathgrid::Point>& nodes,
const PathFinder& pathfinder) const PathFinder& pathfinder)
{ {
@ -646,15 +651,9 @@ namespace MWMechanics
int index = Misc::Rng::rollDice(mAllowedNodes.size()); int index = Misc::Rng::rollDice(mAllowedNodes.size());
ESM::Pathgrid::Point dest = mAllowedNodes[index]; ESM::Pathgrid::Point dest = mAllowedNodes[index];
// apply a slight offset to prevent overcrowding dest.mX += OffsetToPreventOvercrowding();
dest.mX += static_cast<int>(Misc::Rng::rollProbability() * 128 - 64); dest.mY += OffsetToPreventOvercrowding();
dest.mY += static_cast<int>(Misc::Rng::rollProbability() * 128 - 64); ToWorldCoordinates(dest, actor.getCell()->getCell());
if (actor.getCell()->isExterior())
{
dest.mX += actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE;
dest.mY += actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE;
}
MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX), MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX),
static_cast<float>(dest.mY), static_cast<float>(dest.mZ)); static_cast<float>(dest.mY), static_cast<float>(dest.mZ));
@ -664,6 +663,11 @@ namespace MWMechanics
mStoredAvailableNodes = false; mStoredAvailableNodes = false;
} }
int AiWander::OffsetToPreventOvercrowding()
{
return static_cast<int>(DESTINATION_TOLERANCE * (Misc::Rng::rollProbability() * 2.0f - 1.0f));
}
void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell) void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell)
{ {
if (!mStoredInitialActorPosition) if (!mStoredInitialActorPosition)
@ -690,70 +694,85 @@ namespace MWMechanics
// ... pathgrids don't usually include water, so swimmers ignore them // ... pathgrids don't usually include water, so swimmers ignore them
if (mDistance && !actor.getClass().isPureWaterCreature(actor)) if (mDistance && !actor.getClass().isPureWaterCreature(actor))
{ {
float cellXOffset = 0; // get NPC's position in local (i.e. cell) co-ordinates
float cellYOffset = 0; osg::Vec3f npcPos(mInitialActorPosition);
if(cell->isExterior()) if(cell->isExterior())
{ {
cellXOffset = static_cast<float>(cell->mData.mX * ESM::Land::REAL_SIZE); npcPos[0] = npcPos[0] - static_cast<float>(cell->mData.mX * ESM::Land::REAL_SIZE);
cellYOffset = static_cast<float>(cell->mData.mY * ESM::Land::REAL_SIZE); npcPos[1] = npcPos[1] - static_cast<float>(cell->mData.mY * ESM::Land::REAL_SIZE);
} }
// convert npcPos to local (i.e. cell) co-ordinates
osg::Vec3f npcPos(mInitialActorPosition);
npcPos[0] = npcPos[0] - cellXOffset;
npcPos[1] = npcPos[1] - cellYOffset;
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance // mAllowedNodes for this actor with pathgrid point indexes based on mDistance
// NOTE: mPoints and mAllowedNodes are in local co-ordinates // NOTE: mPoints and mAllowedNodes are in local co-ordinates
int pointIndex = 0;
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{ {
osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter])); osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter]));
if((npcPos - nodePos).length2() <= mDistance * mDistance) if((npcPos - nodePos).length2() <= mDistance * mDistance)
{
mAllowedNodes.push_back(pathgrid->mPoints[counter]); mAllowedNodes.push_back(pathgrid->mPoints[counter]);
pointIndex = counter;
}
}
if (mAllowedNodes.size() == 1)
{
AddNonPathGridAllowedPoints(npcPos, pathgrid, pointIndex);
} }
if(!mAllowedNodes.empty()) if(!mAllowedNodes.empty())
{ {
osg::Vec3f firstNodePos(PathFinder::MakeOsgVec3(mAllowedNodes[0])); SetCurrentNodeToClosestAllowedNode(npcPos);
float closestNode = (npcPos - firstNodePos).length2();
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
osg::Vec3f nodePos(PathFinder::MakeOsgVec3(mAllowedNodes[counterThree]));
float tempDist = (npcPos - nodePos).length2();
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
}
// In vanilla Morrowind, sometimes distance is too small to include at least two points,
// in which case, we will take the two closest points regardless of the wander distance
// This is a backup option, as std::sort is potentially O(n^2) in time.
if (mAllowedNodes.empty())
{
// Start with list of PathGrid nodes, sorted by distance from actor
std::vector<PathDistance> nodeDistances;
for (unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{
float distance = (npcPos - PathFinder::MakeOsgVec3(pathgrid->mPoints[counter])).length2();
nodeDistances.push_back(std::make_pair(distance, &pathgrid->mPoints.at(counter)));
}
std::sort(nodeDistances.begin(), nodeDistances.end(), sortByDistance);
// make closest node the current node
mCurrentNode = *nodeDistances[0].second;
// give Actor a 2nd node to walk to
mAllowedNodes.push_back(*nodeDistances[1].second);
} }
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
} }
} }
bool AiWander::sortByDistance(const PathDistance& left, const PathDistance& right) // When only one path grid point in wander distance,
// additional points for NPC to wander to are:
// 1. NPC's initial location
// 2. Partway along the path between the point and its connected points.
void AiWander::AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex)
{ {
return left.first < right.first; mAllowedNodes.push_back(PathFinder::MakePathgridPoint(npcPos));
for (std::vector<ESM::Pathgrid::Edge>::const_iterator it = pathGrid->mEdges.begin(); it != pathGrid->mEdges.end(); ++it)
{
if (it->mV0 == pointIndex)
{
AddPointBetweenPathGridPoints(pathGrid->mPoints[it->mV0], pathGrid->mPoints[it->mV1]);
}
}
}
void AiWander::AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end)
{
osg::Vec3f vectorStart = PathFinder::MakeOsgVec3(start);
osg::Vec3f delta = PathFinder::MakeOsgVec3(end) - vectorStart;
float length = delta.length();
delta.normalize();
int distance = std::max(mDistance / 2, MINIMUM_WANDER_DISTANCE);
// must not travel longer than distance between waypoints or NPC goes past waypoint
distance = std::min(distance, static_cast<int>(length));
delta *= distance;
mAllowedNodes.push_back(PathFinder::MakePathgridPoint(vectorStart + delta));
}
void AiWander::SetCurrentNodeToClosestAllowedNode(osg::Vec3f npcPos)
{
float distanceToClosestNode = FLT_MAX;
unsigned int index = 0;
for (unsigned int counterThree = 0; counterThree < mAllowedNodes.size(); counterThree++)
{
osg::Vec3f nodePos(PathFinder::MakeOsgVec3(mAllowedNodes[counterThree]));
float tempDist = (npcPos - nodePos).length2();
if (tempDist < distanceToClosestNode)
{
index = counterThree;
distanceToClosestNode = tempDist;
}
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
} }
void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const

View file

@ -118,15 +118,19 @@ namespace MWMechanics
GroupIndex_MaxIdle = 9 GroupIndex_MaxIdle = 9
}; };
/// convert point from local (i.e. cell) to world co-ordinates
void ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell);
void SetCurrentNodeToClosestAllowedNode(osg::Vec3f npcPos);
void AddNonPathGridAllowedPoints(osg::Vec3f npcPos, const ESM::Pathgrid * pathGrid, int pointIndex);
void AddPointBetweenPathGridPoints(const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end);
/// lookup table for converting idleSelect value to groupName /// lookup table for converting idleSelect value to groupName
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
/// record distances of pathgrid point nodes to actor static int OffsetToPreventOvercrowding();
/// first value is distance between actor and node, second value is PathGrid node
typedef std::pair<float, const ESM::Pathgrid::Point*> PathDistance;
/// used to sort array of PathDistance objects into ascending order
static bool sortByDistance(const PathDistance& left, const PathDistance& right);
}; };

View file

@ -1259,6 +1259,8 @@ bool CharacterController::updateWeaponState()
} }
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown)
mAttackStrength = complete;
} }
else else
{ {
@ -1789,7 +1791,8 @@ void CharacterController::update(float duration)
} }
} }
if(cls.isBipedal(mPtr)) // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used.
if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr))
forcestateupdate = updateWeaponState() || forcestateupdate; forcestateupdate = updateWeaponState() || forcestateupdate;
else else
forcestateupdate = updateCreatureState() || forcestateupdate; forcestateupdate = updateCreatureState() || forcestateupdate;
@ -2047,6 +2050,27 @@ void CharacterController::setAttackingOrSpell(bool attackingOrSpell)
mAttackingOrSpell = attackingOrSpell; mAttackingOrSpell = attackingOrSpell;
} }
bool CharacterController::readyToPrepareAttack() const
{
return mHitState == CharState_None && mUpperBodyState <= UpperCharState_WeapEquiped;
}
bool CharacterController::readyToStartAttack() const
{
if (mHitState != CharState_None)
return false;
if (mPtr.getClass().hasInventoryStore(mPtr) || mPtr.getClass().isBipedal(mPtr))
return mUpperBodyState == UpperCharState_WeapEquiped;
else
return mUpperBodyState == UpperCharState_Nothing;
}
float CharacterController::getAttackStrength() const
{
return mAttackStrength;
}
void CharacterController::setActive(bool active) void CharacterController::setActive(bool active)
{ {
mAnimation->setActive(active); mAnimation->setActive(active);

View file

@ -240,6 +240,11 @@ public:
void setAttackingOrSpell(bool attackingOrSpell); void setAttackingOrSpell(bool attackingOrSpell);
bool readyToPrepareAttack() const;
bool readyToStartAttack() const;
float getAttackStrength() const;
/// @see Animation::setActive /// @see Animation::setActive
void setActive(bool active); void setActive(bool active);
@ -247,7 +252,6 @@ public:
void setHeadTrackTarget(const MWWorld::Ptr& target); void setHeadTrackTarget(const MWWorld::Ptr& target);
}; };
void getWeaponGroup(WeaponType weaptype, std::string &group);
MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype); MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype);
} }

View file

@ -3,6 +3,8 @@
#include <algorithm> #include <algorithm>
#include <components/esm/creaturestats.hpp> #include <components/esm/creaturestats.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"

View file

@ -6,6 +6,7 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/stolenitems.hpp> #include <components/esm/stolenitems.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"

View file

@ -19,6 +19,8 @@ namespace MWMechanics
public: public:
PathFinder(); PathFinder();
static const int PathTolerance = 32;
static float sgn(float val) static float sgn(float val)
{ {
if(val > 0) if(val > 0)
@ -35,10 +37,7 @@ namespace MWMechanics
void clearPath(); void clearPath();
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, bool checkPathCompleted(float x, float y, float tolerance = PathTolerance);
const MWWorld::CellStore* cell, bool allowShortcuts = true);
bool checkPathCompleted(float x, float y, float tolerance=32.f);
///< \Returns true if we are within \a tolerance units of the last path point. ///< \Returns true if we are within \a tolerance units of the last path point.
/// In degrees /// In degrees
@ -92,6 +91,8 @@ namespace MWMechanics
} }
private: private:
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
const MWWorld::CellStore* cell, bool allowShortcuts = true);
std::list<ESM::Pathgrid::Point> mPath; std::list<ESM::Pathgrid::Point> mPath;

View file

@ -313,27 +313,27 @@ namespace MWMechanics
return path; // for some reason couldn't build a path return path; // for some reason couldn't build a path
// reconstruct path to return, using world co-ordinates // reconstruct path to return, using world co-ordinates
float xCell = 0; int xCell = 0;
float yCell = 0; int yCell = 0;
if (mIsExterior) if (mIsExterior)
{ {
xCell = static_cast<float>(mPathgrid->mData.mX * ESM::Land::REAL_SIZE); xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE;
yCell = static_cast<float>(mPathgrid->mData.mY * ESM::Land::REAL_SIZE); yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE;
} }
while(graphParent[current] != -1) while(graphParent[current] != -1)
{ {
ESM::Pathgrid::Point pt = mPathgrid->mPoints[current]; ESM::Pathgrid::Point pt = mPathgrid->mPoints[current];
pt.mX += static_cast<int>(xCell); pt.mX += xCell;
pt.mY += static_cast<int>(yCell); pt.mY += yCell;
path.push_front(pt); path.push_front(pt);
current = graphParent[current]; current = graphParent[current];
} }
// add first node to path explicitly // add first node to path explicitly
ESM::Pathgrid::Point pt = mPathgrid->mPoints[start]; ESM::Pathgrid::Point pt = mPathgrid->mPoints[start];
pt.mX += static_cast<int>(xCell); pt.mX += xCell;
pt.mY += static_cast<int>(yCell); pt.mY += yCell;
path.push_front(pt); path.push_front(pt);
return path; return path;
} }

View file

@ -5,6 +5,7 @@
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp> #include <components/esm/spellstate.hpp>
#include <components/misc/rng.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

View file

@ -1,28 +1,276 @@
#include "stat.hpp" #include "stat.hpp"
void MWMechanics::AttributeValue::writeState (ESM::StatState<int>& state) const #include <components/esm/statstate.hpp>
namespace MWMechanics
{ {
state.mBase = mBase; template<typename T>
state.mMod = mModifier; Stat<T>::Stat() : mBase (0), mModified (0) {}
state.mDamage = mDamage; template<typename T>
Stat<T>::Stat(T base) : mBase (base), mModified (base) {}
template<typename T>
Stat<T>::Stat(T base, T modified) : mBase (base), mModified (modified) {}
template<typename T>
const T& Stat<T>::getBase() const
{
return mBase;
}
template<typename T>
T Stat<T>::getModified() const
{
return std::max(static_cast<T>(0), mModified);
}
template<typename T>
T Stat<T>::getModifier() const
{
return mModified-mBase;
}
template<typename T>
void Stat<T>::set (const T& value)
{
mBase = mModified = value;
}
template<typename T>
void Stat<T>::modify(const T& diff)
{
mBase += diff;
if(mBase >= static_cast<T>(0))
mModified += diff;
else
{
mModified += diff - mBase;
mBase = static_cast<T>(0);
}
}
template<typename T>
void Stat<T>::setBase (const T& value)
{
T diff = value - mBase;
mBase = value;
mModified += diff;
}
template<typename T>
void Stat<T>::setModified (T value, const T& min, const T& max)
{
T diff = value - mModified;
if (mBase+diff<min)
{
value = min + (mModified - mBase);
diff = value - mModified;
}
else if (mBase+diff>max)
{
value = max + (mModified - mBase);
diff = value - mModified;
}
mModified = value;
mBase += diff;
}
template<typename T>
void Stat<T>::setModifier (const T& modifier)
{
mModified = mBase + modifier;
}
template<typename T>
void Stat<T>::writeState (ESM::StatState<T>& state) const
{
state.mBase = mBase;
state.mMod = mModified;
}
template<typename T>
void Stat<T>::readState (const ESM::StatState<T>& state)
{
mBase = state.mBase;
mModified = state.mMod;
}
template<typename T>
DynamicStat<T>::DynamicStat() : mStatic (0), mCurrent (0) {}
template<typename T>
DynamicStat<T>::DynamicStat(T base) : mStatic (base), mCurrent (base) {}
template<typename T>
DynamicStat<T>::DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {}
template<typename T>
DynamicStat<T>::DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {}
template<typename T>
const T& DynamicStat<T>::getBase() const
{
return mStatic.getBase();
}
template<typename T>
T DynamicStat<T>::getModified() const
{
return mStatic.getModified();
}
template<typename T>
const T& DynamicStat<T>::getCurrent() const
{
return mCurrent;
}
template<typename T>
void DynamicStat<T>::set (const T& value)
{
mStatic.set (value);
mCurrent = value;
}
template<typename T>
void DynamicStat<T>::setBase (const T& value)
{
mStatic.setBase (value);
if (mCurrent>getModified())
mCurrent = getModified();
}
template<typename T>
void DynamicStat<T>::setModified (T value, const T& min, const T& max)
{
mStatic.setModified (value, min, max);
if (mCurrent>getModified())
mCurrent = getModified();
}
template<typename T>
void DynamicStat<T>::modify (const T& diff, bool allowCurrentDecreaseBelowZero)
{
mStatic.modify (diff);
setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);
}
template<typename T>
void DynamicStat<T>::setCurrent (const T& value, bool allowDecreaseBelowZero)
{
if (value > mCurrent)
{
// increase
mCurrent = value;
if (mCurrent > getModified())
mCurrent = getModified();
}
else if (value > 0 || allowDecreaseBelowZero)
{
// allowed decrease
mCurrent = value;
}
else if (mCurrent > 0)
{
// capped decrease
mCurrent = 0;
}
}
template<typename T>
void DynamicStat<T>::setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero)
{
T diff = modifier - mStatic.getModifier();
mStatic.setModifier (modifier);
setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);
}
template<typename T>
void DynamicStat<T>::writeState (ESM::StatState<T>& state) const
{
mStatic.writeState (state);
state.mCurrent = mCurrent;
}
template<typename T>
void DynamicStat<T>::readState (const ESM::StatState<T>& state)
{
mStatic.readState (state);
mCurrent = state.mCurrent;
}
AttributeValue::AttributeValue() :
mBase(0), mModifier(0), mDamage(0)
{
}
int AttributeValue::getModified() const
{
return std::max(0, mBase - (int) mDamage + mModifier);
}
int AttributeValue::getBase() const
{
return mBase;
}
int AttributeValue::getModifier() const
{
return mModifier;
}
void AttributeValue::setBase(int base)
{
mBase = std::max(0, base);
}
void AttributeValue::setModifier(int mod)
{
mModifier = mod;
}
void AttributeValue::damage(float damage)
{
mDamage += std::min(damage, (float)getModified());
}
void AttributeValue::restore(float amount)
{
mDamage -= std::min(mDamage, amount);
}
float AttributeValue::getDamage() const
{
return mDamage;
}
void AttributeValue::writeState (ESM::StatState<int>& state) const
{
state.mBase = mBase;
state.mMod = mModifier;
state.mDamage = mDamage;
}
void AttributeValue::readState (const ESM::StatState<int>& state)
{
mBase = state.mBase;
mModifier = state.mMod;
mDamage = state.mDamage;
}
SkillValue::SkillValue() :
mProgress(0)
{
}
float SkillValue::getProgress() const
{
return mProgress;
}
void SkillValue::setProgress(float progress)
{
mProgress = progress;
}
void SkillValue::writeState (ESM::StatState<int>& state) const
{
AttributeValue::writeState (state);
state.mProgress = mProgress;
}
void SkillValue::readState (const ESM::StatState<int>& state)
{
AttributeValue::readState (state);
mProgress = state.mProgress;
}
} }
void MWMechanics::AttributeValue::readState (const ESM::StatState<int>& state) template class MWMechanics::Stat<int>;
{ template class MWMechanics::Stat<float>;
mBase = state.mBase; template class MWMechanics::DynamicStat<int>;
mModifier = state.mMod; template class MWMechanics::DynamicStat<float>;
mDamage = state.mDamage;
}
void MWMechanics::SkillValue::writeState (ESM::StatState<int>& state) const
{
AttributeValue::writeState (state);
state.mProgress = mProgress;
}
void MWMechanics::SkillValue::readState (const ESM::StatState<int>& state)
{
AttributeValue::readState (state);
mProgress = state.mProgress;
}

View file

@ -1,9 +1,14 @@
#ifndef GAME_MWMECHANICS_STAT_H #ifndef GAME_MWMECHANICS_STAT_H
#define GAME_MWMECHANICS_STAT_H #define GAME_MWMECHANICS_STAT_H
#include <algorithm>
#include <limits> #include <limits>
#include <components/esm/statstate.hpp> namespace ESM
{
template<typename T>
struct StatState;
}
namespace MWMechanics namespace MWMechanics
{ {
@ -16,87 +21,28 @@ namespace MWMechanics
public: public:
typedef T Type; typedef T Type;
Stat() : mBase (0), mModified (0) {} Stat();
Stat(T base) : mBase (base), mModified (base) {} Stat(T base);
Stat(T base, T modified) : mBase (base), mModified (modified) {} Stat(T base, T modified);
const T& getBase() const const T& getBase() const;
{
return mBase;
}
T getModified() const T getModified() const;
{ T getModifier() const;
return std::max(static_cast<T>(0), mModified);
}
T getModifier() const
{
return mModified-mBase;
}
/// Set base and modified to \a value. /// Set base and modified to \a value.
void set (const T& value) void set (const T& value);
{ void modify(const T& diff);
mBase = mModified = value;
}
void modify(const T& diff)
{
mBase += diff;
if(mBase >= static_cast<T>(0))
mModified += diff;
else
{
mModified += diff - mBase;
mBase = static_cast<T>(0);
}
}
/// Set base and adjust modified accordingly. /// Set base and adjust modified accordingly.
void setBase (const T& value) void setBase (const T& value);
{
T diff = value - mBase;
mBase = value;
mModified += diff;
}
/// Set modified value an adjust base accordingly. /// Set modified value an adjust base accordingly.
void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max()) void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());
{ void setModifier (const T& modifier);
T diff = value - mModified;
if (mBase+diff<min) void writeState (ESM::StatState<T>& state) const;
{ void readState (const ESM::StatState<T>& state);
value = min + (mModified - mBase);
diff = value - mModified;
}
else if (mBase+diff>max)
{
value = max + (mModified - mBase);
diff = value - mModified;
}
mModified = value;
mBase += diff;
}
void setModifier (const T& modifier)
{
mModified = mBase + modifier;
}
void writeState (ESM::StatState<T>& state) const
{
state.mBase = mBase;
state.mMod = mModified;
}
void readState (const ESM::StatState<T>& state)
{
mBase = state.mBase;
mModified = state.mMod;
}
}; };
template<typename T> template<typename T>
@ -121,98 +67,32 @@ namespace MWMechanics
public: public:
typedef T Type; typedef T Type;
DynamicStat() : mStatic (0), mCurrent (0) {} DynamicStat();
DynamicStat(T base) : mStatic (base), mCurrent (base) {} DynamicStat(T base);
DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {} DynamicStat(T base, T modified, T current);
DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {} DynamicStat(const Stat<T> &stat, T current);
const T& getBase() const const T& getBase() const;
{ T getModified() const;
return mStatic.getBase(); const T& getCurrent() const;
}
T getModified() const
{
return mStatic.getModified();
}
const T& getCurrent() const
{
return mCurrent;
}
/// Set base, modified and current to \a value. /// Set base, modified and current to \a value.
void set (const T& value) void set (const T& value);
{
mStatic.set (value);
mCurrent = value;
}
/// Set base and adjust modified accordingly. /// Set base and adjust modified accordingly.
void setBase (const T& value) void setBase (const T& value);
{
mStatic.setBase (value);
if (mCurrent>getModified())
mCurrent = getModified();
}
/// Set modified value an adjust base accordingly. /// Set modified value an adjust base accordingly.
void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max()) void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());
{
mStatic.setModified (value, min, max);
if (mCurrent>getModified())
mCurrent = getModified();
}
/// Change modified relatively. /// Change modified relatively.
void modify (const T& diff, bool allowCurrentDecreaseBelowZero=false) void modify (const T& diff, bool allowCurrentDecreaseBelowZero=false);
{
mStatic.modify (diff);
setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);
}
void setCurrent (const T& value, bool allowDecreaseBelowZero = false) void setCurrent (const T& value, bool allowDecreaseBelowZero = false);
{ void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false);
if (value > mCurrent)
{
// increase
mCurrent = value;
if (mCurrent > getModified()) void writeState (ESM::StatState<T>& state) const;
mCurrent = getModified(); void readState (const ESM::StatState<T>& state);
}
else if (value > 0 || allowDecreaseBelowZero)
{
// allowed decrease
mCurrent = value;
}
else if (mCurrent > 0)
{
// capped decrease
mCurrent = 0;
}
}
void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false)
{
T diff = modifier - mStatic.getModifier();
mStatic.setModifier (modifier);
setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);
}
void writeState (ESM::StatState<T>& state) const
{
mStatic.writeState (state);
state.mCurrent = mCurrent;
}
void readState (const ESM::StatState<T>& state)
{
mStatic.readState (state);
mCurrent = state.mCurrent;
}
}; };
template<typename T> template<typename T>
@ -236,26 +116,25 @@ namespace MWMechanics
float mDamage; // needs to be float to allow continuous damage float mDamage; // needs to be float to allow continuous damage
public: public:
AttributeValue() : mBase(0), mModifier(0), mDamage(0) {} AttributeValue();
int getModified() const { return std::max(0, mBase - (int) mDamage + mModifier); } int getModified() const;
int getBase() const { return mBase; } int getBase() const;
int getModifier() const { return mModifier; } int getModifier() const;
void setBase(int base) { mBase = std::max(0, base); } void setBase(int base);
void setModifier(int mod) { mModifier = mod; } void setModifier(int mod);
// Maximum attribute damage is limited to the modified value. // Maximum attribute damage is limited to the modified value.
// Note: I think MW applies damage directly to mModified, since you can also // Note: I think MW applies damage directly to mModified, since you can also
// "restore" drained attributes. We need to rewrite the magic effect system to support this. // "restore" drained attributes. We need to rewrite the magic effect system to support this.
void damage(float damage) { mDamage += std::min(damage, (float)getModified()); } void damage(float damage);
void restore(float amount) { mDamage -= std::min(mDamage, amount); } void restore(float amount);
float getDamage() const { return mDamage; } float getDamage() const;
void writeState (ESM::StatState<int>& state) const; void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state); void readState (const ESM::StatState<int>& state);
}; };
@ -263,12 +142,11 @@ namespace MWMechanics
{ {
float mProgress; float mProgress;
public: public:
SkillValue() : mProgress(0) {} SkillValue();
float getProgress() const { return mProgress; } float getProgress() const;
void setProgress(float progress) { mProgress = progress; } void setProgress(float progress);
void writeState (ESM::StatState<int>& state) const; void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state); void readState (const ESM::StatState<int>& state);
}; };

View file

@ -267,6 +267,8 @@ namespace MWRender
Animation::~Animation() Animation::~Animation()
{ {
setLightEffect(0.f);
if (mObjectRoot) if (mObjectRoot)
mInsert->removeChild(mObjectRoot); mInsert->removeChild(mObjectRoot);
} }
@ -1224,6 +1226,36 @@ namespace MWRender
return found->second; return found->second;
} }
void Animation::setLightEffect(float effect)
{
if (effect == 0)
{
if (mGlowLight)
{
mInsert->removeChild(mGlowLight);
mGlowLight = NULL;
}
}
else
{
if (!mGlowLight)
{
mGlowLight = new SceneUtil::LightSource;
mGlowLight->setLight(new osg::Light);
osg::Light* light = mGlowLight->getLight();
light->setDiffuse(osg::Vec4f(0,0,0,0));
light->setSpecular(osg::Vec4f(0,0,0,0));
light->setAmbient(osg::Vec4f(1.5f,1.5f,1.5f,1.f));
mInsert->addChild(mGlowLight);
}
effect += 3;
osg::Light* light = mGlowLight->getLight();
mGlowLight->setRadius(effect * 66.f);
light->setLinearAttenuation(0.5f/effect);
}
}
void Animation::addControllers() void Animation::addControllers()
{ {
mHeadController = NULL; mHeadController = NULL;

View file

@ -21,6 +21,11 @@ namespace NifOsg
class KeyframeController; class KeyframeController;
} }
namespace SceneUtil
{
class LightSource;
}
namespace MWRender namespace MWRender
{ {
@ -202,6 +207,8 @@ protected:
float mHeadYawRadians; float mHeadYawRadians;
float mHeadPitchRadians; float mHeadPitchRadians;
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
/* Sets the appropriate animations on the bone groups based on priority. /* Sets the appropriate animations on the bone groups based on priority.
*/ */
void resetActiveGroups(); void resetActiveGroups();
@ -377,7 +384,7 @@ public:
// TODO: move outside of this class // TODO: move outside of this class
/// Makes this object glow, by placing a Light in its center. /// Makes this object glow, by placing a Light in its center.
/// @param effect Controls the radius and intensity of the light. /// @param effect Controls the radius and intensity of the light.
virtual void setLightEffect(float effect) {} virtual void setLightEffect(float effect);
virtual void setHeadPitch(float pitchRadians); virtual void setHeadPitch(float pitchRadians);
virtual void setHeadYaw(float yawRadians); virtual void setHeadYaw(float yawRadians);

View file

@ -88,7 +88,8 @@ namespace MWRender
struct ImageDest struct ImageDest
{ {
ImageDest() ImageDest()
: mFramesUntilDone(3) // wait an extra frame to ensure the draw thread has completed its frame. : mX(0), mY(0)
, mFramesUntilDone(3) // wait an extra frame to ensure the draw thread has completed its frame.
{ {
} }

View file

@ -459,7 +459,7 @@ void NpcAnimation::updateParts()
}; };
static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]);
bool wasArrowAttached = 0;//(mAmmunition.get() != NULL); bool wasArrowAttached = (mAmmunition.get() != NULL);
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)
@ -622,7 +622,7 @@ void NpcAnimation::updateParts()
continue; continue;
} }
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) if ((!mNpc->isMale()) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
{ {
// Allow opposite gender's parts as fallback if parts for our gender are missing // Allow opposite gender's parts as fallback if parts for our gender are missing
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
@ -850,7 +850,6 @@ void NpcAnimation::addControllers()
Animation::addControllers(); Animation::addControllers();
mFirstPersonNeckController = NULL; mFirstPersonNeckController = NULL;
mHeadController = NULL;
WeaponAnimation::deleteControllers(); WeaponAnimation::deleteControllers();
if (mViewMode == VM_FirstPerson) if (mViewMode == VM_FirstPerson)

View file

@ -46,7 +46,8 @@ namespace MWRender
{ {
public: public:
StateUpdater() StateUpdater()
: mFogEnd(0.f) : mFogStart(0.f)
, mFogEnd(0.f)
, mWireframe(false) , mWireframe(false)
{ {
} }
@ -56,7 +57,6 @@ namespace MWRender
osg::LightModel* lightModel = new osg::LightModel; osg::LightModel* lightModel = new osg::LightModel;
stateset->setAttribute(lightModel, osg::StateAttribute::ON); stateset->setAttribute(lightModel, osg::StateAttribute::ON);
osg::Fog* fog = new osg::Fog; osg::Fog* fog = new osg::Fog;
fog->setStart(1);
fog->setMode(osg::Fog::LINEAR); fog->setMode(osg::Fog::LINEAR);
stateset->setAttributeAndModes(fog, osg::StateAttribute::ON); stateset->setAttributeAndModes(fog, osg::StateAttribute::ON);
if (mWireframe) if (mWireframe)
@ -75,6 +75,7 @@ namespace MWRender
lightModel->setAmbientIntensity(mAmbientColor); lightModel->setAmbientIntensity(mAmbientColor);
osg::Fog* fog = static_cast<osg::Fog*>(stateset->getAttribute(osg::StateAttribute::FOG)); osg::Fog* fog = static_cast<osg::Fog*>(stateset->getAttribute(osg::StateAttribute::FOG));
fog->setColor(mFogColor); fog->setColor(mFogColor);
fog->setStart(mFogStart);
fog->setEnd(mFogEnd); fog->setEnd(mFogEnd);
} }
@ -88,6 +89,11 @@ namespace MWRender
mFogColor = col; mFogColor = col;
} }
void setFogStart(float start)
{
mFogStart = start;
}
void setFogEnd(float end) void setFogEnd(float end)
{ {
mFogEnd = end; mFogEnd = end;
@ -110,6 +116,7 @@ namespace MWRender
private: private:
osg::Vec4f mAmbientColor; osg::Vec4f mAmbientColor;
osg::Vec4f mFogColor; osg::Vec4f mFogColor;
float mFogStart;
float mFogEnd; float mFogEnd;
bool mWireframe; bool mWireframe;
}; };
@ -118,6 +125,7 @@ namespace MWRender
: mViewer(viewer) : mViewer(viewer)
, mRootNode(rootNode) , mRootNode(rootNode)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mFogDepth(0.f)
, mNightEyeFactor(0.f) , mNightEyeFactor(0.f)
{ {
osg::ref_ptr<SceneUtil::LightManager> lightRoot = new SceneUtil::LightManager; osg::ref_ptr<SceneUtil::LightManager> lightRoot = new SceneUtil::LightManager;
@ -338,8 +346,9 @@ namespace MWRender
configureFog (cell->mAmbi.mFogDensity, color); configureFog (cell->mAmbi.mFogDensity, color);
} }
void RenderingManager::configureFog(float /* fogDepth */, const osg::Vec4f &color) void RenderingManager::configureFog(float fogDepth, const osg::Vec4f &color)
{ {
mFogDepth = fogDepth;
mFogColor = color; mFogColor = color;
} }
@ -364,11 +373,13 @@ namespace MWRender
if (mWater->isUnderwater(cameraPos)) if (mWater->isUnderwater(cameraPos))
{ {
setFogColor(osg::Vec4f(0.090195f, 0.115685f, 0.12745f, 1.f)); setFogColor(osg::Vec4f(0.090195f, 0.115685f, 0.12745f, 1.f));
mStateUpdater->setFogStart(0.f);
mStateUpdater->setFogEnd(1000); mStateUpdater->setFogEnd(1000);
} }
else else
{ {
setFogColor(mFogColor); setFogColor(mFogColor);
mStateUpdater->setFogStart(mViewDistance * (1 - mFogDepth));
mStateUpdater->setFogEnd(mViewDistance); mStateUpdater->setFogEnd(mViewDistance);
} }
} }

View file

@ -190,6 +190,7 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater; osg::ref_ptr<StateUpdater> mStateUpdater;
float mFogDepth;
osg::Vec4f mFogColor; osg::Vec4f mFogColor;
osg::Vec4f mAmbientColor; osg::Vec4f mAmbientColor;

View file

@ -59,16 +59,11 @@ namespace MWRender
private: private:
osg::ref_ptr<osg::Group> mParent; osg::ref_ptr<osg::Group> mParent;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem; osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem;
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode; osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;
std::vector<Emitter> mEmitters; std::vector<Emitter> mEmitters;
float mRippleLifeTime;
float mRippleRotSpeed;
}; };
} }

View file

@ -8,6 +8,7 @@
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
#include <osg/TexEnvCombine> #include <osg/TexEnvCombine>
#include <osg/TexMat> #include <osg/TexMat>
#include <osg/Version>
#include <osgParticle/ParticleSystem> #include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater> #include <osgParticle/ParticleSystemUpdater>
@ -162,6 +163,12 @@ protected:
class CloudUpdater : public SceneUtil::StateSetUpdater class CloudUpdater : public SceneUtil::StateSetUpdater
{ {
public: public:
CloudUpdater()
: mAnimationTimer(0.f)
, mOpacity(0.f)
{
}
void setAnimationTimer(float timer) void setAnimationTimer(float timer)
{ {
mAnimationTimer = timer; mAnimationTimer = timer;
@ -721,7 +728,11 @@ public:
if (stateset->getAttribute(osg::StateAttribute::MATERIAL)) if (stateset->getAttribute(osg::StateAttribute::MATERIAL))
{ {
SceneUtil::CompositeStateSetUpdater* composite = NULL; SceneUtil::CompositeStateSetUpdater* composite = NULL;
#if OSG_MIN_VERSION_REQUIRED(3,3,3)
osg::Callback* callback = node.getUpdateCallback();
#else
osg::NodeCallback* callback = node.getUpdateCallback(); osg::NodeCallback* callback = node.getUpdateCallback();
#endif
while (callback) while (callback)
{ {
if ((composite = dynamic_cast<SceneUtil::CompositeStateSetUpdater*>(callback))) if ((composite = dynamic_cast<SceneUtil::CompositeStateSetUpdater*>(callback)))
@ -762,9 +773,6 @@ public:
mat->setColorMode(osg::Material::OFF); mat->setColorMode(osg::Material::OFF);
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON); stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
} }
private:
float mAlpha;
}; };
void SkyManager::createRain() void SkyManager::createRain()

View file

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/globalscript.hpp> #include <components/esm/globalscript.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"

View file

@ -6,6 +6,8 @@
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>

View file

@ -4,6 +4,7 @@
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/cellstate.hpp> #include <components/esm/cellstate.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

View file

@ -5,6 +5,7 @@
#include <components/esm/cellstate.hpp> #include <components/esm/cellstate.hpp>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp> #include <components/esm/objectstate.hpp>
#include <components/esm/containerstate.hpp> #include <components/esm/containerstate.hpp>

View file

@ -8,6 +8,7 @@
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
namespace MWWorld namespace MWWorld
{ {

View file

@ -1,6 +1,7 @@
#ifndef OPENMW_MWWORLD_ESMSTORE_H #ifndef OPENMW_MWWORLD_ESMSTORE_H
#define OPENMW_MWWORLD_ESMSTORE_H #define OPENMW_MWWORLD_ESMSTORE_H
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <components/esm/records.hpp> #include <components/esm/records.hpp>

View file

@ -6,6 +6,7 @@
#include <components/esm/loadench.hpp> #include <components/esm/loadench.hpp>
#include <components/esm/inventorystate.hpp> #include <components/esm/inventorystate.hpp>
#include <components/misc/rng.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"

View file

@ -2,6 +2,7 @@
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
#include <components/esm/esmwriter.hpp>
#include <components/esm/projectilestate.hpp> #include <components/esm/projectilestate.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>

View file

@ -3,6 +3,7 @@
#include <limits> #include <limits>
#include <components/nif/niffile.hpp> #include <components/nif/niffile.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/weatherstate.hpp> #include <components/esm/weatherstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -66,6 +67,10 @@ void WeatherManager::setFallbackWeather(Weather& weather,const std::string& name
weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View"); weather.mGlareView = mFallback->getFallbackFloat("Weather_"+upper+"_Glare_View");
weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture"); weather.mCloudTexture = mFallback->getFallbackString("Weather_"+upper+"_Cloud_Texture");
static const float fStromWindSpeed = mStore->get<ESM::GameSetting>().find("fStromWindSpeed")->getFloat();
weather.mIsStorm = weather.mWindSpeed > fStromWindSpeed;
bool usesPrecip = mFallback->getFallbackBool("Weather_"+upper+"_Using_Precip"); bool usesPrecip = mFallback->getFallbackBool("Weather_"+upper+"_Using_Precip");
if (usesPrecip) if (usesPrecip)
weather.mRainEffect = "meshes\\raindrop.nif"; weather.mRainEffect = "meshes\\raindrop.nif";
@ -79,7 +84,6 @@ Rain Height Max=700 ?
Rain Threshold=0.6 ? Rain Threshold=0.6 ?
Max Raindrops=650 ? Max Raindrops=650 ?
*/ */
weather.mIsStorm = (name == "ashstorm" || name == "blight");
mWeatherSettings[name] = weather; mWeatherSettings[name] = weather;
} }
@ -112,8 +116,8 @@ float WeatherManager::calculateAngleFade (const std::string& moonName, float ang
return 1.f; return 1.f;
} }
WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) : WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, MWWorld::Fallback* fallback, MWWorld::ESMStore* store) :
mHour(14), mWindSpeed(0.f), mIsStorm(false), mStormDirection(0,1,0), mFallback(fallback), mHour(14), mWindSpeed(0.f), mIsStorm(false), mStormDirection(0,1,0), mFallback(fallback), mStore(store),
mRendering(rendering), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mRendering(rendering), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true),
mRemainingTransitionTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mRemainingTransitionTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50),
mTimePassed(0), mWeatherUpdateTime(0), mThunderSoundDelay(0) mTimePassed(0), mWeatherUpdateTime(0), mThunderSoundDelay(0)

View file

@ -156,7 +156,8 @@ namespace MWWorld
class WeatherManager class WeatherManager
{ {
public: public:
WeatherManager(MWRender::RenderingManager*,MWWorld::Fallback* fallback); // Have to pass fallback and Store, can't use singleton since World isn't fully constructed yet at the time
WeatherManager(MWRender::RenderingManager*, MWWorld::Fallback* fallback, MWWorld::ESMStore* store);
~WeatherManager(); ~WeatherManager();
/** /**
@ -210,6 +211,7 @@ namespace MWWorld
std::string mPlayingSoundID; std::string mPlayingSoundID;
MWWorld::Fallback* mFallback; MWWorld::Fallback* mFallback;
MWWorld::ESMStore* mStore;
void setFallbackWeather(Weather& weather,const std::string& name); void setFallbackWeather(Weather& weather,const std::string& name);
MWRender::RenderingManager* mRendering; MWRender::RenderingManager* mRendering;

View file

@ -12,11 +12,15 @@
#include <osg/ComputeBoundsVisitor> #include <osg/ComputeBoundsVisitor>
#include <osg/PositionAttitudeTransform> #include <osg/PositionAttitudeTransform>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/compiler/locals.hpp> #include <components/compiler/locals.hpp>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include <components/esm/esmreader.hpp>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
@ -163,8 +167,6 @@ namespace MWWorld
mProjectileManager.reset(new ProjectileManager(rootNode, resourceSystem, mPhysics)); mProjectileManager.reset(new ProjectileManager(rootNode, resourceSystem, mPhysics));
mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback); mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
mEsm.resize(contentFiles.size()); mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn(); listener->loadingOn();
@ -193,6 +195,8 @@ namespace MWWorld
mGlobalVariables.fill (mStore); mGlobalVariables.fill (mStore);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback,&mStore);
mWorldScene = new Scene(*mRendering, mPhysics); mWorldScene = new Scene(*mRendering, mPhysics);
} }
@ -265,7 +269,7 @@ namespace MWWorld
// 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;
mWeatherManager = 0; mWeatherManager = 0;
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback,&mStore);
if (!mStartupScript.empty()) if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript); MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript);

View file

@ -52,6 +52,11 @@ namespace MWRender
class Camera; class Camera;
} }
namespace ToUTF8
{
class Utf8Encoder;
}
struct ContentLoader; struct ContentLoader;
namespace MWWorld namespace MWWorld

View file

@ -1,4 +1,6 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CreatureStats::load (ESMReader &esm) void ESM::CreatureStats::load (ESMReader &esm)
{ {

View file

@ -0,0 +1,52 @@
#include "statstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
template<typename T>
StatState<T>::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {}
template<typename T>
void StatState<T>::load(ESMReader &esm)
{
esm.getHNT(mBase, "STBA");
mMod = 0;
esm.getHNOT(mMod, "STMO");
mCurrent = 0;
esm.getHNOT(mCurrent, "STCU");
// mDamage was changed to a float; ensure backwards compatibility
T oldDamage = 0;
esm.getHNOT(oldDamage, "STDA");
mDamage = static_cast<float>(oldDamage);
esm.getHNOT(mDamage, "STDF");
mProgress = 0;
esm.getHNOT(mProgress, "STPR");
}
template<typename T>
void StatState<T>::save(ESMWriter &esm) const
{
esm.writeHNT("STBA", mBase);
if (mMod != 0)
esm.writeHNT("STMO", mMod);
if (mCurrent)
esm.writeHNT("STCU", mCurrent);
if (mDamage)
esm.writeHNT("STDF", mDamage);
if (mProgress)
esm.writeHNT("STPR", mProgress);
}
}
template struct ESM::StatState<int>;
template struct ESM::StatState<float>;

View file

@ -1,11 +1,11 @@
#ifndef OPENMW_ESM_STATSTATE_H #ifndef OPENMW_ESM_STATSTATE_H
#define OPENMW_ESM_STATSTATE_H #define OPENMW_ESM_STATSTATE_H
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM namespace ESM
{ {
class ESMReader;
class ESMWriter;
// format 0, saved games only // format 0, saved games only
template<typename T> template<typename T>
@ -23,48 +23,6 @@ namespace ESM
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;
}; };
template<typename T>
StatState<T>::StatState() : mBase (0), mMod (0), mCurrent (0), mDamage (0), mProgress (0) {}
template<typename T>
void StatState<T>::load (ESMReader &esm)
{
esm.getHNT (mBase, "STBA");
mMod = 0;
esm.getHNOT (mMod, "STMO");
mCurrent = 0;
esm.getHNOT (mCurrent, "STCU");
// mDamage was changed to a float; ensure backwards compatibility
T oldDamage = 0;
esm.getHNOT(oldDamage, "STDA");
mDamage = static_cast<float>(oldDamage);
esm.getHNOT (mDamage, "STDF");
mProgress = 0;
esm.getHNOT (mProgress, "STPR");
}
template<typename T>
void StatState<T>::save (ESMWriter &esm) const
{
esm.writeHNT ("STBA", mBase);
if (mMod != 0)
esm.writeHNT ("STMO", mMod);
if (mCurrent)
esm.writeHNT ("STCU", mCurrent);
if (mDamage)
esm.writeHNT ("STDF", mDamage);
if (mProgress)
esm.writeHNT ("STPR", mProgress);
}
} }
#endif #endif

View file

@ -119,7 +119,7 @@ class Drawable : public osg::Drawable {
// VBOs disabled due to crash in OSG: http://forum.openscenegraph.org/viewtopic.php?t=14909 // VBOs disabled due to crash in OSG: http://forum.openscenegraph.org/viewtopic.php?t=14909
osg::GLBufferObject* bufferobject = 0;//state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : 0; osg::GLBufferObject* bufferobject = 0;//state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : 0;
if (bufferobject) if (0)//bufferobject)
{ {
state->bindVertexBufferObject(bufferobject); state->bindVertexBufferObject(bufferobject);
@ -165,6 +165,8 @@ public:
Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY) Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)
: osg::Drawable(copy, copyop) : osg::Drawable(copy, copyop)
, mParent(copy.mParent) , mParent(copy.mParent)
, mWriteTo(0)
, mReadFrom(0)
{ {
} }
@ -521,12 +523,8 @@ void RenderManager::destroyAllResources()
bool RenderManager::checkTexture(MyGUI::ITexture* _texture) bool RenderManager::checkTexture(MyGUI::ITexture* _texture)
{ {
for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item) // We support external textures that aren't registered via this manager, so can't implement this method sensibly.
{ return true;
if (item->second == _texture)
return true;
}
return false;
} }
} }

View file

@ -407,6 +407,8 @@ FlipController::FlipController(int texSlot, float delta, std::vector<osg::ref_pt
} }
FlipController::FlipController() FlipController::FlipController()
: mTexSlot(0)
, mDelta(0.f)
{ {
} }
@ -434,6 +436,7 @@ ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemCo
} }
ParticleSystemController::ParticleSystemController() ParticleSystemController::ParticleSystemController()
: mEmitStart(0.f), mEmitStop(0.f)
{ {
} }

View file

@ -45,7 +45,7 @@ void InverseWorldMatrix::operator()(osg::Node *node, osg::NodeVisitor *nv)
osg::NodePath path = nv->getNodePath(); osg::NodePath path = nv->getNodePath();
path.pop_back(); path.pop_back();
osg::MatrixTransform* trans = dynamic_cast<osg::MatrixTransform*>(node); osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);
osg::Matrix mat = osg::computeLocalToWorld( path ); osg::Matrix mat = osg::computeLocalToWorld( path );
mat.orthoNormalize(mat); // don't undo the scale mat.orthoNormalize(mat); // don't undo the scale

View file

@ -11,6 +11,7 @@ namespace SceneUtil
class ControllerSource class ControllerSource
{ {
public: public:
virtual ~ControllerSource() { }
virtual float getValue(osg::NodeVisitor* nv) = 0; virtual float getValue(osg::NodeVisitor* nv) = 0;
}; };

View file

@ -244,7 +244,7 @@ namespace SceneUtil
bool sortLights (const LightManager::LightSourceTransform* left, const LightManager::LightSourceTransform* right) bool sortLights (const LightManager::LightSourceTransform* left, const LightManager::LightSourceTransform* right)
{ {
return left->mViewBound.center().length2() < right->mViewBound.center().length2(); return left->mViewBound.center().length2() - left->mViewBound.radius2()/4.f < right->mViewBound.center().length2() - right->mViewBound.radius2()/4.f;
} }
void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv) void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv)