Merge pull request #441 from TES3MP/0.6.3 while resolving conflicts

Conflicts:
	apps/openmw-mp/Script/Functions/Worldstate.cpp
	apps/openmw-mp/Script/Functions/Worldstate.hpp
pull/454/head
David Cernat 7 years ago
commit fc4d3fe3fa

@ -6,4 +6,6 @@ DATE=`date +'%d%m%Y'`
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}` SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg" TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}" if ! curl --ssl -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}" --silent | grep $SHORT_COMMIT > /dev/null; then
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
fi

@ -392,6 +392,8 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
endif() endif()
elseif (MSVC) elseif (MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Zi /bigobj")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
# Enable link-time code generation globally for all linking # Enable link-time code generation globally for all linking
if (OPENMW_LTO_BUILD) if (OPENMW_LTO_BUILD)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")

@ -81,14 +81,14 @@ opencs_units_noqt (view/world
opencs_units (view/widget opencs_units (view/widget
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit
) )
opencs_units (view/render opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater cellwater terraintexturemode
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

@ -222,7 +222,15 @@ void CSMPrefs::State::declare()
EnumValues insertOutsideVisibleCell; EnumValues insertOutsideVisibleCell;
insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway); insertOutsideVisibleCell.add (showAndInsert).add (dontInsert).add (insertAnyway);
declareCategory ("Scene Drops"); EnumValue createAndLandEdit ("Create cell and land, then edit");
EnumValue showAndLandEdit ("Show cell and edit");
EnumValue dontLandEdit ("Discard");
EnumValues landeditOutsideCell;
landeditOutsideCell.add (createAndLandEdit).add (dontLandEdit);
EnumValues landeditOutsideVisibleCell;
landeditOutsideVisibleCell.add (showAndLandEdit).add (dontLandEdit);
declareCategory ("3D Scene Editing");
declareInt ("distance", "Drop Distance", 50). declareInt ("distance", "Drop Distance", 50).
setTooltip ("If an instance drop can not be placed against another object at the " setTooltip ("If an instance drop can not be placed against another object at the "
"insert point, it will be placed by this distance from the insert point instead"); "insert point, it will be placed by this distance from the insert point instead");
@ -230,6 +238,12 @@ void CSMPrefs::State::declare()
addValues (insertOutsideCell); addValues (insertOutsideCell);
declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert).
addValues (insertOutsideVisibleCell); addValues (insertOutsideVisibleCell);
declareEnum ("outside-landedit", "Handling land edit outside of cells", createAndLandEdit).
addValues (landeditOutsideCell);
declareEnum ("outside-visible-landedit", "Handling land edit outside of visible cells", showAndLandEdit).
addValues (landeditOutsideVisibleCell);
declareInt ("texturebrush-maximumsize", "Maximum texture brush size", 50).
setMin (1);
declareCategory ("Key Bindings"); declareCategory ("Key Bindings");

@ -28,9 +28,13 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId);
// test for empty name and description // A class should have a name
if (class_.mName.empty()) if (class_.mName.empty())
messages.push_back (std::make_pair (id, class_.mId + " has an empty name")); messages.push_back (std::make_pair (id, class_.mId + " doesn't have a name"));
// A playable class should have a description
if (class_.mData.mIsPlayable != 0 && class_.mDescription.empty())
messages.push_back (std::make_pair (id, class_.mId + " doesn't have a description and it's playable"));
// test for invalid attributes // test for invalid attributes
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)

@ -11,11 +11,6 @@ namespace CSMWorld
bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const
{ {
int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification);
QModelIndex index = mSourceModel->index(sourceRow, columnIndex);
if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly)
return false;
return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent); return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent);
} }
} }

@ -551,7 +551,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event)
if (noCell) if (noCell)
{ {
std::string mode = CSMPrefs::get()["Scene Drops"]["outside-drop"].toString(); std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-drop"].toString();
// target cell does not exist // target cell does not exist
if (mode=="Discard") if (mode=="Discard")
@ -585,7 +585,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event)
{ {
// target cell exists, but is not shown // target cell exists, but is not shown
std::string mode = std::string mode =
CSMPrefs::get()["Scene Drops"]["outside-visible-drop"].toString(); CSMPrefs::get()["3D Scene Editing"]["outside-visible-drop"].toString();
if (mode=="Discard") if (mode=="Discard")
return; return;

@ -21,6 +21,7 @@
#include "mask.hpp" #include "mask.hpp"
#include "cameracontroller.hpp" #include "cameracontroller.hpp"
#include "cellarrow.hpp" #include "cellarrow.hpp"
#include "terraintexturemode.hpp"
bool CSVRender::PagedWorldspaceWidget::adjustCells() bool CSVRender::PagedWorldspaceWidget::adjustCells()
{ {
@ -136,7 +137,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
"terrain-shape"); "terrain-shape");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain texture editing"), new TerrainTextureMode (this, tool),
"terrain-texture"); "terrain-texture");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),

@ -0,0 +1,542 @@
#include "terraintexturemode.hpp"
#include <string>
#include <sstream>
#include <QWidget>
#include <QIcon>
#include <QEvent>
#include <QDropEvent>
#include <QDragEnterEvent>
#include <QDrag>
#include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp"
#include "../widget/scenetoolbar.hpp"
#include "../widget/scenetooltexturebrush.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/world/columnbase.hpp"
#include "../../model/world/commandmacro.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/land.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/resourcetable.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/world/universalid.hpp"
#include "editmode.hpp"
#include "pagedworldspacewidget.hpp"
#include "mask.hpp"
#include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
mBrushTexture("L0#0"),
mBrushSize(0),
mBrushShape(0),
mTextureBrushScenetool(0)
{
}
void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
{
if(!mTextureBrushScenetool)
{
mTextureBrushScenetool = new CSVWidget::SceneToolTextureBrush (toolbar, "scenetooltexturebrush", getWorldspaceWidget().getDocument());
connect(mTextureBrushScenetool, SIGNAL (clicked()), mTextureBrushScenetool, SLOT (activate()));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushSize(int)), this, SLOT(setBrushSize(int)));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setBrushShape(int)));
connect(mTextureBrushScenetool->mTextureBrushWindow->mSizeSliders->mBrushSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBrushSize(int)));
connect(mTextureBrushScenetool, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));
connect(mTextureBrushScenetool->mTextureBrushWindow, SIGNAL(passTextureId(std::string)), this, SLOT(setBrushTexture(std::string)));
connect(mTextureBrushScenetool, SIGNAL(passEvent(QDropEvent*)), this, SLOT(handleDropEvent(QDropEvent*)));
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool->mTextureBrushWindow, SLOT(setBrushTexture(std::string)));
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
}
EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool);
}
void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
{
if(mTextureBrushScenetool)
{
toolbar->removeTool (mTextureBrushScenetool);
delete mTextureBrushScenetool;
mTextureBrushScenetool = 0;
}
EditMode::deactivate(toolbar);
}
void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult& hit) // Apply changes here
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true)
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
}
undoStack.endMacro();
}
}
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)
{
}
void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{
}
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true)
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
}
}
return true;
}
bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
{
return false;
}
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
{
return false;
}
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
{
return false;
}
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
{
editTerrainTextureGrid(hit);
}
}
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) {
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.endMacro();
}
}
void CSVRender::TerrainTextureMode::dragAborted() {
}
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {}
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) {
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
if (mime->holdsType (CSMWorld::UniversalId::Type_LandTexture))
{
const std::vector<CSMWorld::UniversalId> ids = mime->getData();
for (const CSMWorld::UniversalId& uid : ids)
{
mBrushTexture = uid.getId();
emit passBrushTexture(mBrushTexture);
}
}
if (mime->holdsType (CSMWorld::UniversalId::Type_Texture))
{
const std::vector<CSMWorld::UniversalId> ids = mime->getData();
for (const CSMWorld::UniversalId& uid : ids)
{
std::string textureFileName = uid.toString();
createTexture(textureFileName);
emit passBrushTexture(mBrushTexture);
}
}
}
void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitResult& hit)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {}
std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);
int cellX = cellCoordinates_pair.first.getX();
int cellY = cellCoordinates_pair.first.getY();
// The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5));
if (xHitInCell < 0)
{
xHitInCell = xHitInCell + landTextureSize;
cellX = cellX - 1;
}
if (yHitInCell > 15)
{
yHitInCell = yHitInCell - landTextureSize;
cellY = cellY + 1;
}
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY);
if(allowLandTextureEditing(mCellId)==true) {}
std::string iteratedCellId;
int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex);
std::size_t hashlocation = mBrushTexture.find("#");
std::string mBrushTextureInt = mBrushTexture.substr (hashlocation+1);
int brushInt = stoi(mBrushTexture.substr (hashlocation+1))+1; // All indices are offset by +1
float rf = mBrushSize/2;
int r = (mBrushSize/2)+1;
float distance = 0;
if (mBrushShape == 0)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
if(allowLandTextureEditing(mCellId)==true)
{
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId);
}
}
if (mBrushShape == 1)
{
int upperLeftCellX = cellX - std::floor(r / landTextureSize);
int upperLeftCellY = cellY - std::floor(r / landTextureSize);
if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;
if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;
int lowerrightCellX = cellX + std::floor(r / landTextureSize);
int lowerrightCellY = cellY + std::floor(r / landTextureSize);
if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;
if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;
for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
{
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{
mNew[j*landTextureSize+i] = brushInt;
}
else
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 2)
{
int upperLeftCellX = cellX - std::floor(r / landTextureSize);
int upperLeftCellY = cellY - std::floor(r / landTextureSize);
if (xHitInCell - (r % landTextureSize) < 0) upperLeftCellX--;
if (yHitInCell - (r % landTextureSize) < 0) upperLeftCellY--;
int lowerrightCellX = cellX + std::floor(r / landTextureSize);
int lowerrightCellY = cellY + std::floor(r / landTextureSize);
if (xHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellX++;
if (yHitInCell + (r % landTextureSize) > landTextureSize - 1) lowerrightCellY++;
for(int i_cell = upperLeftCellX; i_cell <= lowerrightCellX; i_cell++)
{
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
{
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize* abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
}
else
{
int distanceX(0);
int distanceY(0);
if (i_cell < cellX) distanceX = xHitInCell + landTextureSize * abs(i_cell-cellX) - i;
if (j_cell < cellY) distanceY = yHitInCell + landTextureSize * abs(j_cell-cellY) - j;
if (i_cell > cellX) distanceX = -xHitInCell + landTextureSize * abs(i_cell-cellX) + i;
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 3)
{
// Not implemented
}
}
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId)
{
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QVariant changedLand;
changedLand.setValue(newLandGrid);
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
}
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = document.getUndoStack();
std::string newId;
int counter=0;
bool freeIndexFound = false;
do {
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;
}
} while (freeIndexFound == false);
std::size_t idlocation = textureFileName.find("Texture: ");
textureFileName = textureFileName.substr (idlocation + 9);
QVariant textureNameVariant;
QVariant textureFileNameVariant;
textureFileNameVariant.setValue(QString::fromStdString(textureFileName));
undoStack.beginMacro ("Add land texture record");
undoStack.push (new CSMWorld::CreateCommand (ltexTable, newId));
QModelIndex index(ltexTable.getModelIndex (newId, ltexTable.findColumnIndex (CSMWorld::Columns::ColumnId_Texture)));
undoStack.push (new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
undoStack.endMacro();
mBrushTexture = newId;
}
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
CSMWorld::IdTree& cellTable = dynamic_cast<CSMWorld::IdTree&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
bool noCell = document.getData().getCells().searchId (cellId)==-1;
bool noLand = document.getData().getLand().searchId (cellId)==-1;
if (noCell)
{
std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString();
// target cell does not exist
if (mode=="Discard")
return false;
if (mode=="Create cell and land, then edit")
{
std::unique_ptr<CSMWorld::CreateCommand> createCommand (
new CSMWorld::CreateCommand (cellTable, cellId));
int parentIndex = cellTable.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
int index = cellTable.findNestedColumnIndex (parentIndex, CSMWorld::Columns::ColumnId_Interior);
createCommand->addNestedValue (parentIndex, index, false);
document.getUndoStack().push (createCommand.release());
if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
CSMWorld::CellSelection selection = paged->getCellSelection();
selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);
paged->setCellSelection (selection);
}
}
}
else if (CSVRender::PagedWorldspaceWidget *paged =
dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
CSMWorld::CellSelection selection = paged->getCellSelection();
if (!selection.has (CSMWorld::CellCoordinates::fromId (cellId).first))
{
// target cell exists, but is not shown
std::string mode =
CSMPrefs::get()["3D Scene Editing"]["outside-visible-landedit"].toString();
if (mode=="Discard")
return false;
if (mode=="Show cell and edit")
{
selection.add (CSMWorld::CellCoordinates::fromId (cellId).first);
paged->setCellSelection (selection);
}
}
}
if (noLand)
{
std::string mode = CSMPrefs::get()["3D Scene Editing"]["outside-landedit"].toString();
// target cell does not exist
if (mode=="Discard")
return false;
if (mode=="Create cell and land, then edit")
{
document.getUndoStack().push (new CSMWorld::CreateCommand (landTable, cellId));
}
}
return true;
}
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) {
}
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
{
mBrushSize = brushSize;
}
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{
mBrushShape = brushShape;
}
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)
{
mBrushTexture = brushTexture;
}

@ -0,0 +1,100 @@
#ifndef CSV_RENDER_TERRAINTEXTUREMODE_H
#define CSV_RENDER_TERRAINTEXTUREMODE_H
#include "editmode.hpp"
#include <string>
#include <QWidget>
#include <QEvent>
#include "../../model/world/data.hpp"
#include "../../model/world/land.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVWidget
{
class SceneToolTextureBrush;
}
namespace CSVRender
{
class TerrainTextureMode : public EditMode
{
Q_OBJECT
public:
/// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr);
/// \brief Create single command for one-click texture editing
void primaryEditPressed (const WorldspaceHitResult& hit);
/// \brief Open brush settings window
void primarySelectPressed(const WorldspaceHitResult&);
void secondarySelectPressed(const WorldspaceHitResult&);
void activate(CSVWidget::SceneToolbar*);
void deactivate(CSVWidget::SceneToolbar*);
/// \brief Start texture editing command macro
virtual bool primaryEditStartDrag (const QPoint& pos);
virtual bool secondaryEditStartDrag (const QPoint& pos);
virtual bool primarySelectStartDrag (const QPoint& pos);
virtual bool secondarySelectStartDrag (const QPoint& pos);
/// \brief Handle texture edit behavior during dragging
virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor);
/// \brief End texture editing command macro
virtual void dragCompleted(const QPoint& pos);
virtual void dragAborted();
virtual void dragWheel (int diff, double speedFactor);
virtual void dragMoveEvent (QDragMoveEvent *event);
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId);
/// \brief Create new land texture record from texture asset
void createTexture(std::string textureFileName);
/// \brief Create new cell and land if needed
bool allowLandTextureEditing(std::string textureFileName);
private:
std::string mCellId;
std::string mBrushTexture;
int mBrushSize;
int mBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
signals:
void passBrushTexture(std::string brushTexture);
public slots:
void handleDropEvent(QDropEvent *event);
void setBrushSize(int brushSize);
void setBrushShape(int brushShape);
void setBrushTexture(std::string brushShape);
};
}
#endif

@ -445,7 +445,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo
// Default placement // Default placement
direction.normalize(); direction.normalize();
direction *= CSMPrefs::get()["Scene Drops"]["distance"].toInt(); direction *= CSMPrefs::get()["3D Scene Editing"]["distance"].toInt();
WorldspaceHitResult hit = { false, 0, 0, 0, 0, start + direction }; WorldspaceHitResult hit = { false, 0, 0, 0, 0, start + direction };
return hit; return hit;
@ -648,6 +648,12 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event)
mDragX = event->posF().x(); mDragX = event->posF().x();
mDragY = height() - event->posF().y(); mDragY = height() - event->posF().y();
#endif #endif
if (mDragMode == InteractionType_PrimaryEdit)
{
EditMode& editMode = dynamic_cast<CSVRender::EditMode&> (*mEditMode->getCurrent());
editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos
}
} }
} }
else else

@ -0,0 +1,379 @@
#include "scenetooltexturebrush.hpp"
#include <QFrame>
#include <QIcon>
#include <QTableWidget>
#include <QHBoxLayout>
#include <QWidget>
#include <QSpinBox>
#include <QGroupBox>
#include <QSlider>
#include <QEvent>
#include <QDropEvent>
#include <QButtonGroup>
#include <QVBoxLayout>
#include <QDragEnterEvent>
#include <QDrag>
#include <QTableWidget>
#include <QHeaderView>
#include <QApplication>
#include <QSizePolicy>
#include "scenetool.hpp"
#include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idcollection.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/universalid.hpp"
CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent)
: QGroupBox(title, parent)
{
mBrushSizeSlider = new QSlider(Qt::Horizontal);
mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);
mBrushSizeSlider->setTickInterval(10);
mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mBrushSizeSlider->setSingleStep(1);
mBrushSizeSpinBox = new QSpinBox;
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mBrushSizeSpinBox->setSingleStep(1);
mLayoutSliderSize = new QHBoxLayout;
mLayoutSliderSize->addWidget(mBrushSizeSlider);
mLayoutSliderSize->addWidget(mBrushSizeSpinBox);
connect(mBrushSizeSlider, SIGNAL(valueChanged(int)), mBrushSizeSpinBox, SLOT(setValue(int)));
connect(mBrushSizeSpinBox, SIGNAL(valueChanged(int)), mBrushSizeSlider, SLOT(setValue(int)));
setLayout(mLayoutSliderSize);
}
CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)
: QFrame(parent, Qt::Popup),
mBrushShape(0),
mBrushSize(0),
mBrushTexture("L0#0"),
mDocument(document)
{
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
} else
{
mBrushTextureLabel = "No selected texture or invalid texture";
mSelectedBrush = new QLabel(QString::fromStdString(mBrushTextureLabel));
}
mButtonPoint = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-point")), "", this);
mButtonSquare = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-square")), "", this);
mButtonCircle = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-circle")), "", this);
mButtonCustom = new QPushButton(QIcon (QPixmap (":scenetoolbar/brush-custom")), "", this);
mSizeSliders = new BrushSizeControls("Brush size", this);
QVBoxLayout *layoutMain = new QVBoxLayout;
layoutMain->setSpacing(0);
layoutMain->setContentsMargins(4,0,4,4);
QHBoxLayout *layoutHorizontal = new QHBoxLayout;
layoutHorizontal->setSpacing(0);
layoutHorizontal->setContentsMargins (QMargins (0, 0, 0, 0));
configureButtonInitialSettings(mButtonPoint);
configureButtonInitialSettings(mButtonSquare);
configureButtonInitialSettings(mButtonCircle);
configureButtonInitialSettings(mButtonCustom);
mButtonPoint->setToolTip (toolTipPoint);
mButtonSquare->setToolTip (toolTipSquare);
mButtonCircle->setToolTip (toolTipCircle);
mButtonCustom->setToolTip (toolTipCustom);
QButtonGroup* brushButtonGroup = new QButtonGroup(this);
brushButtonGroup->addButton(mButtonPoint);
brushButtonGroup->addButton(mButtonSquare);
brushButtonGroup->addButton(mButtonCircle);
brushButtonGroup->addButton(mButtonCustom);
brushButtonGroup->setExclusive(true);
layoutHorizontal->addWidget(mButtonPoint, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonSquare, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonCircle, 0, Qt::AlignTop);
layoutHorizontal->addWidget(mButtonCustom, 0, Qt::AlignTop);
mHorizontalGroupBox = new QGroupBox(tr(""));
mHorizontalGroupBox->setLayout(layoutHorizontal);
layoutMain->addWidget(mHorizontalGroupBox);
layoutMain->addWidget(mSizeSliders);
layoutMain->addWidget(mSelectedBrush);
setLayout(layoutMain);
connect(mButtonPoint, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonSquare, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonCircle, SIGNAL(clicked()), this, SLOT(setBrushShape()));
connect(mButtonCustom, SIGNAL(clicked()), this, SLOT(setBrushShape()));
}
void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton *button)
{
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
button->setContentsMargins (QMargins (0, 0, 0, 0));
button->setIconSize (QSize (48-6, 48-6));
button->setFixedSize (48, 48);
button->setCheckable(true);
}
void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
{
mBrushTexture = brushTexture;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int columnModification = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
int index = landtexturesCollection.searchId(mBrushTexture);
// Check if texture exists in current plugin
if(landtexturesCollection.getData(index, columnModification).value<int>() == 0)
{
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = mDocument.getUndoStack();
QVariant textureFileNameVariant;
textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value<QString>());
std::size_t hashlocation = mBrushTexture.find("#");
std::string mBrushTexturePlugin = "L0#" + mBrushTexture.substr (hashlocation+1);
int indexPlugin = landtexturesCollection.searchId(mBrushTexturePlugin);
// Reindex texture if needed
if (indexPlugin != -1 && !landtexturesCollection.getRecord(indexPlugin).isDeleted())
{
int counter=0;
bool freeIndexFound = false;
do {
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter;
else freeIndexFound = true;
} while (freeIndexFound == false);
}
undoStack.beginMacro ("Add land texture record");
undoStack.push (new CSMWorld::CloneCommand (ltexTable, mBrushTexture, mBrushTexturePlugin, CSMWorld::UniversalId::Type_LandTexture));
undoStack.endMacro();
mBrushTexture = mBrushTexturePlugin;
emit passTextureId(mBrushTexture);
}
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
} else
{
mBrushTextureLabel = "No selected texture or invalid texture";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
}
emit passBrushShape(mBrushShape); // update icon
}
void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)
{
mBrushSize = brushSize;
emit passBrushSize(mBrushSize);
}
void CSVWidget::TextureBrushWindow::setBrushShape()
{
if(mButtonPoint->isChecked()) mBrushShape = 0;
if(mButtonSquare->isChecked()) mBrushShape = 1;
if(mButtonCircle->isChecked()) mBrushShape = 2;
if(mButtonCustom->isChecked()) mBrushShape = 3;
emit passBrushShape(mBrushShape);
}
void CSVWidget::SceneToolTextureBrush::adjustToolTips()
{
}
CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document)
: SceneTool (parent, Type_TopAction),
mToolTip (toolTip),
mDocument (document),
mTextureBrushWindow(new TextureBrushWindow(document, this))
{
mBrushHistory.resize(1);
mBrushHistory[0] = "L0#0";
setAcceptDrops(true);
connect(mTextureBrushWindow, SIGNAL(passBrushShape(int)), this, SLOT(setButtonIcon(int)));
setButtonIcon(mTextureBrushWindow->mBrushShape);
mPanel = new QFrame (this, Qt::Popup);
QHBoxLayout *layout = new QHBoxLayout (mPanel);
layout->setContentsMargins (QMargins (0, 0, 0, 0));
mTable = new QTableWidget (0, 2, this);
mTable->setShowGrid (true);
mTable->verticalHeader()->hide();
mTable->horizontalHeader()->hide();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
mTable->horizontalHeader()->setSectionResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setSectionResizeMode (1, QHeaderView::Stretch);
#else
mTable->horizontalHeader()->setResizeMode (0, QHeaderView::Stretch);
mTable->horizontalHeader()->setResizeMode (1, QHeaderView::Stretch);
#endif
mTable->setSelectionMode (QAbstractItemView::NoSelection);
layout->addWidget (mTable);
connect (mTable, SIGNAL (clicked (const QModelIndex&)),
this, SLOT (clicked (const QModelIndex&)));
}
void CSVWidget::SceneToolTextureBrush::setButtonIcon (int brushShape)
{
QString tooltip = "Change brush settings <p>Currently selected: ";
switch (brushShape)
{
case 0:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-point")));
tooltip += mTextureBrushWindow->toolTipPoint;
break;
case 1:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-square")));
tooltip += mTextureBrushWindow->toolTipSquare;
break;
case 2:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-circle")));
tooltip += mTextureBrushWindow->toolTipCircle;
break;
case 3:
setIcon (QIcon (QPixmap (":scenetoolbar/brush-custom")));
tooltip += mTextureBrushWindow->toolTipCustom;
break;
}
tooltip += "<p>(right click to access of previously used brush settings)";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
tooltip += "<p>Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " ";
tooltip += landtexturesCollection.getData(index, landTextureFilename).value<QString>();
} else
{
tooltip += "<p>No selected texture or invalid texture";
}
tooltip += "<br>(drop texture here to change)";
setToolTip (tooltip);
}
void CSVWidget::SceneToolTextureBrush::showPanel (const QPoint& position)
{
updatePanel();
mPanel->move (position);
mPanel->show();
}
void CSVWidget::SceneToolTextureBrush::updatePanel()
{
mTable->setRowCount (mBrushHistory.size());
for (int i = mBrushHistory.size()-1; i >= 0; --i)
{
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = landtexturesCollection.searchId(mBrushHistory[i]);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mTable->setItem (i, 1, new QTableWidgetItem (landtexturesCollection.getData(index, landTextureFilename).value<QString>()));
mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
} else
{
mTable->setItem (i, 1, new QTableWidgetItem ("Invalid/deleted texture"));
mTable->setItem (i, 0, new QTableWidgetItem (QString::fromStdString(mBrushHistory[i])));
}
}
}
void CSVWidget::SceneToolTextureBrush::updateBrushHistory (const std::string& brushTexture)
{
mBrushHistory.insert(mBrushHistory.begin(), brushTexture);
if(mBrushHistory.size() > 5) mBrushHistory.pop_back();
}
void CSVWidget::SceneToolTextureBrush::clicked (const QModelIndex& index)
{
if (index.column()==0 || index.column()==1)
{
std::string brushTexture = mBrushHistory[index.row()];
std::swap(mBrushHistory[index.row()], mBrushHistory[0]);
mTextureBrushWindow->setBrushTexture(brushTexture);
emit passTextureId(brushTexture);
updatePanel();
mPanel->hide();
}
}
void CSVWidget::SceneToolTextureBrush::activate ()
{
QPoint position = QCursor::pos();
mTextureBrushWindow->mSizeSliders->mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mTextureBrushWindow->mSizeSliders->mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
mTextureBrushWindow->move (position);
mTextureBrushWindow->show();
}
void CSVWidget::SceneToolTextureBrush::dragEnterEvent (QDragEnterEvent *event)
{
emit passEvent(event);
event->accept();
}
void CSVWidget::SceneToolTextureBrush::dropEvent (QDropEvent *event)
{
emit passEvent(event);
event->accept();
}

@ -0,0 +1,133 @@
#ifndef CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H
#define CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H
#include <QIcon>
#include <QFrame>
#include <QModelIndex>
#include <QWidget>
#include <QLabel>
#include <QSpinBox>
#include <QGroupBox>
#include <QSlider>
#include <QEvent>
#include <QHBoxLayout>
#include <QPushButton>
#include "scenetool.hpp"
#include "../../model/doc/document.hpp"
class QTableWidget;
namespace CSVRender
{
class TerrainTextureMode;
}
namespace CSVWidget
{
class SceneToolTextureBrush;
/// \brief Layout-box for some brush button settings
class BrushSizeControls : public QGroupBox
{
Q_OBJECT
public:
BrushSizeControls(const QString &title, QWidget *parent);
private:
QHBoxLayout *mLayoutSliderSize;
QSlider *mBrushSizeSlider;
QSpinBox *mBrushSizeSpinBox;
friend class SceneToolTextureBrush;
friend class CSVRender::TerrainTextureMode;
};
class SceneToolTextureBrush;
/// \brief Brush settings window
class TextureBrushWindow : public QFrame
{
Q_OBJECT
public:
TextureBrushWindow(CSMDoc::Document& document, QWidget *parent = 0);
void configureButtonInitialSettings(QPushButton *button);
const QString toolTipPoint = "Paint single point";
const QString toolTipSquare = "Paint with square brush";
const QString toolTipCircle = "Paint with circle brush";
const QString toolTipCustom = "Paint custom selection (not implemented yet)";
private:
int mBrushShape;
int mBrushSize;
std::string mBrushTexture;
CSMDoc::Document& mDocument;
QLabel *mSelectedBrush;
QGroupBox *mHorizontalGroupBox;
std::string mBrushTextureLabel;
QPushButton *mButtonPoint;
QPushButton *mButtonSquare;
QPushButton *mButtonCircle;
QPushButton *mButtonCustom;
BrushSizeControls* mSizeSliders;
friend class SceneToolTextureBrush;
friend class CSVRender::TerrainTextureMode;
public slots:
void setBrushTexture(std::string brushTexture);
void setBrushShape();
void setBrushSize(int brushSize);
signals:
void passBrushSize (int brushSize);
void passBrushShape(int brushShape);
void passTextureId(std::string brushTexture);
};
class SceneToolTextureBrush : public SceneTool
{
Q_OBJECT
QString mToolTip;
CSMDoc::Document& mDocument;
QFrame *mPanel;
QTableWidget *mTable;
std::vector<std::string> mBrushHistory;
TextureBrushWindow *mTextureBrushWindow;
private:
void adjustToolTips();
public:
SceneToolTextureBrush (SceneToolbar *parent, const QString& toolTip, CSMDoc::Document& document);
virtual void showPanel (const QPoint& position);
void updatePanel ();
void dropEvent (QDropEvent *event);
void dragEnterEvent (QDragEnterEvent *event);
friend class CSVRender::TerrainTextureMode;
public slots:
void setButtonIcon(int brushShape);
void updateBrushHistory (const std::string& mBrushTexture);
void clicked (const QModelIndex& index);
virtual void activate();
signals:
void passEvent(QDropEvent *event);
void passEvent(QDragEnterEvent *event);
void passTextureId(std::string brushTexture);
};
}
#endif

@ -338,6 +338,62 @@ LuaState::LuaState()
}); });
}); });
lua->set_function("setPlayerCollisionState", [](bool state) {
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_COLLISION_OVERRIDE);
tempWorldstate.hasPlayerCollision = state;
Players::for_each([&state, &packet](Player *player) {
tempWorldstate.guid = player->guid;
packet->setWorldstate(&tempWorldstate);
packet->Send(false);
});
});
lua->set_function("setActorCollisionState", [](bool state) {
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_COLLISION_OVERRIDE);
tempWorldstate.hasActorCollision = state;
Players::for_each([&state, &packet](Player *player) {
tempWorldstate.guid = player->guid;
packet->setWorldstate(&tempWorldstate);
packet->Send(false);
});
});
lua->set_function("setPlacedObjectCollisionState", [](bool state) {
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_COLLISION_OVERRIDE);
tempWorldstate.hasPlacedObjectCollision = state;
Players::for_each([&state, &packet](Player *player) {
tempWorldstate.guid = player->guid;
packet->setWorldstate(&tempWorldstate);
packet->Send(false);
});
});
lua->set_function("useActorCollisionForPlacedObjects", [](bool state) {
auto packet = mwmp::Networking::get().getWorldstatePacketController()->GetPacket(ID_WORLD_COLLISION_OVERRIDE);
tempWorldstate.useActorCollisionForPlacedObjects = state;
Players::for_each([&state, &packet](Player *player) {
tempWorldstate.guid = player->guid;
packet->setWorldstate(&tempWorldstate);
packet->Send(false);
});
});
lua->set_function("createChannel", [](){ lua->set_function("createChannel", [](){
return mwmp::Networking::get().createChannel(); return mwmp::Networking::get().createChannel();
}); });

@ -98,7 +98,7 @@ add_openmw_dir (mwbase
) )
add_openmw_dir (mwmp Main Networking LocalPlayer DedicatedPlayer PlayerList LocalActor DedicatedActor ActorList ObjectList add_openmw_dir (mwmp Main Networking LocalPlayer DedicatedPlayer PlayerList LocalActor DedicatedActor ActorList ObjectList
Cell CellController MechanicsHelper RecordHelper GUIController Worldstate Cell CellController MechanicsHelper RecordHelper GUIController
) )
add_openmw_dir (mwmp/GUI GUIChat GUILogin PlayerMarkerCollection GUIDialogList TextInputDialog GUICustomWindow add_openmw_dir (mwmp/GUI GUIChat GUILogin PlayerMarkerCollection GUIDialogList TextInputDialog GUICustomWindow
@ -124,15 +124,15 @@ add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageB
ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic
) )
add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer
ProcessorDoorState ProcessorMusicPlay ProcessorVideoPlay ProcessorObjectAnimPlay ProcessorObjectAttach ProcessorDoorDestination ProcessorDoorState ProcessorMusicPlay ProcessorVideoPlay ProcessorObjectAnimPlay
ProcessorObjectCollision ProcessorObjectDelete ProcessorObjectLock ProcessorObjectMove ProcessorObjectPlace ProcessorObjectAttach ProcessorObjectCollision ProcessorObjectDelete ProcessorObjectLock ProcessorObjectMove
ProcessorObjectReset ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSpawn ProcessorObjectState ProcessorObjectPlace ProcessorObjectReset ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSpawn
ProcessorObjectTrap ProcessorScriptLocalShort ProcessorScriptLocalFloat ProcessorScriptMemberShort ProcessorObjectState ProcessorObjectTrap ProcessorScriptLocalShort ProcessorScriptLocalFloat
ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat ProcessorScriptMemberShort ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat
) )
add_openmw_dir (mwmp/processors/worldstate ProcessorRecordDynamic ProcessorWorldTime add_openmw_dir (mwmp/processors/worldstate ProcessorRecordDynamic ProcessorWorldCollisionOverride ProcessorWorldTime
) )
# Main executable # Main executable

@ -5,9 +5,6 @@
#include <map> #include <map>
#include <set> #include <set>
#include <osg/ref_ptr>
#include <osg/Node>
#include <components/esm/cellid.hpp> #include <components/esm/cellid.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -457,7 +454,6 @@ namespace MWBase
virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0; virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0;
virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0; virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual bool isIdle(const MWWorld::Ptr &ptr) const = 0;
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0; virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
@ -624,7 +620,7 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition /// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0; virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) = 0; virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) = 0;
virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, virtual void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster,
const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id,
@ -666,8 +662,6 @@ namespace MWBase
/// Preload VFX associated with this effect list /// Preload VFX associated with this effect list
virtual void preloadEffects(const ESM::EffectList* effectList) = 0; virtual void preloadEffects(const ESM::EffectList* effectList) = 0;
virtual osg::ref_ptr<osg::Node> getInstance (const std::string& modelName) = 0;
}; };
} }

@ -1,5 +1,16 @@
#include "apparatus.hpp" #include "apparatus.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadappa.hpp> #include <components/esm/loadappa.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -31,6 +42,27 @@ namespace MWClass
void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Apparatus::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const std::string Apparatus::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "armor.hpp" #include "armor.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadarmo.hpp> #include <components/esm/loadarmo.hpp>
#include <components/esm/loadskil.hpp> #include <components/esm/loadskil.hpp>
#include <components/esm/loadgmst.hpp> #include <components/esm/loadgmst.hpp>
@ -37,6 +48,27 @@ namespace MWClass
void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Armor::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const std::string Armor::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "book.hpp" #include "book.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadbook.hpp> #include <components/esm/loadbook.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -34,6 +45,27 @@ namespace MWClass
void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Book::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Book::getModel(const MWWorld::ConstPtr &ptr) const std::string Book::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "clothing.hpp" #include "clothing.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadclot.hpp> #include <components/esm/loadclot.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -33,6 +44,27 @@ namespace MWClass
void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Clothing::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const std::string Clothing::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "ingredient.hpp" #include "ingredient.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadingr.hpp> #include <components/esm/loadingr.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -34,6 +45,27 @@ namespace MWClass
void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Ingredient::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const std::string Ingredient::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "misc.hpp" #include "misc.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadmisc.hpp> #include <components/esm/loadmisc.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -41,6 +52,27 @@ namespace MWClass
void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Miscellaneous::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const std::string Miscellaneous::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "potion.hpp" #include "potion.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadalch.hpp> #include <components/esm/loadalch.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -35,6 +46,27 @@ namespace MWClass
void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Potion::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const std::string Potion::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "repair.hpp" #include "repair.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadrepa.hpp> #include <components/esm/loadrepa.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -31,6 +42,27 @@ namespace MWClass
void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Repair::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const std::string Repair::getModel(const MWWorld::ConstPtr &ptr) const

@ -1,5 +1,16 @@
#include "weapon.hpp" #include "weapon.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
/*
End of tes3mp addition
*/
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -35,6 +46,27 @@ namespace MWClass
void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const void Weapon::insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const
{ {
// TODO: add option somewhere to enable collision for placeable objects // TODO: add option somewhere to enable collision for placeable objects
/*
Start of tes3mp addition
Make it possible to enable collision for this object class from a packet
*/
if (!model.empty())
{
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (worldstate->hasPlacedObjectCollision)
{
if (worldstate->useActorCollisionForPlacedObjects)
physics.addObject(ptr, model, MWPhysics::CollisionType_Actor);
else
physics.addObject(ptr, model, MWPhysics::CollisionType_World);
}
}
/*
End of tes3mp addition
*/
} }
std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const std::string Weapon::getModel(const MWWorld::ConstPtr &ptr) const

@ -244,7 +244,7 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex ()
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,
textColours.journalTopicOver, textColours.journalTopicOver,
textColours.journalTopicPressed, (uint32_t) ch); textColours.journalTopicPressed, (Utf8Stream::UnicodeChar) ch);
if (i == 13) if (i == 13)
typesetter->sectionBreak (); typesetter->sectionBreak ();
@ -274,7 +274,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
sprintf(buffer, "( %c%c )", ch[0], ch[1]); sprintf(buffer, "( %c%c )", ch[0], ch[1]);
Utf8Stream stream ((char*) ch); Utf8Stream stream ((char*) ch);
uint32_t first = stream.peek(); Utf8Stream::UnicodeChar first = stream.peek();
const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours();
BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic,

@ -7,7 +7,6 @@
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/misc/utf8stream.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/journal.hpp" #include "../mwbase/journal.hpp"
@ -307,39 +306,22 @@ struct JournalViewModelImpl : JournalViewModel
visitor (toUtf8Span (topic.getName())); visitor (toUtf8Span (topic.getName()));
} }
void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const
{ {
MWBase::Journal * journal = MWBase::Environment::get().getJournal(); MWBase::Journal * journal = MWBase::Environment::get().getJournal();
for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i)
{ {
Utf8Stream stream (i->first.c_str()); Utf8Stream stream (i->first.c_str());
uint32_t first = toUpper(stream.peek()); Utf8Stream::UnicodeChar first = Misc::StringUtils::toLowerUtf8(stream.peek());
if (first != character) if (first != Misc::StringUtils::toLowerUtf8(character))
continue; continue;
visitor (i->second.getName()); visitor (i->second.getName());
} }
} }
static uint32_t toUpper(uint32_t ch)
{
// Russian alphabet
if (ch >= 0x0430 && ch < 0x0450)
ch -= 0x20;
// Cyrillic IO character
if (ch == 0x0451)
ch -= 0x50;
// Latin alphabet
if (ch >= 0x61 && ch < 0x80)
ch -= 0x20;
return ch;
}
struct TopicEntryImpl : BaseEntry <MWDialogue::Topic::TEntryIter, TopicEntry> struct TopicEntryImpl : BaseEntry <MWDialogue::Topic::TEntryIter, TopicEntry>
{ {
MWDialogue::Topic const & mTopic; MWDialogue::Topic const & mTopic;

@ -6,6 +6,8 @@
#include <functional> #include <functional>
#include <stdint.h> #include <stdint.h>
#include <components/misc/utf8stream.hpp>
namespace MWGui namespace MWGui
{ {
/// View-Model for the journal GUI /// View-Model for the journal GUI
@ -76,7 +78,7 @@ namespace MWGui
virtual void visitTopicName (TopicId topicId, std::function <void (Utf8Span)> visitor) const = 0; virtual void visitTopicName (TopicId topicId, std::function <void (Utf8Span)> visitor) const = 0;
/// walks over the topics whose names start with the character /// walks over the topics whose names start with the character
virtual void visitTopicNamesStartingWith (uint32_t character, std::function < void (const std::string&) > visitor) const = 0; virtual void visitTopicNamesStartingWith (Utf8Stream::UnicodeChar character, std::function < void (const std::string&) > visitor) const = 0;
/// walks over the topic entries for the topic specified by its identifier /// walks over the topic entries for the topic specified by its identifier
virtual void visitTopicEntries (TopicId topicId, std::function <void (TopicEntry const &)> visitor) const = 0; virtual void visitTopicEntries (TopicId topicId, std::function <void (TopicEntry const &)> visitor) const = 0;

@ -14,6 +14,9 @@ namespace MWGui
bool shouldAcceptKeyFocus(MyGUI::Widget* w) bool shouldAcceptKeyFocus(MyGUI::Widget* w)
{ {
if (w && w->getUserString("IgnoreTabKey") == "y")
return false;
return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); return w && !w->castType<MyGUI::Window>(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled();
} }

@ -32,10 +32,14 @@ namespace
namespace MWGui namespace MWGui
{ {
SpellModel::SpellModel(const MWWorld::Ptr &actor, const std::string& filter)
: mActor(actor), mFilter(filter)
{
}
SpellModel::SpellModel(const MWWorld::Ptr &actor) SpellModel::SpellModel(const MWWorld::Ptr &actor)
: mActor(actor) : mActor(actor)
{ {
} }
void SpellModel::update() void SpellModel::update()
@ -48,12 +52,19 @@ namespace MWGui
const MWWorld::ESMStore &esmStore = const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
std::string filter = Misc::StringUtils::lowerCaseUtf8(mFilter);
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{ {
const ESM::Spell* spell = it->first; const ESM::Spell* spell = it->first;
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
continue; continue;
std::string name = Misc::StringUtils::lowerCaseUtf8(spell->mName);
if (name.find(filter) == std::string::npos)
continue;
Spell newSpell; Spell newSpell;
newSpell.mName = spell->mName; newSpell.mName = spell->mName;
if (spell->mData.mType == ESM::Spell::ST_Spell) if (spell->mData.mType == ESM::Spell::ST_Spell)
@ -89,6 +100,11 @@ namespace MWGui
if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce) if (enchant->mData.mType != ESM::Enchantment::WhenUsed && enchant->mData.mType != ESM::Enchantment::CastOnce)
continue; continue;
std::string name = Misc::StringUtils::lowerCaseUtf8(item.getClass().getName(item));
if (name.find(filter) == std::string::npos)
continue;
Spell newSpell; Spell newSpell;
newSpell.mItem = item; newSpell.mItem = item;
newSpell.mId = item.getCellRef().getRefId(); newSpell.mId = item.getCellRef().getRefId();

@ -35,6 +35,7 @@ namespace MWGui
class SpellModel class SpellModel
{ {
public: public:
SpellModel(const MWWorld::Ptr& actor, const std::string& filter);
SpellModel(const MWWorld::Ptr& actor); SpellModel(const MWWorld::Ptr& actor);
typedef int ModelIndex; typedef int ModelIndex;
@ -50,6 +51,8 @@ namespace MWGui
MWWorld::Ptr mActor; MWWorld::Ptr mActor;
std::vector<Spell> mSpells; std::vector<Spell> mSpells;
std::string mFilter;
}; };
} }

@ -2,6 +2,7 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <MyGUI_EditBox.h>
#include <MyGUI_InputManager.h> #include <MyGUI_InputManager.h>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -49,8 +50,12 @@ namespace MWGui
getWidget(mSpellView, "SpellView"); getWidget(mSpellView, "SpellView");
getWidget(mEffectBox, "EffectsBox"); getWidget(mEffectBox, "EffectsBox");
getWidget(mFilterEdit, "FilterEdit");
mFilterEdit->setUserString("IgnoreTabKey", "y");
mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected);
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged);
setCoord(498, 300, 302, 300); setCoord(498, 300, 302, 300);
} }
@ -75,6 +80,11 @@ namespace MWGui
void SpellWindow::onOpen() void SpellWindow::onOpen()
{ {
// Reset the filter focus when opening the window
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget();
if (focus == mFilterEdit)
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
updateSpells(); updateSpells();
} }
@ -93,7 +103,7 @@ namespace MWGui
{ {
mSpellIcons->updateWidgets(mEffectBox, false); mSpellIcons->updateWidgets(mEffectBox, false);
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), mFilterEdit->getCaption()));
} }
void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped) void SpellWindow::onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped)
@ -178,6 +188,11 @@ namespace MWGui
} }
} }
void SpellWindow::onFilterChanged(MyGUI::EditBox *sender)
{
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), sender->getCaption()));
}
void SpellWindow::onSpellSelected(const std::string& spellId) void SpellWindow::onSpellSelected(const std::string& spellId)
{ {
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
@ -233,7 +248,7 @@ namespace MWGui
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery()) if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return; return;
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), ""));
SpellModel::ModelIndex selected = 0; SpellModel::ModelIndex selected = 0;
for (SpellModel::ModelIndex i = 0; i<int(mSpellView->getModel()->getItemCount()); ++i) for (SpellModel::ModelIndex i = 0; i<int(mSpellView->getModel()->getItemCount()); ++i)

@ -32,6 +32,7 @@ namespace MWGui
void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped); void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped);
void onSpellSelected(const std::string& spellId); void onSpellSelected(const std::string& spellId);
void onModelIndexSelected(SpellModel::ModelIndex index); void onModelIndexSelected(SpellModel::ModelIndex index);
void onFilterChanged(MyGUI::EditBox *sender);
void onDeleteSpellAccept(); void onDeleteSpellAccept();
void askDeleteSpell(const std::string& spellId); void askDeleteSpell(const std::string& spellId);
@ -41,6 +42,7 @@ namespace MWGui
SpellView* mSpellView; SpellView* mSpellView;
SpellIcons* mSpellIcons; SpellIcons* mSpellIcons;
MyGUI::EditBox* mFilterEdit;
private: private:
float mUpdateTimer; float mUpdateTimer;

@ -866,7 +866,8 @@ namespace MWMechanics
} }
// place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground. // place above to prevent moving inside objects, e.g. stairs, because a vector between pathgrids can be underground.
dest.mZ += 80; // Adding 20 in adjustPosition() is not enough.
dest.mZ += 60;
ToWorldCoordinates(dest, actor.getCell()->getCell()); ToWorldCoordinates(dest, actor.getCell()->getCell());

@ -863,7 +863,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
mAnimation->runAnimation(0.f); mAnimation->runAnimation(0.f);
mAnimation->updateEffects(0.f);
unpersistAnimationState(); unpersistAnimationState();
} }
@ -2118,13 +2117,6 @@ void CharacterController::update(float duration)
} }
osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration); osg::Vec3f moved = mAnimation->runAnimation(mSkipAnim ? 0.f : duration);
// treat player specifically since he is not in rendering mObjects
if (mPtr == getPlayer())
{
mAnimation->updateEffects(mSkipAnim ? 0.f : duration);
}
if(duration > 0.0f) if(duration > 0.0f)
moved /= duration; moved /= duration;
else else
@ -2157,8 +2149,7 @@ void CharacterController::update(float duration)
moved.z() = 1.0; moved.z() = 1.0;
// Update movement // Update movement
// We should not apply movement for standing actors if(mMovementAnimationControlled && mPtr.getClass().isActor())
if(mMovementAnimationControlled && mPtr.getClass().isActor() && (movement.length2() > 0.f || !world->isIdle(mPtr)))
world->queueMovement(mPtr, moved); world->queueMovement(mPtr, moved);
mSkipAnim = false; mSkipAnim = false;
@ -2339,7 +2330,6 @@ void CharacterController::forceStateUpdate()
} }
mAnimation->runAnimation(0.f); mAnimation->runAnimation(0.f);
mAnimation->updateEffects(0.f);
} }
CharacterController::KillResult CharacterController::kill() CharacterController::KillResult CharacterController::kill()

@ -6,12 +6,8 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <osg/BoundingBox>
#include <osg/ComputeBoundsVisitor>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -44,7 +40,6 @@
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
#include "../mwrender/vismask.hpp"
#include "npcstats.hpp" #include "npcstats.hpp"
#include "actorutil.hpp" #include "actorutil.hpp"
@ -243,8 +238,6 @@ namespace MWMechanics
magicEffects = effects; magicEffects = effects;
float resisted = 0; float resisted = 0;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
{
// Effects with no resistance attribute belonging to them can not be resisted // Effects with no resistance attribute belonging to them can not be resisted
if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
return 0.f; return 0.f;
@ -281,7 +274,6 @@ namespace MWMechanics
x = std::min(x + resistance, 100.f); x = std::min(x + resistance, 100.f);
resisted = x; resisted = x;
}
return resisted; return resisted;
} }
@ -479,13 +471,11 @@ namespace MWMechanics
} }
float magnitudeMult = 1; float magnitudeMult = 1;
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor())
{
if (absorbed)
continue;
// Try reflecting if (!absorbed)
if (!reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) {
// Reflect harmful effects
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && !reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
{ {
float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude(); float reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude();
bool isReflected = (Misc::Rng::roll0to99() < reflect); bool isReflected = (Misc::Rng::roll0to99() < reflect);
@ -509,16 +499,17 @@ namespace MWMechanics
else if (castByPlayer) else if (castByPlayer)
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
} }
else if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && castByPlayer && target != caster)
{
// If player is attempting to cast a harmful spell and it wasn't fully resisted, show the target's HP bar
MWBase::Environment::get().getWindowManager()->setEnemy(target);
}
if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()) if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState())
magnitudeMult = 0; magnitudeMult = 0;
// If player is attempting to cast a harmful spell, show the target's HP bar
if (castByPlayer && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target);
// Notify the target actor they've been hit // Notify the target actor they've been hit
if (target != caster && !caster.isEmpty()) if (target != caster && !caster.isEmpty() && magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
} }
@ -1071,13 +1062,11 @@ namespace MWMechanics
return true; return true;
} }
void CastSpell::playSpellCastingEffects(const std::string &spellid) void CastSpell::playSpellCastingEffects(const std::string &spellid){
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
std::vector<std::string> addedEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin(); for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin();
iter != spell->mEffects.mList.end(); ++iter) iter != spell->mEffects.mList.end(); ++iter)
{ {
@ -1086,6 +1075,8 @@ namespace MWMechanics
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster);
if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL)
{
const ESM::Static* castStatic; const ESM::Static* castStatic;
if (!effect->mCasting.empty()) if (!effect->mCasting.empty())
@ -1093,65 +1084,9 @@ namespace MWMechanics
else else
castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast"); castStatic = store.get<ESM::Static>().find ("VFX_DefaultCast");
// check if the effect was already added
if (std::find(addedEffects.begin(), addedEffects.end(), "meshes\\" + castStatic->mModel) != addedEffects.end())
continue;
std::string texture = effect->mParticle; std::string texture = effect->mParticle;
float scale = 1.0f; animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3());
if (animation && mCaster.getClass().isNpc())
{
// For NOC we should take race height as scaling factor
const ESM::NPC *npc = mCaster.get<ESM::NPC>()->mBase;
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race =
esmStore.get<ESM::Race>().find(npc->mRace);
scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
}
else
{
std::string casterModel = mCaster.getClass().getModel(mCaster);
osg::ref_ptr<osg::Node> model = MWBase::Environment::get().getWorld()->getInstance(casterModel);
osg::ComputeBoundsVisitor computeBoundsVisitor;
computeBoundsVisitor.setTraversalMask(~(MWRender::Mask_ParticleSystem|MWRender::Mask_Effect));
model->accept(computeBoundsVisitor);
osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();
if (bounds.valid())
{
float meshSizeX = std::abs(bounds.xMax() - bounds.xMin());
float meshSizeY = std::abs(bounds.yMax() - bounds.yMin());
float meshSizeZ = std::abs(bounds.zMax() - bounds.zMin());
// TODO: take a size of particle or NPC with height and weight = 1.0 as scale = 1.0
float scaleX = meshSizeX/60.f;
float scaleY = meshSizeY/60.f;
float scaleZ = meshSizeZ/120.f;
scale = std::max({ scaleX, scaleY, scaleZ });
//pos = bounds.center();
//pos[2] = bounds.zMin();
}
}
// If the caster has no animation, add the effect directly to the effectManager
if (animation)
{
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale);
}
else
{
// We should set scale for effect manager manually
float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);
} }
if (animation && !mCaster.getClass().isActor()) if (animation && !mCaster.getClass().isActor())
@ -1161,8 +1096,6 @@ namespace MWMechanics
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
}; };
addedEffects.push_back("meshes\\" + castStatic->mModel);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!effect->mCastSound.empty()) if(!effect->mCastSound.empty())
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f); sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);

@ -1,7 +1,3 @@
//
// Created by koncord on 04.01.16.
//
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -479,7 +475,7 @@ ObjectList *Networking::getObjectList()
return &objectList; return &objectList;
} }
BaseWorldstate *Networking::getWorldstate() Worldstate *Networking::getWorldstate()
{ {
return &worldstate; return &worldstate;
} }

@ -1,7 +1,3 @@
//
// Created by koncord on 04.01.16.
//
#ifndef OPENMW_NETWORKING_HPP #ifndef OPENMW_NETWORKING_HPP
#define OPENMW_NETWORKING_HPP #define OPENMW_NETWORKING_HPP
@ -9,8 +5,6 @@
#include <BitStream.h> #include <BitStream.h>
#include <string> #include <string>
#include "ActorList.hpp"
#include "ObjectList.hpp"
#include <components/openmw-mp/NetworkMessages.hpp> #include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Controllers/PlayerPacketController.hpp> #include <components/openmw-mp/Controllers/PlayerPacketController.hpp>
@ -20,6 +14,10 @@
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include "ActorList.hpp"
#include "ObjectList.hpp"
#include "Worldstate.hpp"
namespace mwmp namespace mwmp
{ {
class LocalPlayer; class LocalPlayer;
@ -47,7 +45,7 @@ namespace mwmp
LocalPlayer *getLocalPlayer(); LocalPlayer *getLocalPlayer();
ActorList *getActorList(); ActorList *getActorList();
ObjectList *getObjectList(); ObjectList *getObjectList();
BaseWorldstate *getWorldstate(); Worldstate *getWorldstate();
std::string getNetworkStatistics(); std::string getNetworkStatistics();
@ -64,7 +62,7 @@ namespace mwmp
ActorList actorList; ActorList actorList;
ObjectList objectList; ObjectList objectList;
BaseWorldstate worldstate; Worldstate worldstate;
void receiveMessage(RakNet::Packet *packet); void receiveMessage(RakNet::Packet *packet);

@ -71,8 +71,10 @@ DedicatedPlayer *PlayerList::getPlayer(const MWWorld::Ptr &ptr)
{ {
if (p.second == 0 || p.second->getPtr().mRef == 0) if (p.second == 0 || p.second->getPtr().mRef == 0)
continue; continue;
string refid = ptr.getCellRef().getRefId();
if (p.second->getPtr().getCellRef().getRefId() == refid) string refId = ptr.getCellRef().getRefId();
if (p.second->getPtr().getCellRef().getRefId() == refId)
return p.second; return p.second;
} }
return 0; return 0;
@ -83,6 +85,10 @@ bool PlayerList::isDedicatedPlayer(const MWWorld::Ptr &ptr)
if (ptr.mRef == nullptr) if (ptr.mRef == nullptr)
return false; return false;
// Players always have 0 as their refNumIndex and mpNum
if (ptr.getCellRef().getRefNum().mIndex != 0 || ptr.getCellRef().getMpNum() != 0)
return false;
return (getPlayer(ptr) != 0); return (getPlayer(ptr) != 0);
} }

@ -0,0 +1,24 @@
#include "Worldstate.hpp"
#include "Main.hpp"
#include "Networking.hpp"
using namespace mwmp;
using namespace std;
Worldstate::Worldstate()
{
hasPlayerCollision = true;
hasActorCollision = true;
hasPlacedObjectCollision = false;
useActorCollisionForPlacedObjects = false;
}
Worldstate::~Worldstate()
{
}
Networking *Worldstate::getNetworking()
{
return mwmp::Main::get().getNetworking();
}

@ -0,0 +1,22 @@
#ifndef OPENMW_WORLDSTATE_HPP
#define OPENMW_WORLDSTATE_HPP
#include <components/openmw-mp/Base/BaseWorldstate.hpp>
namespace mwmp
{
class Networking;
class Worldstate : public BaseWorldstate
{
public:
Worldstate();
virtual ~Worldstate();
private:
Networking *getNetworking();
};
}
#endif //OPENMW_WORLDSTATE_HPP

@ -92,6 +92,7 @@
#include "WorldstateProcessor.hpp" #include "WorldstateProcessor.hpp"
#include "worldstate/ProcessorRecordDynamic.hpp" #include "worldstate/ProcessorRecordDynamic.hpp"
#include "worldstate/ProcessorWorldCollisionOverride.hpp"
#include "worldstate/ProcessorWorldTime.hpp" #include "worldstate/ProcessorWorldTime.hpp"
using namespace mwmp; using namespace mwmp;
@ -186,5 +187,6 @@ void ProcessorInitializer()
ActorProcessor::AddProcessor(new ProcessorActorTest()); ActorProcessor::AddProcessor(new ProcessorActorTest());
WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic()); WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());
WorldstateProcessor::AddProcessor(new ProcessorWorldCollisionOverride());
WorldstateProcessor::AddProcessor(new ProcessorWorldTime()); WorldstateProcessor::AddProcessor(new ProcessorWorldTime());
} }

@ -0,0 +1,28 @@
#ifndef OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP
#define OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP
#include <apps/openmw/mwbase/world.hpp>
#include <apps/openmw/mwbase/environment.hpp>
#include "../WorldstateProcessor.hpp"
namespace mwmp
{
class ProcessorWorldCollisionOverride : public WorldstateProcessor
{
public:
ProcessorWorldCollisionOverride()
{
BPP_INIT(ID_WORLD_COLLISION_OVERRIDE)
}
virtual void Do(WorldstatePacket &packet, BaseWorldstate &worldstate)
{
// Placeholder
}
};
}
#endif //OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP

@ -17,8 +17,6 @@ namespace mwmp
} }
virtual void Do(WorldstatePacket &packet, BaseWorldstate &worldstate) virtual void Do(WorldstatePacket &packet, BaseWorldstate &worldstate)
{
if (isLocal())
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
@ -40,7 +38,6 @@ namespace mwmp
if (worldstate.timeScale != -1) if (worldstate.timeScale != -1)
world->setTimeScale(worldstate.timeScale); world->setTimeScale(worldstate.timeScale);
} }
}
}; };
} }

@ -1,5 +1,18 @@
#include "actor.hpp" #include "actor.hpp"
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include <components/openmw-mp/Log.hpp>
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/PlayerList.hpp"
/*
End of tes3mp addition
*/
#include <BulletCollision/CollisionShapes/btCapsuleShape.h> #include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
#include <BulletCollision/CollisionDispatch/btCollisionWorld.h> #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
@ -18,7 +31,7 @@ namespace MWPhysics
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world) Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
: mCanWaterWalk(false), mWalkingOnWater(false) : mCanWaterWalk(false), mWalkingOnWater(false)
, mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false), mIdle(true) , mCollisionObject(nullptr), mForce(0.f, 0.f, 0.f), mOnGround(true), mOnSlope(false)
, mInternalCollisionMode(true) , mInternalCollisionMode(true)
, mExternalCollisionMode(true) , mExternalCollisionMode(true)
, mCollisionWorld(world) , mCollisionWorld(world)
@ -53,6 +66,27 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape>
updatePosition(); updatePosition();
addCollisionMask(getCollisionMask()); addCollisionMask(getCollisionMask());
/*
Start of tes3mp addition
Make it possible to disable collision for players or regular actors from a packet
*/
mwmp::BaseWorldstate *worldstate = mwmp::Main::get().getNetworking()->getWorldstate();
if (mwmp::PlayerList::isDedicatedPlayer(ptr))
{
if (!worldstate->hasPlayerCollision)
enableCollisionBody(false);
}
else
{
if (!worldstate->hasActorCollision)
enableCollisionBody(false);
}
/*
End of tes3mp addition
*/
} }
Actor::~Actor() Actor::~Actor()
@ -195,11 +229,6 @@ void Actor::setOnSlope(bool slope)
mOnSlope = slope; mOnSlope = slope;
} }
void Actor::setIdle(bool idle)
{
mIdle = idle;
}
bool Actor::isWalkingOnWater() const bool Actor::isWalkingOnWater() const
{ {
return mWalkingOnWater; return mWalkingOnWater;

@ -139,13 +139,6 @@ namespace MWPhysics
return mInternalCollisionMode && mOnSlope; return mInternalCollisionMode && mOnSlope;
} }
void setIdle(bool idle);
bool getIdle() const
{
return mIdle;
}
btCollisionObject* getCollisionObject() const btCollisionObject* getCollisionObject() const
{ {
return mCollisionObject.get(); return mCollisionObject.get();
@ -186,7 +179,6 @@ namespace MWPhysics
osg::Vec3f mForce; osg::Vec3f mForce;
bool mOnGround; bool mOnGround;
bool mOnSlope; bool mOnSlope;
bool mIdle;
bool mInternalCollisionMode; bool mInternalCollisionMode;
bool mExternalCollisionMode; bool mExternalCollisionMode;

@ -249,7 +249,7 @@ namespace MWPhysics
// Check if we actually found a valid spawn point (use an infinitely thin ray this time). // Check if we actually found a valid spawn point (use an infinitely thin ray this time).
// Required for some broken door destinations in Morrowind.esm, where the spawn point // Required for some broken door destinations in Morrowind.esm, where the spawn point
// intersects with other geometry if the actor's base is taken into account // intersects with other geometry if the actor's base is taken into account
btVector3 from = toBullet(position + offset); btVector3 from = toBullet(position);
btVector3 to = from - btVector3(0,0,maxHeight); btVector3 to = from - btVector3(0,0,maxHeight);
btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to);
@ -683,7 +683,6 @@ namespace MWPhysics
, mWaterEnabled(false) , mWaterEnabled(false)
, mParentNode(parentNode) , mParentNode(parentNode)
, mPhysicsDt(1.f / 60.f) , mPhysicsDt(1.f / 60.f)
, mIdleUpdateTimer(0)
{ {
mResourceSystem->addResourceManager(mShapeManager.get()); mResourceSystem->addResourceManager(mShapeManager.get());
@ -740,18 +739,6 @@ namespace MWPhysics
delete mBroadphase; delete mBroadphase;
} }
void PhysicsSystem::updateIdle()
{
for (ActorMap::iterator it = mActors.begin(); it != mActors.end(); ++it)
{
osg::Vec3f pos(it->second->getCollisionObjectPosition());
RayResult result = castRay(pos, pos - osg::Vec3f(0, 0, it->second->getHalfExtents().z() + 2), it->second->getPtr(), std::vector<MWWorld::Ptr>(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door);
it->second->setIdle(result.mHit);
}
}
void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue)
{ {
mUnrefQueue = unrefQueue; mUnrefQueue = unrefQueue;
@ -1080,11 +1067,6 @@ namespace MWPhysics
return physactor && physactor->getOnGround(); return physactor && physactor->getOnGround();
} }
bool PhysicsSystem::isIdle(const MWWorld::Ptr &actor)
{
Actor* physactor = getActor(actor);
return physactor && physactor->getIdle();
}
bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel) bool PhysicsSystem::canMoveToWaterSurface(const MWWorld::ConstPtr &actor, const float waterlevel)
{ {
const Actor* physicActor = getActor(actor); const Actor* physicActor = getActor(actor);
@ -1377,10 +1359,6 @@ namespace MWPhysics
cmode = !cmode; cmode = !cmode;
found->second->enableCollisionMode(cmode); found->second->enableCollisionMode(cmode);
found->second->enableCollisionBody(cmode); found->second->enableCollisionBody(cmode);
if (cmode)
queueObjectMovement(MWMechanics::getPlayer(), osg::Vec3f(0, 0, -0.1f));
return cmode; return cmode;
} }
@ -1500,13 +1478,6 @@ namespace MWPhysics
for (std::set<Object*>::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it) for (std::set<Object*>::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it)
(*it)->animateCollisionShapes(mCollisionWorld); (*it)->animateCollisionShapes(mCollisionWorld);
mIdleUpdateTimer -= dt;
if (mIdleUpdateTimer <= 0.f)
{
mIdleUpdateTimer = 0.5f;
updateIdle();
}
#ifndef BT_NO_PROFILE #ifndef BT_NO_PROFILE
CProfileManager::Reset(); CProfileManager::Reset();
CProfileManager::Increment_Frame_Counter(); CProfileManager::Increment_Frame_Counter();

@ -124,7 +124,6 @@ namespace MWPhysics
bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const; bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const;
bool isOnGround (const MWWorld::Ptr& actor); bool isOnGround (const MWWorld::Ptr& actor);
bool isIdle (const MWWorld::Ptr& actor);
bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel); bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel);
@ -184,7 +183,6 @@ namespace MWPhysics
private: private:
void updateWater(); void updateWater();
void updateIdle();
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue; osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
@ -233,7 +231,6 @@ namespace MWPhysics
osg::ref_ptr<osg::Group> mParentNode; osg::ref_ptr<osg::Group> mParentNode;
float mPhysicsDt; float mPhysicsDt;
float mIdleUpdateTimer;
PhysicsSystem (const PhysicsSystem&); PhysicsSystem (const PhysicsSystem&);
PhysicsSystem& operator= (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&);

@ -8,8 +8,6 @@
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
#include <osg/BlendFunc> #include <osg/BlendFunc>
#include <osg/Material> #include <osg/Material>
#include <osg/ComputeBoundsVisitor>
#include <osg/PositionAttitudeTransform>
#include <osgParticle/ParticleSystem> #include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleProcessor> #include <osgParticle/ParticleProcessor>
@ -1117,6 +1115,8 @@ namespace MWRender
++stateiter; ++stateiter;
} }
updateEffects(duration);
if (mHeadController) if (mHeadController)
{ {
const float epsilon = 0.001f; const float epsilon = 0.001f;
@ -1366,7 +1366,7 @@ namespace MWRender
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue); useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
} }
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture, float scale) void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
{ {
if (!mObjectRoot.get()) if (!mObjectRoot.get())
return; return;
@ -1417,13 +1417,7 @@ namespace MWRender
overrideFirstRootTexture(texture, mResourceSystem, node); overrideFirstRootTexture(texture, mResourceSystem, node);
osg::Vec3f scale3f (scale, scale, scale); // TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box.
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
trans->setScale(scale3f);
trans->addChild(node);
parentNode->removeChild(node);
parentNode->addChild(trans);
mEffects.push_back(params); mEffects.push_back(params);
} }

@ -364,7 +364,7 @@ public:
* @param texture override the texture specified in the model's materials - if empty, do not override * @param texture override the texture specified in the model's materials - if empty, do not override
* @note Will not add an effect twice. * @note Will not add an effect twice.
*/ */
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f); void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "");
void removeEffect (int effectId); void removeEffect (int effectId);
void getLoopingEffects (std::vector<int>& out) const; void getLoopingEffects (std::vector<int>& out) const;
@ -446,6 +446,7 @@ public:
void setLoopingEnabled(const std::string &groupname, bool enabled); void setLoopingEnabled(const std::string &groupname, bool enabled);
/// This is typically called as part of runAnimation, but may be called manually if needed.
void updateEffects(float duration); void updateEffects(float duration);
/// Return a node with the specified name, or NULL if not existing. /// Return a node with the specified name, or NULL if not existing.

@ -26,13 +26,6 @@ EffectManager::~EffectManager()
} }
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX) void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{
osg::Vec3f scale3f (scale, scale, scale);
addEffect(model, textureOverride, worldPosition, scale3f, isMagicVFX);
}
void EffectManager::addEffect(const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale, bool isMagicVFX)
{ {
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model); osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
@ -47,7 +40,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform; osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
trans->setPosition(worldPosition); trans->setPosition(worldPosition);
trans->setScale(scale); trans->setScale(osg::Vec3f(scale, scale, scale));
trans->addChild(node); trans->addChild(node);
SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime);

@ -33,7 +33,6 @@ namespace MWRender
/// Add an effect. When it's finished playing, it will be removed automatically. /// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true); void addEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true);
void addEffect (const std::string &model, const std::string& textureOverride, const osg::Vec3f &worldPosition, const osg::Vec3f &scale3f, bool isMagicVFX);
void update(float dt); void update(float dt);

@ -138,14 +138,6 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr)
return false; return false;
} }
void Objects::updateEffects(float duration)
{
for(PtrAnimationMap::iterator iter = mObjects.begin();iter != mObjects.end();)
{
iter->second->updateEffects(duration);
++iter;
}
}
void Objects::removeCell(const MWWorld::CellStore* store) void Objects::removeCell(const MWWorld::CellStore* store)
{ {

@ -88,8 +88,6 @@ public:
bool removeObject (const MWWorld::Ptr& ptr); bool removeObject (const MWWorld::Ptr& ptr);
///< \return found? ///< \return found?
void updateEffects(float duration);
void removeCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store);
/// Updates containing cell for object rendering data /// Updates containing cell for object rendering data

@ -557,7 +557,6 @@ namespace MWRender
mEffectManager->update(dt); mEffectManager->update(dt);
mSky->update(dt); mSky->update(dt);
mWater->update(dt); mWater->update(dt);
mObjects->updateEffects(dt);
} }
mCamera->update(dt, paused); mCamera->update(dt, paused);
@ -842,11 +841,9 @@ namespace MWRender
mObjects->updatePtr(old, updated); mObjects->updatePtr(old, updated);
} }
void RenderingManager::spawnEffect(const std::string &model, const std::string& texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX) void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const osg::Vec3f &worldPosition, float scale, bool isMagicVFX)
{ {
osg::Vec3f scale3f (scale, scale, scale); mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX);
mEffectManager->addEffect(model, texture, worldPosition, scale3f, isMagicVFX);
} }
void RenderingManager::notifyWorldSpaceChanged() void RenderingManager::notifyWorldSpaceChanged()
@ -1125,12 +1122,6 @@ namespace MWRender
updateProjectionMatrix(); updateProjectionMatrix();
} }
} }
osg::ref_ptr<osg::Node> RenderingManager::getInstance(const std::string& modelName)
{
return mResourceSystem->getSceneManager()->getInstance(modelName);
}
void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format) void RenderingManager::exportSceneGraph(const MWWorld::Ptr &ptr, const std::string &filename, const std::string &format)
{ {
osg::Node* node = mViewer->getSceneData(); osg::Node* node = mViewer->getSceneData();

@ -205,8 +205,6 @@ namespace MWRender
LandManager* getLandManager() const; LandManager* getLandManager() const;
osg::ref_ptr<osg::Node> getInstance(const std::string& modelName);
private: private:
void updateProjectionMatrix(); void updateProjectionMatrix();
void updateTextureFiltering(); void updateTextureFiltering();

@ -270,12 +270,16 @@ namespace MWScript
bool InterpreterContext::menuMode() bool InterpreterContext::menuMode()
{ {
/* Disabled by tes3mp, because being in a menu should not pause scripts in it /*
Start of tes3mp change (major)
return MWBase::Environment::get().getWindowManager()->isGuiMode(); Being in a menu should not pause scripts in multiplayer, so always return false
*/ */
//return MWBase::Environment::get().getWindowManager()->isGuiMode();
return false; return false;
/*
End of tes3mp change (major)
*/
} }
int InterpreterContext::getGlobalShort (const std::string& name) const int InterpreterContext::getGlobalShort (const std::string& name) const
@ -672,10 +676,17 @@ namespace MWScript
Locals& locals = getMemberLocals (scriptId, global); Locals& locals = getMemberLocals (scriptId, global);
// Added by tes3mp so it can be reused by it /*
Start of tes3mp change (minor)
Declare an integer so it can be reused below for multiplayer script sync purposes
*/
int index = findLocalVariableIndex(scriptId, name, 's'); int index = findLocalVariableIndex(scriptId, name, 's');
locals.mShorts[index] = value; locals.mShorts[index] = value;
/*
End of tes3mp change (minor)
*/
/* /*
Start of tes3mp addition Start of tes3mp addition

@ -26,7 +26,6 @@
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/scriptmanager.hpp" #include "../mwbase/scriptmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -771,8 +770,6 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr ptr = R()(runtime);
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, -0.1f));
} }
}; };
@ -1124,7 +1121,6 @@ namespace MWScript
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false); MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr (targetId, false);
MWMechanics::CastSpell cast(ptr, target); MWMechanics::CastSpell cast(ptr, target);
cast.playSpellCastingEffects(spell);
cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mHitPosition = target.getRefData().getPosition().asVec3();
cast.mAlwaysSucceed = true; cast.mAlwaysSucceed = true;
cast.cast(spell); cast.cast(spell);

@ -506,7 +506,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson()) if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson())
MWBase::Environment::get().getWorld()->togglePOV(); MWBase::Environment::get().getWorld()->togglePOV();
const MWWorld::Ptr ptr = MWMechanics::getPlayer(); MWWorld::ConstPtr ptr = MWMechanics::getPlayer();
if (ptr.isInCell()) if (ptr.isInCell())
{ {
@ -538,9 +538,6 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag. // Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
// But make sure the flag is cleared anyway in case it was set from an earlier game. // But make sure the flag is cleared anyway in case it was set from an earlier game.
MWBase::Environment::get().getWorld()->markCellAsUnchanged(); MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// Workaround to fix camera position upon game load
MWBase::Environment::get().getWorld()->queueMovement(ptr, osg::Vec3f(0, 0, 0));
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -280,10 +280,11 @@ namespace MWWorld
{ {
ESM::Position pos; ESM::Position pos;
// Major change made by tes3mp /*
// Start of tes3mp change (major)
// If Pelagiad exists, spawn there; otherwise, spawn at 0,0
If Pelagiad exists, spawn there; otherwise, spawn at 0 ,0
*/
if (findExteriorPosition("Pelagiad", pos)) if (findExteriorPosition("Pelagiad", pos))
{ {
changeToExteriorCell(pos, true); changeToExteriorCell(pos, true);
@ -300,6 +301,9 @@ namespace MWWorld
pos.rot[2] = 0; pos.rot[2] = 0;
mWorldScene->changeToExteriorCell(pos, true); mWorldScene->changeToExteriorCell(pos, true);
} }
/*
End of tes3mp change (major)
*/
} }
} }
@ -1437,6 +1441,8 @@ namespace MWWorld
if (pos.z() < terrainHeight) if (pos.z() < terrainHeight)
pos.z() = terrainHeight; pos.z() = terrainHeight;
pos.z() += 20; // place slightly above. will snap down to ground with code below
if (force || !isFlying(ptr)) if (force || !isFlying(ptr))
{ {
osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500);
@ -1682,11 +1688,6 @@ namespace MWWorld
} }
} }
osg::ref_ptr<osg::Node> World::getInstance (const std::string& modelName)
{
return mRendering->getInstance(modelName);
}
const ESM::Potion *World::createRecord (const ESM::Potion& record) const ESM::Potion *World::createRecord (const ESM::Potion& record)
{ {
return mStore.insert(record); return mStore.insert(record);
@ -2314,11 +2315,6 @@ namespace MWWorld
return mPhysics->isOnGround(ptr); return mPhysics->isOnGround(ptr);
} }
bool World::isIdle(const MWWorld::Ptr &ptr) const
{
return mPhysics->isIdle(ptr);
}
void World::togglePOV() void World::togglePOV()
{ {
mRendering->togglePOV(); mRendering->togglePOV();
@ -2559,10 +2555,16 @@ namespace MWWorld
void World::hurtStandingActors(const ConstPtr &object, float healthPerSecond) void World::hurtStandingActors(const ConstPtr &object, float healthPerSecond)
{ {
/* Disabled by tes3mp, because being in a menu should not pause the game in it /*
Start of tes3mp change (major)
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) Being in a menu should not prevent actors from being hurt in multiplayer,
return; so that check has been commented out
*/
//if (MWBase::Environment::get().getWindowManager()->isGuiMode())
// return;
/*
End of tes3mp change (major)
*/ */
std::vector<MWWorld::Ptr> actors; std::vector<MWWorld::Ptr> actors;
@ -2596,10 +2598,16 @@ namespace MWWorld
void World::hurtCollidingActors(const ConstPtr &object, float healthPerSecond) void World::hurtCollidingActors(const ConstPtr &object, float healthPerSecond)
{ {
/* Disabled by tes3mp, because being in a menu should not pause the game in it /*
Start of tes3mp change (major)
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) Being in a menu should not prevent actors from being hurt in multiplayer,
return; so that check has been commented out
*/
//if (MWBase::Environment::get().getWindowManager()->isGuiMode())
// return;
/*
End of tes3mp change (major)
*/ */
std::vector<MWWorld::Ptr> actors; std::vector<MWWorld::Ptr> actors;
@ -3575,9 +3583,9 @@ namespace MWWorld
mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false); mRendering->spawnEffect(model, texture, worldPosition, 1.0f, false);
} }
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos, float scale, bool isMagicVFX) void World::spawnEffect(const std::string &model, const std::string &textureOverride, const osg::Vec3f &worldPos)
{ {
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX); mRendering->spawnEffect(model, textureOverride, worldPos);
} }
void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType, void World::explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const Ptr& caster, const Ptr& ignore, ESM::RangeType rangeType,

@ -553,7 +553,6 @@ namespace MWWorld
bool isWading(const MWWorld::ConstPtr &object) const override; bool isWading(const MWWorld::ConstPtr &object) const override;
bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override; bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) const override;
bool isOnGround(const MWWorld::Ptr &ptr) const override; bool isOnGround(const MWWorld::Ptr &ptr) const override;
bool isIdle(const MWWorld::Ptr &ptr) const override;
osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override; osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;
@ -725,7 +724,7 @@ namespace MWWorld
/// Spawn a blood effect for \a ptr at \a worldPosition /// Spawn a blood effect for \a ptr at \a worldPosition
void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override; void spawnBloodEffect (const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true) override; void spawnEffect (const std::string& model, const std::string& textureOverride, const osg::Vec3f& worldPos) override;
void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore, void explodeSpell(const osg::Vec3f& origin, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const MWWorld::Ptr& ignore,
ESM::RangeType rangeType, const std::string& id, const std::string& sourceName, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName,
@ -765,8 +764,6 @@ namespace MWWorld
/// Preload VFX associated with this effect list /// Preload VFX associated with this effect list
void preloadEffects(const ESM::EffectList* effectList) override; void preloadEffects(const ESM::EffectList* effectList) override;
osg::ref_ptr<osg::Node> getInstance (const std::string& modelName);
}; };
} }

@ -18,8 +18,8 @@ platform:
- x64 - x64
configuration: configuration:
- Debug # - Debug
# - Release - Release
# For the Qt, Boost, CMake, etc installs # For the Qt, Boost, CMake, etc installs
#os: Visual Studio 2017 #os: Visual Studio 2017
@ -55,6 +55,10 @@ build_script:
- cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64 - cmd: if %PLATFORM%==x64 set build=MSVC%msvc%_64
- cmd: msbuild %build%\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmd: msbuild %build%\OpenMW.sln /t:Build /p:Configuration=%configuration% /m:2 /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
after_build:
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_x64.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\Release\ -xr"!*.pdb"
- cmd: if %PLATFORM%==x64 7z a OpenMW_MSVC%msvc%_x64_pdb.zip %APPVEYOR_BUILD_FOLDER%\MSVC%msvc%_64\Release\*.pdb
test: off test: off
#notifications: #notifications:
@ -63,3 +67,9 @@ test: off
# - # -
# on_build_failure: true # on_build_failure: true
# on_build_status_changed: true # on_build_status_changed: true
artifacts:
- path: OpenMW_MSVC%msvc%_x64.zip
name: OpenMW_MSVC%msvc%_x64
- path: OpenMW_MSVC%msvc%_x64_pdb.zip
name: OpenMW_MSVC%msvc%_x64_pdb

@ -202,7 +202,7 @@ add_component_dir (openmw-mp/Packets/Object
add_component_dir (openmw-mp/Packets/Worldstate add_component_dir (openmw-mp/Packets/Worldstate
WorldstatePacket WorldstatePacket
PacketRecordDynamic PacketWorldTime PacketRecordDynamic PacketWorldCollisionOverride PacketWorldTime
) )
add_component_dir (fallback add_component_dir (fallback

@ -6,6 +6,8 @@
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include "utf8stream.hpp"
namespace Misc namespace Misc
{ {
class StringUtils class StringUtils
@ -56,6 +58,70 @@ public:
}; };
} }
static Utf8Stream::UnicodeChar toLowerUtf8(Utf8Stream::UnicodeChar ch)
{
// Russian alphabet
if (ch >= 0x0410 && ch < 0x0430)
return ch += 0x20;
// Cyrillic IO character
if (ch == 0x0401)
return ch += 0x50;
// Latin alphabet
if (ch >= 0x41 && ch < 0x60)
return ch += 0x20;
// Deutch characters
if (ch == 0xc4 || ch == 0xd6 || ch == 0xdc)
return ch += 0x20;
if (ch == 0x1e9e)
return 0xdf;
// TODO: probably we will need to support characters from other languages
return ch;
}
static std::string lowerCaseUtf8(const std::string str)
{
if (str.empty())
return str;
// Decode string as utf8 characters, convert to lower case and pack them to string
std::string out;
Utf8Stream stream (str.c_str());
while (!stream.eof ())
{
Utf8Stream::UnicodeChar character = toLowerUtf8(stream.peek());
if (character <= 0x7f)
out.append(1, static_cast<char>(character));
else if (character <= 0x7ff)
{
out.append(1, static_cast<char>(0xc0 | ((character >> 6) & 0x1f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
else if (character <= 0xffff)
{
out.append(1, static_cast<char>(0xe0 | ((character >> 12) & 0x0f)));
out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
else
{
out.append(1, static_cast<char>(0xf0 | ((character >> 18) & 0x07)));
out.append(1, static_cast<char>(0x80 | ((character >> 12) & 0x3f)));
out.append(1, static_cast<char>(0x80 | ((character >> 6) & 0x3f)));
out.append(1, static_cast<char>(0x80 | (character & 0x3f)));
}
stream.consume();
}
return out;
}
static bool ciLess(const std::string &x, const std::string &y) { static bool ciLess(const std::string &x, const std::string &y) {
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci());
} }

@ -1,6 +1,7 @@
#ifndef MISC_UTF8ITER_HPP #ifndef MISC_UTF8ITER_HPP
#define MISC_UTF8ITER_HPP #define MISC_UTF8ITER_HPP
#include <cstring>
#include <tuple> #include <tuple>
class Utf8Stream class Utf8Stream

@ -1,7 +1,3 @@
//
// Created by koncord on 07.01.16.
//
#ifndef OPENMW_BASEPLAYER_HPP #ifndef OPENMW_BASEPLAYER_HPP
#define OPENMW_BASEPLAYER_HPP #define OPENMW_BASEPLAYER_HPP

@ -32,6 +32,11 @@ namespace mwmp
int daysPassed; int daysPassed;
float timeScale; float timeScale;
bool hasPlayerCollision;
bool hasActorCollision;
bool hasPlacedObjectCollision;
bool useActorCollisionForPlacedObjects;
bool isValid; bool isValid;
}; };
} }

@ -1,4 +1,5 @@
#include "../Packets/Worldstate/PacketRecordDynamic.hpp" #include "../Packets/Worldstate/PacketRecordDynamic.hpp"
#include "../Packets/Worldstate/PacketWorldCollisionOverride.hpp"
#include "../Packets/Worldstate/PacketWorldTime.hpp" #include "../Packets/Worldstate/PacketWorldTime.hpp"
#include "WorldstatePacketController.hpp" #include "WorldstatePacketController.hpp"
@ -6,5 +7,6 @@
mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer) mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)
{ {
AddPacket<PacketRecordDynamic>(&packets, peer); AddPacket<PacketRecordDynamic>(&packets, peer);
AddPacket<PacketWorldCollisionOverride>(&packets, peer);
AddPacket<PacketWorldTime>(&packets, peer); AddPacket<PacketWorldTime>(&packets, peer);
} }

@ -19,7 +19,6 @@ enum GameMessages
ID_GUI_MESSAGEBOX, ID_GUI_MESSAGEBOX,
ID_GUI_WINDOW, ID_GUI_WINDOW,
ID_WORLD_TIME,
ID_GAME_WEATHER, ID_GAME_WEATHER,
ID_PLAYER_BASEINFO, ID_PLAYER_BASEINFO,
@ -91,7 +90,6 @@ enum GameMessages
ID_OBJECT_TRAP, ID_OBJECT_TRAP,
ID_CELL_CREATE, ID_CELL_CREATE,
ID_RECORD_DYNAMIC,
ID_CONSOLE_COMMAND, ID_CONSOLE_COMMAND,
ID_CONTAINER, ID_CONTAINER,
@ -108,7 +106,11 @@ enum GameMessages
ID_SCRIPT_GLOBAL_FLOAT, ID_SCRIPT_GLOBAL_FLOAT,
ID_GAME_SETTINGS, ID_GAME_SETTINGS,
ID_GAME_PREINIT ID_GAME_PREINIT,
ID_RECORD_DYNAMIC,
ID_WORLD_COLLISION_OVERRIDE,
ID_WORLD_TIME,
}; };
enum OrderingChannel enum OrderingChannel

@ -0,0 +1,20 @@
#include "PacketWorldCollisionOverride.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
using namespace mwmp;
PacketWorldCollisionOverride::PacketWorldCollisionOverride(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
{
packetID = ID_WORLD_COLLISION_OVERRIDE;
orderChannel = CHANNEL_WORLDSTATE;
}
void PacketWorldCollisionOverride::Packet(RakNet::BitStream *bs, bool send)
{
WorldstatePacket::Packet(bs, send);
RW(worldstate->hasPlayerCollision, send);
RW(worldstate->hasActorCollision, send);
RW(worldstate->hasPlacedObjectCollision, send);
RW(worldstate->useActorCollisionForPlacedObjects, send);
}

@ -0,0 +1,17 @@
#ifndef OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP
#define OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP
#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>
namespace mwmp
{
class PacketWorldCollisionOverride : public WorldstatePacket
{
public:
PacketWorldCollisionOverride(RakNet::RakPeerInterface *peer);
virtual void Packet(RakNet::BitStream *bs, bool send);
};
}
#endif //OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP

@ -10,7 +10,11 @@
</Widget> </Widget>
<!-- Spell list --> <!-- Spell list -->
<Widget type="SpellView" skin="MW_SpellView" position="8 38 268 518" align="Left Top Stretch" name="SpellView"> <Widget type="SpellView" skin="MW_SpellView" position="8 38 268 490" align="Left Top Stretch" name="SpellView">
</Widget>
<!-- Search box-->
<Widget type="EditBox" skin="MW_TextBoxEditWithBorder" position="8 535 268 23" align="Left Bottom HStretch" name="FilterEdit">
</Widget> </Widget>
</Widget> </Widget>

@ -150,5 +150,9 @@
<file alias="selection-mode-cube">selection-mode-cube.png</file> <file alias="selection-mode-cube">selection-mode-cube.png</file>
<file alias="selection-mode-cube-corner">selection-mode-cube-corner.png</file> <file alias="selection-mode-cube-corner">selection-mode-cube-corner.png</file>
<file alias="selection-mode-cube-sphere">selection-mode-cube-sphere.png</file> <file alias="selection-mode-cube-sphere">selection-mode-cube-sphere.png</file>
<file alias="brush-point">brush-point.png</file>
<file alias="brush-square">brush-square.png</file>
<file alias="brush-circle">brush-circle.png</file>
<file alias="brush-custom">brush-custom.png</file>
</qresource> </qresource>
</RCC> </RCC>

@ -217,7 +217,7 @@ followers attack on sight = false
can loot during death animation = true can loot during death animation = true
# Makes the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch) # Makes the value of filled soul gems dependent only on soul magnitude (with formula from the Morrowind Code Patch)
rebalance soul gem values = true rebalance soul gem values = false
[General] [General]

@ -112,7 +112,7 @@
<item> <item>
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox"> <widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the value of filled soul gems is dependent only on soul magnitude.&lt;/p&gt;&lt;p&gt;The default value is true.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, the value of filled soul gems is dependent only on soul magnitude.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Rebalance soul gem values</string> <string>Rebalance soul gem values</string>

Loading…
Cancel
Save