Update to upstream/master. Resolve the merge conflict

This commit is contained in:
Stanislav Bas 2015-07-05 16:15:48 +03:00
commit 6162a46fbe
46 changed files with 567 additions and 331 deletions

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 extendedcommandconfigurator
dialoguespinbox recordbuttonbar tableeditidaction extendedcommandconfigurator
)
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

@ -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

@ -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

@ -27,6 +27,7 @@
#include "../../model/settings/usersettings.hpp"
#include "recordstatusdelegate.hpp"
#include "tableeditidaction.hpp"
#include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
@ -44,33 +45,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))
@ -349,10 +330,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);
@ -369,6 +346,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mExtendedRevertAction, SIGNAL (triggered()), this, 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()));
@ -520,7 +501,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

@ -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

@ -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

@ -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

@ -1086,6 +1086,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

@ -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

@ -16,7 +16,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 +88,6 @@ namespace MWMechanics
return mMagicEffects;
}
bool CreatureStats::getAttackingOrSpell() const
{
return mAttackingOrSpell;
}
int CreatureStats::getLevel() const
{
return mLevel;
@ -212,11 +207,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 +477,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 +523,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

@ -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

@ -162,6 +162,12 @@ protected:
class CloudUpdater : public SceneUtil::StateSetUpdater
{
public:
CloudUpdater()
: mAnimationTimer(0.f)
, mOpacity(0.f)
{
}
void setAnimationTimer(float timer)
{
mAnimationTimer = timer;
@ -762,9 +768,6 @@ public:
mat->setColorMode(osg::Material::OFF);
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
}
private:
float mAlpha;
};
void SkyManager::createRain()

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

@ -216,7 +216,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 +325,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 +346,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

@ -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

@ -2045,7 +2045,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

@ -39,8 +39,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 +149,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 +229,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

@ -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)
{
}

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

@ -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)