Add OpenMW commits up to 18 Sep 2019

# Conflicts:
#	apps/openmw/mwclass/container.cpp
#	apps/openmw/mwclass/door.cpp
#	apps/openmw/mwmechanics/security.cpp
#	apps/openmw/mwmechanics/spellcasting.cpp
#	apps/openmw/mwscript/miscextensions.cpp
pull/541/head
David Cernat 5 years ago
commit cc25612b8d

@ -9,6 +9,7 @@
Bug #3109: SetPos/Position handles actors differently Bug #3109: SetPos/Position handles actors differently
Bug #3282: Unintended behaviour when assigning F3 and Windows keys Bug #3282: Unintended behaviour when assigning F3 and Windows keys
Bug #3550: Companion from mod attacks the air after combat has ended Bug #3550: Companion from mod attacks the air after combat has ended
Bug #3609: Items from evidence chest are not considered to be stolen if player is allowed to use the chest
Bug #3623: Display scaling breaks mouse recognition Bug #3623: Display scaling breaks mouse recognition
Bug #3725: Using script function in a non-conditional expression breaks script compilation Bug #3725: Using script function in a non-conditional expression breaks script compilation
Bug #3733: Normal maps are inverted on mirrored UVs Bug #3733: Normal maps are inverted on mirrored UVs
@ -104,6 +105,7 @@
Bug #4999: Drop instruction behaves differently from vanilla Bug #4999: Drop instruction behaves differently from vanilla
Bug #5001: Possible data race in the Animation::setAlpha() Bug #5001: Possible data race in the Animation::setAlpha()
Bug #5004: Werewolves shield their eyes during storm Bug #5004: Werewolves shield their eyes during storm
Bug #5012: "Take all" on owned container generates a messagebox per item
Bug #5018: Spell tooltips don't support purely negative magnitudes Bug #5018: Spell tooltips don't support purely negative magnitudes
Bug #5025: Data race in the ICO::setMaximumNumOfObjectsToCompilePerFrame() Bug #5025: Data race in the ICO::setMaximumNumOfObjectsToCompilePerFrame()
Bug #5028: Offered price caps are not trading-specific Bug #5028: Offered price caps are not trading-specific
@ -137,11 +139,18 @@
Bug #5134: Doors rotation by "Lock" console command is inconsistent Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Bug #5149: Failing lock pick attempts isn't always a crime Bug #5149: Failing lock pick attempts isn't always a crime
Bug #5155: Lock/unlock behavior differs from vanilla
Bug #5158: Objects without a name don't fallback to their ID
Bug #5159: NiMaterialColorController can only control the diffuse color
Bug #5161: Creature companions can't be activated when they are knocked down
Bug #5164: Faction owned items handling is incorrect
Bug #5166: Scripts still should be executed after player's death
Feature #1774: Handle AvoidNode Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis Feature #3610: Option to invert X axis
Feature #3871: Editor: Terrain Selection
Feature #3893: Implicit target for "set" function in console Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller Feature #3980: In-game option to disable controller
Feature #3999: Shift + Double Click should maximize/restore menu size Feature #3999: Shift + Double Click should maximize/restore menu size

@ -329,6 +329,7 @@ bool Launcher::MainDialog::setupGameSettings()
stream.setCodec(QTextCodec::codecForName("UTF-8")); stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGameSettings.readUserFile(stream); mGameSettings.readUserFile(stream);
file.close();
} }
// Now the rest - priority: user > local > global // Now the rest - priority: user > local > global
@ -353,8 +354,8 @@ bool Launcher::MainDialog::setupGameSettings()
stream.setCodec(QTextCodec::codecForName("UTF-8")); stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGameSettings.readFile(stream); mGameSettings.readFile(stream);
file.close();
} }
file.close();
} }
return true; return true;

@ -89,7 +89,7 @@ opencs_units (view/render
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
previewwidget editmode instancemode instanceselectionmode instancemovemode previewwidget editmode instancemode instanceselectionmode instancemovemode
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
cellwater terraintexturemode actor cellwater terraintexturemode actor terrainselection
) )
opencs_units_noqt (view/render opencs_units_noqt (view/render

@ -8,13 +8,6 @@
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
namespace
{
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
}
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
@ -76,10 +69,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x,
return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
} }
std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * landTextureSize / cellSize - 0.25f); const auto xd = static_cast<float>(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f);
const auto yd = static_cast<float>(worldPos.y() * landTextureSize / cellSize + 0.25f); const auto yd = static_cast<float>(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -87,10 +80,10 @@ std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldP
return std::make_pair(x, y); return std::make_pair(x, y);
} }
std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos) std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos)
{ {
const auto xd = static_cast<float>(worldPos.x() * (landSize - 1) / cellSize + 0.5f); const auto xd = static_cast<float>(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto yd = static_cast<float>(worldPos.y() * (landSize - 1) / cellSize + 0.5f); const auto yd = static_cast<float>(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto x = static_cast<int>(std::floor(xd)); const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd)); const auto y = static_cast<int>(std::floor(yd));
@ -98,25 +91,32 @@ std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPo
return std::make_pair(x, y); return std::make_pair(x, y);
} }
float CSMWorld::CellCoordinates::textureSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::textureGlobalToWorldCoords(int textureGlobal)
{ {
return cellSize * static_cast<float>(pos) / landTextureSize; return ESM::Land::REAL_SIZE * static_cast<float>(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE;
} }
float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos) float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal)
{ {
return cellSize * static_cast<float>(pos) / (landSize - 1); return ESM::Land::REAL_SIZE * static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1);
} }
int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos) int CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal)
{ {
return static_cast<int>(pos - std::floor(static_cast<float>(pos) / (landSize - 1)) * (landSize - 1)); return static_cast<int>(vertexGlobal - std::floor(static_cast<float>(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1));
}
std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)
{
int x = std::floor(static_cast<float>(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE);
int y = std::floor(static_cast<float>(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE);
return generateId(x, y);
} }
std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair<int, int> vertexGlobal) std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal)
{ {
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1)); int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1)); int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));
return generateId(x, y); return generateId(x, y);
} }

@ -49,22 +49,25 @@ namespace CSMWorld
static std::pair<int, int> coordinatesToCellIndex (float x, float y); static std::pair<int, int> coordinatesToCellIndex (float x, float y);
///Converts worldspace coordinates to global texture selection, taking in account the texture offset. ///Converts worldspace coordinates to global texture selection, taking in account the texture offset.
static std::pair<int, int> toTextureCoords(osg::Vec3d worldPos); static std::pair<int, int> toTextureCoords(const osg::Vec3d& worldPos);
///Converts worldspace coordinates to global vertex selection. ///Converts worldspace coordinates to global vertex selection.
static std::pair<int, int> toVertexCoords(osg::Vec3d worldPos); static std::pair<int, int> toVertexCoords(const osg::Vec3d& worldPos);
///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture. ///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture.
static float textureSelectionToWorldCoords(int); static float textureGlobalToWorldCoords(int textureGlobal);
///Converts global vertex coordinate to worldspace coordinate ///Converts global vertex coordinate to worldspace coordinate
static float vertexSelectionToWorldCoords(int); static float vertexGlobalToWorldCoords(int vertexGlobal);
///Converts local cell's heightmap coordinates from the global vertex coordinate ///Converts global vertex coordinate to local cell's heightmap coordinates
static int vertexSelectionToInCellCoords(int); static int vertexGlobalToInCellCoords(int vertexGlobal);
///Converts global texture coordinates to cell id
static std::string textureGlobalToCellId(const std::pair<int, int>& textureGlobal);
///Converts global vertex coordinates to cell id ///Converts global vertex coordinates to cell id
static std::string vertexGlobalToCellId(std::pair<int, int>); static std::string vertexGlobalToCellId(const std::pair<int, int>& vertexGlobal);
}; };
bool operator== (const CellCoordinates& left, const CellCoordinates& right); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -140,7 +140,7 @@ void CSVRender::PagedWorldspaceWidget::addEditModeSelectorButtons (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain shape editing"),
"terrain-shape"); "terrain-shape");
tool->addButton ( tool->addButton (
new TerrainTextureMode (this, tool), new TerrainTextureMode (this, mRootNode, tool),
"terrain-texture"); "terrain-texture");
tool->addButton ( tool->addButton (
new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"), new EditMode (this, QIcon (":placeholder"), Mask_Reference, "Terrain vertex paint editing"),

@ -0,0 +1,261 @@
#include "terrainselection.hpp"
#include <algorithm>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
#include "../../model/world/columnimp.hpp"
#include "../../model/world/idtable.hpp"
#include "cell.hpp"
#include "worldspacewidget.hpp"
CSVRender::TerrainSelection::TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type):
mParentNode(parentNode), mWorldspaceWidget (worldspaceWidget), mDraggedOperationFlag(false), mSelectionType(type)
{
mGeometry = new osg::Geometry();
mSelectionNode = new osg::Group();
mSelectionNode->addChild(mGeometry);
activate();
}
CSVRender::TerrainSelection::~TerrainSelection()
{
deactivate();
}
std::vector<std::pair<int, int>> CSVRender::TerrainSelection::getTerrainSelection() const
{
return mSelection;
}
void CSVRender::TerrainSelection::onlySelect(const std::vector<std::pair<int, int>> &localPositions)
{
mSelection = localPositions;
update();
}
void CSVRender::TerrainSelection::addSelect(const std::pair<int, int> &localPos)
{
if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end())
{
mSelection.emplace_back(localPos);
update();
}
}
void CSVRender::TerrainSelection::toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress)
{
if (toggleInProgress)
{
for(auto const& localPos: localPositions)
{
auto iterTemp = std::find(mTemporarySelection.begin(), mTemporarySelection.end(), localPos);
mDraggedOperationFlag = true;
if (iterTemp == mTemporarySelection.end())
{
auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
mTemporarySelection.push_back(localPos);
}
}
else if (mDraggedOperationFlag == false)
{
for(auto const& localPos: localPositions)
{
const auto iter = std::find(mSelection.begin(), mSelection.end(), localPos);
if (iter != mSelection.end())
{
mSelection.erase(iter);
}
else
{
mSelection.emplace_back(localPos);
}
}
}
else
{
mDraggedOperationFlag = false;
mTemporarySelection.clear();
}
update();
}
void CSVRender::TerrainSelection::activate()
{
if (!mParentNode->containsNode(mSelectionNode)) mParentNode->addChild(mSelectionNode);
}
void CSVRender::TerrainSelection::deactivate()
{
mParentNode->removeChild(mSelectionNode);
}
void CSVRender::TerrainSelection::update()
{
mSelectionNode->removeChild(mGeometry);
mGeometry = new osg::Geometry();
const osg::ref_ptr<osg::Vec3Array> vertices (new osg::Vec3Array);
switch (mSelectionType)
{
case TerrainSelectionType::Texture : drawTextureSelection(vertices);
break;
case TerrainSelectionType::Shape : drawShapeSelection(vertices);
break;
}
mGeometry->setVertexArray(vertices);
osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES);
drawArrays->setCount(vertices->size());
if (vertices->size() != 0) mGeometry->addPrimitiveSet(drawArrays);
mSelectionNode->addChild(mGeometry);
}
void CSVRender::TerrainSelection::drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
for (std::pair<int, int> &localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
float xWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x));
float yWorldCoord(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y));
osg::Vec3f pointXY(xWorldCoord, yWorldCoord, calculateLandHeight(x, y) + 2);
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y - 1), calculateLandHeight(x, y - 1) + 2));
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x - 1), yWorldCoord, calculateLandHeight(x - 1, y) + 2));
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(xWorldCoord, CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(y + 1), calculateLandHeight(x, y + 1) + 2));
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
vertices->push_back(pointXY);
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(x + 1), yWorldCoord, calculateLandHeight(x + 1, y) + 2));
}
}
}
}
void CSVRender::TerrainSelection::drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices)
{
if (!mSelection.empty())
{
// Nudge selection by 1/4th of a texture size, similar how blendmaps are nudged
const float nudgePercentage = 0.25f;
const int nudgeOffset = (ESM::Land::REAL_SIZE / ESM::Land::LAND_TEXTURE_SIZE) * nudgePercentage;
const int landHeightsNudge = (ESM::Land::REAL_SIZE / ESM::Land::LAND_SIZE) / (ESM::Land::LAND_SIZE - 1); // Does this work with all land size configurations?
const int textureSizeToLandSizeModifier = (ESM::Land::LAND_SIZE - 1) / ESM::Land::LAND_TEXTURE_SIZE;
for (std::pair<int, int> &localPos : mSelection)
{
int x (localPos.first);
int y (localPos.second);
// convert texture selection to global vertex coordinates at selection box corners
int x1 = x * textureSizeToLandSizeModifier + landHeightsNudge;
int x2 = x * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier + landHeightsNudge;
int y1 = y * textureSizeToLandSizeModifier - landHeightsNudge;
int y2 = y * textureSizeToLandSizeModifier + textureSizeToLandSizeModifier - landHeightsNudge;
// Draw edges (check all sides, draw lines between vertices, +1 height to keep lines above ground)
// Check adjancent selections, draw lines only to edges of the selection
const auto north = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y + 1));
if (north == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+(i-1), y2)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y + 1) - nudgeOffset, calculateLandHeight(x1+i, y2)+2));
}
}
const auto south = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x, y - 1));
if (south == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + (i - 1) *(ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentX = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(drawPreviousX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+(i-1), y1)+2));
vertices->push_back(osg::Vec3f(drawCurrentX + nudgeOffset, CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) - nudgeOffset, calculateLandHeight(x1+i, y1)+2));
}
}
const auto east = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x + 1, y));
if (east == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x2, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x + 1) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x2, y1+i)+2));
}
}
const auto west = std::find(mSelection.begin(), mSelection.end(), std::make_pair(x - 1, y));
if (west == mSelection.end())
{
for(int i = 1; i < (textureSizeToLandSizeModifier + 1); i++)
{
float drawPreviousY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + (i - 1) * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
float drawCurrentY = CSMWorld::CellCoordinates::textureGlobalToWorldCoords(y) + i * (ESM::Land::REAL_SIZE / (ESM::Land::LAND_SIZE - 1));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawPreviousY - nudgeOffset, calculateLandHeight(x1, y1+(i-1))+2));
vertices->push_back(osg::Vec3f(CSMWorld::CellCoordinates::textureGlobalToWorldCoords(x) + nudgeOffset, drawCurrentY - nudgeOffset, calculateLandHeight(x1, y1+i)+2));
}
}
}
}
}
int CSVRender::TerrainSelection::calculateLandHeight(int x, int y) // global vertex coordinates
{
int cellX = std::floor((1.0f*x / (ESM::Land::LAND_SIZE - 1)));
int cellY = std::floor((1.0f*y / (ESM::Land::LAND_SIZE - 1)));
int localX = x - cellX * (ESM::Land::LAND_SIZE - 1);
int localY = y - cellY * (ESM::Land::LAND_SIZE - 1);
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
CSMDoc::Document& document = mWorldspaceWidget->getDocument();
CSMWorld::IdTable& landTable = dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
const CSMWorld::LandHeightsColumn::DataType mPointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
return mPointer[localY*ESM::Land::LAND_SIZE + localX];
}

@ -0,0 +1,70 @@
#ifndef CSV_RENDER_TERRAINSELECTION_H
#define CSV_RENDER_TERRAINSELECTION_H
#include <utility>
#include <vector>
#include <osg/Vec3d>
#include <osg/ref_ptr>
#include <osg/PositionAttitudeTransform>
#include <components/esm/loadland.hpp>
#include "../../model/world/cellcoordinates.hpp"
namespace osg
{
class Group;
}
namespace CSVRender
{
struct WorldspaceHitResult;
class WorldspaceWidget;
enum class TerrainSelectionType
{
Texture,
Shape
};
/// \brief Class handling the terrain selection data and rendering
class TerrainSelection
{
public:
TerrainSelection(osg::Group* parentNode, WorldspaceWidget *worldspaceWidget, TerrainSelectionType type);
~TerrainSelection();
void onlySelect(const std::vector<std::pair<int, int>> &localPositions);
void addSelect(const std::pair<int, int> &localPos);
void toggleSelect(const std::vector<std::pair<int, int>> &localPositions, bool toggleInProgress);
void activate();
void deactivate();
std::vector<std::pair<int, int>> getTerrainSelection() const;
protected:
void update();
void drawShapeSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
void drawTextureSelection(const osg::ref_ptr<osg::Vec3Array> vertices);
int calculateLandHeight(int x, int y);
private:
osg::Group* mParentNode;
WorldspaceWidget *mWorldspaceWidget;
osg::ref_ptr<osg::PositionAttitudeTransform> mBaseNode;
osg::ref_ptr<osg::Geometry> mGeometry;
osg::ref_ptr<osg::Group> mSelectionNode;
std::vector<std::pair<int, int>> mSelection; // Global terrain selection coordinate in either vertex or texture units
std::vector<std::pair<int, int>> mTemporarySelection; // Used during toggle to compare the most recent drag operation
bool mDraggedOperationFlag; //true during drag operation, false when click-operation
TerrainSelectionType mSelectionType;
};
}
#endif

@ -10,6 +10,8 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QDrag> #include <QDrag>
#include <osg/Group>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp" #include "../widget/modebutton.hpp"
@ -36,12 +38,15 @@
#include "object.hpp" // Something small needed regarding pointers from here () #include "object.hpp" // Something small needed regarding pointers from here ()
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent), : EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
mBrushTexture("L0#0"), mBrushTexture("L0#0"),
mBrushSize(0), mBrushSize(0),
mBrushShape(0), mBrushShape(0),
mTextureBrushScenetool(0) mTextureBrushScenetool(0),
mDragMode(InteractionType_None),
mParentNode(parentNode),
mIsEditing(false)
{ {
} }
@ -62,6 +67,11 @@ void CSVRender::TerrainTextureMode::activate(CSVWidget::SceneToolbar* toolbar)
connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string))); connect(this, SIGNAL(passBrushTexture(std::string)), mTextureBrushScenetool, SLOT(updateBrushHistory(std::string)));
} }
if (!mTerrainTextureSelection)
{
mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));
}
EditMode::activate(toolbar); EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool); toolbar->addTool (mTextureBrushScenetool);
} }
@ -74,6 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
delete mTextureBrushScenetool; delete mTextureBrushScenetool;
mTextureBrushScenetool = 0; mTextureBrushScenetool = 0;
} }
if (mTerrainTextureSelection)
{
mTerrainTextureSelection.reset();
}
EditMode::deactivate(toolbar); EditMode::deactivate(toolbar);
} }
@ -95,10 +111,10 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{ {
undoStack.beginMacro ("Edit texture records"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -109,10 +125,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit) void CSVRender::TerrainTextureMode::primarySelectPressed(const WorldspaceHitResult& hit)
{ {
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, false);
}
} }
void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit) void CSVRender::TerrainTextureMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{ {
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false);
}
} }
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
@ -129,13 +153,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{ {
undoStack.beginMacro ("Edit texture records"); undoStack.beginMacro ("Edit texture records");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true) mIsEditing = true;
if(allowLandTextureEditing(mCellId))
{ {
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId)); undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit); editTerrainTextureGrid(hit);
@ -152,47 +179,88 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (const QPoint& pos)
bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::primarySelectStartDrag (const QPoint& pos)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_PrimarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
return false; return false;
} }
bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos) bool CSVRender::TerrainTextureMode::secondarySelectStartDrag (const QPoint& pos)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
mDragMode = InteractionType_SecondarySelect;
if (!hit.hit || hit.tag != 0)
{
mDragMode = InteractionType_None;
return false;
}
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
return false; return false;
} }
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor) void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{ {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask()); if (mDragMode == InteractionType_PrimaryEdit)
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); {
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
std::string cellId = getWorldspaceWidget().getCellId (hit.worldPos);
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{
editTerrainTextureGrid(hit);
}
}
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true) if (mDragMode == InteractionType_PrimarySelect)
{ {
editTerrainTextureGrid(hit); WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 0, true);
}
if (mDragMode == InteractionType_SecondarySelect)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
if (hit.hit && hit.tag == 0) selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, true);
} }
} }
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos) { void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
CSMDoc::Document& document = getWorldspaceWidget().getDocument(); {
QUndoStack& undoStack = document.getUndoStack(); if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures(); CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture); int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted()) if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{ {
undoStack.endMacro(); undoStack.endMacro();
mIsEditing = false;
}
} }
} }
void CSVRender::TerrainTextureMode::dragAborted() { void CSVRender::TerrainTextureMode::dragAborted()
{
} }
void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor) {} void CSVRender::TerrainTextureMode::dragWheel (int diff, double speedFactor)
{
}
void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event) { void CSVRender::TerrainTextureMode::handleDropEvent (QDropEvent *event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
@ -228,7 +296,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Land)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Land));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos); mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId); std::pair<CSMWorld::CellCoordinates, bool> cellCoordinates_pair = CSMWorld::CellCoordinates::fromId (mCellId);
@ -236,8 +304,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
int cellY = cellCoordinates_pair.first.getY(); int cellY = cellCoordinates_pair.first.getY();
// The coordinates of hit in mCellId // The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5)); int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5)); int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));
if (xHitInCell < 0) if (xHitInCell < 0)
{ {
xHitInCell = xHitInCell + landTextureSize; xHitInCell = xHitInCell + landTextureSize;
@ -249,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
cellY = cellY + 1; cellY = cellY + 1;
} }
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY); mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
if(allowLandTextureEditing(mCellId)==true) {} if(allowLandTextureEditing(mCellId)) {}
std::string iteratedCellId; std::string iteratedCellId;
@ -266,13 +334,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 0) if (mBrushShape == 0)
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true) if(allowLandTextureEditing(mCellId))
{ {
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt; newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId); pushEditToCommand(newTerrain, document, landTable, mCellId);
} }
} }
@ -292,19 +360,19 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{ {
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
{ {
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r) if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
{ {
mNew[j*landTextureSize+i] = brushInt; newTerrain[j*landTextureSize+i] = brushInt;
} }
else else
{ {
@ -316,11 +384,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j; if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt; if (distanceX < r && distanceY < r) newTerrain[j*landTextureSize+i] = brushInt;
} }
} }
} }
pushEditToCommand(mNew, document, landTable, iteratedCellId); pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
} }
} }
} }
@ -342,11 +410,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
{ {
for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++) for(int j_cell = upperLeftCellY; j_cell <= lowerrightCellY; j_cell++)
{ {
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell); iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId)==true) if(allowLandTextureEditing(iteratedCellId))
{ {
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>(); CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer); CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++) for(int i = 0; i < landTextureSize; i++)
{ {
for(int j = 0; j < landTextureSize; j++) for(int j = 0; j < landTextureSize; j++)
@ -363,7 +431,7 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
} }
else else
{ {
@ -376,11 +444,104 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell); if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell); if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2))); distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt; if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
} }
} }
} }
pushEditToCommand(mNew, document, landTable, iteratedCellId); pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
}
}
}
}
if (mBrushShape == 3)
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId) && !mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
if(yHitInCell + value.second >= 0 && yHitInCell + value.second <= 15 && xHitInCell + value.first >= 0 && xHitInCell + value.first <= 15)
{
newTerrain[(yHitInCell+value.second)*landTextureSize+xHitInCell+value.first] = brushInt;
}
else
{
int cellXDifference = std::floor(1.0f*(xHitInCell + value.first)/landTextureSize);
int cellYDifference = std::floor(1.0f*(yHitInCell + value.second)/landTextureSize);
int xInOtherCell = xHitInCell + value.first - cellXDifference * landTextureSize;
int yInOtherCell = yHitInCell + value.second - cellYDifference * landTextureSize;
std::string cellId = CSMWorld::CellCoordinates::generateId(cellX+cellXDifference, cellY+cellYDifference);
if (allowLandTextureEditing(cellId))
{
CSMWorld::LandTexturesColumn::DataType newTerrainPointerOtherCell = landTable.data(landTable.getModelIndex(cellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrainOtherCell(newTerrainPointerOtherCell);
newTerrainOtherCell[yInOtherCell*landTextureSize+xInOtherCell] = brushInt;
pushEditToCommand(newTerrainOtherCell, document, landTable, cellId);
}
}
}
pushEditToCommand(newTerrain, document, landTable, mCellId);
}
}
}
bool CSVRender::TerrainTextureMode::isInCellSelection(int globalSelectionX, int globalSelectionY)
{
if (CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget()))
{
std::pair<int, int> textureCoords = std::make_pair(globalSelectionX, globalSelectionY);
std::string cellId = CSMWorld::CellCoordinates::textureGlobalToCellId(textureCoords);
return paged->getCellSelection().has(CSMWorld::CellCoordinates::fromId(cellId).first);
}
return false;
}
void CSVRender::TerrainTextureMode::selectTerrainTextures(const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation)
{
int r = mBrushSize / 2;
std::vector<std::pair<int, int>> selections;
if (mBrushShape == 0)
{
if (isInCellSelection(texCoords.first, texCoords.second)) selections.emplace_back(texCoords);
}
if (mBrushShape == 1)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
}
if (mBrushShape == 2)
{
for (int i = -r; i <= r; i++)
{
for (int j = -r; j <= r; j++)
{
osg::Vec2f coords(i,j);
if (std::round(coords.length()) < r)
{
int x = i + texCoords.first;
int y = j + texCoords.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
} }
} }
} }
@ -388,9 +549,22 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 3) if (mBrushShape == 3)
{ {
// Not implemented if(!mCustomBrushShape.empty())
{
for(auto const& value: mCustomBrushShape)
{
int x = texCoords.first + value.first;
int y = texCoords.second + value.second;
if (isInCellSelection(x, y))
{
selections.emplace_back(x, y);
}
}
}
} }
if(selectMode == 0) mTerrainTextureSelection->onlySelect(selections);
if(selectMode == 1) mTerrainTextureSelection->toggleSelect(selections, dragOperation);
} }
void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
@ -405,8 +579,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex))); QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack(); QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand)); undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
} }
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName) void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
@ -422,13 +596,15 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
int counter=0; int counter=0;
bool freeIndexFound = false; bool freeIndexFound = false;
do { do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1; const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try try
{ {
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter; if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e) }
catch (const std::exception& e)
{ {
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true; freeIndexFound = true;
@ -527,7 +703,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
return true; return true;
} }
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) { void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)
{
} }
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
@ -538,6 +715,28 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
void CSVRender::TerrainTextureMode::setBrushShape(int brushShape) void CSVRender::TerrainTextureMode::setBrushShape(int brushShape)
{ {
mBrushShape = brushShape; mBrushShape = brushShape;
//Set custom brush shape
if (mBrushShape == 3 && !mTerrainTextureSelection->getTerrainSelection().empty())
{
auto terrainSelection = mTerrainTextureSelection->getTerrainSelection();
int selectionCenterX = 0;
int selectionCenterY = 0;
int selectionAmount = 0;
for(auto const& value: terrainSelection)
{
selectionCenterX += value.first;
selectionCenterY += value.second;
++selectionAmount;
}
selectionCenterX /= selectionAmount;
selectionCenterY /= selectionAmount;
mCustomBrushShape.clear();
for (auto const& value: terrainSelection)
mCustomBrushShape.emplace_back(value.first - selectionCenterX, value.second - selectionCenterY);
}
} }
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture) void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)

@ -4,6 +4,7 @@
#include "editmode.hpp" #include "editmode.hpp"
#include <string> #include <string>
#include <memory>
#include <QWidget> #include <QWidget>
#include <QEvent> #include <QEvent>
@ -18,6 +19,13 @@
#include "../../model/world/landtexture.hpp" #include "../../model/world/landtexture.hpp"
#endif #endif
#include "terrainselection.hpp"
namespace osg
{
class Group;
}
namespace CSVWidget namespace CSVWidget
{ {
class SceneToolTextureBrush; class SceneToolTextureBrush;
@ -25,15 +33,23 @@ namespace CSVWidget
namespace CSVRender namespace CSVRender
{ {
class TerrainTextureMode : public EditMode class TerrainTextureMode : public EditMode
{ {
Q_OBJECT Q_OBJECT
public: public:
enum InteractionType
{
InteractionType_PrimaryEdit,
InteractionType_PrimarySelect,
InteractionType_SecondaryEdit,
InteractionType_SecondarySelect,
InteractionType_None
};
/// \brief Editmode for terrain texture grid /// \brief Editmode for terrain texture grid
TerrainTextureMode(WorldspaceWidget*, QWidget* parent = nullptr); TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
void primaryOpenPressed (const WorldspaceHitResult& hit); void primaryOpenPressed (const WorldspaceHitResult& hit);
@ -68,6 +84,12 @@ namespace CSVRender
/// \brief Handle brush mechanics, maths regarding worldspace hit etc. /// \brief Handle brush mechanics, maths regarding worldspace hit etc.
void editTerrainTextureGrid (const WorldspaceHitResult& hit); void editTerrainTextureGrid (const WorldspaceHitResult& hit);
/// \brief Check if global selection coordinate belongs to cell in view
bool isInCellSelection(int globalSelectionX, int globalSelectionY);
/// \brief Handle brush mechanics for texture selection
void selectTerrainTextures (const std::pair<int, int>& texCoords, unsigned char selectMode, bool dragOperation);
/// \brief Push texture edits to command macro /// \brief Push texture edits to command macro
void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document, void pushEditToCommand (CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId); CSMWorld::IdTable& landTable, std::string cellId);
@ -83,7 +105,12 @@ namespace CSVRender
std::string mBrushTexture; std::string mBrushTexture;
int mBrushSize; int mBrushSize;
int mBrushShape; int mBrushShape;
std::vector<std::pair<int, int>> mCustomBrushShape;
CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool;
int mDragMode;
osg::Group* mParentNode;
bool mIsEditing;
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
const int cellSize {ESM::Land::REAL_SIZE}; const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE}; const int landSize {ESM::Land::LAND_SIZE};

@ -194,8 +194,8 @@ bool OMW::Engine::frame(float frametime)
*/ */
osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick(); osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick();
if (mEnvironment.getStateManager()->getState()== if (mEnvironment.getStateManager()->getState()!=
MWBase::StateManager::State_Running) MWBase::StateManager::State_NoGame)
{ {
if (!paused) if (!paused)
{ {

@ -140,8 +140,8 @@ namespace MWBase
* If this parameter is false, it will be determined by a line-of-sight and awareness check. * If this parameter is false, it will be determined by a line-of-sight and awareness check.
* @return was the crime seen? * @return was the crime seen?
*/ */
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type,
OffenseType type, int arg=0, bool victimAware=false) = 0; const std::string& factionId="", int arg=0, bool victimAware=false) = 0;
/// @return false if the attack was considered a "friendly hit" and forgiven /// @return false if the attack was considered a "friendly hit" and forgiven
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;

@ -100,9 +100,12 @@ namespace MWBase
///< Say some text, without an actor ref ///< Say some text, without an actor ref
/// \param filename name of a sound file in "Sound/" in the data directory. /// \param filename name of a sound file in "Sound/" in the data directory.
virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0; virtual bool sayActive(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0;
///< Is actor not speaking? ///< Is actor not speaking?
virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const = 0;
///< For scripting backward compatibility
virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) = 0; virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) = 0;
///< Stop an actor speaking ///< Stop an actor speaking

@ -89,9 +89,7 @@ namespace MWClass
bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>(); return !getName(ptr).empty();
return (ref->mBase->mName != "");
} }
bool Activator::allowTelekinesis(const MWWorld::ConstPtr &ptr) const { bool Activator::allowTelekinesis(const MWWorld::ConstPtr &ptr) const {
@ -103,7 +101,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>(); const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
std::string text; std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) if (MWBase::Environment::get().getWindowManager()->getFullHelp())

@ -20,11 +20,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const; virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const;
///< Return whether this class of object can be activated with telekinesis ///< Return whether this class of object can be activated with telekinesis

@ -91,14 +91,4 @@ namespace MWClass
{ {
return true; return true;
} }
bool Actor::canBeActivated(const MWWorld::Ptr& ptr) const
{
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
if (stats.getAiSequence().isInCombat() && !stats.isDead())
return false;
return true;
}
} }

@ -42,8 +42,6 @@ namespace MWClass
virtual bool isActor() const; virtual bool isActor() const;
virtual bool canBeActivated(const MWWorld::Ptr& ptr) const;
// not implemented // not implemented
Actor(const Actor&); Actor(const Actor&);
Actor& operator= (const Actor&); Actor& operator= (const Actor&);

@ -78,8 +78,9 @@ namespace MWClass
std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>(); const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
@ -126,19 +127,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Apparatus::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>(); const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -20,8 +20,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
@ -33,9 +32,6 @@ namespace MWClass
virtual int getValue (const MWWorld::ConstPtr& ptr) const; virtual int getValue (const MWWorld::ConstPtr& ptr) const;
///< Return trade value of the object. Throws an exception, if the object can't be traded. ///< Return trade value of the object. Throws an exception, if the object can't be traded.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -87,8 +87,9 @@ namespace MWClass
std::string Armor::getName (const MWWorld::ConstPtr& ptr) const std::string Armor::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
@ -233,19 +234,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Armor::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -19,8 +19,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
@ -43,9 +42,6 @@ namespace MWClass
/// Return the index of the skill this item corresponds to when equipped or -1, if there is /// Return the index of the skill this item corresponds to when equipped or -1, if there is
/// no such skill. /// no such skill.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -31,6 +31,11 @@ namespace MWClass
return std::string(); return std::string();
} }
bool BodyPart::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
return false;
}
void BodyPart::registerSelf() void BodyPart::registerSelf()
{ {
std::shared_ptr<MWWorld::Class> instance (new BodyPart); std::shared_ptr<MWWorld::Class> instance (new BodyPart);

@ -18,8 +18,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: true)
static void registerSelf(); static void registerSelf();

@ -83,8 +83,9 @@ namespace MWClass
std::string Book::getName (const MWWorld::ConstPtr& ptr) const std::string Book::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>(); const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Book::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Book::activate (const MWWorld::Ptr& ptr,
@ -142,19 +143,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Book::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>(); const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,8 +17,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
@ -27,9 +26,6 @@ namespace MWClass
virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; virtual std::string getScript (const MWWorld::ConstPtr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -81,8 +81,9 @@ namespace MWClass
std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>(); const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
@ -190,19 +191,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Clothing::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>(); const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,8 +17,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
@ -35,9 +34,6 @@ namespace MWClass
/// Return the index of the skill this item corresponds to when equipped or -1, if there is /// Return the index of the skill this item corresponds to when equipped or -1, if there is
/// no such skill. /// no such skill.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -200,7 +200,7 @@ namespace MWClass
packet to do it instead packet to do it instead
*/ */
//if(isLocked) //if(isLocked)
// unlock(ptr); // ptr.getCellRef().unlock();
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */
@ -288,8 +288,9 @@ namespace MWClass
std::string Container::getName (const MWWorld::ConstPtr& ptr) const std::string Container::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>(); const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr)
@ -316,26 +317,18 @@ namespace MWClass
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
{ {
if (getName(ptr).empty())
return false;
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData()) if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems(); return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
return true; return true;
} }
bool Container::canBeActivated(const MWWorld::Ptr& ptr) const
{
return hasToolTip(ptr);
}
MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>(); const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text; std::string text;
int lockLevel = ptr.getCellRef().getLockLevel(); int lockLevel = ptr.getCellRef().getLockLevel();
@ -346,9 +339,11 @@ namespace MWClass
if (ptr.getCellRef().getTrap() != "") if (ptr.getCellRef().getTrap() != "")
text += "\n#{sTrapped}"; text += "\n#{sTrapped}";
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp())
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef()); { text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script");
if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "stolen_goods"))
text += "\nYou can not use evidence chests";
} }
info.text = text; info.text = text;
@ -369,23 +364,10 @@ namespace MWClass
return getContainerStore (ptr).getWeight(); return getContainerStore (ptr).getWeight();
} }
void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const
{
if(lockLevel != 0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive
else
ptr.getCellRef().setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level
}
void Container::unlock (const MWWorld::Ptr& ptr) const
{
int lockLevel = ptr.getCellRef().getLockLevel();
ptr.getCellRef().setLockLevel(-abs(lockLevel)); //Makes lockLevel negative
}
bool Container::canLock(const MWWorld::ConstPtr &ptr) const bool Container::canLock(const MWWorld::ConstPtr &ptr) const
{ {
return true; const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
return !(ref->mBase->mFlags & ESM::Container::Organic);
} }
MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const

@ -20,15 +20,14 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
@ -57,12 +56,6 @@ namespace MWClass
///< Returns total weight of objects inside this object (including modifications from magic ///< Returns total weight of objects inside this object (including modifications from magic
/// effects). Throws an exception, if the object can't hold other objects. /// effects). Throws an exception, if the object can't hold other objects.
virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const;
///< Lock object
virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object
virtual bool canLock(const MWWorld::ConstPtr &ptr) const; virtual bool canLock(const MWWorld::ConstPtr &ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
@ -73,8 +66,6 @@ namespace MWClass
const; const;
///< Write additional state from \a ptr into \a state. ///< Write additional state from \a ptr into \a state.
virtual bool canBeActivated(const MWWorld::Ptr& ptr) const;
static void registerSelf(); static void registerSelf();
virtual void respawn (const MWWorld::Ptr& ptr) const; virtual void respawn (const MWWorld::Ptr& ptr) const;

@ -229,8 +229,9 @@ namespace MWClass
std::string Creature::getName (const MWWorld::ConstPtr& ptr) const std::string Creature::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const
@ -604,18 +605,15 @@ namespace MWClass
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
} }
else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
if(stats.getAiSequence().isInCombat()) // Tribunal and some mod companions oddly enough must use open action as fallback
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
if(stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
} }
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const
@ -736,7 +734,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text; std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) if (MWBase::Environment::get().getWindowManager()->getFullHelp())

@ -44,11 +44,10 @@ namespace MWClass
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -39,6 +39,11 @@ namespace MWClass
return ""; return "";
} }
bool CreatureLevList::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
return false;
}
void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const
{ {
ensureCustomData(ptr); ensureCustomData(ptr);

@ -12,8 +12,10 @@ namespace MWClass
public: public:
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: true)
static void registerSelf(); static void registerSelf();

@ -114,8 +114,9 @@ namespace MWClass
std::string Door::getName (const MWWorld::ConstPtr& ptr) const std::string Door::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>(); const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Door::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Door::activate (const MWWorld::Ptr& ptr,
@ -181,7 +182,7 @@ namespace MWClass
packet to do it instead packet to do it instead
*/ */
//if(isLocked) //if(isLocked)
// unlock(ptr); //Call the function here. because that makes sense. // ptr.getCellRef().unlock(); //Call the function here. because that makes sense.
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */
@ -304,20 +305,6 @@ namespace MWClass
} }
} }
void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const
{
if(lockLevel != 0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive
else
ptr.getCellRef().setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level
}
void Door::unlock (const MWWorld::Ptr& ptr) const
{
int lockLevel = ptr.getCellRef().getLockLevel();
ptr.getCellRef().setLockLevel(-abs(lockLevel)); //Makes lockLevel negative
}
bool Door::canLock(const MWWorld::ConstPtr &ptr) const bool Door::canLock(const MWWorld::ConstPtr &ptr) const
{ {
return true; return true;
@ -345,19 +332,12 @@ namespace MWClass
registerClass (typeid (ESM::Door).name(), instance); registerClass (typeid (ESM::Door).name(), instance);
} }
bool Door::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>(); const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text; std::string text;

@ -25,28 +25,18 @@ namespace MWClass
virtual bool useAnim() const; virtual bool useAnim() const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
static std::string getDestination (const MWWorld::LiveCellRef<ESM::Door>& door); static std::string getDestination (const MWWorld::LiveCellRef<ESM::Door>& door);
///< @return destination cell name or token ///< @return destination cell name or token
virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const;
///< Lock object
virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object
virtual bool canLock(const MWWorld::ConstPtr &ptr) const; virtual bool canLock(const MWWorld::ConstPtr &ptr) const;
virtual bool allowTelekinesis(const MWWorld::ConstPtr &ptr) const; virtual bool allowTelekinesis(const MWWorld::ConstPtr &ptr) const;

@ -80,8 +80,9 @@ namespace MWClass
std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>(); const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
@ -138,19 +139,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Ingredient::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>(); const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -10,6 +10,11 @@ namespace MWClass
return ""; return "";
} }
bool ItemLevList::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
return false;
}
void ItemLevList::registerSelf() void ItemLevList::registerSelf()
{ {
std::shared_ptr<Class> instance (new ItemLevList); std::shared_ptr<Class> instance (new ItemLevList);

@ -10,8 +10,10 @@ namespace MWClass
public: public:
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: true)
static void registerSelf(); static void registerSelf();
}; };

@ -70,9 +70,10 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
if (ref->mBase->mModel.empty()) if (ref->mBase->mModel.empty())
return ""; return std::string();
return ref->mBase->mName; const std::string& name = ref->mBase->mName;
return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Light::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Light::activate (const MWWorld::Ptr& ptr,
@ -141,9 +142,7 @@ namespace MWClass
bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); return showsInInventory(ptr);
return (ref->mBase->mName != "");
} }
MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Light::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
@ -151,7 +150,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -19,11 +19,10 @@ namespace MWClass
virtual bool useAnim() const; virtual bool useAnim() const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -47,8 +47,9 @@ namespace MWClass
std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>(); const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
@ -104,19 +105,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Lockpick::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>(); const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -89,8 +89,9 @@ namespace MWClass
std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>(); const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
@ -167,13 +168,6 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Miscellaneous::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>(); const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();
@ -192,14 +186,16 @@ namespace MWClass
else // gold displays its count also if it's 1. else // gold displays its count also if it's 1.
countString = " (" + std::to_string(count) + ")"; countString = " (" + std::to_string(count) + ")";
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + countString; info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + countString;
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
if (ref->mRef.getSoul() != "") if (ref->mRef.getSoul() != "")
{ {
const ESM::Creature *creature = store.get<ESM::Creature>().search(ref->mRef.getSoul()); const ESM::Creature *creature = store.get<ESM::Creature>().search(ref->mRef.getSoul());
if (creature) if (creature && !creature->mName.empty())
info.caption += " (" + creature->mName + ")"; info.caption += " (" + creature->mName + ")";
else if (creature)
info.caption += " (" + creature->mId + ")";
} }
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -546,7 +546,9 @@ namespace MWClass
} }
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->mName; const std::string& name = ref->mBase->mName;
return !name.empty() ? name : ref->mBase->mId;
} }
MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const
@ -1069,22 +1071,22 @@ namespace MWClass
// otherwise wait until death animation // otherwise wait until death animation
if(stats.isDeathAnimationFinished()) if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
} }
else if (!stats.getAiSequence().isInCombat())
{
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
if(stats.getAiSequence().isInCombat()) // Can't talk to werewolves
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("")); if (!getNpcStats(ptr).isWerewolf())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown()) }
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
// Can't talk to werewolfs // Tribunal and some mod companions oddly enough must use open action as fallback
if(getNpcStats(ptr).isWerewolf()) if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction("")); return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
} }
MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr)
@ -1256,7 +1258,7 @@ namespace MWClass
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
if(fullHelp && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf()) if(fullHelp && !ref->mBase->mName.empty() && ptr.getRefData().getCustomData() && ptr.getRefData().getCustomData()->asNpcCustomData().mNpcStats.isWerewolf())
{ {
info.caption += " ("; info.caption += " (";
info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName); info.caption += MyGUI::TextIterator::toTagsString(ref->mBase->mName);

@ -48,8 +48,7 @@ namespace MWClass
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
///< Return creature stats ///< Return creature stats
@ -61,7 +60,7 @@ namespace MWClass
///< Return container store ///< Return container store
virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const; virtual bool hasToolTip(const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -82,8 +82,9 @@ namespace MWClass
std::string Potion::getName (const MWWorld::ConstPtr& ptr) const std::string Potion::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>(); const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
@ -131,19 +132,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Potion::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>(); const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -47,8 +47,9 @@ namespace MWClass
std::string Probe::getName (const MWWorld::ConstPtr& ptr) const std::string Probe::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>(); const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
@ -104,19 +105,12 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Probe::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>(); const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -77,8 +77,9 @@ namespace MWClass
std::string Repair::getName (const MWWorld::ConstPtr& ptr) const std::string Repair::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>(); const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
@ -126,13 +127,6 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Repair::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
return (ref->mBase->mName != "");
}
bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const
{ {
return true; return true;
@ -150,7 +144,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>(); const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
std::string text; std::string text;

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -45,6 +45,11 @@ namespace MWClass
return ""; return "";
} }
bool Static::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
return false;
}
void Static::registerSelf() void Static::registerSelf()
{ {
std::shared_ptr<Class> instance (new Static); std::shared_ptr<Class> instance (new Static);

@ -17,8 +17,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: true)
static void registerSelf(); static void registerSelf();

@ -87,8 +87,9 @@ namespace MWClass
std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>(); const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
const std::string& name = ref->mBase->mName;
return ref->mBase->mName; return !name.empty() ? name : ref->mBase->mId;
} }
std::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr, std::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
@ -189,20 +190,13 @@ namespace MWClass
return ref->mBase->mIcon; return ref->mBase->mIcon;
} }
bool Weapon::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
return (ref->mBase->mName != "");
}
MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{ {
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>(); const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType); const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);
MWGui::ToolTipInfo info; MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName) + MWGui::ToolTips::getCountString(count); info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)) + MWGui::ToolTips::getCountString(count);
info.icon = ref->mBase->mIcon; info.icon = ref->mBase->mIcon;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();

@ -18,16 +18,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
virtual bool hasToolTip (const MWWorld::ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false)
virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

@ -590,7 +590,7 @@ namespace MWDialogue
void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic)
{ {
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!sndMgr->sayDone(actor)) if(sndMgr->sayActive(actor))
{ {
// Actor is already saying something. // Actor is already saying something.
return; return;

@ -651,9 +651,22 @@ namespace MWGui
{ {
std::string ret; std::string ret;
ret += getMiscString(cellref.getOwner(), "Owner"); ret += getMiscString(cellref.getOwner(), "Owner");
ret += getMiscString(cellref.getFaction(), "Faction"); const std::string factionId = cellref.getFaction();
if (cellref.getFactionRank() > 0) ret += getMiscString(factionId, "Faction");
ret += getValueString(cellref.getFactionRank(), "Rank"); if (!factionId.empty() && cellref.getFactionRank() >= 0)
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Faction *fact = store.get<ESM::Faction>().search(factionId);
if (fact != nullptr)
{
int rank = cellref.getFactionRank();
const std::string rankName = fact->mRanks[rank];
if (rankName.empty())
ret += getValueString(cellref.getFactionRank(), "Rank");
else
ret += getMiscString(rankName, "Rank");
}
}
std::vector<std::pair<std::string, int> > itemOwners = std::vector<std::pair<std::string, int> > itemOwners =
MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId()); MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId());

@ -187,6 +187,7 @@ namespace MWGui
, mHudEnabled(true) , mHudEnabled(true)
, mCursorVisible(true) , mCursorVisible(true)
, mCursorActive(false) , mCursorActive(false)
, mPlayerBounty(-1)
, mPlayerName() , mPlayerName()
, mPlayerRaceId() , mPlayerRaceId()
, mPlayerAttributes() , mPlayerAttributes()
@ -1068,6 +1069,18 @@ namespace MWGui
if (!gameRunning) if (!gameRunning)
return; return;
// We should display message about crime only once per frame, even if there are several crimes.
// Otherwise we will get message spam when stealing several items via Take All button.
const MWWorld::Ptr player = MWMechanics::getPlayer();
int currentBounty = player.getClass().getNpcStats(player).getBounty();
if (currentBounty != mPlayerBounty)
{
if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty)
messageBox("#{sCrimeMessage}");
mPlayerBounty = currentBounty;
}
mDragAndDrop->onFrame(); mDragAndDrop->onFrame();
mHud->onFrame(frameDuration); mHud->onFrame(frameDuration);
@ -1902,6 +1915,8 @@ namespace MWGui
void WindowManager::clear() void WindowManager::clear()
{ {
mPlayerBounty = -1;
for (WindowBase* window : mWindows) for (WindowBase* window : mWindows)
window->clear(); window->clear();

@ -559,6 +559,8 @@ namespace MWGui
bool mCursorVisible; bool mCursorVisible;
bool mCursorActive; bool mCursorActive;
int mPlayerBounty;
void setCursorVisible(bool visible); void setCursorVisible(bool visible);
/// \todo get rid of this stuff. Move it to the respective UI element classes, if needed. /// \todo get rid of this stuff. Move it to the respective UI element classes, if needed.

@ -403,7 +403,7 @@ namespace MWMechanics
void Actors::playIdleDialogue(const MWWorld::Ptr& actor) void Actors::playIdleDialogue(const MWWorld::Ptr& actor)
{ {
if (!actor.getClass().isActor() || actor == getPlayer() || !MWBase::Environment::get().getSoundManager()->sayDone(actor)) if (!actor.getClass().isActor() || actor == getPlayer() || MWBase::Environment::get().getSoundManager()->sayActive(actor))
return; return;
const CreatureStats &stats = actor.getClass().getCreatureStats(actor); const CreatureStats &stats = actor.getClass().getCreatureStats(actor);

@ -1021,7 +1021,7 @@ namespace MWMechanics
return true; return true;
} }
if (!target.getClass().canBeActivated(target)) if (!target.getClass().hasToolTip(target))
return true; return true;
// TODO: implement a better check to check if target is owned bed // TODO: implement a better check to check if target is owned bed
@ -1067,6 +1067,10 @@ namespace MWMechanics
if (!cellref.getOwner().empty()) if (!cellref.getOwner().empty())
victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true, false); victim = MWBase::Environment::get().getWorld()->searchPtr(cellref.getOwner(), true, false);
// A special case for evidence chest - we should not allow to take items even if it is technically permitted
if (Misc::StringUtils::ciEqual(cellref.getRefId(), "stolen_goods"))
return false;
return (!isOwned && !isFactionOwned); return (!isOwned && !isFactionOwned);
} }
@ -1087,7 +1091,7 @@ namespace MWMechanics
if (isAllowedToUse(ptr, bed, victim)) if (isAllowedToUse(ptr, bed, victim))
return false; return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed)) if(commitCrime(ptr, victim, OT_SleepingInOwnedBed, bed.getCellRef().getFaction()))
{ {
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}");
return true; return true;
@ -1101,7 +1105,7 @@ namespace MWMechanics
MWWorld::Ptr victim; MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item, victim)) if (isAllowedToUse(ptr, item, victim))
return; return;
commitCrime(ptr, victim, OT_Trespassing); commitCrime(ptr, victim, OT_Trespassing, item.getCellRef().getFaction());
} }
std::vector<std::pair<std::string, int> > MechanicsManager::getStolenItemOwners(const std::string& itemid) std::vector<std::pair<std::string, int> > MechanicsManager::getStolenItemOwners(const std::string& itemid)
@ -1182,7 +1186,7 @@ namespace MWMechanics
// move items from player to owner and report about theft // move items from player to owner and report about theft
victim.getClass().getContainerStore(victim).add(item, toRemove, victim); victim.getClass().getContainerStore(victim).add(item, toRemove, victim);
store.remove(item, toRemove, player); store.remove(item, toRemove, player);
commitCrime(player, victim, OT_Theft, item.getClass().getValue(item) * toRemove); commitCrime(player, victim, OT_Theft, item.getCellRef().getFaction(), item.getClass().getValue(item) * toRemove);
} }
void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer) void MechanicsManager::confiscateStolenItems(const MWWorld::Ptr &player, const MWWorld::Ptr &targetContainer)
@ -1212,7 +1216,7 @@ namespace MWMechanics
store.remove(*it, toMove, player); store.remove(*it, toMove, player);
} }
// TODO: unhardcode the locklevel // TODO: unhardcode the locklevel
targetContainer.getClass().lock(targetContainer,50); targetContainer.getCellRef().lock(50);
} }
void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container,
@ -1269,11 +1273,11 @@ namespace MWMechanics
mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;
} }
if (alarm) if (alarm)
commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); commitCrime(ptr, victim, OT_Theft, ownerCellRef->getFaction(), item.getClass().getValue(item) * count);
} }
bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware) bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, const std::string& factionId, int arg, bool victimAware)
{ {
// NOTE: victim may be empty // NOTE: victim may be empty
@ -1331,13 +1335,13 @@ namespace MWMechanics
} }
if (crimeSeen) if (crimeSeen)
reportCrime(player, victim, type, arg); reportCrime(player, victim, type, factionId, arg);
else if (type == OT_Assault && !victim.isEmpty()) else if (type == OT_Assault && !victim.isEmpty())
{ {
bool reported = false; bool reported = false;
if (victim.getClass().isClass(victim, "guard") if (victim.getClass().isClass(victim, "guard")
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) && !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
reported = reportCrime(player, victim, type, arg); reported = reportCrime(player, victim, type, std::string(), arg);
if (!reported) if (!reported)
startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee? startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
@ -1368,7 +1372,7 @@ namespace MWMechanics
return true; return true;
} }
bool MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) bool MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, const std::string& factionId, int arg)
{ {
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -1525,7 +1529,6 @@ namespace MWMechanics
if (reported) if (reported)
{ {
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty()
+ arg); + arg);
@ -1550,6 +1553,14 @@ namespace MWMechanics
player.getClass().getNpcStats(player).expell(factionID); player.getClass().getNpcStats(player).expell(factionID);
} }
} }
else if (!factionId.empty())
{
const std::map<std::string, int>& playerRanks = player.getClass().getNpcStats(player).getFactionRanks();
if (playerRanks.find(Misc::StringUtils::lowerCase(factionId)) != playerRanks.end())
{
player.getClass().getNpcStats(player).expell(factionId);
}
}
if (type == OT_Assault && !victim.isEmpty() if (type == OT_Assault && !victim.isEmpty()
&& !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player) && !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player)
@ -2016,7 +2027,6 @@ namespace MWMechanics
{ {
npcStats.setBounty(npcStats.getBounty()+ npcStats.setBounty(npcStats.getBounty()+
gmst.find("iWereWolfBounty")->mValue.getInteger()); gmst.find("iWereWolfBounty")->mValue.getInteger());
windowManager->messageBox("#{sCrimeMessage}");
} }
} }
} }

@ -140,7 +140,7 @@ namespace MWMechanics
* @return was the crime seen? * @return was the crime seen?
*/ */
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0, bool victimAware=false) override; OffenseType type, const std::string& factionId="", int arg=0, bool victimAware=false) override;
/// @return false if the attack was considered a "friendly hit" and forgiven /// @return false if the attack was considered a "friendly hit" and forgiven
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) override; virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) override;
@ -267,7 +267,7 @@ namespace MWMechanics
bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers);
bool reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, bool reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0); OffenseType type, const std::string& factionId, int arg=0);
}; };
} }

@ -44,7 +44,7 @@ namespace MWMechanics
{ {
if (lock.getCellRef().getLockLevel() <= 0 || if (lock.getCellRef().getLockLevel() <= 0 ||
lock.getCellRef().getLockLevel() == ESM::UnbreakableLock || lock.getCellRef().getLockLevel() == ESM::UnbreakableLock ||
!lock.getClass().canLock(lock)) //If it's unlocked or can not be unlocked back out immediately !lock.getClass().hasToolTip(lock)) //If it's unlocked or can not be unlocked back out immediately
return; return;
int lockStrength = lock.getCellRef().getLockLevel(); int lockStrength = lock.getCellRef().getLockLevel();
@ -70,7 +70,7 @@ namespace MWMechanics
Disable unilateral locking on this client and expect the server's reply to our Disable unilateral locking on this client and expect the server's reply to our
packet to do it instead packet to do it instead
*/ */
//lock.getClass().unlock(lock); //lock.getCellRef().unlock();
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */

@ -757,7 +757,7 @@ namespace MWMechanics
Disable unilateral locking on this client and expect the server's reply to our Disable unilateral locking on this client and expect the server's reply to our
packet to do it instead packet to do it instead
*/ */
//target.getClass().lock(target, static_cast<int>(magnitude)); //target.getCellRef().lock(static_cast<int>(magnitude));
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */
@ -801,7 +801,7 @@ namespace MWMechanics
Disable unilateral locking on this client and expect the server's reply to our Disable unilateral locking on this client and expect the server's reply to our
packet to do it instead packet to do it instead
*/ */
//target.getClass().unlock(target); //target.getCellRef().unlock();
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */

@ -527,9 +527,9 @@ void ObjectList::lockObjects(MWWorld::CellStore* cellStore)
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum()); ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
if (baseObject.lockLevel > 0) if (baseObject.lockLevel > 0)
ptrFound.getClass().lock(ptrFound, baseObject.lockLevel); ptrFound.getCellRef().lock(baseObject.lockLevel);
else else
ptrFound.getClass().unlock(ptrFound); ptrFound.getCellRef().unlock();
} }
} }
} }

@ -184,7 +184,7 @@ void HeadAnimationTime::update(float dt)
if (!mEnabled) if (!mEnabled)
return; return;
if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) if (!MWBase::Environment::get().getSoundManager()->sayActive(mReference))
{ {
mBlinkTimer += dt; mBlinkTimer += dt;

@ -238,7 +238,7 @@ namespace MWScript
Disable unilateral locking on this client and expect the server's reply to our Disable unilateral locking on this client and expect the server's reply to our
packet to do it instead packet to do it instead
*/ */
//ptr.getClass().lock (ptr, lockLevel); //ptr.getCellRef().lock (lockLevel);
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */
@ -285,7 +285,7 @@ namespace MWScript
Disable unilateral unlocking on this client and expect the server's reply to our Disable unilateral unlocking on this client and expect the server's reply to our
packet to do it instead packet to do it instead
*/ */
//ptr.getClass().unlock (ptr); //ptr.getCellRef().unlock ();
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */

@ -566,6 +566,27 @@ namespace MWSound
return true; return true;
} }
bool SoundManager::sayActive(const MWWorld::ConstPtr &ptr) const
{
SaySoundMap::const_iterator snditer = mSaySoundsQueue.find(ptr);
if(snditer != mSaySoundsQueue.end())
{
if(mOutput->isStreamPlaying(snditer->second))
return true;
return false;
}
snditer = mActiveSaySounds.find(ptr);
if(snditer != mActiveSaySounds.end())
{
if(mOutput->isStreamPlaying(snditer->second))
return true;
return false;
}
return false;
}
void SoundManager::stopSay(const MWWorld::ConstPtr &ptr) void SoundManager::stopSay(const MWWorld::ConstPtr &ptr)
{ {
SaySoundMap::iterator snditer = mSaySoundsQueue.find(ptr); SaySoundMap::iterator snditer = mSaySoundsQueue.find(ptr);

@ -179,9 +179,12 @@ namespace MWSound
///< Say some text, without an actor ref ///< Say some text, without an actor ref
/// \param filename name of a sound file in "Sound/" in the data directory. /// \param filename name of a sound file in "Sound/" in the data directory.
virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const; virtual bool sayActive(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const;
///< Is actor not speaking? ///< Is actor not speaking?
virtual bool sayDone(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()) const;
///< For scripting backward compatibility
virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr()); virtual void stopSay(const MWWorld::ConstPtr &reference=MWWorld::ConstPtr());
///< Stop an actor speaking ///< Stop an actor speaking

@ -305,6 +305,19 @@ namespace MWWorld
} }
} }
void CellRef::lock(int lockLevel)
{
if(lockLevel != 0)
setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive
else
setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level
}
void CellRef::unlock()
{
setLockLevel(-abs(mCellRef.mLockLevel)); //Makes lockLevel negative
}
std::string CellRef::getKey() const std::string CellRef::getKey() const
{ {
return mCellRef.mKey; return mCellRef.mKey;

@ -161,6 +161,8 @@ namespace MWWorld
// For an unlocked door, it is set to -(previous locklevel) // For an unlocked door, it is set to -(previous locklevel)
int getLockLevel() const; int getLockLevel() const;
void setLockLevel(int lockLevel); void setLockLevel(int lockLevel);
void lock(int lockLevel);
void unlock();
// Key and trap ID names, if any // Key and trap ID names, if any
std::string getKey() const; std::string getKey() const;
std::string getTrap() const; std::string getTrap() const;

@ -109,11 +109,6 @@ namespace MWWorld
throw std::runtime_error("class cannot block"); throw std::runtime_error("class cannot block");
} }
bool Class::canBeActivated(const Ptr& ptr) const
{
return !getName(ptr).empty();
}
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, const osg::Vec3f& hitPosition, bool successful) const
{ {
throw std::runtime_error("class cannot be hit"); throw std::runtime_error("class cannot be hit");
@ -157,16 +152,6 @@ namespace MWWorld
return false; return false;
} }
void Class::lock (const Ptr& ptr, int lockLevel) const
{
throw std::runtime_error ("class does not support locking");
}
void Class::unlock (const Ptr& ptr) const
{
throw std::runtime_error ("class does not support unlocking");
}
bool Class::canLock(const ConstPtr &ptr) const bool Class::canLock(const ConstPtr &ptr) const
{ {
return false; return false;
@ -311,7 +296,7 @@ namespace MWWorld
bool Class::hasToolTip (const ConstPtr& ptr) const bool Class::hasToolTip (const ConstPtr& ptr) const
{ {
return false; return true;
} }
std::string Class::getEnchantment (const ConstPtr& ptr) const std::string Class::getEnchantment (const ConstPtr& ptr) const

@ -83,8 +83,7 @@ namespace MWWorld
///< Add reference into a cell for rendering (default implementation: don't render anything). ///< Add reference into a cell for rendering (default implementation: don't render anything).
virtual std::string getName (const ConstPtr& ptr) const = 0; virtual std::string getName (const ConstPtr& ptr) const = 0;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name or ID; can return an empty string.
/// can return an empty string.
virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const; virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
///< Adjust position to stand on ground. Must be called post model load ///< Adjust position to stand on ground. Must be called post model load
@ -95,7 +94,7 @@ namespace MWWorld
/// (default implementation: throw an exception) /// (default implementation: throw an exception)
virtual bool hasToolTip (const ConstPtr& ptr) const; virtual bool hasToolTip (const ConstPtr& ptr) const;
///< @return true if this object has a tooltip when focused (default implementation: false) ///< @return true if this object has a tooltip when focused (default implementation: true)
virtual MWGui::ToolTipInfo getToolTipInfo (const ConstPtr& ptr, int count) const; virtual MWGui::ToolTipInfo getToolTipInfo (const ConstPtr& ptr, int count) const;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.
@ -140,10 +139,6 @@ namespace MWWorld
///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield ///< Play the appropriate sound for a blocked attack, depending on the currently equipped shield
/// (default implementation: throw an exception) /// (default implementation: throw an exception)
virtual bool canBeActivated(const Ptr& ptr) const;
///< \return Can the player activate this object?
/// (default implementation: true if object's user-readable name is not empty, false otherwise)
virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const; virtual std::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
///< Generate action for activation (default implementation: return a null action). ///< Generate action for activation (default implementation: return a null action).
@ -174,12 +169,6 @@ namespace MWWorld
virtual bool hasInventoryStore (const Ptr& ptr) const; virtual bool hasInventoryStore (const Ptr& ptr) const;
///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false) ///< Does this object have an inventory store, i.e. equipment slots? (default implementation: false)
virtual void lock (const Ptr& ptr, int lockLevel) const;
///< Lock object (default implementation: throw an exception)
virtual void unlock (const Ptr& ptr) const;
///< Unlock object (default implementation: throw an exception)
virtual bool canLock (const ConstPtr& ptr) const; virtual bool canLock (const ConstPtr& ptr) const;
virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const;

@ -248,7 +248,7 @@ namespace MWWorld
if (toActivate.isEmpty()) if (toActivate.isEmpty())
return; return;
if (!toActivate.getClass().canBeActivated(toActivate)) if (!toActivate.getClass().hasToolTip(toActivate))
return; return;
/* /*

@ -3414,7 +3414,7 @@ namespace MWWorld
target = getFacedObject(); target = getFacedObject();
// if the faced object can not be activated, do not use it // if the faced object can not be activated, do not use it
if (!target.isEmpty() && !target.getClass().canBeActivated(target)) if (!target.isEmpty() && !target.getClass().hasToolTip(target))
target = nullptr; target = nullptr;
if (target.isEmpty()) if (target.isEmpty())
@ -3473,7 +3473,7 @@ namespace MWWorld
{ {
target = result2.mHitObject; target = result2.mHitObject;
hitPosition = result2.mHitPointWorld; hitPosition = result2.mHitPointWorld;
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().hasToolTip(target))
target = nullptr; target = nullptr;
} }
} }
@ -4082,7 +4082,7 @@ namespace MWWorld
if (fromProjectile && effectInfo.mArea <= 0) if (fromProjectile && effectInfo.mArea <= 0)
continue; // Don't play explosion for projectiles with 0-area effects continue; // Don't play explosion for projectiles with 0-area effects
if (!fromProjectile && effectInfo.mRange == ESM::RT_Touch && (!ignore.isEmpty()) && (!ignore.getClass().isActor() && !ignore.getClass().canBeActivated(ignore))) if (!fromProjectile && effectInfo.mRange == ESM::RT_Touch && !ignore.isEmpty() && !ignore.getClass().isActor() && !ignore.getClass().hasToolTip(ignore))
continue; // Don't play explosion for touch spells on non-activatable objects except when spell is from the projectile enchantment continue; // Don't play explosion for touch spells on non-activatable objects except when spell is from the projectile enchantment
// Spawn the explosion orb effect // Spawn the explosion orb effect

@ -208,18 +208,13 @@ namespace
void init(Nif::Extra& value) void init(Nif::Extra& value)
{ {
value.extra = Nif::ExtraPtr(nullptr); value.next = Nif::ExtraPtr(nullptr);
}
void init(Nif::Controlled& value)
{
init(static_cast<Nif::Extra&>(value));
value.controller = Nif::ControllerPtr(nullptr);
} }
void init(Nif::Named& value) void init(Nif::Named& value)
{ {
init(static_cast<Nif::Controlled&>(value)); value.extra = Nif::ExtraPtr(nullptr);
value.controller = Nif::ControllerPtr(nullptr);
} }
void init(Nif::Node& value) void init(Nif::Node& value)
@ -254,7 +249,7 @@ namespace
value.phase = 0; value.phase = 0;
value.timeStart = 0; value.timeStart = 0;
value.timeStop = 0; value.timeStop = 0;
value.target = Nif::ControlledPtr(nullptr); value.target = Nif::NamedPtr(nullptr);
} }
void copy(const btTransform& src, Nif::Transformation& dst) void copy(const btTransform& src, Nif::Transformation& dst)
@ -884,7 +879,7 @@ namespace
TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape) TEST_F(TestBulletNifLoader, for_tri_shape_child_node_with_not_first_extra_data_string_starting_with_nc_should_return_shape_with_null_collision_shape)
{ {
mNiStringExtraData.extra = Nif::ExtraPtr(&mNiStringExtraData2); mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NC___"; mNiStringExtraData2.string = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData; mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData); mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);

@ -10,17 +10,19 @@
namespace Nif namespace Nif
{ {
/** A record that can have extra data. The extra data objects // An extra data record. All the extra data connected to an object form a linked list.
themselves descend from the Extra class, and all the extra data
connected to an object form a linked list
*/
class Extra : public Record class Extra : public Record
{ {
public: public:
ExtraPtr extra; ExtraPtr next; // Next extra data record in the list
void read(NIFStream *nif)
{
next.read(nif);
nif->getUInt(); // Size of the record
}
void read(NIFStream *nif) { extra.read(nif); } void post(NIFFile *nif) { next.post(nif); }
void post(NIFFile *nif) { extra.post(nif); }
}; };
class Controller : public Record class Controller : public Record
@ -30,43 +32,33 @@ public:
int flags; int flags;
float frequency, phase; float frequency, phase;
float timeStart, timeStop; float timeStart, timeStop;
ControlledPtr target; NamedPtr target;
void read(NIFStream *nif); void read(NIFStream *nif);
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
/// Anything that has a controller /// Has name, extra-data and controller
class Controlled : public Extra class Named : public Record
{ {
public: public:
std::string name;
ExtraPtr extra;
ControllerPtr controller; ControllerPtr controller;
void read(NIFStream *nif) void read(NIFStream *nif)
{ {
Extra::read(nif); name = nif->getString();
extra.read(nif);
controller.read(nif); controller.read(nif);
} }
void post(NIFFile *nif) void post(NIFFile *nif)
{ {
Extra::post(nif); extra.post(nif);
controller.post(nif); controller.post(nif);
} }
}; };
/// Has name, extra-data and controller
class Named : public Controlled
{
public:
std::string name;
void read(NIFStream *nif)
{
name = nif->getString();
Controlled::read(nif);
}
};
typedef Named NiSequenceStreamHelper; typedef Named NiSequenceStreamHelper;
} // Namespace } // Namespace

@ -31,28 +31,40 @@ namespace Nif
data.post(nif); data.post(nif);
} }
void NiParticleModifier::read(NIFStream *nif)
{
next.read(nif);
controller.read(nif);
}
void NiParticleModifier::post(NIFFile *nif)
{
next.post(nif);
controller.post(nif);
}
void NiParticleGrowFade::read(NIFStream *nif) void NiParticleGrowFade::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
growTime = nif->getFloat(); growTime = nif->getFloat();
fadeTime = nif->getFloat(); fadeTime = nif->getFloat();
} }
void NiParticleColorModifier::read(NIFStream *nif) void NiParticleColorModifier::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
data.read(nif); data.read(nif);
} }
void NiParticleColorModifier::post(NIFFile *nif) void NiParticleColorModifier::post(NIFFile *nif)
{ {
Controlled::post(nif); NiParticleModifier::post(nif);
data.post(nif); data.post(nif);
} }
void NiGravity::read(NIFStream *nif) void NiGravity::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
mDecay = nif->getFloat(); mDecay = nif->getFloat();
mForce = nif->getFloat(); mForce = nif->getFloat();
@ -61,11 +73,17 @@ namespace Nif
mDirection = nif->getVector3(); mDirection = nif->getVector3();
} }
void NiPlanarCollider::read(NIFStream *nif) void NiParticleCollider::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
mBounceFactor = nif->getFloat(); mBounceFactor = nif->getFloat();
}
void NiPlanarCollider::read(NIFStream *nif)
{
NiParticleCollider::read(nif);
/*unknown*/nif->getFloat(); /*unknown*/nif->getFloat();
for (int i=0;i<10;++i) for (int i=0;i<10;++i)
@ -77,7 +95,7 @@ namespace Nif
void NiParticleRotation::read(NIFStream *nif) void NiParticleRotation::read(NIFStream *nif)
{ {
Controlled::read(nif); NiParticleModifier::read(nif);
/* /*
byte (0 or 1) byte (0 or 1)
@ -89,9 +107,8 @@ namespace Nif
void NiSphericalCollider::read(NIFStream* nif) void NiSphericalCollider::read(NIFStream* nif)
{ {
Controlled::read(nif); NiParticleCollider::read(nif);
mBounceFactor = nif->getFloat();
mRadius = nif->getFloat(); mRadius = nif->getFloat();
mCenter = nif->getVector3(); mCenter = nif->getVector3();
} }

@ -66,7 +66,16 @@ public:
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
class NiParticleGrowFade : public Controlled struct NiParticleModifier : public Record
{
NiParticleModifierPtr next;
ControllerPtr controller;
void read(NIFStream *nif);
void post(NIFFile *nif);
};
class NiParticleGrowFade : public NiParticleModifier
{ {
public: public:
float growTime; float growTime;
@ -75,7 +84,7 @@ public:
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
class NiParticleColorModifier : public Controlled class NiParticleColorModifier : public NiParticleModifier
{ {
public: public:
NiColorDataPtr data; NiColorDataPtr data;
@ -84,7 +93,7 @@ public:
void post(NIFFile *nif); void post(NIFFile *nif);
}; };
class NiGravity : public Controlled class NiGravity : public NiParticleModifier
{ {
public: public:
float mForce; float mForce;
@ -99,29 +108,32 @@ public:
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
struct NiParticleCollider : public NiParticleModifier
{
float mBounceFactor;
void read(NIFStream *nif);
};
// NiPinaColada // NiPinaColada
class NiPlanarCollider : public Controlled class NiPlanarCollider : public NiParticleCollider
{ {
public: public:
void read(NIFStream *nif); void read(NIFStream *nif);
float mBounceFactor;
osg::Vec3f mPlaneNormal; osg::Vec3f mPlaneNormal;
float mPlaneDistance; float mPlaneDistance;
}; };
class NiSphericalCollider : public Controlled class NiSphericalCollider : public NiParticleCollider
{ {
public: public:
float mBounceFactor;
float mRadius; float mRadius;
osg::Vec3f mCenter; osg::Vec3f mCenter;
void read(NIFStream *nif); void read(NIFStream *nif);
}; };
class NiParticleRotation : public Controlled class NiParticleRotation : public NiParticleModifier
{ {
public: public:
void read(NIFStream *nif); void read(NIFStream *nif);

@ -72,8 +72,8 @@ public:
int activeCount; int activeCount;
std::vector<Particle> particles; std::vector<Particle> particles;
ExtraPtr affectors; NiParticleModifierPtr affectors;
ExtraPtr colliders; NiParticleModifierPtr colliders;
void read(NIFStream *nif); void read(NIFStream *nif);
void post(NIFFile *nif); void post(NIFFile *nif);

@ -6,8 +6,6 @@ namespace Nif
void NiStringExtraData::read(NIFStream *nif) void NiStringExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
nif->getInt(); // size of string + 4. Really useful...
string = nif->getString(); string = nif->getString();
} }
@ -15,8 +13,6 @@ void NiTextKeyExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
nif->getInt(); // 0
int keynum = nif->getInt(); int keynum = nif->getInt();
list.resize(keynum); list.resize(keynum);
for(int i=0; i<keynum; i++) for(int i=0; i<keynum; i++)
@ -30,12 +26,7 @@ void NiVertWeightsExtraData::read(NIFStream *nif)
{ {
Extra::read(nif); Extra::read(nif);
// We should have s*4+2 == i, for some reason. Might simply be the nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess
// size of the rest of the record, unhelpful as that may be.
/*int i =*/ nif->getInt();
int s = nif->getUShort();
nif->skip(s * sizeof(float)); // vertex weights I guess
} }

@ -127,7 +127,7 @@ class NiUVData;
class NiPosData; class NiPosData;
class NiVisData; class NiVisData;
class Controller; class Controller;
class Controlled; class Named;
class NiSkinData; class NiSkinData;
class NiFloatData; class NiFloatData;
struct NiMorphData; struct NiMorphData;
@ -141,6 +141,7 @@ class NiSourceTexture;
class NiRotatingParticlesData; class NiRotatingParticlesData;
class NiAutoNormalParticlesData; class NiAutoNormalParticlesData;
class NiPalette; class NiPalette;
struct NiParticleModifier;
typedef RecordPtrT<Node> NodePtr; typedef RecordPtrT<Node> NodePtr;
typedef RecordPtrT<Extra> ExtraPtr; typedef RecordPtrT<Extra> ExtraPtr;
@ -148,7 +149,7 @@ typedef RecordPtrT<NiUVData> NiUVDataPtr;
typedef RecordPtrT<NiPosData> NiPosDataPtr; typedef RecordPtrT<NiPosData> NiPosDataPtr;
typedef RecordPtrT<NiVisData> NiVisDataPtr; typedef RecordPtrT<NiVisData> NiVisDataPtr;
typedef RecordPtrT<Controller> ControllerPtr; typedef RecordPtrT<Controller> ControllerPtr;
typedef RecordPtrT<Controlled> ControlledPtr; typedef RecordPtrT<Named> NamedPtr;
typedef RecordPtrT<NiSkinData> NiSkinDataPtr; typedef RecordPtrT<NiSkinData> NiSkinDataPtr;
typedef RecordPtrT<NiMorphData> NiMorphDataPtr; typedef RecordPtrT<NiMorphData> NiMorphDataPtr;
typedef RecordPtrT<NiPixelData> NiPixelDataPtr; typedef RecordPtrT<NiPixelData> NiPixelDataPtr;
@ -162,6 +163,7 @@ typedef RecordPtrT<NiSourceTexture> NiSourceTexturePtr;
typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr; typedef RecordPtrT<NiRotatingParticlesData> NiRotatingParticlesDataPtr;
typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr; typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
typedef RecordPtrT<NiPalette> NiPalettePtr; typedef RecordPtrT<NiPalette> NiPalettePtr;
typedef RecordPtrT<NiParticleModifier> NiParticleModifierPtr;
typedef RecordListT<Node> NodeList; typedef RecordListT<Node> NodeList;
typedef RecordListT<Property> PropertyList; typedef RecordListT<Property> PropertyList;

@ -260,18 +260,13 @@ void BulletNifLoader::handleNode(const std::string& fileName, const Nif::Node *n
Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName << ". Treating it as a common NiTriShape."; Log(Debug::Info) << "RootCollisionNode is not attached to the root node in " << fileName << ". Treating it as a common NiTriShape.";
// Check for extra data // Check for extra data
Nif::Extra const *e = node; for (Nif::ExtraPtr e = node->extra; !e.empty(); e = e->next)
while (!e->extra.empty())
{ {
// Get the next extra data in the list
e = e->extra.getPtr();
assert(e != nullptr);
if (e->recType == Nif::RC_NiStringExtraData) if (e->recType == Nif::RC_NiStringExtraData)
{ {
// String markers may contain important information // String markers may contain important information
// affecting the entire subtree of this node // affecting the entire subtree of this node
Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e; Nif::NiStringExtraData *sd = (Nif::NiStringExtraData*)e.getPtr();
if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0) if (Misc::StringUtils::ciCompareLen(sd->string, "NC", 2) == 0)
{ {

@ -385,8 +385,9 @@ void AlphaController::apply(osg::StateSet *stateset, osg::NodeVisitor *nv)
} }
} }
MaterialColorController::MaterialColorController(const Nif::NiPosData *data) MaterialColorController::MaterialColorController(const Nif::NiPosData *data, TargetColor color)
: mData(data->mKeyList, osg::Vec3f(1,1,1)) : mData(data->mKeyList, osg::Vec3f(1,1,1))
, mTargetColor(color)
{ {
} }
@ -397,6 +398,7 @@ MaterialColorController::MaterialColorController()
MaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop) MaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop)
: StateSetUpdater(copy, copyop), Controller(copy) : StateSetUpdater(copy, copyop), Controller(copy)
, mData(copy.mData) , mData(copy.mData)
, mTargetColor(copy.mTargetColor)
{ {
} }
@ -413,9 +415,37 @@ void MaterialColorController::apply(osg::StateSet *stateset, osg::NodeVisitor *n
{ {
osg::Vec3f value = mData.interpKey(getInputValue(nv)); osg::Vec3f value = mData.interpKey(getInputValue(nv));
osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL)); osg::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK); switch (mTargetColor)
diffuse.set(value.x(), value.y(), value.z(), diffuse.a()); {
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse); case Diffuse:
{
osg::Vec4f diffuse = mat->getDiffuse(osg::Material::FRONT_AND_BACK);
diffuse.set(value.x(), value.y(), value.z(), diffuse.a());
mat->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
break;
}
case Specular:
{
osg::Vec4f specular = mat->getSpecular(osg::Material::FRONT_AND_BACK);
specular.set(value.x(), value.y(), value.z(), specular.a());
mat->setSpecular(osg::Material::FRONT_AND_BACK, specular);
break;
}
case Emissive:
{
osg::Vec4f emissive = mat->getEmission(osg::Material::FRONT_AND_BACK);
emissive.set(value.x(), value.y(), value.z(), emissive.a());
mat->setEmission(osg::Material::FRONT_AND_BACK, emissive);
break;
}
case Ambient:
default:
{
osg::Vec4f ambient = mat->getAmbient(osg::Material::FRONT_AND_BACK);
ambient.set(value.x(), value.y(), value.z(), ambient.a());
mat->setAmbient(osg::Material::FRONT_AND_BACK, ambient);
}
}
} }
} }

@ -281,11 +281,15 @@ namespace NifOsg
class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller
{ {
private:
Vec3Interpolator mData;
public: public:
MaterialColorController(const Nif::NiPosData *data); enum TargetColor
{
Ambient = 0,
Diffuse = 1,
Specular = 2,
Emissive = 3
};
MaterialColorController(const Nif::NiPosData *data, TargetColor color);
MaterialColorController(); MaterialColorController();
MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop);
@ -294,6 +298,10 @@ namespace NifOsg
virtual void setDefaults(osg::StateSet* stateset); virtual void setDefaults(osg::StateSet* stateset);
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv);
private:
Vec3Interpolator mData;
TargetColor mTargetColor;
}; };
class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller

@ -222,9 +222,9 @@ namespace NifOsg
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys); extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), target.mTextKeys);
extra = extra->extra; extra = extra->next;
Nif::ControllerPtr ctrl = seq->controller; Nif::ControllerPtr ctrl = seq->controller;
for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) for(;!extra.empty() && !ctrl.empty();(extra=extra->next),(ctrl=ctrl->next))
{ {
if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{ {
@ -524,7 +524,7 @@ namespace NifOsg
node->getOrCreateUserDataContainer()->addUserObject( node->getOrCreateUserDataContainer()->addUserObject(
new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation));
for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next)
{ {
if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
{ {
@ -750,7 +750,13 @@ namespace NifOsg
else if (ctrl->recType == Nif::RC_NiMaterialColorController) else if (ctrl->recType == Nif::RC_NiMaterialColorController)
{ {
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr()); const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
osg::ref_ptr<MaterialColorController> osgctrl(new MaterialColorController(matctrl->data.getPtr())); // Two bits that correspond to the controlled material color.
// 00: Ambient
// 01: Diffuse
// 10: Specular
// 11: Emissive
MaterialColorController::TargetColor targetColor = static_cast<MaterialColorController::TargetColor>((matctrl->flags >> 4) & 3);
osg::ref_ptr<MaterialColorController> osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor));
setupController(matctrl, osgctrl, animflags); setupController(matctrl, osgctrl, animflags);
composite->addController(osgctrl); composite->addController(osgctrl);
} }
@ -802,13 +808,13 @@ namespace NifOsg
} }
} }
void handleParticlePrograms(Nif::ExtraPtr affectors, Nif::ExtraPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf)
{ {
osgParticle::ModularProgram* program = new osgParticle::ModularProgram; osgParticle::ModularProgram* program = new osgParticle::ModularProgram;
attachTo->addChild(program); attachTo->addChild(program);
program->setParticleSystem(partsys); program->setParticleSystem(partsys);
program->setReferenceFrame(rf); program->setReferenceFrame(rf);
for (; !affectors.empty(); affectors = affectors->extra) for (; !affectors.empty(); affectors = affectors->next)
{ {
if (affectors->recType == Nif::RC_NiParticleGrowFade) if (affectors->recType == Nif::RC_NiParticleGrowFade)
{ {
@ -833,7 +839,7 @@ namespace NifOsg
else else
Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename;
} }
for (; !colliders.empty(); colliders = colliders->extra) for (; !colliders.empty(); colliders = colliders->next)
{ {
if (colliders->recType == Nif::RC_NiPlanarCollider) if (colliders->recType == Nif::RC_NiPlanarCollider)
{ {

@ -48,11 +48,6 @@ const vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745);
const float RAIN_RIPPLE_GAPS = 5.0; const float RAIN_RIPPLE_GAPS = 5.0;
const float RAIN_RIPPLE_RADIUS = 0.1; const float RAIN_RIPPLE_RADIUS = 0.1;
int modulo(int v1, int v2)
{
return v1 - v2 * int(floor(float(v1) / float(v2)));
}
vec2 randOffset(vec2 c) vec2 randOffset(vec2 c)
{ {
return fract(vec2( return fract(vec2(
@ -170,8 +165,6 @@ void main(void)
vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
screenCoords.y = (1.0-screenCoords.y); screenCoords.y = (1.0-screenCoords.y);
vec2 nCoord = vec2(0.0);
#define waterTimer osg_SimulationTime #define waterTimer osg_SimulationTime
vec3 normal0 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.05, 0.04, waterTimer, -0.015, -0.005, vec3(0.0,0.0,0.0))).rgb - 1.0; vec3 normal0 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.05, 0.04, waterTimer, -0.015, -0.005, vec3(0.0,0.0,0.0))).rgb - 1.0;
@ -195,100 +188,66 @@ void main(void)
vec2 smallWaves = mix(vec2(SMALL_WAVES_X,SMALL_WAVES_Y),vec2(SMALL_WAVES_RAIN_X,SMALL_WAVES_RAIN_Y),rainIntensity); vec2 smallWaves = mix(vec2(SMALL_WAVES_X,SMALL_WAVES_Y),vec2(SMALL_WAVES_RAIN_X,SMALL_WAVES_RAIN_Y),rainIntensity);
float bump = mix(BUMP,BUMP_RAIN,rainIntensity); float bump = mix(BUMP,BUMP_RAIN,rainIntensity);
vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + normal2 * midWaves.x +
normal2 * midWaves.x + normal3 * midWaves.y + normal3 * midWaves.y + normal4 * smallWaves.x + normal5 * smallWaves.y + rippleAdd);
normal4 * smallWaves.x + normal5 * smallWaves.y + normal = normalize(vec3(-normal.x * bump, -normal.y * bump, normal.z));
rippleAdd);
normal = normalize(vec3(normal.x * bump, normal.y * bump, normal.z));
normal = vec3(-normal.x, -normal.y, normal.z);
// normal for sunlight scattering
vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 +
normal2 * midWaves.x * 0.2 + normal3 * midWaves.y * 0.2 +
normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 +
rippleAdd).xyz;
lNormal = normalize(vec3(lNormal.x * bump, lNormal.y * bump, lNormal.z));
lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z);
vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz); vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz);
vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz; vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz;
vec3 vVec = normalize(position.xyz - cameraPos.xyz); vec3 vVec = normalize(position.xyz - cameraPos.xyz);
float isUnderwater = (cameraPos.z > 0.0) ? 0.0 : 1.0;
// sunlight scattering
vec3 pNormal = vec3(0,0,1);
vec3 lR = reflect(lVec, lNormal);
vec3 llR = reflect(lVec, pNormal);
float sunHeight = lVec.z;
float sunFade = length(gl_LightModel.ambient.xyz); float sunFade = length(gl_LightModel.ambient.xyz);
float s = clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0);
float lightScatter = shadow * clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * s * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0);
vec3 scatterColour = mix(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0));
// fresnel // fresnel
float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air
float fresnel = fresnel_dielectric(vVec, normal, ior); float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0);
fresnel = clamp(fresnel, 0.0, 1.0);
vec2 screenCoordsOffset = normal.xy * REFL_BUMP;
#if REFRACTION #if REFRACTION
float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x); float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x);
float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-(normal.xy*REFR_BUMP)).x); float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x);
float surfaceDepth = linearizeDepth(gl_FragCoord.z); float surfaceDepth = linearizeDepth(gl_FragCoord.z);
float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum
float shore = clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1);
#else
float shore = 1.0;
#endif #endif
vec2 screenCoordsOffset = normal.xy * REFL_BUMP * shore;
// reflection // reflection
vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb; vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb;
// refraction
#if REFRACTION
vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb;
// brighten up the refraction underwater
refraction = (cameraPos.z < 0.0) ? clamp(refraction * 1.5, 0.0, 1.0) : refraction;
#endif
// specular // specular
vec3 R = reflect(vVec, normal); float specular = pow(max(dot(reflect(vVec, normal), lVec), 0.0),SPEC_HARDNESS) * shadow;
float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow;
vec3 waterColor = WATER_COLOR; vec3 waterColor = WATER_COLOR * sunFade;
waterColor = waterColor * length(gl_LightModel.ambient.xyz);
#if REFRACTION #if REFRACTION
if (cameraPos.z > 0.0) // refraction
vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb;
// brighten up the refraction underwater
if (cameraPos.z < 0.0)
refraction = clamp(refraction * 1.5, 0.0, 1.0);
else
refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0)); refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0));
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz; // sunlight scattering
// normal for sunlight scattering
vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 + normal2 * midWaves.x * 0.2 +
normal3 * midWaves.y * 0.2 + normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 + rippleAdd);
lNormal = normalize(vec3(-lNormal.x * bump, -lNormal.y * bump, lNormal.z));
float sunHeight = lVec.z;
vec3 scatterColour = mix(SCATTER_COLOUR*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0));
vec3 lR = reflect(lVec, lNormal);
float lightScatter = shadow * clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0) * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0);
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.2;
gl_FragData[0].w = 1.0;
#else #else
gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz; gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.7;
gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);
#endif #endif
// fog // fog
float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
#if REFRACTION
gl_FragData[0].xyz += vec3(rainRipple.w) * 0.2;
#else
gl_FragData[0].xyz += vec3(rainRipple.w) * 0.7;
#endif
#if REFRACTION
gl_FragData[0].w = 1.0;
#else
gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);
#endif
applyShadowDebugOverlay(); applyShadowDebugOverlay();
} }

@ -16,7 +16,7 @@ void main(void)
0.5, 0.5, 0.5, 1.0); 0.5, 0.5, 0.5, 1.0);
vec4 texcoordProj = ((scalemat) * ( gl_Position)); vec4 texcoordProj = ((scalemat) * ( gl_Position));
screenCoordsPassthrough = vec3(texcoordProj.x, texcoordProj.y, texcoordProj.w); screenCoordsPassthrough = texcoordProj.xyw;
position = gl_Vertex; position = gl_Vertex;

Loading…
Cancel
Save