1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-01 17:06:40 +00:00

Merge branch 'master' into wnamfix

This commit is contained in:
unelsson 2019-10-07 01:40:23 +03:00 committed by GitHub
commit 3deebf0247
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 1733 additions and 722 deletions

View file

@ -4,11 +4,13 @@
Bug #1515: Opening console masks dialogue, inventory menu
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
Bug #2969: Scripted items can stack
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
Bug #2987: Editor: some chance and AI data fields can overflow
Bug #3006: 'else if' operator breaks script compilation
Bug #3109: SetPos/Position handles actors differently
Bug #3282: Unintended behaviour when assigning F3 and Windows keys
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 #3725: Using script function in a non-conditional expression breaks script compilation
Bug #3733: Normal maps are inverted on mirrored UVs
@ -25,6 +27,7 @@
Bug #4383: Bow model obscures crosshair when arrow is drawn
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons
Bug #4411: Reloading a saved game while falling prevents damage in some cases
Bug #4456: AiActivate should not be cancelled after target activation
Bug #4540: Rain delay when exiting water
Bug #4600: Crash when no sound output is available or --no-sound is used.
Bug #4639: Black screen after completing first mages guild mission + training
@ -113,6 +116,7 @@
Bug #5050: Invalid spell effects are not handled gracefully
Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame
Bug #5056: Calling Cast function on player doesn't equip the spell but casts it
Bug #5059: Modded animation with combined attack keys always does max damage and can double damage
Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends
Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden
Bug #5067: Ranged attacks on unaware opponents ("critical hits") differ from the vanilla engine
@ -121,6 +125,7 @@
Bug #5075: Enchanting cast style can be changed if there's no object
Bug #5078: DisablePlayerLooking is broken
Bug #5082: Scrolling with controller in GUI mode is broken
Bug #5087: Some valid script names can't be used as string arguments
Bug #5089: Swimming/Underwater creatures only swim around ground level
Bug #5092: NPCs with enchanted weapons play sound when out of charges
Bug #5093: Hand to hand sound plays on knocked out enemies
@ -136,14 +141,26 @@
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled
Bug #5126: Swimming creatures without RunForward animations are motionless during combat
Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5136: LegionUniform script: can not access local variables
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 #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
Bug #5167: Player can select and cast spells before magic menu is enabled
Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change
Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn
Bug #5175: Random script function returns an integer value
Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis
Feature #3871: Editor: Terrain Selection
Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller
Feature #3999: Shift + Double Click should maximize/restore menu size

View file

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

View file

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

View file

@ -8,13 +8,6 @@
#include <components/esm/loadland.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 (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));
}
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 yd = static_cast<float>(worldPos.y() * 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() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f);
const auto x = static_cast<int>(std::floor(xd));
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);
}
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 yd = static_cast<float>(worldPos.y() * (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() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f);
const auto x = static_cast<int>(std::floor(xd));
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);
}
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::vertexGlobalToCellId(std::pair<int, int> vertexGlobal)
std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair<int, int>& textureGlobal)
{
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1));
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(const std::pair<int, int>& vertexGlobal)
{
int x = std::floor(static_cast<float>(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1));
return generateId(x, y);
}

View file

@ -49,22 +49,25 @@ namespace CSMWorld
static std::pair<int, int> coordinatesToCellIndex (float x, float y);
///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.
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.
static float textureSelectionToWorldCoords(int);
static float textureGlobalToWorldCoords(int textureGlobal);
///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
static int vertexSelectionToInCellCoords(int);
///Converts global vertex coordinate to local cell's heightmap coordinates
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
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);

View file

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

View file

@ -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];
}

View file

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

View file

@ -10,6 +10,8 @@
#include <QDragEnterEvent>
#include <QDrag>
#include <osg/Group>
#include <components/esm/loadland.hpp>
#include "../widget/modebutton.hpp"
@ -36,12 +38,15 @@
#include "object.hpp" // Something small needed regarding pointers from here ()
#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),
mBrushTexture("L0#0"),
mBrushSize(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)));
}
if (!mTerrainTextureSelection)
{
mTerrainTextureSelection.reset(new TerrainSelection(mParentNode, &getWorldspaceWidget(), TerrainSelectionType::Texture));
}
EditMode::activate(toolbar);
toolbar->addTool (mTextureBrushScenetool);
}
@ -74,6 +84,12 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
delete mTextureBrushScenetool;
mTextureBrushScenetool = 0;
}
if (mTerrainTextureSelection)
{
mTerrainTextureSelection.reset();
}
EditMode::deactivate(toolbar);
}
@ -95,10 +111,10 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
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");
if(allowLandTextureEditing(mCellId)==true)
if(allowLandTextureEditing(mCellId))
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
@ -109,10 +125,18 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
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)
{
if(hit.hit && hit.tag == 0)
{
selectTerrainTextures(CSMWorld::CellCoordinates::toTextureCoords(hit.worldPos), 1, false);
}
}
bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
@ -129,13 +153,16 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag (const QPoint& pos)
QUndoStack& undoStack = document.getUndoStack();
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
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");
if(allowLandTextureEditing(mCellId)==true && hit.hit == true)
mIsEditing = true;
if(allowLandTextureEditing(mCellId))
{
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, mCellId));
editTerrainTextureGrid(hit);
@ -152,47 +179,88 @@ bool CSVRender::TerrainTextureMode::secondaryEditStartDrag (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;
}
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;
}
void CSVRender::TerrainTextureMode::drag (const QPoint& pos, int diffX, int diffY, double speedFactor)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit == true)
if (mDragMode == InteractionType_PrimaryEdit)
{
editTerrainTextureGrid(hit);
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();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == 0)
{
editTerrainTextureGrid(hit);
}
}
if (mDragMode == InteractionType_PrimarySelect)
{
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) {
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == InteractionType_PrimaryEdit && mIsEditing)
{
undoStack.endMacro();
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
undoStack.endMacro();
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());
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));
mCellId = getWorldspaceWidget().getCellId (hit.worldPos);
if(allowLandTextureEditing(mCellId)==true) {}
if(allowLandTextureEditing(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();
// The coordinates of hit in mCellId
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.5));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.5));
int xHitInCell (float(((hit.worldPos.x() - (cellX* cellSize)) * landTextureSize / cellSize) - 0.25));
int yHitInCell (float(((hit.worldPos.y() - (cellY* cellSize)) * landTextureSize / cellSize) + 0.25));
if (xHitInCell < 0)
{
xHitInCell = xHitInCell + landTextureSize;
@ -249,8 +317,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
cellY = cellY + 1;
}
mCellId = "#" + std::to_string(cellX) + " " + std::to_string(cellY);
if(allowLandTextureEditing(mCellId)==true) {}
mCellId = CSMWorld::CellCoordinates::generateId(cellX, cellY);
if(allowLandTextureEditing(mCellId)) {}
std::string iteratedCellId;
@ -266,13 +334,13 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 0)
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
if(allowLandTextureEditing(mCellId)==true)
if(allowLandTextureEditing(mCellId))
{
mNew[yHitInCell*landTextureSize+xHitInCell] = brushInt;
pushEditToCommand(mNew, document, landTable, mCellId);
newTerrain[yHitInCell*landTextureSize+xHitInCell] = brushInt;
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++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId))
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++)
{
for(int j = 0; j < landTextureSize; j++)
{
if (i_cell == cellX && j_cell == cellY && abs(i-xHitInCell) < r && abs(j-yHitInCell) < r)
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
{
@ -316,11 +384,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (j_cell > cellY) distanceY = -yHitInCell + landTextureSize * abs(j_cell-cellY) + j;
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
if (distanceX < r && distanceY < r) mNew[j*landTextureSize+i] = brushInt;
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++)
{
iteratedCellId = "#" + std::to_string(i_cell) + " " + std::to_string(j_cell);
if(allowLandTextureEditing(iteratedCellId)==true)
iteratedCellId = CSMWorld::CellCoordinates::generateId(i_cell, j_cell);
if(allowLandTextureEditing(iteratedCellId))
{
CSMWorld::LandTexturesColumn::DataType mPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType mNew(mPointer);
CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value<CSMWorld::LandTexturesColumn::DataType>();
CSMWorld::LandTexturesColumn::DataType newTerrain(newTerrainPointer);
for(int i = 0; i < landTextureSize; i++)
{
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 (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
}
else
{
@ -376,11 +444,11 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (i_cell == cellX) distanceX = abs(i-xHitInCell);
if (j_cell == cellY) distanceY = abs(j-yHitInCell);
distance = std::round(sqrt(pow(distanceX, 2)+pow(distanceY, 2)));
if (distance < rf) mNew[j*landTextureSize+i] = brushInt;
if (distance < rf) newTerrain[j*landTextureSize+i] = brushInt;
}
}
}
pushEditToCommand(mNew, document, landTable, iteratedCellId);
pushEditToCommand(newTerrain, document, landTable, iteratedCellId);
}
}
}
@ -388,9 +456,115 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
if (mBrushShape == 3)
{
// Not implemented
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);
}
}
}
}
}
if (mBrushShape == 3)
{
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,
@ -405,8 +579,8 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandTexturesIndex)));
QUndoStack& undoStack = document.getUndoStack();
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
}
void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
@ -422,13 +596,15 @@ void CSVRender::TerrainTextureMode::createTexture(std::string textureFileName)
int counter=0;
bool freeIndexFound = false;
do {
do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
if (ltexTable.getRecord(newId).isDeleted() == 0) counter = (counter + 1) % maxCounter;
} catch (const std::exception& e)
}
catch (const std::exception& e)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
freeIndexFound = true;
@ -527,7 +703,8 @@ bool CSVRender::TerrainTextureMode::allowLandTextureEditing(std::string cellId)
return true;
}
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event) {
void CSVRender::TerrainTextureMode::dragMoveEvent (QDragMoveEvent *event)
{
}
void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
@ -538,6 +715,28 @@ void CSVRender::TerrainTextureMode::setBrushSize(int brushSize)
void CSVRender::TerrainTextureMode::setBrushShape(int 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)

View file

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

View file

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

View file

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

View file

@ -403,7 +403,7 @@ namespace MWBase
virtual osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const = 0;
virtual void togglePOV() = 0;
virtual void togglePOV(bool force = false) = 0;
virtual bool isFirstPerson() const = 0;
virtual void togglePreviewMode(bool enable) = 0;
virtual bool toggleVanityMode(bool enable) = 0;

View file

@ -89,9 +89,7 @@ namespace MWClass
bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();
return (ref->mBase->mName != "");
return !getName(ptr).empty();
}
bool Activator::allowTelekinesis(const MWWorld::ConstPtr &ptr) const {
@ -103,7 +101,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();
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;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View file

@ -20,11 +20,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; 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: false)
///< @return true if this object has a tooltip when focused (default implementation: true)
virtual bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const;
///< Return whether this class of object can be activated with telekinesis

View file

@ -91,14 +91,4 @@ namespace MWClass
{
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;
}
}

View file

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

View file

@ -45,8 +45,9 @@ namespace MWClass
std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -93,19 +94,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Apparatus> *ref = ptr.get<ESM::Apparatus>();
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;
std::string text;

View file

@ -20,8 +20,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
@ -33,9 +32,6 @@ namespace MWClass
virtual int getValue (const MWWorld::ConstPtr& ptr) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -53,8 +53,9 @@ namespace MWClass
std::string Armor::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -199,19 +200,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
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;
std::string text;

View file

@ -19,8 +19,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
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
/// 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

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

View file

@ -18,8 +18,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; 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();

View file

@ -50,8 +50,9 @@ namespace MWClass
std::string Book::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -109,19 +110,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Book> *ref = ptr.get<ESM::Book>();
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;
std::string text;

View file

@ -17,8 +17,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
@ -27,9 +26,6 @@ namespace MWClass
virtual std::string getScript (const MWWorld::ConstPtr& ptr) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -48,8 +48,9 @@ namespace MWClass
std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -157,19 +158,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Clothing> *ref = ptr.get<ESM::Clothing>();
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;
std::string text;

View file

@ -17,8 +17,7 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
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
/// 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -181,7 +181,7 @@ namespace MWClass
{
MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}");
if(isLocked)
unlock(ptr);
ptr.getCellRef().unlock();
// using a key disarms the trap
if(isTrapped)
{
@ -224,8 +224,9 @@ namespace MWClass
std::string Container::getName (const MWWorld::ConstPtr& ptr) const
{
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)
@ -252,26 +253,18 @@ namespace MWClass
bool Container::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
if (getName(ptr).empty())
return false;
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
return !canBeHarvested(ptr) || data->asContainerCustomData().mContainerStore.hasVisibleItems();
return true;
}
bool Container::canBeActivated(const MWWorld::Ptr& ptr) const
{
return hasToolTip(ptr);
}
MWGui::ToolTipInfo Container::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
{
const MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text;
int lockLevel = ptr.getCellRef().getLockLevel();
@ -282,9 +275,11 @@ namespace MWClass
if (ptr.getCellRef().getTrap() != "")
text += "\n#{sTrapped}";
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{ text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
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;
@ -305,23 +300,10 @@ namespace MWClass
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
{
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

View file

@ -20,15 +20,14 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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)
///< @return true if this object has a tooltip when focused (default implementation: true)
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.
@ -47,12 +46,6 @@ namespace MWClass
///< Returns total weight of objects inside this object (including modifications from magic
/// 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 void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
@ -63,8 +56,6 @@ namespace MWClass
const;
///< Write additional state from \a ptr into \a state.
virtual bool canBeActivated(const MWWorld::Ptr& ptr) const;
static void registerSelf();
virtual void respawn (const MWWorld::Ptr& ptr) const;

View file

@ -214,8 +214,9 @@ namespace MWClass
std::string Creature::getName (const MWWorld::ConstPtr& ptr) const
{
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
@ -454,18 +455,15 @@ namespace MWClass
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
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())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
// Tribunal and some mod companions oddly enough must use open action as fallback
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
@ -586,7 +584,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())

View file

@ -44,11 +44,10 @@ namespace MWClass
///< Add reference into a cell for rendering
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);
/// can return an empty string.
///< \return name or ID; 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: 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

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

View file

@ -12,8 +12,10 @@ namespace MWClass
public:
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);
/// can return an empty string.
///< \return name or ID; 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();

View file

@ -102,8 +102,9 @@ namespace MWClass
std::string Door::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -162,7 +163,7 @@ namespace MWClass
if(actor == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}");
if(isLocked)
unlock(ptr); //Call the function here. because that makes sense.
ptr.getCellRef().unlock(); //Call the function here. because that makes sense.
// using a key disarms the trap
if(isTrapped)
{
@ -240,20 +241,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
{
return true;
@ -281,19 +268,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Door> *ref = ptr.get<ESM::Door>();
MWGui::ToolTipInfo info;
info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName);
info.caption = MyGUI::TextIterator::toTagsString(getName(ptr));
std::string text;

View file

@ -25,28 +25,18 @@ namespace MWClass
virtual bool useAnim() 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @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);
///< @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 allowTelekinesis(const MWWorld::ConstPtr &ptr) const;

View file

@ -47,8 +47,9 @@ namespace MWClass
std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -105,19 +106,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Ingredient> *ref = ptr.get<ESM::Ingredient>();
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;
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

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

View file

@ -10,8 +10,10 @@ namespace MWClass
public:
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);
/// can return an empty string.
///< \return name or ID; 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();
};

View file

@ -70,9 +70,10 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
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,
@ -141,9 +142,7 @@ namespace MWClass
bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
return (ref->mBase->mName != "");
return showsInInventory(ptr);
}
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>();
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;
std::string text;

View file

@ -19,11 +19,10 @@ namespace MWClass
virtual bool useAnim() 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);
/// can return an empty string.
///< \return name or ID; 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: 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -47,8 +47,9 @@ namespace MWClass
std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -104,19 +105,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Lockpick> *ref = ptr.get<ESM::Lockpick>();
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;
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -56,8 +56,9 @@ namespace MWClass
std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -134,13 +135,6 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = ptr.get<ESM::Miscellaneous>();
@ -159,14 +153,16 @@ namespace MWClass
else // gold displays its count also if it's 1.
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;
if (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 + ")";
else if (creature)
info.caption += " (" + creature->mId + ")";
}
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -531,7 +531,9 @@ namespace MWClass
}
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
@ -884,22 +886,22 @@ namespace MWClass
// otherwise wait until death animation
if(stats.isDeathAnimationFinished())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr));
}
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
// death animation is not finished, do nothing
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
// Can't talk to werewolves
if (!getNpcStats(ptr).isWerewolf())
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
}
if(stats.getAiSequence().isInCombat())
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
// Tribunal and some mod companions oddly enough must use open action as fallback
if (!getScript(ptr).empty() && ptr.getRefData().getLocals().getIntVar(getScript(ptr), "companion"))
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(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
if(getNpcStats(ptr).isWerewolf())
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& Npc::getContainerStore (const MWWorld::Ptr& ptr)
@ -1071,7 +1073,7 @@ namespace MWClass
MWGui::ToolTipInfo info;
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 += MyGUI::TextIterator::toTagsString(ref->mBase->mName);

View file

@ -48,8 +48,7 @@ namespace MWClass
///< Add reference into a cell for rendering
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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
///< Return creature stats
@ -61,7 +60,7 @@ namespace MWClass
///< Return container store
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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -49,8 +49,9 @@ namespace MWClass
std::string Potion::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -98,19 +99,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Potion> *ref = ptr.get<ESM::Potion>();
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;
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -47,8 +47,9 @@ namespace MWClass
std::string Probe::getName (const MWWorld::ConstPtr& ptr) const
{
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,
const MWWorld::Ptr& actor) const
@ -104,19 +105,12 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Probe> *ref = ptr.get<ESM::Probe>();
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;
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -44,8 +44,9 @@ namespace MWClass
std::string Repair::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -93,13 +94,6 @@ namespace MWClass
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
{
return true;
@ -117,7 +111,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Repair> *ref = ptr.get<ESM::Repair>();
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;
std::string text;

View file

@ -17,16 +17,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

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

View file

@ -17,8 +17,10 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; 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();

View file

@ -53,8 +53,9 @@ namespace MWClass
std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const
{
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,
@ -155,20 +156,13 @@ namespace MWClass
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
{
const MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
const ESM::WeaponType* weaponType = MWMechanics::getWeaponType(ref->mBase->mData.mType);
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;
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();

View file

@ -18,16 +18,12 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) 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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< 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;
///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip.

View file

@ -98,7 +98,7 @@ namespace MWGui
if (pickpocket.finish())
{
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true;
}
@ -126,7 +126,7 @@ namespace MWGui
if (pickpocket.pick(item, count))
{
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true;
return false;

View file

@ -651,9 +651,22 @@ namespace MWGui
{
std::string ret;
ret += getMiscString(cellref.getOwner(), "Owner");
ret += getMiscString(cellref.getFaction(), "Faction");
if (cellref.getFactionRank() > 0)
ret += getValueString(cellref.getFactionRank(), "Rank");
const std::string factionId = cellref.getFaction();
ret += getMiscString(factionId, "Faction");
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 =
MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId());

View file

@ -471,19 +471,19 @@ namespace MWInput
quickLoad();
break;
case A_CycleSpellLeft:
if (checkAllowedToUseItems())
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(false);
break;
case A_CycleSpellRight:
if (checkAllowedToUseItems())
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Magic))
MWBase::Environment::get().getWindowManager()->cycleSpell(true);
break;
case A_CycleWeaponLeft:
if (checkAllowedToUseItems())
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(false);
break;
case A_CycleWeaponRight:
if (checkAllowedToUseItems())
if (checkAllowedToUseItems() && MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))
MWBase::Environment::get().getWindowManager()->cycleWeapon(true);
break;
case A_Sneak:
@ -1312,6 +1312,9 @@ namespace MWInput
if (!checkAllowedToUseItems())
return;
if (MWBase::Environment::get().getWorld()->getGlobalFloat ("chargenstate")!=-1)
return;
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWindowManager()->activateQuickKey (index);
}

View file

@ -1637,6 +1637,20 @@ namespace MWMechanics
++mDeathCount[Misc::StringUtils::lowerCase(actor.getCellRef().getRefId())];
}
void Actors::resurrect(const MWWorld::Ptr &ptr)
{
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
{
if(iter->second->getCharacterController()->isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->getCharacterController()->resurrect();
}
}
}
void Actors::killDeadActors()
{
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
@ -1645,17 +1659,7 @@ namespace MWMechanics
CreatureStats &stats = cls.getCreatureStats(iter->first);
if(!stats.isDead())
{
if(iter->second->getCharacterController()->isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->getCharacterController()->resurrect();
}
if(!stats.isDead())
continue;
}
continue;
MWBase::Environment::get().getWorld()->removeActorPath(iter->first);
CharacterController::KillResult killResult = iter->second->getCharacterController()->kill();

View file

@ -94,6 +94,8 @@ namespace MWMechanics
///
/// \note Ignored, if \a ptr is not a registered actor.
void resurrect (const MWWorld::Ptr& ptr);
void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
void updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);

View file

@ -9,6 +9,7 @@
#include "creaturestats.hpp"
#include "movement.hpp"
#include "steering.hpp"
namespace MWMechanics
{
@ -33,16 +34,18 @@ namespace MWMechanics
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
return true;
//Set the target destination for the actor
const osg::Vec3f dest = target.getRefData().getPosition().asVec3();
// Turn to target and move to it directly, without pathfinding.
const osg::Vec3f targetDir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range
zTurn(actor, std::atan2(targetDir.x(), targetDir.y()), 0.f);
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
if (MWBase::Environment::get().getWorld()->getMaxActivationDistance() >= targetDir.length())
{
// activate when reached
// Note: we intentionally do not cancel package after activation here for backward compatibility with original engine.
MWBase::Environment::get().getWorld()->activate(target, actor);
return true;
}
return false;
}

View file

@ -1,5 +1,7 @@
#include "aiavoiddoor.hpp"
#include <components/misc/rng.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -11,8 +13,10 @@
#include "actorutil.hpp"
#include "steering.hpp"
static const int MAX_DIRECTIONS = 4;
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr)
: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mLastPos(ESM::Position()), mAdjAngle(0)
: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0)
{
}
@ -22,25 +26,18 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
ESM::Position pos = actor.getRefData().getPosition();
if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing
mLastPos = pos;
mLastPos = pos.asVec3();
mDuration -= duration; //Update timer
if(mDuration < 0) {
float x = pos.pos[0] - mLastPos.pos[0];
float y = pos.pos[1] - mLastPos.pos[1];
float z = pos.pos[2] - mLastPos.pos[2];
float distance = x * x + y * y + z * z;
if(distance < 10 * 10) { //Got stuck, didn't move
if(mAdjAngle == 0) //Try going in various directions
mAdjAngle = osg::PI / 2;
else if (mAdjAngle == osg::PI / 2)
mAdjAngle = -osg::PI / 2;
else
mAdjAngle = 0;
if (mDuration < 0)
{
if (isStuck(pos.asVec3()))
{
adjustDirection();
mDuration = 1; //reset timer
}
else //Not stuck
else
return true; // We have tried backing up for more than one second, we've probably cleared it
}
@ -54,7 +51,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
// Turn away from the door and move when turn completed
if (zTurn(actor, std::atan2(x,y) + mAdjAngle, osg::DegreesToRadians(5.f)))
if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
else
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
@ -90,4 +87,17 @@ unsigned int MWMechanics::AiAvoidDoor::getPriority() const
return 2;
}
bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{
return (actorPos - mLastPos).length2() < 10 * 10;
}
void MWMechanics::AiAvoidDoor::adjustDirection()
{
mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS);
}
float MWMechanics::AiAvoidDoor::getAdjustedAngle() const
{
return 2 * osg::PI / MAX_DIRECTIONS * mDirection;
}

View file

@ -36,8 +36,14 @@ namespace MWMechanics
private:
float mDuration;
MWWorld::ConstPtr mDoorPtr;
ESM::Position mLastPos;
float mAdjAngle;
osg::Vec3f mLastPos;
int mDirection;
bool isStuck(const osg::Vec3f& actorPos) const;
void adjustDirection();
float getAdjustedAngle() const;
};
}
#endif

View file

@ -1629,7 +1629,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
{
float attackStrength = complete;
if (!mPtr.getClass().isNpc())
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (minAttackTime == maxAttackTime)
{
// most creatures don't actually have an attack wind-up animation, so use a uniform random value
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
@ -1735,7 +1737,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
// Happens if the player did not hold the attack button.
// Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be 1.
// Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be random.
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (mAttackingOrSpell || minAttackTime == maxAttackTime)

View file

@ -198,9 +198,6 @@ namespace MWMechanics
mDynamic[index].setModifier(0);
mDynamic[index].setCurrentModifier(0);
mDynamic[index].setCurrent(0);
if (MWBase::Environment::get().getWorld()->getGodModeState())
MWBase::Environment::get().getMechanicsManager()->keepPlayerAlive();
}
}

View file

@ -19,16 +19,14 @@ namespace MWMechanics
{
/// @return ID of resulting item, or empty if none
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, unsigned char failChance=0)
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature)
{
const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
const MWWorld::Ptr& player = getPlayer();
int playerLevel = player.getClass().getCreatureStats(player).getLevel();
failChance += levItem->mChanceNone;
if (Misc::Rng::roll0to99() < failChance)
if (Misc::Rng::roll0to99() < levItem->mChanceNone)
return std::string();
std::vector<std::string> candidates;
@ -76,9 +74,9 @@ namespace MWMechanics
else
{
if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name())
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false, failChance);
return getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
else
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true, failChance);
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, true);
}
}

View file

@ -959,7 +959,7 @@ namespace MWMechanics
return true;
}
if (!target.getClass().canBeActivated(target))
if (!target.getClass().hasToolTip(target))
return true;
// TODO: implement a better check to check if target is owned bed
@ -1005,6 +1005,10 @@ namespace MWMechanics
if (!cellref.getOwner().empty())
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);
}
@ -1025,7 +1029,7 @@ namespace MWMechanics
if (isAllowedToUse(ptr, bed, victim))
return false;
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed))
if(commitCrime(ptr, victim, OT_SleepingInOwnedBed, bed.getCellRef().getFaction()))
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage64}");
return true;
@ -1039,7 +1043,7 @@ namespace MWMechanics
MWWorld::Ptr victim;
if (isAllowedToUse(ptr, item, victim))
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)
@ -1120,7 +1124,7 @@ namespace MWMechanics
// move items from player to owner and report about theft
victim.getClass().getContainerStore(victim).add(item, toRemove, victim);
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)
@ -1150,7 +1154,7 @@ namespace MWMechanics
store.remove(*it, toMove, player);
}
// 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,
@ -1207,11 +1211,11 @@ namespace MWMechanics
mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;
}
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
@ -1258,13 +1262,13 @@ namespace MWMechanics
}
if (crimeSeen)
reportCrime(player, victim, type, arg);
reportCrime(player, victim, type, factionId, arg);
else if (type == OT_Assault && !victim.isEmpty())
{
bool reported = false;
if (victim.getClass().isClass(victim, "guard")
&& !victim.getClass().getCreatureStats(victim).getAiSequence().hasPackage(AiPackage::TypeIdPursue))
reported = reportCrime(player, victim, type, arg);
reported = reportCrime(player, victim, type, std::string(), arg);
if (!reported)
startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee?
@ -1295,7 +1299,7 @@ namespace MWMechanics
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>();
@ -1466,6 +1470,14 @@ namespace MWMechanics
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()
&& !victim.getClass().getCreatureStats(victim).getAiSequence().isInCombat(player)
@ -1808,12 +1820,14 @@ namespace MWMechanics
return (fight >= 100);
}
void MechanicsManager::keepPlayerAlive()
void MechanicsManager::resurrect(const MWWorld::Ptr &ptr)
{
MWWorld::Ptr player = getPlayer();
CreatureStats& stats = player.getClass().getCreatureStats(player);
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
if (stats.isDead())
{
stats.resurrect();
mActors.resurrect(ptr);
}
}
bool MechanicsManager::isCastingSpell(const MWWorld::Ptr &ptr) const

View file

@ -130,7 +130,7 @@ namespace MWMechanics
* @return was the crime seen?
*/
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
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) override;
@ -197,7 +197,7 @@ namespace MWMechanics
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) override;
virtual void keepPlayerAlive() override;
virtual void resurrect(const MWWorld::Ptr& ptr) override;
virtual bool isCastingSpell (const MWWorld::Ptr& ptr) const override;
@ -247,7 +247,7 @@ namespace MWMechanics
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,
OffenseType type, int arg=0);
OffenseType type, const std::string& factionId, int arg=0);
};
}

View file

@ -30,7 +30,7 @@ namespace MWMechanics
{
if (lock.getCellRef().getLockLevel() <= 0 ||
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;
int lockStrength = lock.getCellRef().getLockLevel();
@ -50,7 +50,7 @@ namespace MWMechanics
{
if (Misc::Rng::roll0to99() <= x)
{
lock.getClass().unlock(lock);
lock.getCellRef().unlock();
resultMessage = "#{sLockSuccess}";
resultSound = "Open Lock";
mActor.getClass().skillUsageSucceeded(mActor, ESM::Skill::Security, 1);

View file

@ -696,7 +696,7 @@ namespace MWMechanics
{
if (caster == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
target.getClass().lock(target, static_cast<int>(magnitude));
target.getCellRef().lock(static_cast<int>(magnitude));
}
return true;
}
@ -716,7 +716,7 @@ namespace MWMechanics
if (caster == getPlayer())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}");
}
target.getClass().unlock(target);
target.getCellRef().unlock();
}
else
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f);

View file

@ -1333,9 +1333,9 @@ namespace MWRender
return mCurrentCameraPos;
}
void RenderingManager::togglePOV()
void RenderingManager::togglePOV(bool force)
{
mCamera->toggleViewMode();
mCamera->toggleViewMode(force);
}
void RenderingManager::togglePreviewMode(bool enable)

View file

@ -204,7 +204,7 @@ namespace MWRender
float getCameraDistance() const;
Camera* getCamera();
const osg::Vec3f& getCameraPosition() const;
void togglePOV();
void togglePOV(bool force = false);
void togglePreviewMode(bool enable);
bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow);

View file

@ -182,7 +182,7 @@ namespace MWScript
runtime.pop();
}
ptr.getClass().lock (ptr, lockLevel);
ptr.getCellRef().lock (lockLevel);
// Instantly reset door to closed state
// This is done when using Lock in scripts, but not when using Lock spells.
@ -202,7 +202,7 @@ namespace MWScript
{
MWWorld::Ptr ptr = R()(runtime);
ptr.getClass().unlock (ptr);
ptr.getCellRef().unlock ();
}
};
@ -358,7 +358,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
if (!MWBase::Environment::get().getWorld()->isFirstPerson())
MWBase::Environment::get().getWorld()->togglePOV();
MWBase::Environment::get().getWorld()->togglePOV(true);
}
};
@ -367,7 +367,7 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime)
{
if (MWBase::Environment::get().getWorld()->isFirstPerson())
MWBase::Environment::get().getWorld()->togglePOV();
MWBase::Environment::get().getWorld()->togglePOV(true);
}
};

View file

@ -1184,7 +1184,7 @@ namespace MWScript
if (ptr == MWMechanics::getPlayer())
{
ptr.getClass().getCreatureStats(ptr).resurrect();
MWBase::Environment::get().getMechanicsManager()->resurrect(ptr);
if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Ended)
MWBase::Environment::get().getStateManager()->resumeGame();
}

View file

@ -225,6 +225,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
{
return mCellRef.mKey;

View file

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

View file

@ -109,11 +109,6 @@ namespace MWWorld
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
{
throw std::runtime_error("class cannot be hit");
@ -144,16 +139,6 @@ namespace MWWorld
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
{
return false;
@ -298,7 +283,7 @@ namespace MWWorld
bool Class::hasToolTip (const ConstPtr& ptr) const
{
return false;
return true;
}
std::string Class::getEnchantment (const ConstPtr& ptr) const

View file

@ -83,8 +83,7 @@ namespace MWWorld
///< Add reference into a cell for rendering (default implementation: don't render anything).
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);
/// can return an empty string.
///< \return name or ID; can return an empty string.
virtual void adjustPosition(const MWWorld::Ptr& ptr, bool force) const;
///< Adjust position to stand on ground. Must be called post model load
@ -95,7 +94,7 @@ namespace MWWorld
/// (default implementation: throw an exception)
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;
///< @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
/// (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;
///< Generate action for activation (default implementation: return a null action).
@ -163,12 +158,6 @@ namespace MWWorld
virtual bool hasInventoryStore (const Ptr& ptr) const;
///< 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 void setRemainingUsageTime (const Ptr& ptr, float duration) const;

View file

@ -477,50 +477,64 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
int count, bool topLevel, const std::string& levItem)
{
if (count == 0) return; //Don't restock with nothing.
try {
try
{
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
{
const ESM::ItemLevList* levItemList = ref.getPtr().get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
}
else
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count);
// Adding just one item per time to make sure there isn't a stack of scripted items
for (int i = 0; i < abs(count); i++)
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
}
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what();
}
}
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner,
int count, bool topLevel, const std::string& levItem)
{
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
{
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
}
else
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ptr.getCellRef().setOwner(owner);
addImp (ptr, count);
}
}
void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)

View file

@ -94,6 +94,7 @@ namespace MWWorld
mutable bool mWeightUpToDate;
ContainerStoreIterator addImp (const Ptr& ptr, int count);
void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection,

View file

@ -236,7 +236,7 @@ namespace MWWorld
if (toActivate.isEmpty())
return;
if (!toActivate.getClass().canBeActivated(toActivate))
if (!toActivate.getClass().hasToolTip(toActivate))
return;
MWBase::Environment::get().getWorld()->activate(toActivate, player);

View file

@ -1091,6 +1091,18 @@ namespace MWWorld
return RecordId(dialogue.mId, isDeleted);
}
template<>
bool Store<ESM::Dialogue>::eraseStatic(const std::string &id)
{
auto it = mStatic.find(Misc::StringUtils::lowerCase(id));
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
mStatic.erase(it);
}
return true;
}
}
template class MWWorld::Store<ESM::Activator>;

View file

@ -1649,7 +1649,6 @@ namespace MWWorld
}
// we need to undo the rotation
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
@ -1671,6 +1670,8 @@ namespace MWWorld
if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
}
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
}
// the rotation order we want to use
@ -2395,9 +2396,9 @@ namespace MWWorld
return mPhysics->isOnGround(ptr);
}
void World::togglePOV()
void World::togglePOV(bool force)
{
mRendering->togglePOV();
mRendering->togglePOV(force);
}
bool World::isFirstPerson() const
@ -3023,7 +3024,7 @@ namespace MWWorld
target = getFacedObject();
// 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;
if (target.isEmpty())
@ -3082,7 +3083,7 @@ namespace MWWorld
{
target = result2.mHitObject;
hitPosition = result2.mHitPointWorld;
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target))
if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().hasToolTip(target))
target = nullptr;
}
}
@ -3650,7 +3651,7 @@ namespace MWWorld
if (fromProjectile && effectInfo.mArea <= 0)
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
// Spawn the explosion orb effect

View file

@ -518,7 +518,7 @@ namespace MWWorld
osg::Matrixf getActorHeadTransform(const MWWorld::ConstPtr& actor) const override;
void togglePOV() override;
void togglePOV(bool force = false) override;
bool isFirstPerson() const override;

View file

@ -208,18 +208,13 @@ namespace
void init(Nif::Extra& value)
{
value.extra = Nif::ExtraPtr(nullptr);
}
void init(Nif::Controlled& value)
{
init(static_cast<Nif::Extra&>(value));
value.controller = Nif::ControllerPtr(nullptr);
value.next = Nif::ExtraPtr(nullptr);
}
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)
@ -254,7 +249,7 @@ namespace
value.phase = 0;
value.timeStart = 0;
value.timeStop = 0;
value.target = Nif::ControlledPtr(nullptr);
value.target = Nif::NamedPtr(nullptr);
}
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)
{
mNiStringExtraData.extra = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData.next = Nif::ExtraPtr(&mNiStringExtraData2);
mNiStringExtraData2.string = "NC___";
mNiStringExtraData2.recType = Nif::RC_NiStringExtraData;
mNiTriShape.extra = Nif::ExtraPtr(&mNiStringExtraData);

View file

@ -489,7 +489,7 @@ namespace Compiler
parseArguments ("l", scanner);
Generator::random (mCode);
mOperands.push_back ('l');
mOperands.push_back ('f');
mNextOperand = false;
return true;

View file

@ -56,6 +56,23 @@ namespace Compiler
}
}
if (keyword==Scanner::K_end || keyword==Scanner::K_begin ||
keyword==Scanner::K_short || keyword==Scanner::K_long ||
keyword==Scanner::K_float || keyword==Scanner::K_if ||
keyword==Scanner::K_endif || keyword==Scanner::K_else ||
keyword==Scanner::K_elseif || keyword==Scanner::K_while ||
keyword==Scanner::K_endwhile || keyword==Scanner::K_return ||
keyword==Scanner::K_messagebox || keyword==Scanner::K_set ||
keyword==Scanner::K_to || keyword==Scanner::K_startscript ||
keyword==Scanner::K_stopscript || keyword==Scanner::K_enable ||
keyword==Scanner::K_disable || keyword==Scanner::K_getdisabled ||
keyword==Scanner::K_getdistance || keyword==Scanner::K_scriptrunning ||
keyword==Scanner::K_getsquareroot || keyword==Scanner::K_menumode ||
keyword==Scanner::K_random || keyword==Scanner::K_getsecondspassed)
{
return parseName (loc.mLiteral, loc, scanner);
}
return Parser::parseKeyword (keyword, loc, scanner);
}

View file

@ -39,10 +39,7 @@ namespace Fallback
std::string key(temp.substr(0, sep));
std::string value(temp.substr(sep + 1));
if (map->mMap.find(key) == map->mMap.end())
{
map->mMap.insert(std::make_pair(key, value));
}
map->mMap[key] = value;
}
}
}

View file

@ -51,9 +51,6 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
bool silent = mSilent;
mSilent = quiet;
loadConfig(mFixedPath.getUserConfigPath(), variables, description);
boost::program_options::notify(variables);
// read either local or global config depending on type of installation
bool loaded = loadConfig(mFixedPath.getLocalPath(), variables, description);
boost::program_options::notify(variables);
@ -63,6 +60,10 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
boost::program_options::notify(variables);
}
// User config has the highest priority.
loadConfig(mFixedPath.getUserConfigPath(), variables, description);
boost::program_options::notify(variables);
mSilent = silent;
}

View file

@ -190,9 +190,7 @@ namespace Interpreter
throw std::runtime_error (
"random: argument out of range (Don't be so negative!)");
Type_Integer value = Misc::Rng::rollDice(limit); // [o, limit)
runtime[0].mInteger = value;
runtime[0].mFloat = static_cast<Type_Float>(Misc::Rng::rollDice(limit)); // [o, limit)
}
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -78,7 +78,7 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD
continue;
unsigned short a = strip[0], b = strip[0], c = strip[1];
for (int i = 2; i < static_cast<int>(strip.size()); i++)
for (size_t i = 2; i < strip.size(); i++)
{
a = b;
b = c;
@ -86,9 +86,21 @@ void fillTriangleMeshWithTransform(btTriangleMesh& mesh, const Nif::NiTriStripsD
if (a != b && b != c && a != c)
{
if (i%2==0)
mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[b]), getbtVector(vertices[c]));
{
mesh.addTriangle(
getbtVector(vertices[a] * transform),
getbtVector(vertices[b] * transform),
getbtVector(vertices[c] * transform)
);
}
else
mesh.addTriangle(getbtVector(vertices[a]), getbtVector(vertices[c]), getbtVector(vertices[b]));
{
mesh.addTriangle(
getbtVector(vertices[a] * transform),
getbtVector(vertices[c] * transform),
getbtVector(vertices[b] * transform)
);
}
}
}
}
@ -260,18 +272,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.";
// Check for extra data
Nif::Extra const *e = node;
while (!e->extra.empty())
for (Nif::ExtraPtr e = node->extra; !e.empty(); e = e->next)
{
// Get the next extra data in the list
e = e->extra.getPtr();
assert(e != nullptr);
if (e->recType == Nif::RC_NiStringExtraData)
{
// String markers may contain important information
// 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)
{

View file

@ -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))
, mTargetColor(color)
{
}
@ -397,6 +398,7 @@ MaterialColorController::MaterialColorController()
MaterialColorController::MaterialColorController(const MaterialColorController &copy, const osg::CopyOp &copyop)
: StateSetUpdater(copy, copyop), Controller(copy)
, 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::Material* mat = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
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);
switch (mTargetColor)
{
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);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more