diff --git a/CHANGELOG.md b/CHANGELOG.md index bbed2fa815..d84a661ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 2982a30f7a..637017ce81 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -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; diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b0bd95eb95..00855dad0c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index 1e545e38de..9f98c7b4c6 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -8,13 +8,6 @@ #include #include -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 CSMWorld::CellCoordinates::coordinatesToCellIndex (float x, return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits)); } -std::pair CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos) +std::pair CSMWorld::CellCoordinates::toTextureCoords(const osg::Vec3d& worldPos) { - const auto xd = static_cast(worldPos.x() * landTextureSize / cellSize - 0.25f); - const auto yd = static_cast(worldPos.y() * landTextureSize / cellSize + 0.25f); + const auto xd = static_cast(worldPos.x() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE - 0.25f); + const auto yd = static_cast(worldPos.y() * ESM::Land::LAND_TEXTURE_SIZE / ESM::Land::REAL_SIZE + 0.25f); const auto x = static_cast(std::floor(xd)); const auto y = static_cast(std::floor(yd)); @@ -87,10 +80,10 @@ std::pair CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldP return std::make_pair(x, y); } -std::pair CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos) +std::pair CSMWorld::CellCoordinates::toVertexCoords(const osg::Vec3d& worldPos) { - const auto xd = static_cast(worldPos.x() * (landSize - 1) / cellSize + 0.5f); - const auto yd = static_cast(worldPos.y() * (landSize - 1) / cellSize + 0.5f); + const auto xd = static_cast(worldPos.x() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f); + const auto yd = static_cast(worldPos.y() * (ESM::Land::LAND_SIZE - 1) / ESM::Land::REAL_SIZE + 0.5f); const auto x = static_cast(std::floor(xd)); const auto y = static_cast(std::floor(yd)); @@ -98,25 +91,32 @@ std::pair 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(pos) / landTextureSize; + return ESM::Land::REAL_SIZE * static_cast(textureGlobal) / ESM::Land::LAND_TEXTURE_SIZE; } -float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos) +float CSMWorld::CellCoordinates::vertexGlobalToWorldCoords(int vertexGlobal) { - return cellSize * static_cast(pos) / (landSize - 1); + return ESM::Land::REAL_SIZE * static_cast(vertexGlobal) / (ESM::Land::LAND_SIZE - 1); } -int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos) +int CSMWorld::CellCoordinates::vertexGlobalToInCellCoords(int vertexGlobal) { - return static_cast(pos - std::floor(static_cast(pos) / (landSize - 1)) * (landSize - 1)); + return static_cast(vertexGlobal - std::floor(static_cast(vertexGlobal) / (ESM::Land::LAND_SIZE - 1)) * (ESM::Land::LAND_SIZE - 1)); } -std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair vertexGlobal) +std::string CSMWorld::CellCoordinates::textureGlobalToCellId(const std::pair& textureGlobal) { - int x = std::floor(static_cast(vertexGlobal.first) / (landSize - 1)); - int y = std::floor(static_cast(vertexGlobal.second) / (landSize - 1)); + int x = std::floor(static_cast(textureGlobal.first) / ESM::Land::LAND_TEXTURE_SIZE); + int y = std::floor(static_cast(textureGlobal.second) / ESM::Land::LAND_TEXTURE_SIZE); + return generateId(x, y); +} + +std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(const std::pair& vertexGlobal) +{ + int x = std::floor(static_cast(vertexGlobal.first) / (ESM::Land::LAND_SIZE - 1)); + int y = std::floor(static_cast(vertexGlobal.second) / (ESM::Land::LAND_SIZE - 1)); return generateId(x, y); } diff --git a/apps/opencs/model/world/cellcoordinates.hpp b/apps/opencs/model/world/cellcoordinates.hpp index 3b21195174..77d76f6efb 100644 --- a/apps/opencs/model/world/cellcoordinates.hpp +++ b/apps/opencs/model/world/cellcoordinates.hpp @@ -49,22 +49,25 @@ namespace CSMWorld static std::pair coordinatesToCellIndex (float x, float y); ///Converts worldspace coordinates to global texture selection, taking in account the texture offset. - static std::pair toTextureCoords(osg::Vec3d worldPos); + static std::pair toTextureCoords(const osg::Vec3d& worldPos); ///Converts worldspace coordinates to global vertex selection. - static std::pair toVertexCoords(osg::Vec3d worldPos); + static std::pair 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& textureGlobal); ///Converts global vertex coordinates to cell id - static std::string vertexGlobalToCellId(std::pair); + static std::string vertexGlobalToCellId(const std::pair& vertexGlobal); }; bool operator== (const CellCoordinates& left, const CellCoordinates& right); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 7f31373ee8..540a15dd10 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -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"), diff --git a/apps/opencs/view/render/terrainselection.cpp b/apps/opencs/view/render/terrainselection.cpp new file mode 100644 index 0000000000..225cfc20b6 --- /dev/null +++ b/apps/opencs/view/render/terrainselection.cpp @@ -0,0 +1,261 @@ +#include "terrainselection.hpp" + +#include + +#include +#include +#include + +#include + +#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> CSVRender::TerrainSelection::getTerrainSelection() const +{ + return mSelection; +} + +void CSVRender::TerrainSelection::onlySelect(const std::vector> &localPositions) +{ + mSelection = localPositions; + update(); +} + +void CSVRender::TerrainSelection::addSelect(const std::pair &localPos) +{ + if (std::find(mSelection.begin(), mSelection.end(), localPos) == mSelection.end()) + { + mSelection.emplace_back(localPos); + update(); + } +} + +void CSVRender::TerrainSelection::toggleSelect(const std::vector> &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 vertices (new osg::Vec3Array); + + switch (mSelectionType) + { + case TerrainSelectionType::Texture : drawTextureSelection(vertices); + break; + case TerrainSelectionType::Shape : drawShapeSelection(vertices); + break; + } + + mGeometry->setVertexArray(vertices); + osg::ref_ptr 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 vertices) +{ + if (!mSelection.empty()) + { + for (std::pair &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 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 &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 ( + *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(); + + return mPointer[localY*ESM::Land::LAND_SIZE + localX]; +} diff --git a/apps/opencs/view/render/terrainselection.hpp b/apps/opencs/view/render/terrainselection.hpp new file mode 100644 index 0000000000..ba80aeb073 --- /dev/null +++ b/apps/opencs/view/render/terrainselection.hpp @@ -0,0 +1,70 @@ +#ifndef CSV_RENDER_TERRAINSELECTION_H +#define CSV_RENDER_TERRAINSELECTION_H + +#include +#include + +#include +#include +#include + +#include +#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> &localPositions); + void addSelect(const std::pair &localPos); + void toggleSelect(const std::vector> &localPositions, bool toggleInProgress); + + void activate(); + void deactivate(); + + std::vector> getTerrainSelection() const; + + protected: + + void update(); + + void drawShapeSelection(const osg::ref_ptr vertices); + void drawTextureSelection(const osg::ref_ptr vertices); + + int calculateLandHeight(int x, int y); + + private: + + osg::Group* mParentNode; + WorldspaceWidget *mWorldspaceWidget; + osg::ref_ptr mBaseNode; + osg::ref_ptr mGeometry; + osg::ref_ptr mSelectionNode; + std::vector> mSelection; // Global terrain selection coordinate in either vertex or texture units + std::vector> mTemporarySelection; // Used during toggle to compare the most recent drag operation + bool mDraggedOperationFlag; //true during drag operation, false when click-operation + TerrainSelectionType mSelectionType; + }; +} + +#endif diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 4205188e40..b8181eed59 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #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& 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& 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& 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& 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& 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& 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 (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 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 mNew(mPointer); + CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(mCellId, textureColumn)).value(); + 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 mNew(mPointer); + CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value(); + 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 mNew(mPointer); + CSMWorld::LandTexturesColumn::DataType newTerrainPointer = landTable.data(landTable.getModelIndex(iteratedCellId, textureColumn)).value(); + 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 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 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 (&getWorldspaceWidget())) + { + std::pair 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& texCoords, unsigned char selectMode, bool dragOperation) +{ + int r = mBrushSize / 2; + std::vector> 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::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) diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 10ea842c98..0d670d725f 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -4,6 +4,7 @@ #include "editmode.hpp" #include +#include #include #include @@ -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& 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> mCustomBrushShape; CSVWidget::SceneToolTextureBrush *mTextureBrushScenetool; + int mDragMode; + osg::Group* mParentNode; + bool mIsEditing; + std::unique_ptr mTerrainTextureSelection; const int cellSize {ESM::Land::REAL_SIZE}; const int landSize {ESM::Land::LAND_SIZE}; diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index d5ab73213e..7c92236ed9 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -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) { diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index aecff2cc6d..ae898b24e5 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -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; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 955e77168d..261f296085 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -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; diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 411b720fe4..c54b1c3691 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -89,9 +89,7 @@ namespace MWClass bool Activator::hasToolTip (const MWWorld::ConstPtr& ptr) const { - const MWWorld::LiveCellRef *ref = ptr.get(); - - 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 *ref = ptr.get(); 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()) diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index b92dc75cbd..d5175b739c 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -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 diff --git a/apps/openmw/mwclass/actor.cpp b/apps/openmw/mwclass/actor.cpp index aa356f62d1..4eb9728a1a 100644 --- a/apps/openmw/mwclass/actor.cpp +++ b/apps/openmw/mwclass/actor.cpp @@ -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; - } } diff --git a/apps/openmw/mwclass/actor.hpp b/apps/openmw/mwclass/actor.hpp index c3720050cc..7cdee8061f 100644 --- a/apps/openmw/mwclass/actor.hpp +++ b/apps/openmw/mwclass/actor.hpp @@ -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&); diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index ca610efc29..518695fabf 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -45,8 +45,9 @@ namespace MWClass std::string Apparatus::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Apparatus::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index e9b6457635..ea06f74bd5 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 44e708d5b0..e649bba12b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -53,8 +53,9 @@ namespace MWClass std::string Armor::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Armor::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 6b098fa9ab..e25a4ae8aa 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/bodypart.cpp b/apps/openmw/mwclass/bodypart.cpp index 41a9d998e0..0315d3ddb0 100644 --- a/apps/openmw/mwclass/bodypart.cpp +++ b/apps/openmw/mwclass/bodypart.cpp @@ -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 instance (new BodyPart); diff --git a/apps/openmw/mwclass/bodypart.hpp b/apps/openmw/mwclass/bodypart.hpp index 79c7c860de..b75dee7544 100644 --- a/apps/openmw/mwclass/bodypart.hpp +++ b/apps/openmw/mwclass/bodypart.hpp @@ -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(); diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index ce08ce4224..4ea71e3ac2 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -50,8 +50,9 @@ namespace MWClass std::string Book::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Book::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index 4cb51ff583..6bdb4e79bd 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c252085952..6d7960aac2 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -48,8 +48,9 @@ namespace MWClass std::string Clothing::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Clothing::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 81b5c2ef41..e71e9b307b 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 2b36f23390..369077d6c2 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -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 *ref = ptr.get(); + 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 *ref = ptr.get(); 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 *ref = ptr.get(); + return !(ref->mBase->mFlags & ESM::Container::Organic); } MWWorld::Ptr Container::copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index b137411870..b84c5787be 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -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 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; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index ba0bbf97f9..b53b7d0403 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -214,8 +214,9 @@ namespace MWClass std::string Creature::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + 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(new MWWorld::ActionOpen(ptr)); - - // death animation is not finished, do nothing - return std::shared_ptr (new MWWorld::FailedAction("")); } + else if (!stats.getAiSequence().isInCombat() && !stats.getKnockedDown()) + return std::shared_ptr(new MWWorld::ActionTalk(ptr)); - if(stats.getAiSequence().isInCombat()) - return std::shared_ptr(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(new MWWorld::ActionOpen(ptr)); - if(stats.getKnockedDown()) - return std::shared_ptr(new MWWorld::FailedAction("")); - - return std::shared_ptr(new MWWorld::ActionTalk(ptr)); + return std::shared_ptr(new MWWorld::FailedAction("")); } MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const @@ -586,7 +584,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); 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()) diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index e3689204a7..35688ed1ac 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -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. diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index b8c4cbb620..1dab9e4839 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -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); diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index 25b5cbddfb..3a05f5272c 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -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(); diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 4f144e1f7a..f2d79a0a3e 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -102,8 +102,9 @@ namespace MWClass std::string Door::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Door::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); MWGui::ToolTipInfo info; - info.caption = MyGUI::TextIterator::toTagsString(ref->mBase->mName); + info.caption = MyGUI::TextIterator::toTagsString(getName(ptr)); std::string text; diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index b3e4e383c3..84761900ca 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -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 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& 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; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index b632953919..bd61131bfc 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -47,8 +47,9 @@ namespace MWClass std::string Ingredient::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Ingredient::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 668fea29a9..8b2ea4f86a 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/itemlevlist.cpp b/apps/openmw/mwclass/itemlevlist.cpp index d2c246a851..5608a8d233 100644 --- a/apps/openmw/mwclass/itemlevlist.cpp +++ b/apps/openmw/mwclass/itemlevlist.cpp @@ -10,6 +10,11 @@ namespace MWClass return ""; } + bool ItemLevList::hasToolTip(const MWWorld::ConstPtr& ptr) const + { + return false; + } + void ItemLevList::registerSelf() { std::shared_ptr instance (new ItemLevList); diff --git a/apps/openmw/mwclass/itemlevlist.hpp b/apps/openmw/mwclass/itemlevlist.hpp index a8b313185c..36019f491d 100644 --- a/apps/openmw/mwclass/itemlevlist.hpp +++ b/apps/openmw/mwclass/itemlevlist.hpp @@ -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(); }; diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index f31df3aed2..3bdf10f475 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -70,9 +70,10 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); 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 Light::activate (const MWWorld::Ptr& ptr, @@ -141,9 +142,7 @@ namespace MWClass bool Light::hasToolTip (const MWWorld::ConstPtr& ptr) const { - const MWWorld::LiveCellRef *ref = ptr.get(); - - 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 *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 013b9eee34..ba3179971e 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -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. diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 33d4632cae..9b8abc8f23 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -47,8 +47,9 @@ namespace MWClass std::string Lockpick::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Lockpick::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index c8df860f67..20ca2d1666 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index af68f4743c..4eb3eabef3 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -56,8 +56,9 @@ namespace MWClass std::string Miscellaneous::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Miscellaneous::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); @@ -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().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; diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 77e7d105bf..4812deb5f8 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 868ab9d85d..5c8557dfd2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -531,7 +531,9 @@ namespace MWClass } const MWWorld::LiveCellRef *ref = ptr.get(); - 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(new MWWorld::ActionOpen(ptr)); + } + else if (!stats.getAiSequence().isInCombat()) + { + if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown()) + return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing - // death animation is not finished, do nothing - return std::shared_ptr (new MWWorld::FailedAction("")); + // Can't talk to werewolves + if (!getNpcStats(ptr).isWerewolf()) + return std::shared_ptr(new MWWorld::ActionTalk(ptr)); } - if(stats.getAiSequence().isInCombat()) - return std::shared_ptr(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(new MWWorld::ActionOpen(ptr)); - if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak) || stats.getKnockedDown()) - return std::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing - - // Can't talk to werewolfs - if(getNpcStats(ptr).isWerewolf()) - return std::shared_ptr (new MWWorld::FailedAction("")); - - return std::shared_ptr(new MWWorld::ActionTalk(ptr)); + return std::shared_ptr (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); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index e91dc7113b..9b92f63382 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -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. diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 5c40f17d3e..4af97e6345 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -49,8 +49,9 @@ namespace MWClass std::string Potion::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Potion::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 90dbe5281e..8ec4aef446 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 0a0c1548fa..dba4e8c063 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -47,8 +47,9 @@ namespace MWClass std::string Probe::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Probe::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 86f6f677a8..51a5f8231a 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 9050d9f94e..8907c8212e 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -44,8 +44,9 @@ namespace MWClass std::string Repair::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - bool Repair::hasItemHealth (const MWWorld::ConstPtr& ptr) const { return true; @@ -117,7 +111,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); 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; diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index f60dbad909..5d2cfb6828 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -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 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. diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index a6e4fe5e79..5551b3d731 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -45,6 +45,11 @@ namespace MWClass return ""; } + bool Static::hasToolTip(const MWWorld::ConstPtr& ptr) const + { + return false; + } + void Static::registerSelf() { std::shared_ptr instance (new Static); diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index 076c39cf10..6b3b8088c5 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -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(); diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 82740a2e19..0d6a27cf60 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -53,8 +53,9 @@ namespace MWClass std::string Weapon::getName (const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef *ref = ptr.get(); + const std::string& name = ref->mBase->mName; - return ref->mBase->mName; + return !name.empty() ? name : ref->mBase->mId; } std::shared_ptr 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 *ref = ptr.get(); - - return (ref->mBase->mName != ""); - } - MWGui::ToolTipInfo Weapon::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const { const MWWorld::LiveCellRef *ref = ptr.get(); 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(); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 423ba0dd27..2c1197b69e 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -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 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. diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index e85715e87f..2a167be2dc 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -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; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 3891cf8a26..d6d51d4463 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -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().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 > itemOwners = MWBase::Environment::get().getMechanicsManager()->getStolenItemOwners(cellref.getRefId()); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index edc45b8842..6e0f3375b5 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -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); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8cab2f00bd..86c50ab705 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -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(); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 9621a8619b..68b3bb9767 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -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); diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index e222c82bc0..c412a7ea1d 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -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; } diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 793bd89ea1..c476c9b576 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -1,5 +1,7 @@ #include "aiavoiddoor.hpp" +#include + #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; +} diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 7344a1797c..4c8be29ebb 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -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 diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9b6c1e0c3b..2dbbfea359 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -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) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 7cc6ea7843..5a80779da9 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -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(); } } diff --git a/apps/openmw/mwmechanics/levelledlist.hpp b/apps/openmw/mwmechanics/levelledlist.hpp index 45f7e86fec..697e2eda81 100644 --- a/apps/openmw/mwmechanics/levelledlist.hpp +++ b/apps/openmw/mwmechanics/levelledlist.hpp @@ -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& 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 candidates; @@ -76,9 +74,9 @@ namespace MWMechanics else { if (ref.getPtr().getTypeName() == typeid(ESM::ItemLevList).name()) - return getLevelledItem(ref.getPtr().get()->mBase, false, failChance); + return getLevelledItem(ref.getPtr().get()->mBase, false); else - return getLevelledItem(ref.getPtr().get()->mBase, true, failChance); + return getLevelledItem(ref.getPtr().get()->mBase, true); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7547f086b2..a1ba0a5e7e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -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 > 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& store = MWBase::Environment::get().getWorld()->getStore().get(); @@ -1466,6 +1470,14 @@ namespace MWMechanics player.getClass().getNpcStats(player).expell(factionID); } } + else if (!factionId.empty()) + { + const std::map& 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 diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 5a8826a6a5..0ac9092e8f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -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 &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); }; } diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index d09355ccab..cafc65b992 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -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); diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 359e74fc69..2d979e034a 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -696,7 +696,7 @@ namespace MWMechanics { if (caster == getPlayer()) MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); - target.getClass().lock(target, static_cast(magnitude)); + target.getCellRef().lock(static_cast(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); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c361ddf031..ac4fb3a169 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -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) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 49da50be36..fb94caec88 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -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); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 29a25c9978..906e5d0088 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -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); } }; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 847fe0bfc2..7ce3f1da39 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -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(); } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index 094669bddf..fb9fd592a9 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -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; diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index 5646bafb04..04e807ce59 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -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; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 191552bc5a..0287db56fa 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -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 diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 964ef19a5f..8c3740eddf 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -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 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; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 8c17dc0a9d..76555de02c 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -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()->mBase; - - if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each) - { - for (int i=0; i 0 ? 1 : -1, true, levItemList->mId); - return; - } - else - { - std::string itemId = MWMechanics::getLevelledItem(ref.getPtr().get()->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, 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()->mBase; + + if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each) + { + for (int i=0; i 0 ? 1 : -1, true, levItemList->mId); + return; + } + else + { + std::string itemId = MWMechanics::getLevelledItem(ptr.get()->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, 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) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b06e8b8ce6..1e1e4a844e 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -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 ContainerStoreIterator getState (CellRefList& collection, diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 9e2d9d6ff4..8e047677bc 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -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); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 4446502170..8ceab26b64 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1091,6 +1091,18 @@ namespace MWWorld return RecordId(dialogue.mId, isDeleted); } + template<> + bool Store::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; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 07680e76f3..c914acfba7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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 diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 76d0154777..f803079fbd 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -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; diff --git a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp index c94907e684..b02b8b9ef7 100644 --- a/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp +++ b/apps/openmw_test_suite/nifloader/testbulletnifloader.cpp @@ -208,18 +208,13 @@ namespace void init(Nif::Extra& value) { - value.extra = Nif::ExtraPtr(nullptr); - } - - void init(Nif::Controlled& value) - { - init(static_cast(value)); - value.controller = Nif::ControllerPtr(nullptr); + value.next = Nif::ExtraPtr(nullptr); } void init(Nif::Named& value) { - init(static_cast(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); diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index e3a306b8f3..017e363732 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -489,7 +489,7 @@ namespace Compiler parseArguments ("l", scanner); Generator::random (mCode); - mOperands.push_back ('l'); + mOperands.push_back ('f'); mNextOperand = false; return true; diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index 8041b0f024..ad88bb857b 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -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); } diff --git a/components/fallback/validate.hpp b/components/fallback/validate.hpp index a1d3f9601d..cef52e4624 100644 --- a/components/fallback/validate.hpp +++ b/components/fallback/validate.hpp @@ -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; } } } diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 3df6faf623..3bc6e17729 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -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; } diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 29d1c063b1..a77e0d7d89 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -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(Misc::Rng::rollDice(limit)); // [o, limit) } }; diff --git a/components/nif/base.hpp b/components/nif/base.hpp index 4b2e40dec0..f67de02216 100644 --- a/components/nif/base.hpp +++ b/components/nif/base.hpp @@ -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 diff --git a/components/nif/controlled.cpp b/components/nif/controlled.cpp index 52e7a7302f..51ccf85412 100644 --- a/components/nif/controlled.cpp +++ b/components/nif/controlled.cpp @@ -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(); } diff --git a/components/nif/controlled.hpp b/components/nif/controlled.hpp index be48e912ea..00ff45eda9 100644 --- a/components/nif/controlled.hpp +++ b/components/nif/controlled.hpp @@ -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); diff --git a/components/nif/controller.hpp b/components/nif/controller.hpp index 113a7becdd..52ab6f1f61 100644 --- a/components/nif/controller.hpp +++ b/components/nif/controller.hpp @@ -72,8 +72,8 @@ public: int activeCount; std::vector particles; - ExtraPtr affectors; - ExtraPtr colliders; + NiParticleModifierPtr affectors; + NiParticleModifierPtr colliders; void read(NIFStream *nif); void post(NIFFile *nif); diff --git a/components/nif/extra.cpp b/components/nif/extra.cpp index b7e2216688..cb654d5a0d 100644 --- a/components/nif/extra.cpp +++ b/components/nif/extra.cpp @@ -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; igetInt(); - int s = nif->getUShort(); - - nif->skip(s * sizeof(float)); // vertex weights I guess + nif->skip(nif->getUShort() * sizeof(float)); // vertex weights I guess } diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index e23beb7869..fbd148a046 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -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 NodePtr; typedef RecordPtrT ExtraPtr; @@ -148,7 +149,7 @@ typedef RecordPtrT NiUVDataPtr; typedef RecordPtrT NiPosDataPtr; typedef RecordPtrT NiVisDataPtr; typedef RecordPtrT ControllerPtr; -typedef RecordPtrT ControlledPtr; +typedef RecordPtrT NamedPtr; typedef RecordPtrT NiSkinDataPtr; typedef RecordPtrT NiMorphDataPtr; typedef RecordPtrT NiPixelDataPtr; @@ -162,6 +163,7 @@ typedef RecordPtrT NiSourceTexturePtr; typedef RecordPtrT NiRotatingParticlesDataPtr; typedef RecordPtrT NiAutoNormalParticlesDataPtr; typedef RecordPtrT NiPalettePtr; +typedef RecordPtrT NiParticleModifierPtr; typedef RecordListT NodeList; typedef RecordListT PropertyList; diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 2f24a40674..91f8494210 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -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(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) { diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 4029e9b158..1842e00170 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -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 ©, const osg::CopyOp ©op) : 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(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); + } + } } } diff --git a/components/nifosg/controller.hpp b/components/nifosg/controller.hpp index 36217f31a3..d5fb56f0ef 100644 --- a/components/nifosg/controller.hpp +++ b/components/nifosg/controller.hpp @@ -281,11 +281,15 @@ namespace NifOsg class MaterialColorController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller { - private: - Vec3Interpolator mData; - public: - MaterialColorController(const Nif::NiPosData *data); + enum TargetColor + { + Ambient = 0, + Diffuse = 1, + Specular = 2, + Emissive = 3 + }; + MaterialColorController(const Nif::NiPosData *data, TargetColor color); MaterialColorController(); MaterialColorController(const MaterialColorController& copy, const osg::CopyOp& copyop); @@ -294,6 +298,10 @@ namespace NifOsg virtual void setDefaults(osg::StateSet* stateset); virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv); + + private: + Vec3Interpolator mData; + TargetColor mTargetColor; }; class FlipController : public SceneUtil::StateSetUpdater, public SceneUtil::Controller diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 568286a4d6..58e336b5d7 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -222,9 +222,9 @@ namespace NifOsg extractTextKeys(static_cast(extra.getPtr()), target.mTextKeys); - extra = extra->extra; + extra = extra->next; Nif::ControllerPtr ctrl = seq->controller; - for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + for(;!extra.empty() && !ctrl.empty();(extra=extra->next),(ctrl=ctrl->next)) { if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) { @@ -524,7 +524,7 @@ namespace NifOsg node->getOrCreateUserDataContainer()->addUserObject( new NodeUserData(nifNode->recIndex, nifNode->trafo.scale, nifNode->trafo.rotation)); - for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->extra) + for (Nif::ExtraPtr e = nifNode->extra; !e.empty(); e = e->next) { if(e->recType == Nif::RC_NiTextKeyExtraData && textKeys) { @@ -750,7 +750,13 @@ namespace NifOsg else if (ctrl->recType == Nif::RC_NiMaterialColorController) { const Nif::NiMaterialColorController* matctrl = static_cast(ctrl.getPtr()); - osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr())); + // Two bits that correspond to the controlled material color. + // 00: Ambient + // 01: Diffuse + // 10: Specular + // 11: Emissive + MaterialColorController::TargetColor targetColor = static_cast((matctrl->flags >> 4) & 3); + osg::ref_ptr osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor)); setupController(matctrl, osgctrl, animflags); composite->addController(osgctrl); } @@ -802,13 +808,13 @@ namespace NifOsg } } - void handleParticlePrograms(Nif::ExtraPtr affectors, Nif::ExtraPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) + void handleParticlePrograms(Nif::NiParticleModifierPtr affectors, Nif::NiParticleModifierPtr colliders, osg::Group *attachTo, osgParticle::ParticleSystem* partsys, osgParticle::ParticleProcessor::ReferenceFrame rf) { osgParticle::ModularProgram* program = new osgParticle::ModularProgram; attachTo->addChild(program); program->setParticleSystem(partsys); program->setReferenceFrame(rf); - for (; !affectors.empty(); affectors = affectors->extra) + for (; !affectors.empty(); affectors = affectors->next) { if (affectors->recType == Nif::RC_NiParticleGrowFade) { @@ -833,7 +839,7 @@ namespace NifOsg else Log(Debug::Info) << "Unhandled particle modifier " << affectors->recName << " in " << mFilename; } - for (; !colliders.empty(); colliders = colliders->extra) + for (; !colliders.empty(); colliders = colliders->next) { if (colliders->recType == Nif::RC_NiPlanarCollider) { diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index b2f119b88f..b2cb7a3b80 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -210,7 +210,6 @@ public: private: Terrain::Storage* mStorage; - float mLodFactor; float mMinX, mMaxX, mMinY, mMaxY; float mMinSize; diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 9b207c448f..8bcc0522b8 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -1,5 +1,5 @@ -Shadow Settings -############### +Shadows Settings +################ Main settings ************* diff --git a/files/openmw.cfg b/files/openmw.cfg index d71b6b53d5..afbf0810cc 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -13,6 +13,8 @@ script-blacklist=wereDreamScript2 script-blacklist=wereDreamScript3 # lighting +fallback=LightAttenuation_UseConstant,0 +fallback=LightAttenuation_ConstantValue,0.0 fallback=LightAttenuation_UseLinear,1 fallback=LightAttenuation_LinearMethod,1 fallback=LightAttenuation_LinearValue,3.0 diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index 804d168b30..76f829379b 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -12,3 +12,490 @@ script-blacklist=doortestwarp script-blacklist=WereChange2Script script-blacklist=wereDreamScript2 script-blacklist=wereDreamScript3 + +# lighting +fallback=LightAttenuation_UseConstant,0 +fallback=LightAttenuation_ConstantValue,0.0 +fallback=LightAttenuation_UseLinear,1 +fallback=LightAttenuation_LinearMethod,1 +fallback=LightAttenuation_LinearValue,3.0 +fallback=LightAttenuation_LinearRadiusMult,1.0 +fallback=LightAttenuation_UseQuadratic,0 +fallback=LightAttenuation_QuadraticMethod,2 +fallback=LightAttenuation_QuadraticValue,16.0 +fallback=LightAttenuation_QuadraticRadiusMult,1.0 +fallback=LightAttenuation_OutQuadInLin,0 + +# inventory +fallback=Inventory_DirectionalDiffuseR,1.0 +fallback=Inventory_DirectionalDiffuseG,1.0 +fallback=Inventory_DirectionalDiffuseB,1.0 +fallback=Inventory_DirectionalAmbientR,0.0 +fallback=Inventory_DirectionalAmbientG,0.0 +fallback=Inventory_DirectionalAmbientB,0.0 +fallback=Inventory_DirectionalRotationX,110 +fallback=Inventory_DirectionalRotationY,90 +fallback=Inventory_UniformScaling,0 + +# water effects +fallback=Water_Map_Alpha,0.4 +fallback=Water_World_Alpha,0.75 +fallback=Water_SurfaceTextureSize,128 +fallback=Water_SurfaceTileCount,10 +fallback=Water_SurfaceFPS,12 +fallback=Water_SurfaceTexture,water +fallback=Water_SurfaceFrameCount,32 +fallback=Water_TileTextureDivisor,4.75 +fallback=Water_RippleTexture,ripple +fallback=Water_RippleFrameCount,4 +fallback=Water_RippleLifetime,3.0 +fallback=Water_MaxNumberRipples,75 +fallback=Water_RippleScale,0.15, 6.5 +fallback=Water_RippleRotSpeed,0.5 +fallback=Water_RippleAlphas,0.7, 0.1, 0.01 +fallback=Water_NearWaterRadius,1000 +fallback=Water_NearWaterPoints,8 +fallback=Water_NearWaterUnderwaterFreq,0.3 +fallback=Water_NearWaterUnderwaterVolume,0.9 +fallback=Water_NearWaterIndoorTolerance,512.0 +fallback=Water_NearWaterOutdoorTolerance,1024.0 +fallback=Water_NearWaterIndoorID,Water Layer +fallback=Water_NearWaterOutdoorID,Water Layer +fallback=Water_UnderwaterSunriseFog,3 +fallback=Water_UnderwaterDayFog,2.5 +fallback=Water_UnderwaterSunsetFog,3 +fallback=Water_UnderwaterNightFog,4 +fallback=Water_UnderwaterIndoorFog,3 +fallback=Water_UnderwaterColor,012,030,037 +fallback=Water_UnderwaterColorWeight,0.85 + +# fonts +fallback=Fonts_Font_0,magic_cards_regular +fallback=Fonts_Font_1,century_gothic_font_regular +fallback=Fonts_Font_2,daedric_font +fallback=FontColor_color_normal,202,165,96 +fallback=FontColor_color_normal_over,223,201,159 +fallback=FontColor_color_normal_pressed,243,237,221 +fallback=FontColor_color_active,96,112,202 +fallback=FontColor_color_active_over,159,169,223 +fallback=FontColor_color_active_pressed,223,226,244 +fallback=FontColor_color_disabled,179,168,135 +fallback=FontColor_color_disabled_over,223,201,159 +fallback=FontColor_color_disabled_pressed,243,237,221 +fallback=FontColor_color_link,112,126,207 +fallback=FontColor_color_link_over,143,155,218 +fallback=FontColor_color_link_pressed,175,184,228 +fallback=FontColor_color_journal_link,37,49,112 +fallback=FontColor_color_journal_link_over,58,77,175 +fallback=FontColor_color_journal_link_pressed,112,126,207 +fallback=FontColor_color_journal_topic,0,0,0 +fallback=FontColor_color_journal_topic_over,58,77,175 +fallback=FontColor_color_journal_topic_pressed,112,126,207 +fallback=FontColor_color_answer,150,50,30 +fallback=FontColor_color_answer_over,223,201,159 +fallback=FontColor_color_answer_pressed,243,237,221 +fallback=FontColor_color_header,223,201,159 +fallback=FontColor_color_notify,223,201,159 +fallback=FontColor_color_big_normal,202,165,96 +fallback=FontColor_color_big_normal_over,223,201,159 +fallback=FontColor_color_big_normal_pressed,243,237,221 +fallback=FontColor_color_big_link,112,126,207 +fallback=FontColor_color_big_link_over,143,155,218 +fallback=FontColor_color_big_link_pressed,175,184,228 +fallback=FontColor_color_big_answer,150,50,30 +fallback=FontColor_color_big_answer_over,223,201,159 +fallback=FontColor_color_big_answer_pressed,243,237,22 +fallback=FontColor_color_big_header,223,201,159 +fallback=FontColor_color_big_notify,223,201,159 +fallback=FontColor_color_background,0,0,0 +fallback=FontColor_color_focus,80,80,80 +fallback=FontColor_color_health,200,60,30 +fallback=FontColor_color_magic,53,69,159 +fallback=FontColor_color_fatigue,0,150,60 +fallback=FontColor_color_misc,0,205,205 +fallback=FontColor_color_weapon_fill,200,60,30 +fallback=FontColor_color_magic_fill,200,60,30 +fallback=FontColor_color_positive,223,201,159 +fallback=FontColor_color_negative,200,60,30 +fallback=FontColor_color_count,223,201,159 + +# leveling +fallback=Level_Up_Level2,The last few days have opened your eyes: you realize now that there is more to the life than you thought. An uncertain future awaits you. +fallback=Level_Up_Level3,You finally come to understand that great doings start from hard work and determination. +fallback=Level_Up_Level4,After reminiscing upon all that time you lost in your youth, it's as clear as day to you now: this - this is the life, and all the experience you gain gives you the tools you need to succeed in it. +fallback=Level_Up_Level5,Things seem to be working out for you. As you become more capable, the world is opening up. +fallback=Level_Up_Level6,With every push you did to gain knowledge, and yet your thirst for it increased, you realize there is so much more to learn still. +fallback=Level_Up_Level7,You dig deep within yourself, trying to finally become the one who you thought you would be... but there's much to keep digging through. +fallback=Level_Up_Level8,Success doesn't come easily, but you make it appear that way to others. +fallback=Level_Up_Level9,Everything may slowly become a second nature to you, but it also can turn just a bit more dangerous. +fallback=Level_Up_Level10,Nothing in life is easy, or it wouldn't have been worth the blood, sweat and tears. +fallback=Level_Up_Level11,Working smarter, not harder is something even a barbarian could benefit from. +fallback=Level_Up_Level12,Perhaps some would call you delusional for keeping at it, but you know better. Your dilegence has already paid off, after all. +fallback=Level_Up_Level13,One night's sleep is all the difference between something being difficult and something being easy. +fallback=Level_Up_Level14,After waking up, you wonder: could today be the best day in your life? You never know. +fallback=Level_Up_Level15,Ouch! You lean back feeling your whole body for what seems the first time. You'll be more mindful in the future -- you can only live once. +fallback=Level_Up_Level16,Trusting your instincts has gotten you this far, but now it's clear to you that you need to be smarter to survive. +fallback=Level_Up_Level17,You're forging your spirit in the crucible that is experience. Be ever vigilant. +fallback=Level_Up_Level18,The frustrations of the day before vanish as you wake up. Today is a new day. +fallback=Level_Up_Level19,Today isn't yesterday, you feel that deep inside. You know that there is still more to this life. +fallback=Level_Up_Level20,Luck has never been a factor in your success -- that's obvious from the scars on your body and from the number of trails you've endured. +fallback=Level_Up_Default,Through sheer force of will, you push onwards and upwards. The toil of your persistence is paying off. + +# character creation +fallback=Question_1_Question,Before you lies a creature you have never seen before. Its hind leg is caught in a hunter's trap, and you can tell that the leg is broken. What will you do? +fallback=Question_1_AnswerOne,Pull out you knife for a short and merciful kill. +fallback=Question_1_AnswerTwo,Reach into your herbal pouch to find something to ease its suffering before putting it to sleep. +fallback=Question_1_AnswerThree,Leave the leg alone, but take the time to observe and learn from this odd creature. +fallback=Question_1_Sound,Voice\CharGen\QA1.mp3 +fallback=Question_2_Question,Your mother suggests you to help with work around your family household. Do you decide to... +fallback=Question_2_AnswerOne,Help your father with fence post replacement? +fallback=Question_2_AnswerTwo,Collect a few herbs in the forest for tonight's supper? +fallback=Question_2_AnswerThree,Help the fish to find their way into the kitchen? +fallback=Question_2_Sound,Voice\CharGen\QA2.mp3 +fallback=Question_3_Question,Your brother teases you mercilessly in front of everyone with embarrassing details you would rather have forgotten. +fallback=Question_3_AnswerOne,Give him a black-eye and dare him to keep it up. +fallback=Question_3_AnswerTwo,Beat him to the punch and play it up -- if you can control the negative, then he can't embarrass you. +fallback=Question_3_AnswerThree,Wait until he sleeps, put his hand in a bowl of water and call everyone around the next day to see the results. +fallback=Question_3_Sound,Voice\CharGen\QA3.mp3 +fallback=Question_4_Question,Rumor has it that the King's security console has a new tool at their disposal for sniffing out the truth, which could be used for reading minds. +fallback=Question_4_AnswerOne,You recoil at the thought of someone being able to read your thoughts. It's not that you think something that you'll ever act on it. +fallback=Question_4_AnswerTwo,For those who have done nothing, there is nothing to fear. This is just yet another utility for catching thieves, murderers and plots against the crown. +fallback=Question_4_AnswerThree,While you loath the idea of someone reading your mind, you accept that it does have its uses if tempered by law and common sense. You wouldn't believe the mind of a madman. +fallback=Question_4_Sound,Voice\CharGen\QA4.mp3 +fallback=Question_5_Question,You are returning home from the market after acquiring supplies, and notice that one of the merchants had given you too much back in change. +fallback=Question_5_AnswerOne,How dreadful, what if it was you? You head back to the merchant. +fallback=Question_5_AnswerTwo,Happy days indeed. Put that extra money towards the needs of your family? +fallback=Question_5_AnswerThree,You win some and you lose some. In this case you have won and they have lost, and the oversight is the merchant's problem, not yours, right? +fallback=Question_5_Sound,Voice\CharGen\QA5.mp3 +fallback=Question_6_Question,While at market, a noble yells out to the city watch about a theft. As you turn around a man bumps into you and drops a sack, startled he darts into the crowd. +fallback=Question_6_AnswerOne,You pick up the sack and head over to the noble to return his property? +fallback=Question_6_AnswerTwo,Just walk away, the last thing you need is someone thinking you had anything to do with it? +fallback=Question_6_AnswerThree,Finders, keepers... you whistle as you walk away with the bag hidden? +fallback=Question_6_Sound,Voice\CharGen\QA6.mp3 +fallback=Question_7_Question,If it's one thing you hate, it's cleaning out the stalls. It has to be done before sunset before the critters return. On your way there an old friend greets you and offers to help if you promise to help them in the future. +fallback=Question_7_AnswerOne,You thank him for the offer but would rather get it over with and not make promises you can't keep? +fallback=Question_7_AnswerTwo,You reason that two pairs of hands are better than one regardless of the task? +fallback=Question_7_AnswerThree,You say that it sounds great and anything is better than cleaning the stalls? +fallback=Question_7_Sound,Voice\CharGen\QA7.mp3 +fallback=Question_8_Question,You just climbed down the ladder after working on the roof. Your mother thanks you for the hard work but just at the moment you notice that a hammer is about to fall down on her head. +fallback=Question_8_AnswerOne,You lunge at your mother, pushing her out the way while the hammer falls on top of you? +fallback=Question_8_AnswerTwo,Try to use the ladder to intercept the hammer before it lands on her? +fallback=Question_8_AnswerThree,Warn her to take a step back? +fallback=Question_8_Sound,Voice\CharGen\QA8.mp3 +fallback=Question_9_Question,It's the end of the week, and you have just got your wages for your hard work. You decide to take the quick way back home, darting into a alley only to be confronted by ruffians who demand that you empty your pockets. +fallback=Question_9_AnswerOne,You tell them to go pack sand, planting your feet and raising your fists? +fallback=Question_9_AnswerTwo,Acting dejected, you turn your wages over. You know that you can count on your friends to help you track these brigands down and recover what's yours? +fallback=Question_9_AnswerThree,Tossing the sack into the air, you charge the leader who's attention is squarely focused on the coins in flight? +fallback=Question_9_Sound,Voice\CharGen\QA9.mp3 +fallback=Question_10_Question,Upon coming to town, you see a clown running at you with an angry mob following after him. +fallback=Question_10_AnswerOne,Stand your ground, you hate clowns? +fallback=Question_10_AnswerTwo,Move out of the way, letting fate decide the clown's fate? +fallback=Question_10_AnswerThree,Come to the clown's aid, clowns are people too? +fallback=Question_10_Sound,Voice\CharGen\QA10.mp3 + +# blood splatters +fallback=Blood_Model_0,BloodSplat.nif +fallback=Blood_Model_1,BloodSplat2.nif +fallback=Blood_Model_2,BloodSplat3.nif +fallback=Blood_Texture_0,Tx_Blood.dds +fallback=Blood_Texture_1,Tx_Blood_White.dds +fallback=Blood_Texture_2,Tx_Blood_Gold.dds +fallback=Blood_Texture_Name_0,Default (Red) +fallback=Blood_Texture_Name_1,Skeleton (White) +fallback=Blood_Texture_Name_2,Metal Sparks (Gold) + +# movies +fallback=Movies_Project_Logo,openmw_project_logo.webm +fallback=Movies_Game_Logo,openmw_example_suite_logo.webm +fallback=Movies_New_Game,new_game.webm +fallback=Movies_Loading,loading.webm +fallback=Movies_Options_Menu,options_menu.webm + +# weather +fallback=Weather_Thunderstorm_Thunder_Sound_ID_0,Thunder0 +fallback=Weather_Thunderstorm_Thunder_Sound_ID_1,Thunder1 +fallback=Weather_Thunderstorm_Thunder_Sound_ID_2,Thunder2 +fallback=Weather_Thunderstorm_Thunder_Sound_ID_3,Thunder3 +fallback=Weather_Sunrise_Time,6 +fallback=Weather_Sunset_Time,18 +fallback=Weather_Sunrise_Duration,2 +fallback=Weather_Sunset_Duration,2 +fallback=Weather_Hours_Between_Weather_Changes,20 +fallback=Weather_Thunderstorm_Thunder_Frequency,.4 +fallback=Weather_Thunderstorm_Thunder_Threshold,0.6 +fallback=Weather_EnvReduceColor,255,255,255,255 +fallback=Weather_LerpCloseColor,037,046,048,255 +fallback=Weather_BumpFadeColor,230,239,255,255 +fallback=Weather_AlphaReduce,0.35 +fallback=Weather_Minimum_Time_Between_Environmental_Sounds,1.0 +fallback=Weather_Maximum_Time_Between_Environmental_Sounds,5.0 +fallback=Weather_Sun_Glare_Fader_Max,0.5 +fallback=Weather_Sun_Glare_Fader_Angle_Max,30.0 +fallback=Weather_Sun_Glare_Fader_Color,222,095,039 +fallback=Weather_Timescale_Clouds,0 +fallback=Weather_Precip_Gravity,575 +fallback=Weather_Rain_Ripples,1 +fallback=Weather_Rain_Ripple_Radius,1024 +fallback=Weather_Rain_Ripples_Per_Drop,1 +fallback=Weather_Rain_Ripple_Scale,0.3 +fallback=Weather_Rain_Ripple_Speed,1.0 +fallback=Weather_Fog_Depth_Change_Speed,3 +fallback=Weather_Sky_Pre-Sunrise_Time,.5 +fallback=Weather_Sky_Post-Sunrise_Time,1 +fallback=Weather_Sky_Pre-Sunset_Time,1.5 +fallback=Weather_Sky_Post-Sunset_Time,.5 +fallback=Weather_Ambient_Pre-Sunrise_Time,.5 +fallback=Weather_Ambient_Post-Sunrise_Time,2 +fallback=Weather_Ambient_Pre-Sunset_Time,1 +fallback=Weather_Ambient_Post-Sunset_Time,1.25 +fallback=Weather_Fog_Pre-Sunrise_Time,.5 +fallback=Weather_Fog_Post-Sunrise_Time,1 +fallback=Weather_Fog_Pre-Sunset_Time,2 +fallback=Weather_Fog_Post-Sunset_Time,1 +fallback=Weather_Sun_Pre-Sunrise_Time,0 +fallback=Weather_Sun_Post-Sunrise_Time,0 +fallback=Weather_Sun_Pre-Sunset_Time,1 +fallback=Weather_Sun_Post-Sunset_Time,1.25 +fallback=Weather_Stars_Post-Sunset_Start,1 +fallback=Weather_Stars_Pre-Sunrise_Finish,2 +fallback=Weather_Stars_Fading_Duration,2 +fallback=Weather_Snow_Ripples,0 +fallback=Weather_Snow_Ripple_Radius,1024 +fallback=Weather_Snow_Ripples_Per_Flake,1 +fallback=Weather_Snow_Ripple_Scale,0.3 +fallback=Weather_Snow_Ripple_Speed,1.0 +fallback=Weather_Snow_Gravity_Scale,0.1 +fallback=Weather_Snow_High_Kill,700 +fallback=Weather_Snow_Low_Kill,150 +fallback=Weather_Clear_Cloud_Texture,Tx_Sky_Clear.dds +fallback=Weather_Clear_Clouds_Maximum_Percent,1.0 +fallback=Weather_Clear_Transition_Delta,.015 +fallback=Weather_Clear_Sky_Sunrise_Color,117,141,164 +fallback=Weather_Clear_Sky_Day_Color,095,135,203 +fallback=Weather_Clear_Sky_Sunset_Color,056,089,129 +fallback=Weather_Clear_Sky_Night_Color,009,010,011 +fallback=Weather_Clear_Fog_Sunrise_Color,255,189,157 +fallback=Weather_Clear_Fog_Day_Color,206,227,255 +fallback=Weather_Clear_Fog_Sunset_Color,255,189,157 +fallback=Weather_Clear_Fog_Night_Color,009,010,011 +fallback=Weather_Clear_Ambient_Sunrise_Color,047,066,096 +fallback=Weather_Clear_Ambient_Day_Color,137,140,160 +fallback=Weather_Clear_Ambient_Sunset_Color,068,075,096 +fallback=Weather_Clear_Ambient_Night_Color,032,035,042 +fallback=Weather_Clear_Sun_Sunrise_Color,242,159,119 +fallback=Weather_Clear_Sun_Day_Color,255,252,238 +fallback=Weather_Clear_Sun_Sunset_Color,255,114,079 +fallback=Weather_Clear_Sun_Night_Color,059,097,176 +fallback=Weather_Clear_Sun_Disc_Sunset_Color,255,189,157 +fallback=Weather_Clear_Land_Fog_Day_Depth,.69 +fallback=Weather_Clear_Land_Fog_Night_Depth,.69 +fallback=Weather_Clear_Wind_Speed,.1 +fallback=Weather_Clear_Cloud_Speed,1.25 +fallback=Weather_Clear_Glare_View,1 +fallback=Weather_Clear_Ambient_Loop_Sound_ID,None +fallback=Weather_Cloudy_Cloud_Texture,Tx_Sky_Cloudy.dds +fallback=Weather_Cloudy_Clouds_Maximum_Percent,1.0 +fallback=Weather_Cloudy_Transition_Delta,.015 +fallback=Weather_Cloudy_Sky_Sunrise_Color,126,158,173 +fallback=Weather_Cloudy_Sky_Day_Color,117,160,215 +fallback=Weather_Cloudy_Sky_Sunset_Color,111,114,159 +fallback=Weather_Cloudy_Sky_Night_Color,009,010,011 +fallback=Weather_Cloudy_Fog_Sunrise_Color,255,207,149 +fallback=Weather_Cloudy_Fog_Day_Color,245,235,224 +fallback=Weather_Cloudy_Fog_Sunset_Color,255,155,106 +fallback=Weather_Cloudy_Fog_Night_Color,009,010,011 +fallback=Weather_Cloudy_Ambient_Sunrise_Color,066,074,087 +fallback=Weather_Cloudy_Ambient_Day_Color,137,145,160 +fallback=Weather_Cloudy_Ambient_Sunset_Color,071,080,092 +fallback=Weather_Cloudy_Ambient_Night_Color,032,039,054 +fallback=Weather_Cloudy_Sun_Sunrise_Color,241,177,099 +fallback=Weather_Cloudy_Sun_Day_Color,255,236,221 +fallback=Weather_Cloudy_Sun_Sunset_Color,255,089,000 +fallback=Weather_Cloudy_Sun_Night_Color,077,091,124 +fallback=Weather_Cloudy_Sun_Disc_Sunset_Color,255,202,179 +fallback=Weather_Cloudy_Land_Fog_Day_Depth,.72 +fallback=Weather_Cloudy_Land_Fog_Night_Depth,.72 +fallback=Weather_Cloudy_Wind_Speed,.2 +fallback=Weather_Cloudy_Cloud_Speed,2 +fallback=Weather_Cloudy_Glare_View,1 +fallback=Weather_Cloudy_Ambient_Loop_Sound_ID,None +fallback=Weather_Foggy_Cloud_Texture,Tx_Sky_Foggy.dds +fallback=Weather_Foggy_Clouds_Maximum_Percent,1.0 +fallback=Weather_Foggy_Transition_Delta,.015 +fallback=Weather_Foggy_Sky_Sunrise_Color,197,190,180 +fallback=Weather_Foggy_Sky_Day_Color,184,211,228 +fallback=Weather_Foggy_Sky_Sunset_Color,142,159,176 +fallback=Weather_Foggy_Sky_Night_Color,018,023,028 +fallback=Weather_Foggy_Fog_Sunrise_Color,173,164,148 +fallback=Weather_Foggy_Fog_Day_Color,150,187,209 +fallback=Weather_Foggy_Fog_Sunset_Color,113,135,157 +fallback=Weather_Foggy_Fog_Night_Color,019,024,029 +fallback=Weather_Foggy_Ambient_Sunrise_Color,048,043,037 +fallback=Weather_Foggy_Ambient_Day_Color,092,109,120 +fallback=Weather_Foggy_Ambient_Sunset_Color,029,053,076 +fallback=Weather_Foggy_Ambient_Night_Color,028,033,039 +fallback=Weather_Foggy_Sun_Sunrise_Color,177,162,137 +fallback=Weather_Foggy_Sun_Day_Color,111,131,151 +fallback=Weather_Foggy_Sun_Sunset_Color,125,157,189 +fallback=Weather_Foggy_Sun_Night_Color,081,100,119 +fallback=Weather_Foggy_Sun_Disc_Sunset_Color,223,223,223 +fallback=Weather_Foggy_Land_Fog_Day_Depth,1.0 +fallback=Weather_Foggy_Land_Fog_Night_Depth,1.9 +fallback=Weather_Foggy_Wind_Speed,0 +fallback=Weather_Foggy_Cloud_Speed,1.25 +fallback=Weather_Foggy_Glare_View,0.25 +fallback=Weather_Foggy_Ambient_Loop_Sound_ID,None +fallback=Weather_Thunderstorm_Cloud_Texture,Tx_Sky_Thunder.dds +fallback=Weather_Thunderstorm_Clouds_Maximum_Percent,0.66 +fallback=Weather_Thunderstorm_Transition_Delta,.030 +fallback=Weather_Thunderstorm_Sky_Sunrise_Color,035,036,039 +fallback=Weather_Thunderstorm_Sky_Day_Color,097,104,115 +fallback=Weather_Thunderstorm_Sky_Sunset_Color,035,036,039 +fallback=Weather_Thunderstorm_Sky_Night_Color,019,020,022 +fallback=Weather_Thunderstorm_Fog_Sunrise_Color,070,074,085 +fallback=Weather_Thunderstorm_Fog_Day_Color,097,104,115 +fallback=Weather_Thunderstorm_Fog_Sunset_Color,070,074,085 +fallback=Weather_Thunderstorm_Fog_Night_Color,019,020,022 +fallback=Weather_Thunderstorm_Ambient_Sunrise_Color,054,054,054 +fallback=Weather_Thunderstorm_Ambient_Day_Color,090,090,090 +fallback=Weather_Thunderstorm_Ambient_Sunset_Color,054,054,054 +fallback=Weather_Thunderstorm_Ambient_Night_Color,049,051,054 +fallback=Weather_Thunderstorm_Sun_Sunrise_Color,091,099,122 +fallback=Weather_Thunderstorm_Sun_Day_Color,138,144,155 +fallback=Weather_Thunderstorm_Sun_Sunset_Color,096,101,117 +fallback=Weather_Thunderstorm_Sun_Night_Color,055,076,110 +fallback=Weather_Thunderstorm_Sun_Disc_Sunset_Color,128,128,128 +fallback=Weather_Thunderstorm_Land_Fog_Day_Depth,1 +fallback=Weather_Thunderstorm_Land_Fog_Night_Depth,1.15 +fallback=Weather_Thunderstorm_Wind_Speed,.5 +fallback=Weather_Thunderstorm_Cloud_Speed,3 +fallback=Weather_Thunderstorm_Glare_View,0 +fallback=Weather_Thunderstorm_Rain_Loop_Sound_ID,rain heavy +fallback=Weather_Thunderstorm_Using_Precip,1 +fallback=Weather_Thunderstorm_Rain_Diameter,600 +fallback=Weather_Thunderstorm_Rain_Height_Min,200 +fallback=Weather_Thunderstorm_Rain_Height_Max,700 +fallback=Weather_Thunderstorm_Rain_Threshold,0.6 +fallback=Weather_Thunderstorm_Max_Raindrops,650 +fallback=Weather_Thunderstorm_Rain_Entrance_Speed,5 +fallback=Weather_Thunderstorm_Ambient_Loop_Sound_ID,None +fallback=Weather_Thunderstorm_Flash_Decrement,4 +fallback=Weather_Rain_Cloud_Texture,Tx_Sky_Rainy.dds +fallback=Weather_Rain_Clouds_Maximum_Percent,0.66 +fallback=Weather_Rain_Transition_Delta,.015 +fallback=Weather_Rain_Sky_Sunrise_Color,071,074,075 +fallback=Weather_Rain_Sky_Day_Color,116,120,122 +fallback=Weather_Rain_Sky_Sunset_Color,073,073,073 +fallback=Weather_Rain_Sky_Night_Color,024,025,026 +fallback=Weather_Rain_Fog_Sunrise_Color,071,074,075 +fallback=Weather_Rain_Fog_Day_Color,116,120,122 +fallback=Weather_Rain_Fog_Sunset_Color,073,073,073 +fallback=Weather_Rain_Fog_Night_Color,024,025,026 +fallback=Weather_Rain_Ambient_Sunrise_Color,097,090,088 +fallback=Weather_Rain_Ambient_Day_Color,105,110,113 +fallback=Weather_Rain_Ambient_Sunset_Color,088,097,097 +fallback=Weather_Rain_Ambient_Night_Color,050,055,067 +fallback=Weather_Rain_Sun_Sunrise_Color,131,122,120 +fallback=Weather_Rain_Sun_Day_Color,149,157,170 +fallback=Weather_Rain_Sun_Sunset_Color,120,126,131 +fallback=Weather_Rain_Sun_Night_Color,050,062,101 +fallback=Weather_Rain_Sun_Disc_Sunset_Color,128,128,128 +fallback=Weather_Rain_Land_Fog_Day_Depth,.8 +fallback=Weather_Rain_Land_Fog_Night_Depth,.8 +fallback=Weather_Rain_Wind_Speed,.3 +fallback=Weather_Rain_Cloud_Speed,2 +fallback=Weather_Rain_Glare_View,0 +fallback=Weather_Rain_Rain_Loop_Sound_ID,Rain +fallback=Weather_Rain_Using_Precip,1 +fallback=Weather_Rain_Rain_Diameter,600 +fallback=Weather_Rain_Rain_Height_Min,200 +fallback=Weather_Rain_Rain_Height_Max,700 +fallback=Weather_Rain_Rain_Threshold,0.6 +fallback=Weather_Rain_Rain_Entrance_Speed,7 +fallback=Weather_Rain_Ambient_Loop_Sound_ID,None +fallback=Weather_Rain_Max_Raindrops,450 +fallback=Weather_Overcast_Cloud_Texture,Tx_Sky_Overcast.dds +fallback=Weather_Overcast_Clouds_Maximum_Percent,1.0 +fallback=Weather_Overcast_Transition_Delta,.015 +fallback=Weather_Overcast_Sky_Sunrise_Color,091,099,106 +fallback=Weather_Overcast_Sky_Day_Color,143,146,149 +fallback=Weather_Overcast_Sky_Sunset_Color,108,115,121 +fallback=Weather_Overcast_Sky_Night_Color,019,022,025 +fallback=Weather_Overcast_Fog_Sunrise_Color,091,099,106 +fallback=Weather_Overcast_Fog_Day_Color,143,146,149 +fallback=Weather_Overcast_Fog_Sunset_Color,108,115,121 +fallback=Weather_Overcast_Fog_Night_Color,019,022,025 +fallback=Weather_Overcast_Ambient_Sunrise_Color,084,088,092 +fallback=Weather_Overcast_Ambient_Day_Color,093,096,105 +fallback=Weather_Overcast_Ambient_Sunset_Color,083,077,075 +fallback=Weather_Overcast_Ambient_Night_Color,057,060,066 +fallback=Weather_Overcast_Sun_Sunrise_Color,087,125,163 +fallback=Weather_Overcast_Sun_Day_Color,163,169,183 +fallback=Weather_Overcast_Sun_Sunset_Color,085,103,157 +fallback=Weather_Overcast_Sun_Night_Color,032,054,100 +fallback=Weather_Overcast_Sun_Disc_Sunset_Color,128,128,128 +fallback=Weather_Overcast_Land_Fog_Day_Depth,.70 +fallback=Weather_Overcast_Land_Fog_Night_Depth,.70 +fallback=Weather_Overcast_Wind_Speed,.2 +fallback=Weather_Overcast_Cloud_Speed,1.5 +fallback=Weather_Overcast_Glare_View,0 +fallback=Weather_Overcast_Ambient_Loop_Sound_ID,None +fallback=Weather_Snow_Cloud_Texture,Tx_Sky_Snow.dds +fallback=Weather_Snow_Clouds_Maximum_Percent,1.0 +fallback=Weather_Snow_Transition_Delta,.015 +fallback=Weather_Snow_Sky_Sunrise_Color,106,091,091 +fallback=Weather_Snow_Sky_Day_Color,153,158,166 +fallback=Weather_Snow_Sky_Sunset_Color,096,115,134 +fallback=Weather_Snow_Sky_Night_Color,031,035,039 +fallback=Weather_Snow_Fog_Sunrise_Color,106,091,091 +fallback=Weather_Snow_Fog_Day_Color,153,158,166 +fallback=Weather_Snow_Fog_Sunset_Color,096,115,134 +fallback=Weather_Snow_Fog_Night_Color,031,035,039 +fallback=Weather_Snow_Ambient_Sunrise_Color,092,084,084 +fallback=Weather_Snow_Ambient_Day_Color,093,096,105 +fallback=Weather_Snow_Ambient_Sunset_Color,070,079,087 +fallback=Weather_Snow_Ambient_Night_Color,049,058,068 +fallback=Weather_Snow_Sun_Sunrise_Color,141,109,109 +fallback=Weather_Snow_Sun_Day_Color,163,169,183 +fallback=Weather_Snow_Sun_Sunset_Color,101,121,141 +fallback=Weather_Snow_Sun_Night_Color,055,066,077 +fallback=Weather_Snow_Sun_Disc_Sunset_Color,128,128,128 +fallback=Weather_Snow_Land_Fog_Day_Depth,1.0 +fallback=Weather_Snow_Land_Fog_Night_Depth,1.2 +fallback=Weather_Snow_Wind_Speed,0 +fallback=Weather_Snow_Cloud_Speed,1.5 +fallback=Weather_Snow_Glare_View,0 +fallback=Weather_Snow_Snow_Diameter,800 +fallback=Weather_Snow_Snow_Height_Min,400 +fallback=Weather_Snow_Snow_Height_Max,700 +fallback=Weather_Snow_Snow_Entrance_Speed,6 +fallback=Weather_Snow_Max_Snowflakes,750 +fallback=Weather_Snow_Ambient_Loop_Sound_ID,None +fallback=Weather_Snow_Snow_Threshold,0.5 +fallback=Weather_Blizzard_Cloud_Texture,Tx_Sky_Blizzard.dds +fallback=Weather_Blizzard_Clouds_Maximum_Percent,1.0 +fallback=Weather_Blizzard_Transition_Delta,.030 +fallback=Weather_Blizzard_Sky_Sunrise_Color,091,099,106 +fallback=Weather_Blizzard_Sky_Day_Color,121,133,145 +fallback=Weather_Blizzard_Sky_Sunset_Color,108,115,121 +fallback=Weather_Blizzard_Sky_Night_Color,027,029,031 +fallback=Weather_Blizzard_Fog_Sunrise_Color,091,099,106 +fallback=Weather_Blizzard_Fog_Day_Color,121,133,145 +fallback=Weather_Blizzard_Fog_Sunset_Color,108,115,121 +fallback=Weather_Blizzard_Fog_Night_Color,021,024,028 +fallback=Weather_Blizzard_Ambient_Sunrise_Color,084,088,092 +fallback=Weather_Blizzard_Ambient_Day_Color,093,096,105 +fallback=Weather_Blizzard_Ambient_Sunset_Color,083,077,075 +fallback=Weather_Blizzard_Ambient_Night_Color,053,062,070 +fallback=Weather_Blizzard_Sun_Sunrise_Color,114,128,146 +fallback=Weather_Blizzard_Sun_Day_Color,163,169,183 +fallback=Weather_Blizzard_Sun_Sunset_Color,106,114,136 +fallback=Weather_Blizzard_Sun_Night_Color,057,066,074 +fallback=Weather_Blizzard_Sun_Disc_Sunset_Color,128,128,128 +fallback=Weather_Blizzard_Land_Fog_Day_Depth,2.8 +fallback=Weather_Blizzard_Land_Fog_Night_Depth,3.0 +fallback=Weather_Blizzard_Wind_Speed,.9 +fallback=Weather_Blizzard_Cloud_Speed,7.5 +fallback=Weather_Blizzard_Glare_View,0 +fallback=Weather_Blizzard_Ambient_Loop_Sound_ID, Blizzard +fallback=Weather_Blizzard_Storm_Threshold,.50 diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index cc211a3d64..2ad4ddeb6b 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -48,11 +48,6 @@ const vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745); const float RAIN_RIPPLE_GAPS = 5.0; const float RAIN_RIPPLE_RADIUS = 0.1; -int modulo(int v1, int v2) -{ - return v1 - v2 * int(floor(float(v1) / float(v2))); -} - vec2 randOffset(vec2 c) { return fract(vec2( @@ -170,8 +165,6 @@ void main(void) vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1.0-screenCoords.y); - vec2 nCoord = vec2(0.0); - #define waterTimer osg_SimulationTime vec3 normal0 = 2.0 * texture2D(normalMap,normalCoords(UV, 0.05, 0.04, waterTimer, -0.015, -0.005, vec3(0.0,0.0,0.0))).rgb - 1.0; @@ -195,100 +188,66 @@ void main(void) vec2 smallWaves = mix(vec2(SMALL_WAVES_X,SMALL_WAVES_Y),vec2(SMALL_WAVES_RAIN_X,SMALL_WAVES_RAIN_Y),rainIntensity); float bump = mix(BUMP,BUMP_RAIN,rainIntensity); - vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + - normal2 * midWaves.x + normal3 * midWaves.y + - normal4 * smallWaves.x + normal5 * smallWaves.y + - rippleAdd); - - normal = normalize(vec3(normal.x * bump, normal.y * bump, normal.z)); - - normal = vec3(-normal.x, -normal.y, normal.z); - - // normal for sunlight scattering - vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 + - normal2 * midWaves.x * 0.2 + normal3 * midWaves.y * 0.2 + - normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 + - rippleAdd).xyz; - - lNormal = normalize(vec3(lNormal.x * bump, lNormal.y * bump, lNormal.z)); - lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z); + vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + normal2 * midWaves.x + + normal3 * midWaves.y + normal4 * smallWaves.x + normal5 * smallWaves.y + rippleAdd); + normal = normalize(vec3(-normal.x * bump, -normal.y * bump, normal.z)); vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz); vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz; vec3 vVec = normalize(position.xyz - cameraPos.xyz); - float isUnderwater = (cameraPos.z > 0.0) ? 0.0 : 1.0; - - // sunlight scattering - vec3 pNormal = vec3(0,0,1); - vec3 lR = reflect(lVec, lNormal); - vec3 llR = reflect(lVec, pNormal); - - float sunHeight = lVec.z; float sunFade = length(gl_LightModel.ambient.xyz); - float s = clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0); - float lightScatter = shadow * clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * s * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0); - vec3 scatterColour = mix(vec3(SCATTER_COLOUR)*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0)); - // fresnel float ior = (cameraPos.z>0.0)?(1.333/1.0):(1.0/1.333); // air to water; water to air - float fresnel = fresnel_dielectric(vVec, normal, ior); - - fresnel = clamp(fresnel, 0.0, 1.0); + float fresnel = clamp(fresnel_dielectric(vVec, normal, ior), 0.0, 1.0); + vec2 screenCoordsOffset = normal.xy * REFL_BUMP; #if REFRACTION float depthSample = linearizeDepth(texture2D(refractionDepthMap,screenCoords).x); - float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-(normal.xy*REFR_BUMP)).x); + float depthSampleDistorted = linearizeDepth(texture2D(refractionDepthMap,screenCoords-screenCoordsOffset).x); float surfaceDepth = linearizeDepth(gl_FragCoord.z); float realWaterDepth = depthSample - surfaceDepth; // undistorted water depth in view direction, independent of frustum - float shore = clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); -#else - float shore = 1.0; + screenCoordsOffset *= clamp(realWaterDepth / BUMP_SUPPRESS_DEPTH,0,1); #endif - vec2 screenCoordsOffset = normal.xy * REFL_BUMP * shore; - // reflection vec3 reflection = texture2D(reflectionMap, screenCoords + screenCoordsOffset).rgb; - // refraction + // specular + float specular = pow(max(dot(reflect(vVec, normal), lVec), 0.0),SPEC_HARDNESS) * shadow; + + vec3 waterColor = WATER_COLOR * sunFade; + #if REFRACTION + // refraction vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb; // brighten up the refraction underwater - refraction = (cameraPos.z < 0.0) ? clamp(refraction * 1.5, 0.0, 1.0) : refraction; -#endif - // specular - vec3 R = reflect(vVec, normal); - float specular = pow(max(dot(R, lVec), 0.0),SPEC_HARDNESS) * shadow; - - vec3 waterColor = WATER_COLOR; - waterColor = waterColor * length(gl_LightModel.ambient.xyz); - -#if REFRACTION - if (cameraPos.z > 0.0) + if (cameraPos.z < 0.0) + refraction = clamp(refraction * 1.5, 0.0, 1.0); + else refraction = mix(refraction, waterColor, clamp(depthSampleDistorted/VISIBILITY, 0.0, 1.0)); - gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz; + // sunlight scattering + // normal for sunlight scattering + vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 + normal2 * midWaves.x * 0.2 + + normal3 * midWaves.y * 0.2 + normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 + rippleAdd); + lNormal = normalize(vec3(-lNormal.x * bump, -lNormal.y * bump, lNormal.z)); + float sunHeight = lVec.z; + vec3 scatterColour = mix(SCATTER_COLOUR*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0)); + vec3 lR = reflect(lVec, lNormal); + float lightScatter = shadow * clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0) * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0); + gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.2; + gl_FragData[0].w = 1.0; #else - gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz; + gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.7; + gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); #endif + // fog float fogValue = clamp((depthPassthrough - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0); gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue); -#if REFRACTION - gl_FragData[0].xyz += vec3(rainRipple.w) * 0.2; -#else - gl_FragData[0].xyz += vec3(rainRipple.w) * 0.7; -#endif - -#if REFRACTION - gl_FragData[0].w = 1.0; -#else - gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); -#endif - applyShadowDebugOverlay(); } diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 575f8f3c2f..2377f0af4b 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -16,7 +16,7 @@ void main(void) 0.5, 0.5, 0.5, 1.0); vec4 texcoordProj = ((scalemat) * ( gl_Position)); - screenCoordsPassthrough = vec3(texcoordProj.x, texcoordProj.y, texcoordProj.w); + screenCoordsPassthrough = texcoordProj.xyw; position = gl_Vertex;