1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-02-28 13:09:40 +00:00

Merge remote-tracking branch 'upstream/master' into HEAD

This commit is contained in:
scrawl 2015-07-11 00:44:17 +02:00
commit 05400d469a
83 changed files with 2340 additions and 1611 deletions

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 apt-add-repository ppa:openmw/openmw
echo "yes" | sudo apt-add-repository ppa:boost-latest/ppa
sudo apt-get update -qq
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 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

View file

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

View file

@ -327,7 +327,7 @@ namespace ESSImport
ESM::ESMWriter writer;
writer.setFormat (ESM::Header::CurrentFormat);
writer.setFormat (ESM::SavedGame::sCurrentFormat);
std::ofstream stream(mOutFile.c_str(), std::ios::binary);
// all unused

View file

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

View file

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

View file

@ -479,6 +479,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mMetaData.addColumn (new StringIdColumn<MetaData> (true));
mMetaData.addColumn (new RecordStateColumn<MetaData>);
mMetaData.addColumn (new FixedRecordTypeColumn<MetaData> (UniversalId::Type_MetaData));
mMetaData.addColumn (new FormatColumn<MetaData>);
mMetaData.addColumn (new AuthorColumn<MetaData>);
mMetaData.addColumn (new FileDescriptionColumn<MetaData>);

View file

@ -161,8 +161,19 @@ namespace CSMWorld
template<typename ESXRecordT, typename IdAccessorT>
int NestedIdCollection<ESXRecordT, IdAccessorT>::getNestedColumnsCount(int row, int column) const
{
return getAdapter(Collection<ESXRecordT, IdAccessorT>::getColumn(column)).getColumnsCount(
Collection<ESXRecordT, IdAccessorT>::getRecord(row));
const ColumnBase &nestedColumn = Collection<ESXRecordT, IdAccessorT>::getColumn(column);
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>

View file

@ -11,6 +11,7 @@
#include <QPainter>
#include <QContextMenuEvent>
#include <QMouseEvent>
#include <QSortFilterProxyModel>
#include "../../model/tools/reportmodel.hpp"
@ -63,7 +64,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
for (QModelIndexList::const_iterator iter (selectedRows.begin());
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')
{
@ -152,7 +153,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
setSelectionBehavior (QAbstractItemView::SelectRows);
setSelectionMode (QAbstractItemView::ExtendedSelection);
setModel (mModel);
mProxyModel = new QSortFilterProxyModel (this);
mProxyModel->setSourceModel (mModel);
setModel (mProxyModel);
setColumnHidden (2, true);
mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0,
@ -197,7 +201,7 @@ std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() co
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
ids.push_back (mModel->getUniversalId (iter->row()));
ids.push_back (mModel->getUniversalId (mProxyModel->mapToSource (*iter).row()));
}
return ids;
@ -245,13 +249,22 @@ std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<int> rows;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++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')
indices.push_back (iter->row());
indices.push_back (*iter);
}
}
else
@ -279,18 +292,28 @@ void CSVTools::ReportTable::showSelection()
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++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()
{
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)
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();
}

View file

@ -6,6 +6,7 @@
#include "../world/dragrecordtable.hpp"
class QAction;
class QSortFilterProxyModel;
namespace CSMTools
{
@ -31,6 +32,7 @@ namespace CSVTools
Action_EditAndRemove
};
QSortFilterProxyModel *mProxyModel;
CSMTools::ReportModel *mModel;
CSVWorld::CommandDelegate *mIdTypeDelegate;
QAction *mShowAction;
@ -63,11 +65,14 @@ namespace CSVTools
void clear();
// Return indices of rows that are suitable for replacement.
//
// \param selection Only list selected rows.
/// Return indices of rows that are suitable for replacement.
///
/// \param selection Only list selected rows.
///
/// \return rows in the original model
std::vector<int> getReplaceIndices (bool selection) const;
/// \param index row in the original model
void flagAsReplaced (int index);
private slots:

View file

@ -19,6 +19,7 @@
#include <QComboBox>
#include <QHeaderView>
#include <QScrollBar>
#include <QMenu>
#include "../../model/world/nestedtableproxymodel.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=====================================================
*/
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()
{
for (unsigned i = 0; i < mNestedModels.size(); ++i)
@ -455,6 +587,11 @@ void CSVWorld::EditWidget::remake(int row)
tablesLayout->addWidget(label);
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))
{
@ -488,6 +625,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false);
label->setEnabled(false);
}
createEditorContextMenu(editor, display, row);
}
}
else
@ -539,6 +678,8 @@ void CSVWorld::EditWidget::remake(int row)
editor->setEnabled(false);
label->setEnabled(false);
}
createEditorContextMenu(editor, display, row);
}
}
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);
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)

View file

@ -1,6 +1,7 @@
#ifndef CSV_WORLD_DIALOGUESUBVIEW_H
#define CSV_WORLD_DIALOGUESUBVIEW_H
#include <set>
#include <map>
#include <memory>
@ -11,12 +12,14 @@
#include "../../model/world/columnbase.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "../../model/world/universalid.hpp"
class QDataWidgetMapper;
class QSize;
class QEvent;
class QLabel;
class QVBoxLayout;
class QMenu;
namespace CSMWorld
{
@ -149,6 +152,36 @@ namespace CSVWorld
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
{
Q_OBJECT
@ -162,6 +195,9 @@ namespace CSVWorld
CSMDoc::Document& mDocument;
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:
EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table,
@ -171,6 +207,9 @@ namespace CSVWorld
virtual ~EditWidget();
void remake(int row);
signals:
void editIdRequest(const CSMWorld::UniversalId &id, const std::string &hint);
};
class SimpleDialogueSubView : public CSVDoc::SubView

View file

@ -55,12 +55,10 @@ void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event)
if (index.flags() & Qt::ItemIsEditable)
{
event->accept();
return;
}
}
else
{
event->ignore();
}
}
void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event)

View file

@ -1,15 +1,18 @@
#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 <QContextMenuEvent>
#include <QMenu>
#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,
CSMWorld::UniversalId id,
CSMWorld::NestedTableProxyModel* model,
@ -55,6 +58,9 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document,
connect(mRemoveRowAction, SIGNAL(triggered()),
this, SLOT(removeRowActionTriggered()));
mEditIdAction = new TableEditIdAction(*this, this);
connect(mEditIdAction, SIGNAL(triggered()), this, SLOT(editCell()));
}
std::vector<CSMWorld::UniversalId> CSVWorld::NestedTable::getDraggedRecords() const
@ -69,6 +75,15 @@ void CSVWorld::NestedTable::contextMenuEvent (QContextMenuEvent *event)
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)
menu.addAction(mRemoveRowAction);
@ -92,3 +107,8 @@ void CSVWorld::NestedTable::addNewRowActionTriggered()
selectionModel()->selectedRows().size(),
mModel->getParentColumn()));
}
void CSVWorld::NestedTable::editCell()
{
emit editRequest(mEditIdAction->getCurrentId(), "");
}

View file

@ -22,12 +22,15 @@ namespace CSMDoc
namespace CSVWorld
{
class TableEditIdAction;
class NestedTable : public DragRecordTable
{
Q_OBJECT
QAction *mAddNewRowAction;
QAction *mRemoveRowAction;
TableEditIdAction *mEditIdAction;
CSMWorld::NestedTableProxyModel* mModel;
CSMWorld::CommandDispatcher *mDispatcher;
@ -46,6 +49,11 @@ namespace CSVWorld
void removeRowActionTriggered();
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())
{
QString str = textCursor().selection().toPlainText();
int selectedLines = str.count("\n")+1;
int offset = str.count("\n");
if(textCursor().position() < textCursor().anchor())
endBlock += selectedLines;
endBlock += offset;
else
startBlock -= selectedLines;
startBlock -= offset;
}
painter.setBackgroundMode(Qt::OpaqueMode);
QFont font = painter.font();

View file

@ -27,6 +27,7 @@
#include "../../model/settings/usersettings.hpp"
#include "recordstatusdelegate.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp"
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
int currentRow = rowAt(event->y());
int currentColumn = columnAt(event->x());
if (mEditIdAction->isValidIdCell(currentRow, currentColumn))
{
// Request UniversalId editing from table columns.
int currRow = rowAt( event->y() ),
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() );
}
mEditIdAction->setCell(currentRow, currentColumn);
menu.addAction(mEditIdAction);
menu.addSeparator();
}
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()));
addAction (mMoveDownAction);
mEditCellAction = new QAction( tr("Edit Cell"), this );
connect( mEditCellAction, SIGNAL(triggered()), this, SLOT(editCell()) );
addAction( mEditCellAction );
mViewAction = new QAction (tr ("View"), this);
connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord()));
addAction (mViewAction);
@ -387,6 +364,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction);
mEditIdAction = new TableEditIdAction (*this, this);
connect (mEditIdAction, SIGNAL (triggered()), this, SLOT (editCell()));
addAction (mEditIdAction);
connect (mProxyModel, SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
@ -522,7 +503,7 @@ void CSVWorld::Table::moveDownRecord()
void CSVWorld::Table::editCell()
{
emit editRequest( mEditCellId, std::string() );
emit editRequest(mEditIdAction->getCurrentId(), "");
}
void CSVWorld::Table::viewRecord()

View file

@ -30,6 +30,7 @@ namespace CSMWorld
namespace CSVWorld
{
class CommandDelegate;
class TableEditIdAction;
///< Table widget
class Table : public DragRecordTable
@ -57,15 +58,14 @@ namespace CSVWorld
QAction *mMoveUpAction;
QAction *mMoveDownAction;
QAction *mViewAction;
QAction *mEditCellAction;
QAction *mPreviewAction;
QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction;
TableEditIdAction *mEditIdAction;
CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTableBase *mModel;
int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher;
CSMWorld::UniversalId mEditCellId;
std::map<Qt::KeyboardModifiers, DoubleClickAction> mDoubleClickActions;
bool mJumpToAddedRecord;
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)
// For OutputDebugString
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
// makes __argc and __argv available on windows
#include <cstdlib>

View file

@ -798,12 +798,8 @@ namespace MWClass
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (state.mVersion > 0)
{
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
@ -818,7 +814,9 @@ namespace MWClass
ptr.getRefData().setCustomData (data.release());
}
*/
}
else
ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());

View file

@ -1233,18 +1233,17 @@ namespace MWClass
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (state.mVersion > 0)
{
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<NpcCustomData> data (new NpcCustomData);
ptr.getRefData().setCustomData (data.release());
}
*/
}
else
ensureCustomData(ptr); // in openmw 0.30 savegames not all state was saved yet, so need to load it regardless.
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());

View file

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

View file

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

View file

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

View file

@ -72,6 +72,11 @@ namespace MWGui
{
MarkerUserData(MWRender::LocalMap* 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_ImageBox.h>
#include <components/esm/esmwriter.hpp>
#include <components/esm/quickkeys.hpp>
#include "../mwworld/inventorystore.hpp"

View file

@ -22,6 +22,9 @@
#include <components/sdlutil/sdlcursormanager.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/fontloader/fontloader.hpp>
#include <components/resource/resourcesystem.hpp>

View file

@ -1,7 +1,6 @@
#include "inputmanagerimp.hpp"
#include <cmath>
#include <iostream>
#include <boost/lexical_cast.hpp>
@ -22,7 +21,6 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
@ -31,8 +29,6 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwdialogue/dialoguemanagerimp.hpp"
using namespace ICS;
namespace MWInput
@ -207,7 +203,7 @@ namespace MWInput
if (mControlSwitch["playercontrols"])
{
if (action == A_Use)
mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue != 0);
mPlayer->setAttackingOrSpell(currentValue != 0);
else if (action == A_Jump)
mAttemptJump = (currentValue == 1.0 && previousValue == 0.0);
}

View file

@ -4,7 +4,10 @@
#include <osg/PositionAttitudeTransform>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/esmreader.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
@ -1086,6 +1089,9 @@ namespace MWMechanics
iter->second->getCharacterController()->setActive(inProcessingRange);
if (iter->first == player)
iter->second->getCharacterController()->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell());
if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
{
updateActor(iter->first, duration);

View file

@ -31,8 +31,6 @@ namespace
//chooses an attack depending on probability to avoid uniformity
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,
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.
struct AiCombatStorage : AiTemporaryBase
{
float mTimerAttack;
float mAttackCooldown;
float mTimerReact;
float mTimerCombatMove;
bool mReadyToAttack;
@ -95,15 +93,13 @@ namespace MWMechanics
boost::shared_ptr<Action> mCurrentAction;
float mActionCooldown;
float mStrength;
float mMinMaxAttackDuration[3][2];
bool mMinMaxAttackDurationInitialised;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
osg::Vec3f mLastActorPos;
MWMechanics::Movement mMovement;
AiCombatStorage():
mTimerAttack(0),
mAttackCooldown(0),
mTimerReact(0),
mTimerCombatMove(0),
mReadyToAttack(false),
@ -115,7 +111,6 @@ namespace MWMechanics
mCurrentAction(),
mActionCooldown(0),
mStrength(),
mMinMaxAttackDurationInitialised(false),
mForceNoShortcut(false),
mLastActorPos(0,0,0),
mMovement(){}
@ -234,43 +229,13 @@ namespace MWMechanics
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& readyToAttack = storage.mReadyToAttack;
float& timerAttack = storage.mTimerAttack;
bool& minMaxAttackDurationInitialised = storage.mMinMaxAttackDurationInitialised;
float (&minMaxAttackDuration)[3][2] = storage.mMinMaxAttackDuration;
if(readyToAttack)
{
if (!minMaxAttackDurationInitialised)
{
// 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;
if (attack && (characterController.getAttackStrength() >= storage.mStrength || characterController.readyToPrepareAttack()))
attack = false;
}
actorClass.getCreatureStats(actor).setAttackingOrSpell(attack);
characterController.setAttackingOrSpell(attack);
float& actionCooldown = storage.mActionCooldown;
actionCooldown -= duration;
@ -301,10 +266,6 @@ namespace MWMechanics
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);
if (actionCooldown > 0)
@ -313,11 +274,7 @@ namespace MWMechanics
float rangeAttack = 0;
float rangeFollow = 0;
boost::shared_ptr<Action>& currentAction = storage.mCurrentAction;
// TODO: upperBodyReady() works fine for checking if we can start an attack,
// 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())
if (characterController.readyToPrepareAttack())
{
currentAction = prepareNextAction(actor, target);
actionCooldown = currentAction->getActionCooldown();
@ -334,9 +291,6 @@ namespace MWMechanics
// Get weapon characteristics
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
MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(actorClass.getCreatureStats(actor), actorClass.getInventoryStore(actor), &weaptype);
@ -386,32 +340,36 @@ namespace MWMechanics
float& strength = storage.mStrength;
// start new attack
if(readyToAttack)
if(readyToAttack && characterController.readyToStartAttack())
{
if(timerAttack <= -attacksPeriod)
if (storage.mAttackCooldown <= 0)
{
attack = true; // attack starts just now
characterController.setAttackingOrSpell(attack);
if (!distantCombat) attackType = chooseBestAttack(weapon, movement);
else attackType = ESM::Weapon::AT_Chop; // cause it's =0
if (!distantCombat)
chooseBestAttack(weapon, movement);
strength = Misc::Rng::rollClosedProbability();
// Note: may be 0 for some animations
timerAttack = minMaxAttackDuration[attackType][0] +
(minMaxAttackDuration[attackType][1] - minMaxAttackDuration[attackType][0]) * strength;
const MWWorld::ESMStore &store = world->getStore();
//say a provoking combat phrase
if (actor.getClass().isNpc())
{
const MWWorld::ESMStore &store = world->getStore();
int chance = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->getInt();
if (Misc::Rng::roll0to99() < chance)
{
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;
}
@ -476,7 +434,7 @@ namespace MWMechanics
movement.mPosition[1] = 0;
movement.mPosition[2] = 0;
readyToAttack = false;
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
characterController.setAttackingOrSpell(false);
return false;
}
@ -619,57 +577,6 @@ namespace MWMechanics
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;
}
@ -795,70 +702,6 @@ ESM::Weapon::AttackType chooseBestAttack(const ESM::Weapon* weapon, MWMechanics:
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,
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();
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)
{
nearestDist = distTo;

View file

@ -1,5 +1,7 @@
#include "aiwander.hpp"
#include <cfloat>
#include <components/misc/rng.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_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] =
{
std::string("idle2"),
@ -214,7 +222,7 @@ namespace MWMechanics
// Are we there yet?
bool& chooseAction = storage.mChooseAction;
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);
moveNow = false;
@ -267,6 +275,7 @@ namespace MWMechanics
moveNow = false;
walking = false;
chooseAction = true;
mStuckCount = 0;
}
//#endif
}
@ -410,7 +419,7 @@ namespace MWMechanics
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// 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())
{
@ -497,30 +506,17 @@ namespace MWMechanics
{
assert(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]);
if (currentCell->getCell()->isExterior())
{
dest.mX += currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE;
dest.mY += currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE;
}
ToWorldCoordinates(dest, currentCell->getCell());
// actor position is already in world co-ordinates
ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos));
// 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())
{
// 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):
ESM::Pathgrid::Point temp = mAllowedNodes[randNode];
mAllowedNodes.erase(mAllowedNodes.begin() + randNode);
@ -543,6 +539,15 @@ namespace MWMechanics
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,
const PathFinder& pathfinder)
{
@ -646,15 +651,9 @@ namespace MWMechanics
int index = Misc::Rng::rollDice(mAllowedNodes.size());
ESM::Pathgrid::Point dest = mAllowedNodes[index];
// apply a slight offset to prevent overcrowding
dest.mX += static_cast<int>(Misc::Rng::rollProbability() * 128 - 64);
dest.mY += static_cast<int>(Misc::Rng::rollProbability() * 128 - 64);
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;
}
dest.mX += OffsetToPreventOvercrowding();
dest.mY += OffsetToPreventOvercrowding();
ToWorldCoordinates(dest, actor.getCell()->getCell());
MWBase::Environment::get().getWorld()->moveObject(actor, static_cast<float>(dest.mX),
static_cast<float>(dest.mY), static_cast<float>(dest.mZ));
@ -664,6 +663,11 @@ namespace MWMechanics
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)
{
if (!mStoredInitialActorPosition)
@ -690,70 +694,85 @@ namespace MWMechanics
// ... pathgrids don't usually include water, so swimmers ignore them
if (mDistance && !actor.getClass().isPureWaterCreature(actor))
{
float cellXOffset = 0;
float cellYOffset = 0;
// get NPC's position in local (i.e. cell) co-ordinates
osg::Vec3f npcPos(mInitialActorPosition);
if(cell->isExterior())
{
cellXOffset = static_cast<float>(cell->mData.mX * ESM::Land::REAL_SIZE);
cellYOffset = static_cast<float>(cell->mData.mY * ESM::Land::REAL_SIZE);
npcPos[0] = npcPos[0] - static_cast<float>(cell->mData.mX * 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
// NOTE: mPoints and mAllowedNodes are in local co-ordinates
int pointIndex = 0;
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{
osg::Vec3f nodePos(PathFinder::MakeOsgVec3(pathgrid->mPoints[counter]));
if((npcPos - nodePos).length2() <= mDistance * mDistance)
{
mAllowedNodes.push_back(pathgrid->mPoints[counter]);
pointIndex = counter;
}
}
if (mAllowedNodes.size() == 1)
{
AddNonPathGridAllowedPoints(npcPos, pathgrid, pointIndex);
}
if(!mAllowedNodes.empty())
{
osg::Vec3f firstNodePos(PathFinder::MakeOsgVec3(mAllowedNodes[0]));
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);
SetCurrentNodeToClosestAllowedNode(npcPos);
}
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

View file

@ -118,15 +118,19 @@ namespace MWMechanics
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
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
/// record distances of pathgrid point nodes to actor
/// 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);
static int OffsetToPreventOvercrowding();
};

View file

@ -654,6 +654,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSecondsOfSwimming(0)
, mSecondsOfRunning(0)
, mTurnAnimationThreshold(0)
, mAttackingOrSpell(false)
{
if(!mAnimation)
return;
@ -937,7 +938,7 @@ bool CharacterController::updateCreatureState()
mAnimation->disable(mCurrentWeapon);
}
if(stats.getAttackingOrSpell())
if(mAttackingOrSpell)
{
if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None)
{
@ -997,7 +998,7 @@ bool CharacterController::updateCreatureState()
}
}
stats.setAttackingOrSpell(false);
mAttackingOrSpell = false;
}
bool animPlaying = mAnimation->getInfo(mCurrentWeapon);
@ -1142,7 +1143,7 @@ bool CharacterController::updateWeaponState()
float complete;
bool animPlaying;
if(stats.getAttackingOrSpell())
if(mAttackingOrSpell)
{
if(mUpperBodyState == UpperCharState_WeapEquiped && mHitState == CharState_None)
{
@ -1152,7 +1153,7 @@ bool CharacterController::updateWeaponState()
{
// Unset casting flag, otherwise pressing the mouse button down would
// continue casting every frame if there is no animation
stats.setAttackingOrSpell(false);
mAttackingOrSpell = false;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
@ -1258,6 +1259,8 @@ bool CharacterController::updateWeaponState()
}
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && mHitState != CharState_KnockDown)
mAttackStrength = complete;
}
else
{
@ -1788,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;
else
forcestateupdate = updateCreatureState() || forcestateupdate;
@ -2041,6 +2045,32 @@ bool CharacterController::isKnockedOut() const
return mHitState == CharState_KnockOut;
}
void CharacterController::setAttackingOrSpell(bool 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)
{
mAnimation->setActive(active);

View file

@ -180,6 +180,9 @@ class CharacterController : public MWRender::Animation::TextKeyListener
float mTurnAnimationThreshold; // how long to continue playing turning animation after actor stopped turning
std::string mAttackType; // slash, chop or thrust
bool mAttackingOrSpell;
void determineAttackType();
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);
@ -235,6 +238,13 @@ public:
bool isReadyToBlock() const;
bool isKnockedOut() const;
void setAttackingOrSpell(bool attackingOrSpell);
bool readyToPrepareAttack() const;
bool readyToStartAttack() const;
float getAttackStrength() const;
/// @see Animation::setActive
void setActive(bool active);
@ -242,7 +252,6 @@ public:
void setHeadTrackTarget(const MWWorld::Ptr& target);
};
void getWeaponGroup(WeaponType weaptype, std::string &group);
MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype);
}

View file

@ -3,6 +3,8 @@
#include <algorithm>
#include <components/esm/creaturestats.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include "../mwworld/esmstore.hpp"
@ -16,7 +18,7 @@ namespace MWMechanics
CreatureStats::CreatureStats()
: mDrawState (DrawState_Nothing), mDead (false), mDied (false), mMurdered(false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false), mAttacked (false), mAttackingOrSpell(false),
mTalkedTo (false), mAlarmed (false), mAttacked (false),
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
mHitRecovery(false), mBlock(false), mMovementFlags(0),
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1),
@ -88,11 +90,6 @@ namespace MWMechanics
return mMagicEffects;
}
bool CreatureStats::getAttackingOrSpell() const
{
return mAttackingOrSpell;
}
int CreatureStats::getLevel() const
{
return mLevel;
@ -212,11 +209,6 @@ namespace MWMechanics
mMagicEffects.setModifiers(effects);
}
void CreatureStats::setAttackingOrSpell(bool attackingOrSpell)
{
mAttackingOrSpell = attackingOrSpell;
}
void CreatureStats::setAiSetting (AiSetting index, Stat<int> value)
{
mAiSettings[index] = value;
@ -487,7 +479,6 @@ namespace MWMechanics
state.mTalkedTo = mTalkedTo;
state.mAlarmed = mAlarmed;
state.mAttacked = mAttacked;
state.mAttackingOrSpell = mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
state.mKnockdown = mKnockdown;
state.mKnockdownOneFrame = mKnockdownOneFrame;
@ -534,7 +525,6 @@ namespace MWMechanics
mTalkedTo = state.mTalkedTo;
mAlarmed = state.mAlarmed;
mAttacked = state.mAttacked;
mAttackingOrSpell = state.mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
mKnockdown = state.mKnockdown;
mKnockdownOneFrame = state.mKnockdownOneFrame;

View file

@ -40,7 +40,6 @@ namespace MWMechanics
bool mTalkedTo;
bool mAlarmed;
bool mAttacked;
bool mAttackingOrSpell;
bool mKnockdown;
bool mKnockdownOneFrame;
bool mKnockdownOverOneFrame;

View file

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

View file

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

View file

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

View file

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

View file

@ -1,28 +1,276 @@
#include "stat.hpp"
void MWMechanics::AttributeValue::writeState (ESM::StatState<int>& state) const
#include <components/esm/statstate.hpp>
namespace MWMechanics
{
template<typename T>
Stat<T>::Stat() : mBase (0), mModified (0) {}
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 MWMechanics::AttributeValue::readState (const ESM::StatState<int>& state)
{
void AttributeValue::readState (const ESM::StatState<int>& state)
{
mBase = state.mBase;
mModifier = state.mMod;
mDamage = state.mDamage;
}
}
void MWMechanics::SkillValue::writeState (ESM::StatState<int>& state) const
{
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 MWMechanics::SkillValue::readState (const ESM::StatState<int>& state)
{
void SkillValue::readState (const ESM::StatState<int>& state)
{
AttributeValue::readState (state);
mProgress = state.mProgress;
}
}
template class MWMechanics::Stat<int>;
template class MWMechanics::Stat<float>;
template class MWMechanics::DynamicStat<int>;
template class MWMechanics::DynamicStat<float>;

View file

@ -1,9 +1,14 @@
#ifndef GAME_MWMECHANICS_STAT_H
#define GAME_MWMECHANICS_STAT_H
#include <algorithm>
#include <limits>
#include <components/esm/statstate.hpp>
namespace ESM
{
template<typename T>
struct StatState;
}
namespace MWMechanics
{
@ -16,87 +21,28 @@ namespace MWMechanics
public:
typedef T Type;
Stat() : mBase (0), mModified (0) {}
Stat(T base) : mBase (base), mModified (base) {}
Stat(T base, T modified) : mBase (base), mModified (modified) {}
Stat();
Stat(T base);
Stat(T base, T modified);
const T& getBase() const
{
return mBase;
}
const T& getBase() const;
T getModified() const
{
return std::max(static_cast<T>(0), mModified);
}
T getModifier() const
{
return mModified-mBase;
}
T getModified() const;
T getModifier() const;
/// Set base and modified to \a value.
void set (const T& value)
{
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);
}
}
void set (const T& value);
void modify(const T& diff);
/// Set base and adjust modified accordingly.
void setBase (const T& value)
{
T diff = value - mBase;
mBase = value;
mModified += diff;
}
void setBase (const T& value);
/// Set modified value an adjust base accordingly.
void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max())
{
T diff = value - mModified;
void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());
void setModifier (const T& modifier);
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;
}
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;
}
void writeState (ESM::StatState<T>& state) const;
void readState (const ESM::StatState<T>& state);
};
template<typename T>
@ -121,98 +67,32 @@ namespace MWMechanics
public:
typedef T Type;
DynamicStat() : mStatic (0), mCurrent (0) {}
DynamicStat(T base) : mStatic (base), mCurrent (base) {}
DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {}
DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {}
DynamicStat();
DynamicStat(T base);
DynamicStat(T base, T modified, T current);
DynamicStat(const Stat<T> &stat, T current);
const T& getBase() const
{
return mStatic.getBase();
}
T getModified() const
{
return mStatic.getModified();
}
const T& getCurrent() const
{
return mCurrent;
}
const T& getBase() const;
T getModified() const;
const T& getCurrent() const;
/// Set base, modified and current to \a value.
void set (const T& value)
{
mStatic.set (value);
mCurrent = value;
}
void set (const T& value);
/// Set base and adjust modified accordingly.
void setBase (const T& value)
{
mStatic.setBase (value);
if (mCurrent>getModified())
mCurrent = getModified();
}
void setBase (const T& value);
/// Set modified value an adjust base accordingly.
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();
}
void setModified (T value, const T& min, const T& max = std::numeric_limits<T>::max());
/// Change modified relatively.
void modify (const T& diff, bool allowCurrentDecreaseBelowZero=false)
{
mStatic.modify (diff);
setCurrent (getCurrent()+diff, allowCurrentDecreaseBelowZero);
}
void modify (const T& diff, bool allowCurrentDecreaseBelowZero=false);
void setCurrent (const T& value, bool allowDecreaseBelowZero = false)
{
if (value > mCurrent)
{
// increase
mCurrent = value;
void setCurrent (const T& value, bool allowDecreaseBelowZero = false);
void setModifier (const T& modifier, bool allowCurrentDecreaseBelowZero=false);
if (mCurrent > getModified())
mCurrent = getModified();
}
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;
}
void writeState (ESM::StatState<T>& state) const;
void readState (const ESM::StatState<T>& state);
};
template<typename T>
@ -236,26 +116,25 @@ namespace MWMechanics
float mDamage; // needs to be float to allow continuous damage
public:
AttributeValue() : mBase(0), mModifier(0), mDamage(0) {}
AttributeValue();
int getModified() const { return std::max(0, mBase - (int) mDamage + mModifier); }
int getBase() const { return mBase; }
int getModifier() const { return mModifier; }
int getModified() const;
int getBase() const;
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.
// 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.
void damage(float damage) { mDamage += std::min(damage, (float)getModified()); }
void restore(float amount) { mDamage -= std::min(mDamage, amount); }
void damage(float damage);
void restore(float amount);
float getDamage() const { return mDamage; }
float getDamage() const;
void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state);
};
@ -263,12 +142,11 @@ namespace MWMechanics
{
float mProgress;
public:
SkillValue() : mProgress(0) {}
float getProgress() const { return mProgress; }
void setProgress(float progress) { mProgress = progress; }
SkillValue();
float getProgress() const;
void setProgress(float progress);
void writeState (ESM::StatState<int>& state) const;
void readState (const ESM::StatState<int>& state);
};

View file

@ -267,6 +267,8 @@ namespace MWRender
Animation::~Animation()
{
setLightEffect(0.f);
if (mObjectRoot)
mInsert->removeChild(mObjectRoot);
}
@ -1224,6 +1226,36 @@ namespace MWRender
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()
{
mHeadController = NULL;

View file

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

View file

@ -88,7 +88,8 @@ namespace MWRender
struct 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

@ -850,7 +850,6 @@ void NpcAnimation::addControllers()
Animation::addControllers();
mFirstPersonNeckController = NULL;
mHeadController = NULL;
WeaponAnimation::deleteControllers();
if (mViewMode == VM_FirstPerson)

View file

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

View file

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

View file

@ -65,10 +65,6 @@ namespace MWRender
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;
std::vector<Emitter> mEmitters;
float mRippleLifeTime;
float mRippleRotSpeed;
};
}

View file

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

View file

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

View file

@ -29,9 +29,6 @@ void MWState::Character::addSlot (const boost::filesystem::path& path, const std
ESM::ESMReader reader;
reader.open (slot.mPath.string());
if (reader.getFormat()>ESM::Header::CurrentFormat)
return; // format is too new -> ignore
if (reader.getRecName()!=ESM::REC_SAVE)
return; // invalid save file -> ignore

View file

@ -6,6 +6,8 @@
#include <components/esm/cellid.hpp>
#include <components/esm/loadcell.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
@ -216,7 +218,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
++iter)
writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0
writer.setFormat (ESM::Header::CurrentFormat);
writer.setFormat (ESM::SavedGame::sCurrentFormat);
// all unused
writer.setVersion(0);
@ -325,8 +327,6 @@ void MWState::StateManager::loadGame(const std::string& filepath)
// have to peek into the save file to get the player name
ESM::ESMReader reader;
reader.open (filepath);
if (reader.getFormat()>ESM::Header::CurrentFormat)
return; // format is too new -> ignore
if (reader.getRecName()!=ESM::REC_SAVE)
return; // invalid save file -> ignore
reader.getRecHeader();
@ -348,6 +348,9 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
ESM::ESMReader reader;
reader.open (filepath);
if (reader.getFormat() > ESM::SavedGame::sCurrentFormat)
throw std::runtime_error("This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file.");
std::map<int, int> contentFileMap = buildContentFileIndexMap (reader);
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,7 +28,7 @@
namespace MWWorld
{
Player::Player (const ESM::NPC *player, const MWBase::World& world)
Player::Player (const ESM::NPC *player)
: mCellStore(0),
mLastKnownExteriorPosition(0,0,0),
mMarkedCell(NULL),
@ -36,7 +36,8 @@ namespace MWWorld
mForwardBackward(0),
mTeleported(false),
mCurrentCrimeId(-1),
mPaidCrimeId(-1)
mPaidCrimeId(-1),
mAttackingOrSpell(false)
{
ESM::CellRef cellRef;
cellRef.blank();
@ -216,6 +217,16 @@ namespace MWWorld
mTeleported = teleported;
}
void Player::setAttackingOrSpell(bool attackingOrSpell)
{
mAttackingOrSpell = attackingOrSpell;
}
bool Player::getAttackingOrSpell() const
{
return mAttackingOrSpell;
}
bool Player::isInCombat() {
return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0;
}

View file

@ -17,12 +17,6 @@ namespace ESM
class ESMReader;
}
namespace MWBase
{
class World;
class Ptr;
}
namespace Loading
{
class Listener;
@ -56,9 +50,11 @@ namespace MWWorld
MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length];
MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length];
bool mAttackingOrSpell;
public:
Player(const ESM::NPC *player, const MWBase::World& world);
Player(const ESM::NPC *player);
void saveSkillsAttributes();
void restoreSkillsAttributes();
@ -104,6 +100,9 @@ namespace MWWorld
bool wasTeleported() const;
void setTeleported(bool teleported);
void setAttackingOrSpell(bool attackingOrSpell);
bool getAttackingOrSpell() const;
///Checks all nearby actors to see if anyone has an aipackage against you
bool isInCombat();

View file

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

View file

@ -3,6 +3,7 @@
#include <limits>
#include <components/nif/niffile.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/settings/settings.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/esm/esmwriter.hpp>
#include <components/esm/weatherstate.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.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");
if (usesPrecip)
weather.mRainEffect = "meshes\\raindrop.nif";
@ -79,7 +84,6 @@ Rain Height Max=700 ?
Rain Threshold=0.6 ?
Max Raindrops=650 ?
*/
weather.mIsStorm = (name == "ashstorm" || name == "blight");
mWeatherSettings[name] = weather;
}
@ -112,8 +116,8 @@ float WeatherManager::calculateAngleFade (const std::string& moonName, float ang
return 1.f;
}
WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fallback* fallback) :
mHour(14), mWindSpeed(0.f), mIsStorm(false), mStormDirection(0,1,0), mFallback(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), mStore(store),
mRendering(rendering), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true),
mRemainingTransitionTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50),
mTimePassed(0), mWeatherUpdateTime(0), mThunderSoundDelay(0)

View file

@ -156,7 +156,8 @@ namespace MWWorld
class WeatherManager
{
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();
/**
@ -210,6 +211,7 @@ namespace MWWorld
std::string mPlayingSoundID;
MWWorld::Fallback* mFallback;
MWWorld::ESMStore* mStore;
void setFallbackWeather(Weather& weather,const std::string& name);
MWRender::RenderingManager* mRendering;

View file

@ -12,11 +12,15 @@
#include <osg/ComputeBoundsVisitor>
#include <osg/PositionAttitudeTransform>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/misc/rng.hpp>
#include <components/files/collections.hpp>
#include <components/compiler/locals.hpp>
#include <components/esm/cellid.hpp>
#include <components/esm/esmreader.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcesystem.hpp>
@ -163,8 +167,6 @@ namespace MWWorld
mProjectileManager.reset(new ProjectileManager(rootNode, resourceSystem, mPhysics));
mRendering = new MWRender::RenderingManager(viewer, rootNode, resourceSystem, &mFallback);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
mEsm.resize(contentFiles.size());
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
@ -193,6 +195,8 @@ namespace MWWorld
mGlobalVariables.fill (mStore);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback,&mStore);
mWorldScene = new Scene(*mRendering, mPhysics);
}
@ -265,7 +269,7 @@ namespace MWWorld
// we don't want old weather to persist on a new game
delete mWeatherManager;
mWeatherManager = 0;
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback,&mStore);
if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole(mStartupScript);
@ -2045,7 +2049,7 @@ namespace MWWorld
{
const ESM::NPC *player = mStore.get<ESM::NPC>().find("player");
if (!mPlayer)
mPlayer = new MWWorld::Player(player, *this);
mPlayer = new MWWorld::Player(player);
else
{
// Remove the old CharacterController

View file

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

View file

@ -1,4 +1,6 @@
#include "creaturestats.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::CreatureStats::load (ESMReader &esm)
{
@ -39,8 +41,8 @@ void ESM::CreatureStats::load (ESMReader &esm)
if (esm.isNextSub("HOST"))
esm.skipHSub(); // Hostile, no longer used
mAttackingOrSpell = false;
esm.getHNOT (mAttackingOrSpell, "ATCK");
if (esm.isNextSub("ATCK"))
esm.skipHSub(); // attackingOrSpell, no longer used
mKnockdown = false;
esm.getHNOT (mKnockdown, "KNCK");
@ -149,9 +151,6 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mAttacked)
esm.writeHNT ("ATKD", mAttacked);
if (mAttackingOrSpell)
esm.writeHNT ("ATCK", mAttackingOrSpell);
if (mKnockdown)
esm.writeHNT ("KNCK", mKnockdown);
@ -232,7 +231,6 @@ void ESM::CreatureStats::blank()
mTalkedTo = false;
mAlarmed = false;
mAttacked = false;
mAttackingOrSpell = false;
mKnockdown = false;
mKnockdownOneFrame = false;
mKnockdownOverOneFrame = false;

View file

@ -45,7 +45,6 @@ namespace ESM
bool mTalkedTo;
bool mAlarmed;
bool mAttacked;
bool mAttackingOrSpell;
bool mKnockdown;
bool mKnockdownOneFrame;
bool mKnockdownOverOneFrame;

View file

@ -6,6 +6,8 @@
void ESM::ObjectState::load (ESMReader &esm)
{
mVersion = esm.getFormat();
mRef.loadData(esm);
mHasLocals = 0;

View file

@ -29,7 +29,9 @@ namespace ESM
// Is there any class-specific state following the ObjectState
bool mHasCustomState;
ObjectState() : mHasCustomState(true)
unsigned int mVersion;
ObjectState() : mHasCustomState(true), mVersion(0)
{}
/// @note Does not load the CellRef ID, it should already be loaded before calling this method

View file

@ -6,6 +6,7 @@
#include "defs.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 1;
void ESM::SavedGame::load (ESMReader &esm)
{

View file

@ -15,6 +15,8 @@ namespace ESM
{
static unsigned int sRecordId;
static int sCurrentFormat;
struct TimeStamp
{
float mGameHour;

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
#define OPENMW_ESM_STATSTATE_H
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
template<typename T>
@ -23,48 +23,6 @@ namespace ESM
void load (ESMReader &esm);
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

View file

@ -165,6 +165,8 @@ public:
Drawable(const Drawable &copy, const osg::CopyOp &copyop=osg::CopyOp::SHALLOW_COPY)
: osg::Drawable(copy, copyop)
, mParent(copy.mParent)
, mWriteTo(0)
, mReadFrom(0)
{
}
@ -521,12 +523,8 @@ void RenderManager::destroyAllResources()
bool RenderManager::checkTexture(MyGUI::ITexture* _texture)
{
for (MapTexture::const_iterator item = mTextures.begin(); item != mTextures.end(); ++item)
{
if (item->second == _texture)
// We support external textures that aren't registered via this manager, so can't implement this method sensibly.
return true;
}
return false;
}
}

View file

@ -407,6 +407,7 @@ FlipController::FlipController(int texSlot, float delta, std::vector<osg::ref_pt
}
FlipController::FlipController()
: mDelta(0.f)
{
}
@ -434,6 +435,7 @@ ParticleSystemController::ParticleSystemController(const Nif::NiParticleSystemCo
}
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();
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 );
mat.orthoNormalize(mat); // don't undo the scale

View file

@ -11,6 +11,7 @@ namespace SceneUtil
class ControllerSource
{
public:
virtual ~ControllerSource() { }
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)
{
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)