diff --git a/CI/deploy.osx.sh b/CI/deploy.osx.sh index 53bfa18b5..5fa2b70a3 100755 --- a/CI/deploy.osx.sh +++ b/CI/deploy.osx.sh @@ -6,4 +6,6 @@ DATE=`date +'%d%m%Y'` SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}` 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index c19a5d734..f5ecaf055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") endif() 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 if (OPENMW_LTO_BUILD) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f07b518a9..d1ebcde42 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -81,14 +81,14 @@ opencs_units_noqt (view/world opencs_units (view/widget scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton - scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit + scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit ) opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget previewwidget editmode instancemode instanceselectionmode instancemovemode orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller - cellwater + cellwater terraintexturemode ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 8a9dad7f3..6f64da72e 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -222,7 +222,15 @@ void CSMPrefs::State::declare() EnumValues insertOutsideVisibleCell; 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). 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"); @@ -230,6 +238,12 @@ void CSMPrefs::State::declare() addValues (insertOutsideCell); declareEnum ("outside-visible-drop", "Handling drops outside of visible cells", showAndInsert). 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"); diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index 316fc0ce5..79cb704bf 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -28,9 +28,13 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages) 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()) - 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 for (int i=0; i<2; ++i) diff --git a/apps/opencs/model/world/landtexturetableproxymodel.cpp b/apps/opencs/model/world/landtexturetableproxymodel.cpp index cf33fab9e..e064bbe8a 100644 --- a/apps/opencs/model/world/landtexturetableproxymodel.cpp +++ b/apps/opencs/model/world/landtexturetableproxymodel.cpp @@ -11,11 +11,6 @@ namespace CSMWorld 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); } } diff --git a/apps/opencs/view/render/instancemode.cpp b/apps/opencs/view/render/instancemode.cpp index ee3e18d71..4b14e29bf 100644 --- a/apps/opencs/view/render/instancemode.cpp +++ b/apps/opencs/view/render/instancemode.cpp @@ -551,7 +551,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) 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 if (mode=="Discard") @@ -585,7 +585,7 @@ void CSVRender::InstanceMode::dropEvent (QDropEvent* event) { // target cell exists, but is not shown std::string mode = - CSMPrefs::get()["Scene Drops"]["outside-visible-drop"].toString(); + CSMPrefs::get()["3D Scene Editing"]["outside-visible-drop"].toString(); if (mode=="Discard") return; diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 4a745195b..1d1a7cd17 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -21,6 +21,7 @@ #include "mask.hpp" #include "cameracontroller.hpp" #include "cellarrow.hpp" +#include "terraintexturemode.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { @@ -136,7 +137,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), "terrain-shape"); tool->addButton ( - new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain texture editing"), + new TerrainTextureMode (this, tool), "terrain-texture"); tool->addButton ( new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp new file mode 100644 index 000000000..376258c5e --- /dev/null +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -0,0 +1,542 @@ +#include "terraintexturemode.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#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 ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + QUndoStack& undoStack = document.getUndoStack(); + CSMWorld::IdCollection& 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 ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + + QUndoStack& undoStack = document.getUndoStack(); + + CSMWorld::IdCollection& 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& 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& 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 (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 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 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 ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + + mCellId = getWorldspaceWidget().getCellId (hit.worldPos); + if(allowLandTextureEditing(mCellId)==true) {} + + std::pair 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 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 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 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 ( + *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 ( + *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::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 ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); + CSMWorld::IdTree& cellTable = dynamic_cast ( + *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 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 (&getWorldspaceWidget())) + { + CSMWorld::CellSelection selection = paged->getCellSelection(); + selection.add (CSMWorld::CellCoordinates::fromId (cellId).first); + paged->setCellSelection (selection); + } + } + } + else if (CSVRender::PagedWorldspaceWidget *paged = + dynamic_cast (&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; +} diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp new file mode 100644 index 000000000..e1538e243 --- /dev/null +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -0,0 +1,100 @@ +#ifndef CSV_RENDER_TERRAINTEXTUREMODE_H +#define CSV_RENDER_TERRAINTEXTUREMODE_H + +#include "editmode.hpp" + +#include + +#include +#include + +#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 diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index af53c86f0..084fb87e6 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -445,7 +445,7 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo // Default placement 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 }; return hit; @@ -648,6 +648,12 @@ void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) mDragX = event->posF().x(); mDragY = height() - event->posF().y(); #endif + + if (mDragMode == InteractionType_PrimaryEdit) + { + EditMode& editMode = dynamic_cast (*mEditMode->getCurrent()); + editMode.drag (event->pos(), mDragX, mDragY, mDragFactor); // note: terraintexturemode only uses pos + } } } else diff --git a/apps/opencs/view/widget/scenetooltexturebrush.cpp b/apps/opencs/view/widget/scenetooltexturebrush.cpp new file mode 100644 index 000000000..2208f88a6 --- /dev/null +++ b/apps/opencs/view/widget/scenetooltexturebrush.cpp @@ -0,0 +1,379 @@ +#include "scenetooltexturebrush.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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& 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()); + } 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& 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() == 0) + { + CSMWorld::IdTable& ltexTable = dynamic_cast ( + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures)); + + QUndoStack& undoStack = mDocument.getUndoStack(); + + QVariant textureFileNameVariant; + textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value()); + + 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::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()); + } 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

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 += "

(right click to access of previously used brush settings)"; + + + CSMWorld::IdCollection& 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 += "

Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " "; + + tooltip += landtexturesCollection.getData(index, landTextureFilename).value(); + } else + { + tooltip += "

No selected texture or invalid texture"; + } + + tooltip += "
(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& 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())); + 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(); +} diff --git a/apps/opencs/view/widget/scenetooltexturebrush.hpp b/apps/opencs/view/widget/scenetooltexturebrush.hpp new file mode 100644 index 000000000..4669f432e --- /dev/null +++ b/apps/opencs/view/widget/scenetooltexturebrush.hpp @@ -0,0 +1,133 @@ +#ifndef CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H +#define CSV_WIDGET_SCENETOOLTEXTUREBRUSH_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 diff --git a/apps/openmw-mp/Script/LuaState.cpp b/apps/openmw-mp/Script/LuaState.cpp index e4315b774..7f95f1ecf 100644 --- a/apps/openmw-mp/Script/LuaState.cpp +++ b/apps/openmw-mp/Script/LuaState.cpp @@ -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", [](){ return mwmp::Networking::get().createChannel(); }); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 7cd604857..1ccd1f47f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -98,7 +98,7 @@ add_openmw_dir (mwbase ) 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 @@ -124,15 +124,15 @@ add_openmw_dir (mwmp/processors/player ProcessorChatMessage ProcessorGUIMessageB ProcessorPlayerSkill ProcessorPlayerSpeech ProcessorPlayerSpellbook ProcessorPlayerStatsDynamic ProcessorPlayerTopic ) -add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer ProcessorDoorDestination - ProcessorDoorState ProcessorMusicPlay ProcessorVideoPlay ProcessorObjectAnimPlay ProcessorObjectAttach - ProcessorObjectCollision ProcessorObjectDelete ProcessorObjectLock ProcessorObjectMove ProcessorObjectPlace - ProcessorObjectReset ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSpawn ProcessorObjectState - ProcessorObjectTrap ProcessorScriptLocalShort ProcessorScriptLocalFloat ProcessorScriptMemberShort - ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat +add_openmw_dir (mwmp/processors/object BaseObjectProcessor ProcessorConsoleCommand ProcessorContainer + ProcessorDoorDestination ProcessorDoorState ProcessorMusicPlay ProcessorVideoPlay ProcessorObjectAnimPlay + ProcessorObjectAttach ProcessorObjectCollision ProcessorObjectDelete ProcessorObjectLock ProcessorObjectMove + ProcessorObjectPlace ProcessorObjectReset ProcessorObjectRotate ProcessorObjectScale ProcessorObjectSpawn + ProcessorObjectState ProcessorObjectTrap ProcessorScriptLocalShort ProcessorScriptLocalFloat + ProcessorScriptMemberShort ProcessorScriptMemberFloat ProcessorScriptGlobalShort ProcessorScriptGlobalFloat ) -add_openmw_dir (mwmp/processors/worldstate ProcessorRecordDynamic ProcessorWorldTime +add_openmw_dir (mwmp/processors/worldstate ProcessorRecordDynamic ProcessorWorldCollisionOverride ProcessorWorldTime ) # Main executable diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 53a530216..e7acff1ae 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -5,9 +5,6 @@ #include #include -#include -#include - #include #include "../mwworld/ptr.hpp" @@ -457,7 +454,6 @@ namespace MWBase virtual bool isUnderwater(const MWWorld::ConstPtr &object, const float heightRatio) const = 0; virtual bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) 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; @@ -624,7 +620,7 @@ namespace MWBase /// Spawn a blood effect for \a ptr at \a worldPosition 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, const MWWorld::Ptr& ignore, ESM::RangeType rangeType, const std::string& id, @@ -666,8 +662,6 @@ namespace MWBase /// Preload VFX associated with this effect list virtual void preloadEffects(const ESM::EffectList* effectList) = 0; - - virtual osg::ref_ptr getInstance (const std::string& modelName) = 0; }; } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index a271dc5e9..ae16b289a 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 460cd517a..6220c79d5 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -1,5 +1,16 @@ #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 #include #include @@ -37,6 +48,27 @@ namespace MWClass 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 + + /* + 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 diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 3d6455dc6..e16a99398 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index b37d1e90b..2f790e559 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 09d149331..6c03f31ef 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 62b15bc86..0625dfdd6 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -1,5 +1,16 @@ #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 #include @@ -41,6 +52,27 @@ namespace MWClass 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 + + /* + 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 diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index ffdccf438..475b4ef50 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 566d16106..ec84bb803 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -1,5 +1,16 @@ #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 #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 { // 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 diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index e59567aac..4d0b7411b 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -1,5 +1,16 @@ #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 #include @@ -35,6 +46,27 @@ namespace MWClass 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 + + /* + 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 diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index e8aa23158..1075239fa 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -244,7 +244,7 @@ BookTypesetter::Ptr JournalBooks::createLatinJournalIndex () const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, textColours.journalTopicOver, - textColours.journalTopicPressed, (uint32_t) ch); + textColours.journalTopicPressed, (Utf8Stream::UnicodeChar) ch); if (i == 13) typesetter->sectionBreak (); @@ -274,7 +274,7 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex () sprintf(buffer, "( %c%c )", ch[0], ch[1]); Utf8Stream stream ((char*) ch); - uint32_t first = stream.peek(); + Utf8Stream::UnicodeChar first = stream.peek(); const MWGui::TextColours& textColours = MWBase::Environment::get().getWindowManager()->getTextColours(); BookTypesetter::Style* style = typesetter->createHotStyle (body, textColours.journalTopic, diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 6ff68c9c5..63b48eab1 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -7,7 +7,6 @@ #include #include -#include #include "../mwbase/world.hpp" #include "../mwbase/journal.hpp" @@ -307,39 +306,22 @@ struct JournalViewModelImpl : JournalViewModel 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(); for (MWBase::Journal::TTopicIter i = journal->topicBegin (); i != journal->topicEnd (); ++i) { 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; 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 const & mTopic; diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp index 01dcb49de..fa4090225 100644 --- a/apps/openmw/mwgui/journalviewmodel.hpp +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -6,6 +6,8 @@ #include #include +#include + namespace MWGui { /// View-Model for the journal GUI @@ -76,7 +78,7 @@ namespace MWGui virtual void visitTopicName (TopicId topicId, std::function visitor) const = 0; /// 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 virtual void visitTopicEntries (TopicId topicId, std::function visitor) const = 0; diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 1374ef93d..cde8a17cc 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -14,6 +14,9 @@ namespace MWGui bool shouldAcceptKeyFocus(MyGUI::Widget* w) { + if (w && w->getUserString("IgnoreTabKey") == "y") + return false; + return w && !w->castType(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); } diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index f83b72096..a73d343f9 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -32,10 +32,14 @@ namespace namespace MWGui { + SpellModel::SpellModel(const MWWorld::Ptr &actor, const std::string& filter) + : mActor(actor), mFilter(filter) + { + } + SpellModel::SpellModel(const MWWorld::Ptr &actor) : mActor(actor) { - } void SpellModel::update() @@ -48,12 +52,19 @@ namespace MWGui const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); + std::string filter = Misc::StringUtils::lowerCaseUtf8(mFilter); + for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = it->first; if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell) continue; + std::string name = Misc::StringUtils::lowerCaseUtf8(spell->mName); + + if (name.find(filter) == std::string::npos) + continue; + Spell newSpell; newSpell.mName = spell->mName; 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) continue; + std::string name = Misc::StringUtils::lowerCaseUtf8(item.getClass().getName(item)); + + if (name.find(filter) == std::string::npos) + continue; + Spell newSpell; newSpell.mItem = item; newSpell.mId = item.getCellRef().getRefId(); diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index 21fbc9a6e..6b10f7127 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -35,6 +35,7 @@ namespace MWGui class SpellModel { public: + SpellModel(const MWWorld::Ptr& actor, const std::string& filter); SpellModel(const MWWorld::Ptr& actor); typedef int ModelIndex; @@ -50,6 +51,8 @@ namespace MWGui MWWorld::Ptr mActor; std::vector mSpells; + + std::string mFilter; }; } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index bc35929a7..295ecde53 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -49,8 +50,12 @@ namespace MWGui getWidget(mSpellView, "SpellView"); getWidget(mEffectBox, "EffectsBox"); + getWidget(mFilterEdit, "FilterEdit"); + + mFilterEdit->setUserString("IgnoreTabKey", "y"); mSpellView->eventSpellClicked += MyGUI::newDelegate(this, &SpellWindow::onModelIndexSelected); + mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &SpellWindow::onFilterChanged); setCoord(498, 300, 302, 300); } @@ -75,6 +80,11 @@ namespace MWGui 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(); } @@ -93,7 +103,7 @@ namespace MWGui { 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) @@ -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) { MWWorld::Ptr player = MWMechanics::getPlayer(); @@ -233,7 +248,7 @@ namespace MWGui if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery()) return; - mSpellView->setModel(new SpellModel(MWMechanics::getPlayer())); + mSpellView->setModel(new SpellModel(MWMechanics::getPlayer(), "")); SpellModel::ModelIndex selected = 0; for (SpellModel::ModelIndex i = 0; igetModel()->getItemCount()); ++i) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index f8fead9ea..ce10770f5 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -32,6 +32,7 @@ namespace MWGui void onEnchantedItemSelected(MWWorld::Ptr item, bool alreadyEquipped); void onSpellSelected(const std::string& spellId); void onModelIndexSelected(SpellModel::ModelIndex index); + void onFilterChanged(MyGUI::EditBox *sender); void onDeleteSpellAccept(); void askDeleteSpell(const std::string& spellId); @@ -41,6 +42,7 @@ namespace MWGui SpellView* mSpellView; SpellIcons* mSpellIcons; + MyGUI::EditBox* mFilterEdit; private: float mUpdateTimer; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 511a47a49..8199170dc 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -866,7 +866,8 @@ namespace MWMechanics } // 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()); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 019c663d5..ff0139681 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -863,7 +863,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true); mAnimation->runAnimation(0.f); - mAnimation->updateEffects(0.f); unpersistAnimationState(); } @@ -2118,13 +2117,6 @@ void CharacterController::update(float 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) moved /= duration; else @@ -2157,8 +2149,7 @@ void CharacterController::update(float duration) moved.z() = 1.0; // Update movement - // We should not apply movement for standing actors - if(mMovementAnimationControlled && mPtr.getClass().isActor() && (movement.length2() > 0.f || !world->isIdle(mPtr))) + if(mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); mSkipAnim = false; @@ -2339,7 +2330,6 @@ void CharacterController::forceStateUpdate() } mAnimation->runAnimation(0.f); - mAnimation->updateEffects(0.f); } CharacterController::KillResult CharacterController::kill() diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 31721e8a8..6fa4f5b8f 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -6,12 +6,8 @@ #include -#include -#include - #include #include -#include /* Start of tes3mp addition @@ -44,7 +40,6 @@ #include "../mwworld/inventorystore.hpp" #include "../mwrender/animation.hpp" -#include "../mwrender/vismask.hpp" #include "npcstats.hpp" #include "actorutil.hpp" @@ -243,45 +238,42 @@ namespace MWMechanics magicEffects = effects; float resisted = 0; - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful) - { - // Effects with no resistance attribute belonging to them can not be resisted - if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) - return 0.f; + // Effects with no resistance attribute belonging to them can not be resisted + if (ESM::MagicEffect::getResistanceEffect(effectId) == -1) + return 0.f; - float resistance = getEffectResistanceAttribute(effectId, magicEffects); + float resistance = getEffectResistanceAttribute(effectId, magicEffects); - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); - float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); + int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); - // This makes spells that are easy to cast harder to resist and vice versa - float castChance = 100.f; - if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) - { - castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance - } - if (castChance > 0) - x *= 50 / castChance; + // This makes spells that are easy to cast harder to resist and vice versa + float castChance = 100.f; + if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) + { + castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance + } + if (castChance > 0) + x *= 50 / castChance; - float roll = Misc::Rng::rollClosedProbability() * 100; - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - roll -= resistance; + float roll = Misc::Rng::rollClosedProbability() * 100; + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + roll -= resistance; - if (x <= roll) - x = 0; + if (x <= roll) + x = 0; + else + { + if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) + x = 100; else - { - if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude) - x = 100; - else - x = roll / std::min(x, 100.f); - } + x = roll / std::min(x, 100.f); + } - x = std::min(x + resistance, 100.f); + x = std::min(x + resistance, 100.f); - resisted = x; - } + resisted = x; return resisted; } @@ -479,13 +471,11 @@ namespace MWMechanics } float magnitudeMult = 1; - if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) - { - if (absorbed) - continue; - // Try reflecting - if (!reflected && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable)) + if (!absorbed) + { + // 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(); bool isReflected = (Misc::Rng::roll0to99() < reflect); @@ -509,16 +499,17 @@ namespace MWMechanics else if (castByPlayer) 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()) 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 - 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); } @@ -1071,13 +1062,11 @@ namespace MWMechanics 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 ESM::Spell *spell = store.get().find(spellid); - std::vector addedEffects; - for (std::vector::const_iterator iter = spell->mEffects.mList.begin(); iter != spell->mEffects.mList.end(); ++iter) { @@ -1086,72 +1075,18 @@ namespace MWMechanics MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(mCaster); - const ESM::Static* castStatic; - - if (!effect->mCasting.empty()) - castStatic = store.get().find (effect->mCasting); - else - castStatic = store.get().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; - - float scale = 1.0f; - 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()->mBase; - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Race *race = - esmStore.get().find(npc->mRace); - - scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale; - } - else + if (animation && mCaster.getClass().isActor()) // TODO: Non-actors should also create a spell cast vfx even if they are disabled (animation == NULL) { - std::string casterModel = mCaster.getClass().getModel(mCaster); - osg::ref_ptr model = MWBase::Environment::get().getWorld()->getInstance(casterModel); + const ESM::Static* castStatic; - 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 }); + if (!effect->mCasting.empty()) + castStatic = store.get().find (effect->mCasting); + else + castStatic = store.get().find ("VFX_DefaultCast"); - //pos = bounds.center(); - //pos[2] = bounds.zMin(); - } - } + std::string texture = effect->mParticle; - // 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); + animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture); } if (animation && !mCaster.getClass().isActor()) @@ -1161,8 +1096,6 @@ namespace MWMechanics "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; - addedEffects.push_back("meshes\\" + castStatic->mModel); - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); if(!effect->mCastSound.empty()) sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f); diff --git a/apps/openmw/mwmp/Networking.cpp b/apps/openmw/mwmp/Networking.cpp index 12392f837..73e0ee5bc 100644 --- a/apps/openmw/mwmp/Networking.cpp +++ b/apps/openmw/mwmp/Networking.cpp @@ -1,7 +1,3 @@ -// -// Created by koncord on 04.01.16. -// - #include #include #include @@ -479,7 +475,7 @@ ObjectList *Networking::getObjectList() return &objectList; } -BaseWorldstate *Networking::getWorldstate() +Worldstate *Networking::getWorldstate() { return &worldstate; } diff --git a/apps/openmw/mwmp/Networking.hpp b/apps/openmw/mwmp/Networking.hpp index 3254ded5b..2bf1b9acc 100644 --- a/apps/openmw/mwmp/Networking.hpp +++ b/apps/openmw/mwmp/Networking.hpp @@ -1,7 +1,3 @@ -// -// Created by koncord on 04.01.16. -// - #ifndef OPENMW_NETWORKING_HPP #define OPENMW_NETWORKING_HPP @@ -9,8 +5,6 @@ #include #include -#include "ActorList.hpp" -#include "ObjectList.hpp" #include #include @@ -20,6 +14,10 @@ #include +#include "ActorList.hpp" +#include "ObjectList.hpp" +#include "Worldstate.hpp" + namespace mwmp { class LocalPlayer; @@ -47,7 +45,7 @@ namespace mwmp LocalPlayer *getLocalPlayer(); ActorList *getActorList(); ObjectList *getObjectList(); - BaseWorldstate *getWorldstate(); + Worldstate *getWorldstate(); std::string getNetworkStatistics(); @@ -64,7 +62,7 @@ namespace mwmp ActorList actorList; ObjectList objectList; - BaseWorldstate worldstate; + Worldstate worldstate; void receiveMessage(RakNet::Packet *packet); diff --git a/apps/openmw/mwmp/PlayerList.cpp b/apps/openmw/mwmp/PlayerList.cpp index 24e392511..e07c16b07 100644 --- a/apps/openmw/mwmp/PlayerList.cpp +++ b/apps/openmw/mwmp/PlayerList.cpp @@ -71,8 +71,10 @@ DedicatedPlayer *PlayerList::getPlayer(const MWWorld::Ptr &ptr) { if (p.second == 0 || p.second->getPtr().mRef == 0) 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 0; @@ -83,6 +85,10 @@ bool PlayerList::isDedicatedPlayer(const MWWorld::Ptr &ptr) if (ptr.mRef == nullptr) 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); } diff --git a/apps/openmw/mwmp/Worldstate.cpp b/apps/openmw/mwmp/Worldstate.cpp new file mode 100644 index 000000000..e3c487445 --- /dev/null +++ b/apps/openmw/mwmp/Worldstate.cpp @@ -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(); +} diff --git a/apps/openmw/mwmp/Worldstate.hpp b/apps/openmw/mwmp/Worldstate.hpp new file mode 100644 index 000000000..97b89375d --- /dev/null +++ b/apps/openmw/mwmp/Worldstate.hpp @@ -0,0 +1,22 @@ +#ifndef OPENMW_WORLDSTATE_HPP +#define OPENMW_WORLDSTATE_HPP + +#include + +namespace mwmp +{ + class Networking; + class Worldstate : public BaseWorldstate + { + public: + + Worldstate(); + virtual ~Worldstate(); + + private: + Networking *getNetworking(); + + }; +} + +#endif //OPENMW_WORLDSTATE_HPP diff --git a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp index 30e2950d6..f9883dce0 100644 --- a/apps/openmw/mwmp/processors/ProcessorInitializer.cpp +++ b/apps/openmw/mwmp/processors/ProcessorInitializer.cpp @@ -92,6 +92,7 @@ #include "WorldstateProcessor.hpp" #include "worldstate/ProcessorRecordDynamic.hpp" +#include "worldstate/ProcessorWorldCollisionOverride.hpp" #include "worldstate/ProcessorWorldTime.hpp" using namespace mwmp; @@ -186,5 +187,6 @@ void ProcessorInitializer() ActorProcessor::AddProcessor(new ProcessorActorTest()); WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic()); + WorldstateProcessor::AddProcessor(new ProcessorWorldCollisionOverride()); WorldstateProcessor::AddProcessor(new ProcessorWorldTime()); } diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorWorldCollisionOverride.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorWorldCollisionOverride.hpp new file mode 100644 index 000000000..e918e7d09 --- /dev/null +++ b/apps/openmw/mwmp/processors/worldstate/ProcessorWorldCollisionOverride.hpp @@ -0,0 +1,28 @@ +#ifndef OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP +#define OPENMW_PROCESSORWORLDCOLLISIONOVERRIDE_HPP + + +#include +#include +#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 diff --git a/apps/openmw/mwmp/processors/worldstate/ProcessorWorldTime.hpp b/apps/openmw/mwmp/processors/worldstate/ProcessorWorldTime.hpp index 8b5d3ee2d..002917ec5 100644 --- a/apps/openmw/mwmp/processors/worldstate/ProcessorWorldTime.hpp +++ b/apps/openmw/mwmp/processors/worldstate/ProcessorWorldTime.hpp @@ -18,28 +18,25 @@ namespace mwmp virtual void Do(WorldstatePacket &packet, BaseWorldstate &worldstate) { - if (isLocal()) - { - MWBase::World *world = MWBase::Environment::get().getWorld(); - - if (worldstate.hour != -1) - world->setHour(worldstate.hour); - - if (worldstate.day != -1) - world->setDay(worldstate.day); - - if (worldstate.month != -1) - world->setMonth(worldstate.month); - - if (worldstate.year != -1) - world->setYear(worldstate.year); - - if (worldstate.daysPassed != -1) - world->setDaysPassed(worldstate.daysPassed); - - if (worldstate.timeScale != -1) - world->setTimeScale(worldstate.timeScale); - } + MWBase::World *world = MWBase::Environment::get().getWorld(); + + if (worldstate.hour != -1) + world->setHour(worldstate.hour); + + if (worldstate.day != -1) + world->setDay(worldstate.day); + + if (worldstate.month != -1) + world->setMonth(worldstate.month); + + if (worldstate.year != -1) + world->setYear(worldstate.year); + + if (worldstate.daysPassed != -1) + world->setDaysPassed(worldstate.daysPassed); + + if (worldstate.timeScale != -1) + world->setTimeScale(worldstate.timeScale); } }; } diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 6de0d1984..22d789046 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -1,5 +1,18 @@ #include "actor.hpp" +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include +#include "../mwmp/Main.hpp" +#include "../mwmp/Networking.hpp" +#include "../mwmp/PlayerList.hpp" +/* + End of tes3mp addition +*/ + #include #include #include @@ -18,7 +31,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) : 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) , mExternalCollisionMode(true) , mCollisionWorld(world) @@ -53,6 +66,27 @@ Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr updatePosition(); 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() @@ -195,11 +229,6 @@ void Actor::setOnSlope(bool slope) mOnSlope = slope; } -void Actor::setIdle(bool idle) -{ - mIdle = idle; -} - bool Actor::isWalkingOnWater() const { return mWalkingOnWater; diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index bdafc1235..8ec94200f 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -139,13 +139,6 @@ namespace MWPhysics return mInternalCollisionMode && mOnSlope; } - void setIdle(bool idle); - - bool getIdle() const - { - return mIdle; - } - btCollisionObject* getCollisionObject() const { return mCollisionObject.get(); @@ -186,7 +179,6 @@ namespace MWPhysics osg::Vec3f mForce; bool mOnGround; bool mOnSlope; - bool mIdle; bool mInternalCollisionMode; bool mExternalCollisionMode; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index cc40a96f4..5863897b9 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -249,7 +249,7 @@ namespace MWPhysics // 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 // 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); btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); @@ -683,7 +683,6 @@ namespace MWPhysics , mWaterEnabled(false) , mParentNode(parentNode) , mPhysicsDt(1.f / 60.f) - , mIdleUpdateTimer(0) { mResourceSystem->addResourceManager(mShapeManager.get()); @@ -740,18 +739,6 @@ namespace MWPhysics 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(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); - - it->second->setIdle(result.mHit); - } - } - void PhysicsSystem::setUnrefQueue(SceneUtil::UnrefQueue *unrefQueue) { mUnrefQueue = unrefQueue; @@ -1080,11 +1067,6 @@ namespace MWPhysics 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) { const Actor* physicActor = getActor(actor); @@ -1377,10 +1359,6 @@ namespace MWPhysics cmode = !cmode; found->second->enableCollisionMode(cmode); found->second->enableCollisionBody(cmode); - - if (cmode) - queueObjectMovement(MWMechanics::getPlayer(), osg::Vec3f(0, 0, -0.1f)); - return cmode; } @@ -1500,13 +1478,6 @@ namespace MWPhysics for (std::set::iterator it = mAnimatedObjects.begin(); it != mAnimatedObjects.end(); ++it) (*it)->animateCollisionShapes(mCollisionWorld); - mIdleUpdateTimer -= dt; - if (mIdleUpdateTimer <= 0.f) - { - mIdleUpdateTimer = 0.5f; - updateIdle(); - } - #ifndef BT_NO_PROFILE CProfileManager::Reset(); CProfileManager::Increment_Frame_Counter(); diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index fb95e3173..b458f936e 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -124,7 +124,6 @@ namespace MWPhysics bool getLineOfSight(const MWWorld::ConstPtr& actor1, const MWWorld::ConstPtr& actor2) const; bool isOnGround (const MWWorld::Ptr& actor); - bool isIdle (const MWWorld::Ptr& actor); bool canMoveToWaterSurface (const MWWorld::ConstPtr &actor, const float waterlevel); @@ -184,7 +183,6 @@ namespace MWPhysics private: void updateWater(); - void updateIdle(); osg::ref_ptr mUnrefQueue; @@ -233,7 +231,6 @@ namespace MWPhysics osg::ref_ptr mParentNode; float mPhysicsDt; - float mIdleUpdateTimer; PhysicsSystem (const PhysicsSystem&); PhysicsSystem& operator= (const PhysicsSystem&); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1b508e593..1bd839ead 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include @@ -1117,6 +1115,8 @@ namespace MWRender ++stateiter; } + updateEffects(duration); + if (mHeadController) { const float epsilon = 0.001f; @@ -1366,7 +1366,7 @@ namespace MWRender 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()) return; @@ -1417,13 +1417,7 @@ namespace MWRender overrideFirstRootTexture(texture, mResourceSystem, node); - osg::Vec3f scale3f (scale, scale, scale); - - osg::ref_ptr trans = new osg::PositionAttitudeTransform; - trans->setScale(scale3f); - trans->addChild(node); - parentNode->removeChild(node); - parentNode->addChild(trans); + // TODO: in vanilla morrowind the effect is scaled based on the host object's bounding box. mEffects.push_back(params); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 74224c6bd..cff98a4b7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -364,7 +364,7 @@ public: * @param texture override the texture specified in the model's materials - if empty, do not override * @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 getLoopingEffects (std::vector& out) const; @@ -446,6 +446,7 @@ public: 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); /// Return a node with the specified name, or NULL if not existing. diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index c44c5428a..3e785a769 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -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) -{ - 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 node = mResourceSystem->getSceneManager()->getInstance(model); @@ -47,7 +40,7 @@ void EffectManager::addEffect(const std::string &model, const std::string& textu osg::ref_ptr trans = new osg::PositionAttitudeTransform; trans->setPosition(worldPosition); - trans->setScale(scale); + trans->setScale(osg::Vec3f(scale, scale, scale)); trans->addChild(node); SceneUtil::AssignControllerSourcesVisitor assignVisitor(effect.mAnimTime); diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index cc1c1b42e..5873c00dd 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -33,7 +33,6 @@ namespace MWRender /// 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, const osg::Vec3f &scale3f, bool isMagicVFX); void update(float dt); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 523f1774c..f0a3d2e38 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -138,14 +138,6 @@ bool Objects::removeObject (const MWWorld::Ptr& ptr) 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) { diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index e97395213..659853763 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -88,8 +88,6 @@ public: bool removeObject (const MWWorld::Ptr& ptr); ///< \return found? - void updateEffects(float duration); - void removeCell(const MWWorld::CellStore* store); /// Updates containing cell for object rendering data diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e5933a72a..6f0ddba3a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -557,7 +557,6 @@ namespace MWRender mEffectManager->update(dt); mSky->update(dt); mWater->update(dt); - mObjects->updateEffects(dt); } mCamera->update(dt, paused); @@ -842,11 +841,9 @@ namespace MWRender 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, scale3f, isMagicVFX); + mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX); } void RenderingManager::notifyWorldSpaceChanged() @@ -1125,12 +1122,6 @@ namespace MWRender updateProjectionMatrix(); } } - - osg::ref_ptr 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) { osg::Node* node = mViewer->getSceneData(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 7a570f249..1c689d29f 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -205,8 +205,6 @@ namespace MWRender LandManager* getLandManager() const; - osg::ref_ptr getInstance(const std::string& modelName); - private: void updateProjectionMatrix(); void updateTextureFiltering(); diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 9a810e0e3..fd5ffae00 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -270,12 +270,16 @@ namespace MWScript 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; + /* + End of tes3mp change (major) + */ } int InterpreterContext::getGlobalShort (const std::string& name) const @@ -672,10 +676,17 @@ namespace MWScript 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'); locals.mShorts[index] = value; + /* + End of tes3mp change (minor) + */ /* Start of tes3mp addition diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index fcac037e3..382f6012e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -26,7 +26,6 @@ #include #include "../mwbase/environment.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/world.hpp" @@ -771,8 +770,6 @@ namespace MWScript 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); MWMechanics::CastSpell cast(ptr, target); - cast.playSpellCastingEffects(spell); cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 911e0ebdc..c1bb589e8 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -506,7 +506,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str if (firstPersonCam != MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(); - const MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::ConstPtr ptr = MWMechanics::getPlayer(); 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. // But make sure the flag is cleared anyway in case it was set from an earlier game. 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) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 22a9ac47e..d3aea2115 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -280,10 +280,11 @@ namespace MWWorld { ESM::Position pos; - // Major change made by tes3mp - // - // If Pelagiad exists, spawn there; otherwise, spawn at 0,0 + /* + Start of tes3mp change (major) + If Pelagiad exists, spawn there; otherwise, spawn at 0 ,0 + */ if (findExteriorPosition("Pelagiad", pos)) { changeToExteriorCell(pos, true); @@ -300,6 +301,9 @@ namespace MWWorld pos.rot[2] = 0; mWorldScene->changeToExteriorCell(pos, true); } + /* + End of tes3mp change (major) + */ } } @@ -1437,6 +1441,8 @@ namespace MWWorld if (pos.z() < terrainHeight) pos.z() = terrainHeight; + pos.z() += 20; // place slightly above. will snap down to ground with code below + if (force || !isFlying(ptr)) { osg::Vec3f traced = mPhysics->traceDown(ptr, pos, 500); @@ -1682,11 +1688,6 @@ namespace MWWorld } } - osg::ref_ptr World::getInstance (const std::string& modelName) - { - return mRendering->getInstance(modelName); - } - const ESM::Potion *World::createRecord (const ESM::Potion& record) { return mStore.insert(record); @@ -2314,11 +2315,6 @@ namespace MWWorld return mPhysics->isOnGround(ptr); } - bool World::isIdle(const MWWorld::Ptr &ptr) const - { - return mPhysics->isIdle(ptr); - } - void World::togglePOV() { mRendering->togglePOV(); @@ -2559,10 +2555,16 @@ namespace MWWorld 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()) - return; + Being in a menu should not prevent actors from being hurt in multiplayer, + so that check has been commented out + */ + //if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + // return; + /* + End of tes3mp change (major) */ std::vector actors; @@ -2596,10 +2598,16 @@ namespace MWWorld 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()) - return; + Being in a menu should not prevent actors from being hurt in multiplayer, + so that check has been commented out + */ + //if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + // return; + /* + End of tes3mp change (major) */ std::vector actors; @@ -3575,9 +3583,9 @@ namespace MWWorld 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, diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b639e23bd..8e5bb692d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -553,7 +553,6 @@ namespace MWWorld bool isWading(const MWWorld::ConstPtr &object) const override; bool isWaterWalkingCastableOnTarget(const MWWorld::ConstPtr &target) 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; @@ -725,7 +724,7 @@ namespace MWWorld /// Spawn a blood effect for \a ptr at \a worldPosition 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, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName, @@ -765,8 +764,6 @@ namespace MWWorld /// Preload VFX associated with this effect list void preloadEffects(const ESM::EffectList* effectList) override; - - osg::ref_ptr getInstance (const std::string& modelName); }; } diff --git a/appveyor.yml b/appveyor.yml index eae4bd5b1..21fb7e1d6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,8 +18,8 @@ platform: - x64 configuration: - - Debug -# - Release +# - Debug + - Release # For the Qt, Boost, CMake, etc installs #os: Visual Studio 2017 @@ -55,6 +55,10 @@ build_script: - 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" +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 #notifications: @@ -63,3 +67,9 @@ test: off # - # on_build_failure: 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 diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 638bde6c8..088ec36a3 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -202,7 +202,7 @@ add_component_dir (openmw-mp/Packets/Object add_component_dir (openmw-mp/Packets/Worldstate WorldstatePacket - PacketRecordDynamic PacketWorldTime + PacketRecordDynamic PacketWorldCollisionOverride PacketWorldTime ) add_component_dir (fallback diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 9f4931d72..0fde1c96c 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -6,6 +6,8 @@ #include #include +#include "utf8stream.hpp" + namespace Misc { 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(character)); + else if (character <= 0x7ff) + { + out.append(1, static_cast(0xc0 | ((character >> 6) & 0x1f))); + out.append(1, static_cast(0x80 | (character & 0x3f))); + } + else if (character <= 0xffff) + { + out.append(1, static_cast(0xe0 | ((character >> 12) & 0x0f))); + out.append(1, static_cast(0x80 | ((character >> 6) & 0x3f))); + out.append(1, static_cast(0x80 | (character & 0x3f))); + } + else + { + out.append(1, static_cast(0xf0 | ((character >> 18) & 0x07))); + out.append(1, static_cast(0x80 | ((character >> 12) & 0x3f))); + out.append(1, static_cast(0x80 | ((character >> 6) & 0x3f))); + out.append(1, static_cast(0x80 | (character & 0x3f))); + } + + stream.consume(); + } + + return out; + } + static bool ciLess(const std::string &x, const std::string &y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); } diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index 368374a64..e499d15e6 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -1,6 +1,7 @@ #ifndef MISC_UTF8ITER_HPP #define MISC_UTF8ITER_HPP +#include #include class Utf8Stream diff --git a/components/openmw-mp/Base/BasePlayer.hpp b/components/openmw-mp/Base/BasePlayer.hpp index 226e471df..e62253f8f 100644 --- a/components/openmw-mp/Base/BasePlayer.hpp +++ b/components/openmw-mp/Base/BasePlayer.hpp @@ -1,7 +1,3 @@ -// -// Created by koncord on 07.01.16. -// - #ifndef OPENMW_BASEPLAYER_HPP #define OPENMW_BASEPLAYER_HPP diff --git a/components/openmw-mp/Base/BaseWorldstate.hpp b/components/openmw-mp/Base/BaseWorldstate.hpp index f8549b74e..6e2518c0f 100644 --- a/components/openmw-mp/Base/BaseWorldstate.hpp +++ b/components/openmw-mp/Base/BaseWorldstate.hpp @@ -32,6 +32,11 @@ namespace mwmp int daysPassed; float timeScale; + bool hasPlayerCollision; + bool hasActorCollision; + bool hasPlacedObjectCollision; + bool useActorCollisionForPlacedObjects; + bool isValid; }; } diff --git a/components/openmw-mp/Controllers/WorldstatePacketController.cpp b/components/openmw-mp/Controllers/WorldstatePacketController.cpp index 29dd300bc..94cc6b528 100644 --- a/components/openmw-mp/Controllers/WorldstatePacketController.cpp +++ b/components/openmw-mp/Controllers/WorldstatePacketController.cpp @@ -1,4 +1,5 @@ #include "../Packets/Worldstate/PacketRecordDynamic.hpp" +#include "../Packets/Worldstate/PacketWorldCollisionOverride.hpp" #include "../Packets/Worldstate/PacketWorldTime.hpp" #include "WorldstatePacketController.hpp" @@ -6,5 +7,6 @@ mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer) { AddPacket(&packets, peer); + AddPacket(&packets, peer); AddPacket(&packets, peer); } diff --git a/components/openmw-mp/NetworkMessages.hpp b/components/openmw-mp/NetworkMessages.hpp index 655016a25..9b2d87abe 100644 --- a/components/openmw-mp/NetworkMessages.hpp +++ b/components/openmw-mp/NetworkMessages.hpp @@ -19,7 +19,6 @@ enum GameMessages ID_GUI_MESSAGEBOX, ID_GUI_WINDOW, - ID_WORLD_TIME, ID_GAME_WEATHER, ID_PLAYER_BASEINFO, @@ -91,7 +90,6 @@ enum GameMessages ID_OBJECT_TRAP, ID_CELL_CREATE, - ID_RECORD_DYNAMIC, ID_CONSOLE_COMMAND, ID_CONTAINER, @@ -108,7 +106,11 @@ enum GameMessages ID_SCRIPT_GLOBAL_FLOAT, ID_GAME_SETTINGS, - ID_GAME_PREINIT + ID_GAME_PREINIT, + + ID_RECORD_DYNAMIC, + ID_WORLD_COLLISION_OVERRIDE, + ID_WORLD_TIME, }; enum OrderingChannel diff --git a/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.cpp b/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.cpp new file mode 100644 index 000000000..ee9720600 --- /dev/null +++ b/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.cpp @@ -0,0 +1,20 @@ +#include "PacketWorldCollisionOverride.hpp" +#include + +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); +} diff --git a/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.hpp b/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.hpp new file mode 100644 index 000000000..2698ae0f7 --- /dev/null +++ b/components/openmw-mp/Packets/Worldstate/PacketWorldCollisionOverride.hpp @@ -0,0 +1,17 @@ +#ifndef OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP +#define OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP + +#include + +namespace mwmp +{ + class PacketWorldCollisionOverride : public WorldstatePacket + { + public: + PacketWorldCollisionOverride(RakNet::RakPeerInterface *peer); + + virtual void Packet(RakNet::BitStream *bs, bool send); + }; +} + +#endif //OPENMW_PACKETWORLDCOLLISIONOVERRIDE_HPP diff --git a/files/mygui/openmw_spell_window.layout b/files/mygui/openmw_spell_window.layout index 21bf74267..8a98c47b2 100644 --- a/files/mygui/openmw_spell_window.layout +++ b/files/mygui/openmw_spell_window.layout @@ -10,7 +10,11 @@ - + + + + + diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index bfa256faf..0afd855f9 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -68,7 +68,7 @@ record-preview.png record-clone.png record-add.png - resources-icon.png + resources-icon.png resources-mesh.png resources-music.png resources-sound.png @@ -149,6 +149,10 @@ transform-scale.png selection-mode-cube.png selection-mode-cube-corner.png - selection-mode-cube-sphere.png + selection-mode-cube-sphere.png + brush-point.png + brush-square.png + brush-circle.png + brush-custom.png diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4483e3d9d..b10b91eb1 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -217,7 +217,7 @@ followers attack on sight = false 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) -rebalance soul gem values = true +rebalance soul gem values = false [General] diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 4e0234e2e..f436b4db3 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -112,7 +112,7 @@ - <html><head/><body><p>If this setting is true, the value of filled soul gems is dependent only on soul magnitude.</p><p>The default value is true.</p></body></html> + <html><head/><body><p>If this setting is true, the value of filled soul gems is dependent only on soul magnitude.</p><p>The default value is false.</p></body></html> Rebalance soul gem values