diff --git a/apps/opencs/view/render/commands.cpp b/apps/opencs/view/render/commands.cpp index 7b3760296..699bf5d01 100644 --- a/apps/opencs/view/render/commands.cpp +++ b/apps/opencs/view/render/commands.cpp @@ -1,19 +1,44 @@ #include "commands.hpp" +#include + +#include #include +#include "editmode.hpp" #include "terrainselection.hpp" +#include "terrainshapemode.hpp" +#include "terraintexturemode.hpp" +#include "worldspacewidget.hpp" -CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent) - : mTerrainSelection(terrainSelection) +CSVRender::DrawTerrainSelectionCommand::DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent) + : mWorldspaceWidget(worldspaceWidget) { } void CSVRender::DrawTerrainSelectionCommand::redo() { - mTerrainSelection.update(); + tryUpdate(); } void CSVRender::DrawTerrainSelectionCommand::undo() { - mTerrainSelection.update(); + tryUpdate(); +} + +void CSVRender::DrawTerrainSelectionCommand::tryUpdate() +{ + if (!mWorldspaceWidget) + { + Log(Debug::Verbose) << "Can't update terrain selection, no WorldspaceWidget found!"; + return; + } + + auto terrainMode = dynamic_cast(mWorldspaceWidget->getEditMode()); + if (!terrainMode) + { + Log(Debug::Verbose) << "Can't update terrain selection in current EditMode"; + return; + } + + terrainMode->getTerrainSelection()->update(); } diff --git a/apps/opencs/view/render/commands.hpp b/apps/opencs/view/render/commands.hpp index cdc389e33..62b7fbfdc 100644 --- a/apps/opencs/view/render/commands.hpp +++ b/apps/opencs/view/render/commands.hpp @@ -1,8 +1,12 @@ #ifndef CSV_RENDER_COMMANDS_HPP #define CSV_RENDER_COMMANDS_HPP +#include + #include +#include "worldspacewidget.hpp" + namespace CSVRender { class TerrainSelection; @@ -21,14 +25,17 @@ namespace CSVRender */ class DrawTerrainSelectionCommand : public QUndoCommand { + private: - TerrainSelection& mTerrainSelection; + QPointer mWorldspaceWidget; public: - DrawTerrainSelectionCommand(TerrainSelection& terrainSelection, QUndoCommand* parent = nullptr); + DrawTerrainSelectionCommand(WorldspaceWidget* worldspaceWidget, QUndoCommand* parent = nullptr); void redo() override; void undo() override; + + void tryUpdate(); }; } diff --git a/apps/opencs/view/render/terrainshapemode.cpp b/apps/opencs/view/render/terrainshapemode.cpp index 866ff69cd..050494495 100644 --- a/apps/opencs/view/render/terrainshapemode.cpp +++ b/apps/opencs/view/render/terrainshapemode.cpp @@ -287,7 +287,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() undoStack.beginMacro ("Edit shape and normal records"); // One command at the beginning of the macro for redrawing the terrain-selection grid when undoing the changes. - undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); + undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget())); for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells) { @@ -358,7 +358,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges() pushNormalsEditToCommand(landNormalsNew, document, landTable, cellId); } // One command at the end of the macro for redrawing the terrain-selection grid when redoing the changes. - undoStack.push(new DrawTerrainSelectionCommand(*mTerrainShapeSelection)); + undoStack.push(new DrawTerrainSelectionCommand(&getWorldspaceWidget())); undoStack.endMacro(); clearTransientEdits(); @@ -1049,7 +1049,7 @@ void CSVRender::TerrainShapeMode::handleSelection(int globalSelectionX, int glob */ if (xIsAtCellBorder && yIsAtCellBorder) { - /* + /* Handle the NW, NE, and SE corner vertices. NW corner: (+1, -1) offset to reach current cell. NE corner: (-1, -1) offset to reach current cell. @@ -1132,7 +1132,7 @@ void CSVRender::TerrainShapeMode::selectTerrainShapes(const std::pair& selectAction = CSMPrefs::get()["3D Scene Editing"]["primary-select-action"].toString(); else selectAction = CSMPrefs::get()["3D Scene Editing"]["secondary-select-action"].toString(); - + if (selectAction == "Select only") mTerrainShapeSelection->onlySelect(selections); else if (selectAction == "Add to selection") @@ -1444,6 +1444,11 @@ void CSVRender::TerrainShapeMode::mouseMoveEvent (QMouseEvent *event) mBrushDraw->hide(); } +std::shared_ptr CSVRender::TerrainShapeMode::getTerrainSelection() +{ + return mTerrainShapeSelection; +} + void CSVRender::TerrainShapeMode::setBrushSize(int brushSize) { mBrushSize = brushSize; diff --git a/apps/opencs/view/render/terrainshapemode.hpp b/apps/opencs/view/render/terrainshapemode.hpp index a88e60c9c..abc4b8aba 100644 --- a/apps/opencs/view/render/terrainshapemode.hpp +++ b/apps/opencs/view/render/terrainshapemode.hpp @@ -92,6 +92,8 @@ namespace CSVRender void dragMoveEvent (QDragMoveEvent *event) override; void mouseMoveEvent (QMouseEvent *event) override; + std::shared_ptr getTerrainSelection(); + private: /// Remove duplicates and sort mAlteredCells, then limitAlteredHeights forward and reverse @@ -176,7 +178,7 @@ namespace CSVRender int mDragMode = InteractionType_None; osg::Group* mParentNode; bool mIsEditing = false; - std::unique_ptr mTerrainShapeSelection; + std::shared_ptr mTerrainShapeSelection; int mTotalDiffY = 0; std::vector mAlteredCells; osg::Vec3d mEditingPos; diff --git a/apps/opencs/view/render/terraintexturemode.cpp b/apps/opencs/view/render/terraintexturemode.cpp index 4e267e942..dfcc29ae0 100644 --- a/apps/opencs/view/render/terraintexturemode.cpp +++ b/apps/opencs/view/render/terraintexturemode.cpp @@ -712,6 +712,11 @@ void CSVRender::TerrainTextureMode::mouseMoveEvent (QMouseEvent *event) mBrushDraw->hide(); } +std::shared_ptr CSVRender::TerrainTextureMode::getTerrainSelection() +{ + return mTerrainTextureSelection; +} + void CSVRender::TerrainTextureMode::setBrushSize(int brushSize) { diff --git a/apps/opencs/view/render/terraintexturemode.hpp b/apps/opencs/view/render/terraintexturemode.hpp index 31932df21..e0c6e4b40 100644 --- a/apps/opencs/view/render/terraintexturemode.hpp +++ b/apps/opencs/view/render/terraintexturemode.hpp @@ -85,6 +85,8 @@ namespace CSVRender void mouseMoveEvent (QMouseEvent *event) override; + std::shared_ptr getTerrainSelection(); + private: /// \brief Handle brush mechanics, maths regarding worldspace hit etc. void editTerrainTextureGrid (const WorldspaceHitResult& hit); @@ -115,7 +117,7 @@ namespace CSVRender int mDragMode; osg::Group* mParentNode; bool mIsEditing; - std::unique_ptr mTerrainTextureSelection; + std::shared_ptr mTerrainTextureSelection; const int cellSize {ESM::Land::REAL_SIZE}; const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 723ffcb6a..2ebc083f2 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -452,6 +452,11 @@ CSVRender::WorldspaceHitResult CSVRender::WorldspaceWidget::mousePick (const QPo return hit; } +CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() +{ + return dynamic_cast (mEditMode->getCurrent()); +} + void CSVRender::WorldspaceWidget::abortDrag() { if (mDragging) @@ -697,11 +702,6 @@ void CSVRender::WorldspaceWidget::handleInteractionPress (const WorldspaceHitRes editMode.primaryOpenPressed (hit); } -CSVRender::EditMode *CSVRender::WorldspaceWidget::getEditMode() -{ - return dynamic_cast (mEditMode->getCurrent()); -} - void CSVRender::WorldspaceWidget::primaryOpen(bool activate) { handleInteraction(InteractionType_PrimaryOpen, activate); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 5e224b380..cf244ce71 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -189,6 +189,8 @@ namespace CSVRender /// Erase all overrides and restore the visual representation to its true state. virtual void reset (unsigned int elementMask) = 0; + EditMode *getEditMode(); + protected: /// Visual elements in a scene @@ -215,8 +217,6 @@ namespace CSVRender void settingChanged (const CSMPrefs::Setting *setting) override; - EditMode *getEditMode(); - bool getSpeedMode(); private: diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 05047336e..6d77b67d3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1931,6 +1931,7 @@ namespace MWMechanics // Standing NPCs give way to moving ones if they are not in combat (or pursue) mode and either // follow player or have a AIWander package with non-empty wander area. bool shouldAvoidCollision = isMoving; + bool shouldGiveWay = false; bool shouldTurnToApproachingActor = !isMoving; MWWorld::Ptr currentTarget; // Combat or pursue target (NPCs should not avoid collision with their targets). const auto& aiSequence = ptr.getClass().getCreatureStats(ptr).getAiSequence(); @@ -1941,7 +1942,7 @@ namespace MWMechanics else if (package->getTypeId() == AiPackageTypeId::Wander && giveWayWhenIdle) { if (!static_cast(package.get())->isStationary()) - shouldAvoidCollision = true; + shouldGiveWay = true; } else if (package->getTypeId() == AiPackageTypeId::Combat || package->getTypeId() == AiPackageTypeId::Pursue) { @@ -1951,7 +1952,7 @@ namespace MWMechanics break; } } - if (!shouldAvoidCollision) + if (!shouldAvoidCollision && !shouldGiveWay) continue; osg::Vec2f baseSpeed = origMovement * maxSpeed; @@ -1960,14 +1961,14 @@ namespace MWMechanics osg::Vec3f halfExtents = world->getHalfExtents(ptr); float maxDistToCheck = isMoving ? maxDistForPartialAvoiding : maxDistForStrictAvoiding; - float timeToCollision = maxTimeToCheck; + float timeToCheck = maxTimeToCheck; + if (!shouldGiveWay && !aiSequence.isEmpty()) + timeToCheck = std::min(timeToCheck, getTimeToDestination(**aiSequence.begin(), basePos, maxSpeed, duration, halfExtents)); + + float timeToCollision = timeToCheck; osg::Vec2f movementCorrection(0, 0); float angleToApproachingActor = 0; - const float timeToDestination = aiSequence.isEmpty() - ? std::numeric_limits::max() - : getTimeToDestination(**aiSequence.begin(), basePos, maxSpeed, duration, halfExtents); - // Iterate through all other actors and predict collisions. for(PtrActorMap::iterator otherIter(mActors.begin()); otherIter != mActors.end(); ++otherIter) { @@ -2004,7 +2005,7 @@ namespace MWMechanics continue; // No solution; distance is always >= collisionDist. float t = (-vr - std::sqrt(Dh)) / v2; - if (t < 0 || t > timeToCollision || t > timeToDestination) + if (t < 0 || t > timeToCollision) continue; // Check visibility and awareness last as it's expensive. @@ -2024,7 +2025,7 @@ namespace MWMechanics movementCorrection.y() *= 0.5f; } - if (timeToCollision < maxTimeToCheck) + if (timeToCollision < timeToCheck) { // Try to evade the nearest collision. osg::Vec2f newMovement = origMovement + movementCorrection; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c57530665..18003e818 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -414,7 +414,7 @@ namespace MWMechanics bool runFallback = true; - if (pathgrid && !actor.getClass().isPureWaterCreature(actor)) + if (pathgrid != nullptr && !pathgrid->mPoints.empty() && !actor.getClass().isPureWaterCreature(actor)) { ESM::Pathgrid::PointList points; Misc::CoordinateConverter coords(storage.mCell->getCell()); diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 0e424b2f8..175836b11 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -754,6 +754,9 @@ namespace MWMechanics const ESM::Pathgrid *pathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*currentCell->getCell()); + if (pathgrid == nullptr || pathgrid->mPoints.empty()) + return; + int index = PathFinder::getClosestPoint(pathgrid, PathFinder::makeOsgVec3(dest)); getPathGridGraph(currentCell).getNeighbouringPoints(index, points); diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 2f8e83043..0e704cd46 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -206,9 +206,6 @@ namespace MWMechanics endPointInLocalCoords, startNode); - if (!endNode.second) - return; - // if it's shorter for actor to travel from start to end, than to travel from either // start or end to nearest pathgrid point, just travel from start to end. float startToEndLength2 = (endPointInLocalCoords - startPointInLocalCoords).length2(); @@ -279,7 +276,8 @@ namespace MWMechanics // unreachable pathgrid point. // // The AI routines will have to deal with such situations. - *out++ = endPoint; + if (endNode.second) + *out++ = endPoint; } float PathFinder::getZAngleToNext(float x, float y) const