forked from mirror/openmw-tes3mp
Update to upstream/master. Resolve the merge conflict
This commit is contained in:
commit
6162a46fbe
46 changed files with 567 additions and 331 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QPainter>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "../../model/tools/reportmodel.hpp"
|
||||
|
||||
|
@ -23,7 +24,7 @@ namespace CSVTools
|
|||
public:
|
||||
|
||||
RichTextDelegate (QObject *parent = 0);
|
||||
|
||||
|
||||
virtual void paint(QPainter *painter, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index) const;
|
||||
};
|
||||
|
@ -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')
|
||||
{
|
||||
|
@ -78,7 +79,7 @@ void CSVTools::ReportTable::contextMenuEvent (QContextMenuEvent *event)
|
|||
|
||||
if (mRefreshAction)
|
||||
menu.addAction (mRefreshAction);
|
||||
|
||||
|
||||
menu.exec (event->globalPos());
|
||||
}
|
||||
|
||||
|
@ -106,14 +107,14 @@ void CSVTools::ReportTable::mouseDoubleClickEvent (QMouseEvent *event)
|
|||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch (iter->second)
|
||||
{
|
||||
case Action_None:
|
||||
|
||||
event->accept();
|
||||
break;
|
||||
|
||||
|
||||
case Action_Edit:
|
||||
|
||||
event->accept();
|
||||
|
@ -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,
|
||||
|
@ -162,7 +166,7 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
|
|||
|
||||
if (richTextDescription)
|
||||
setItemDelegateForColumn (mModel->columnCount()-1, new RichTextDelegate (this));
|
||||
|
||||
|
||||
mShowAction = new QAction (tr ("Show"), this);
|
||||
connect (mShowAction, SIGNAL (triggered()), this, SLOT (showSelection()));
|
||||
addAction (mShowAction);
|
||||
|
@ -182,10 +186,10 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document,
|
|||
connect (mRefreshAction, SIGNAL (triggered()), this, SIGNAL (refreshRequest()));
|
||||
addAction (mRefreshAction);
|
||||
}
|
||||
|
||||
|
||||
mDoubleClickActions.insert (std::make_pair (Qt::NoModifier, Action_Edit));
|
||||
mDoubleClickActions.insert (std::make_pair (Qt::ShiftModifier, Action_Remove));
|
||||
mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove));
|
||||
mDoubleClickActions.insert (std::make_pair (Qt::ControlModifier, Action_EditAndRemove));
|
||||
}
|
||||
|
||||
std::vector<CSMWorld::UniversalId> CSVTools::ReportTable::getDraggedRecords() const
|
||||
|
@ -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;
|
||||
|
@ -234,7 +238,7 @@ void CSVTools::ReportTable::updateUserSetting (const QString& name, const QStrin
|
|||
mDoubleClickActions[modifiers] = action;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
|
||||
|
@ -245,13 +249,22 @@ std::vector<int> CSVTools::ReportTable::getReplaceIndices (bool selection) const
|
|||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
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
|
||||
|
@ -272,25 +285,35 @@ void CSVTools::ReportTable::flagAsReplaced (int index)
|
|||
{
|
||||
mModel->flagAsReplaced (index);
|
||||
}
|
||||
|
||||
|
||||
void CSVTools::ReportTable::showSelection()
|
||||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "../world/dragrecordtable.hpp"
|
||||
|
||||
class QAction;
|
||||
class QSortFilterProxyModel;
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
|
@ -30,7 +31,8 @@ namespace CSVTools
|
|||
Action_Remove,
|
||||
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:
|
||||
|
@ -78,8 +83,8 @@ namespace CSVTools
|
|||
|
||||
public slots:
|
||||
|
||||
void stateChanged (int state, CSMDoc::Document *document);
|
||||
|
||||
void stateChanged (int state, CSMDoc::Document *document);
|
||||
|
||||
signals:
|
||||
|
||||
void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(), "");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
49
apps/opencs/view/world/tableeditidaction.cpp
Normal file
49
apps/opencs/view/world/tableeditidaction.cpp
Normal 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();
|
||||
}
|
31
apps/opencs/view/world/tableeditidaction.hpp
Normal file
31
apps/opencs/view/world/tableeditidaction.hpp
Normal 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
|
|
@ -798,27 +798,25 @@ 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 (!ptr.getRefData().getCustomData())
|
||||
if (state.mVersion > 0)
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
// Create a CustomData, but don't fill it from ESM records (not needed)
|
||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
|
||||
if (ref->mBase->mFlags & ESM::Creature::Weapon)
|
||||
data->mContainerStore = new MWWorld::InventoryStore();
|
||||
else
|
||||
data->mContainerStore = new MWWorld::ContainerStore();
|
||||
if (ref->mBase->mFlags & ESM::Creature::Weapon)
|
||||
data->mContainerStore = new MWWorld::InventoryStore();
|
||||
else
|
||||
data->mContainerStore = new MWWorld::ContainerStore();
|
||||
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
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());
|
||||
|
||||
|
|
|
@ -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 (!ptr.getRefData().getCustomData())
|
||||
if (state.mVersion > 0)
|
||||
{
|
||||
// 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());
|
||||
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());
|
||||
|
||||
|
|
|
@ -72,6 +72,11 @@ namespace MWGui
|
|||
{
|
||||
MarkerUserData(MWRender::LocalMap* map)
|
||||
: mLocalMapRender(map)
|
||||
, interior(false)
|
||||
, cellX(0)
|
||||
, cellY(0)
|
||||
, nX(0.f)
|
||||
, nY(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(){}
|
||||
|
@ -233,44 +228,14 @@ 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);
|
||||
|
@ -384,34 +338,38 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
|
||||
float& strength = storage.mStrength;
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -40,7 +40,6 @@ namespace MWMechanics
|
|||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
bool mAttacked;
|
||||
bool mAttackingOrSpell;
|
||||
bool mKnockdown;
|
||||
bool mKnockdownOneFrame;
|
||||
bool mKnockdownOverOneFrame;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -850,7 +850,6 @@ void NpcAnimation::addControllers()
|
|||
Animation::addControllers();
|
||||
|
||||
mFirstPersonNeckController = NULL;
|
||||
mHeadController = NULL;
|
||||
WeaponAnimation::deleteControllers();
|
||||
|
||||
if (mViewMode == VM_FirstPerson)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,6 +190,7 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||
|
||||
float mFogDepth;
|
||||
osg::Vec4f mFogColor;
|
||||
|
||||
osg::Vec4f mAmbientColor;
|
||||
|
|
|
@ -65,10 +65,6 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;
|
||||
|
||||
std::vector<Emitter> mEmitters;
|
||||
|
||||
float mRippleLifeTime;
|
||||
float mRippleRotSpeed;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -45,7 +45,6 @@ namespace ESM
|
|||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
bool mAttacked;
|
||||
bool mAttackingOrSpell;
|
||||
bool mKnockdown;
|
||||
bool mKnockdownOneFrame;
|
||||
bool mKnockdownOverOneFrame;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
void ESM::ObjectState::load (ESMReader &esm)
|
||||
{
|
||||
mVersion = esm.getFormat();
|
||||
|
||||
mRef.loadData(esm);
|
||||
|
||||
mHasLocals = 0;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace ESM
|
|||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
static int sCurrentFormat;
|
||||
|
||||
struct TimeStamp
|
||||
{
|
||||
float mGameHour;
|
||||
|
|
|
@ -165,6 +165,8 @@ public:
|
|||
Drawable(const Drawable ©, const osg::CopyOp ©op=osg::CopyOp::SHALLOW_COPY)
|
||||
: osg::Drawable(copy, copyop)
|
||||
, mParent(copy.mParent)
|
||||
, mWriteTo(0)
|
||||
, mReadFrom(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue