From 7693f712bcc5e661961157b03dd2ade794b7ea24 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Feb 2014 11:35:46 +0100 Subject: [PATCH 001/240] started making CellStore into a proper class; encapsulated mCell member --- apps/openmw/mwdialogue/filter.cpp | 4 +- apps/openmw/mwgui/mapwindow.cpp | 2 +- apps/openmw/mwgui/travelwindow.cpp | 6 ++- apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 16 ++++---- apps/openmw/mwmechanics/aiactivate.cpp | 10 ++--- apps/openmw/mwmechanics/aicombat.cpp | 30 +++++++------- apps/openmw/mwmechanics/aiescort.cpp | 22 +++++----- apps/openmw/mwmechanics/aifollow.cpp | 8 ++-- apps/openmw/mwmechanics/aitravel.cpp | 10 ++--- apps/openmw/mwmechanics/aiwander.cpp | 10 ++--- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++-- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- apps/openmw/mwrender/animation.cpp | 4 +- apps/openmw/mwrender/debugging.cpp | 16 ++++---- apps/openmw/mwrender/localmap.cpp | 8 ++-- apps/openmw/mwrender/renderingmanager.cpp | 20 ++++----- apps/openmw/mwscript/cellextensions.cpp | 10 ++--- apps/openmw/mwsound/soundmanagerimp.cpp | 4 +- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- apps/openmw/mwworld/cells.cpp | 4 +- apps/openmw/mwworld/cellstore.cpp | 5 +++ apps/openmw/mwworld/cellstore.hpp | 23 +++++++---- apps/openmw/mwworld/physicssystem.cpp | 2 +- apps/openmw/mwworld/player.cpp | 4 +- apps/openmw/mwworld/scene.cpp | 50 +++++++++++------------ apps/openmw/mwworld/weather.cpp | 4 +- apps/openmw/mwworld/worldimp.cpp | 20 ++++----- 28 files changed, 161 insertions(+), 145 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 3d45b85ce0..846cff1141 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -110,7 +110,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const // check cell if (!info.mCell.empty()) - if (!Misc::StringUtils::ciEqual(player.getCell()->mCell->mName, info.mCell)) + if (!Misc::StringUtils::ciEqual(player.getCell()->getCell()->mName, info.mCell)) return false; return true; @@ -445,7 +445,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_NotCell: - return !Misc::StringUtils::ciEqual(mActor.getCell()->mCell->mName, select.getName()); + return !Misc::StringUtils::ciEqual(mActor.getCell()->getCell()->mName, select.getName()); case SelectWrapper::Function_NotLocal: { diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1b4af6d828..267c761e2d 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -356,7 +356,7 @@ namespace MWGui ESM::Position markedPosition; MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell && markedCell->isExterior() == !mInterior - && (!mInterior || Misc::StringUtils::ciEqual(markedCell->mCell->mName, mPrefix))) + && (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->mName, mPrefix))) { MarkerPosition markerPos; MyGUI::IntPoint widgetPos = getMarkerPosition(markedPosition.pos[0], markedPosition.pos[1], markerPos); diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index c314ce1fda..348178d81b 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -109,7 +109,11 @@ namespace MWGui int x,y; MWBase::Environment::get().getWorld()->positionToIndex(mPtr.get()->mBase->mTransport[i].mPos.pos[0], mPtr.get()->mBase->mTransport[i].mPos.pos[1],x,y); - if(cellname == "") {cellname = MWBase::Environment::get().getWorld()->getExterior(x,y)->mCell->mName; interior= false;} + if (cellname == "") + { + cellname = MWBase::Environment::get().getWorld()->getExterior(x,y)->getCell()->mName; + interior = false; + } addDestination(cellname,mPtr.get()->mBase->mTransport[i].mPos,interior); } diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 3c1a4b3faf..fd487c87fb 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -129,7 +129,7 @@ namespace MWGui MWWorld::Ptr player = world->getPlayerPtr(); if (mSleeping && player.getCell()->isExterior()) { - std::string regionstr = player.getCell()->mCell->mRegion; + std::string regionstr = player.getCell()->getCell()->mRegion; if (!regionstr.empty()) { const ESM::Region *region = world->getStore().get().find (regionstr); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5448bc3c42..ba96845898 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -739,22 +739,22 @@ namespace MWGui mMap->setCellName( name ); mHud->setCellName( name ); - if (cell->mCell->isExterior()) + if (cell->getCell()->isExterior()) { - if (!cell->mCell->mName.empty()) - mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); + if (!cell->getCell()->mName.empty()) + mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->getCell()->getGridX (), cell->getCell()->getGridY ()); - mMap->cellExplored(cell->mCell->getGridX(), cell->mCell->getGridY()); + mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY()); mMap->setCellPrefix("Cell"); mHud->setCellPrefix("Cell"); - mMap->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); - mHud->setActiveCell( cell->mCell->getGridX(), cell->mCell->getGridY() ); + mMap->setActiveCell (cell->getCell()->getGridX(), cell->getCell()->getGridY()); + mHud->setActiveCell (cell->getCell()->getGridX(), cell->getCell()->getGridY()); } else { - mMap->setCellPrefix( cell->mCell->mName ); - mHud->setCellPrefix( cell->mCell->mName ); + mMap->setCellPrefix (cell->getCell()->mName ); + mHud->setCellPrefix (cell->getCell()->mName ); Ogre::Vector3 worldPos; if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 1f3c585216..ea146625a7 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -33,12 +33,12 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); Movement &movement = actor.getClass().getMovementSettings(actor); - const ESM::Cell *cell = actor.getCell()->mCell; + const ESM::Cell *cell = actor.getCell()->getCell(); MWWorld::Ptr player = world->getPlayerPtr(); - if(cell->mData.mX != player.getCell()->mCell->mData.mX) + if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); + int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) @@ -47,9 +47,9 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) return false; } } - if(cell->mData.mY != player.getCell()->mCell->mData.mY) + if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY); + int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 3653587f88..b0e0c2f7b3 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -34,9 +34,9 @@ namespace namespace MWMechanics { AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTarget(actor), - mTimerAttack(0), - mTimerReact(0), + mTarget(actor), + mTimerAttack(0), + mTimerReact(0), mTimerCombatMove(0), mFollowTarget(false), mReadyToAttack(false), @@ -77,7 +77,7 @@ namespace MWMechanics if (zTurn(actor, Ogre::Degree(mTargetAngle))) mRotate = false; } - + mTimerAttack -= duration; actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); @@ -105,7 +105,7 @@ namespace MWMechanics //Also it seems that this time is different for slash/thrust/chop mTimerAttack = 0.35f * static_cast(rand())/RAND_MAX; mStrike = true; - + //say a provoking combat phrase if (actor.getClass().isNpc()) { @@ -126,7 +126,7 @@ namespace MWMechanics mTimerAttack = -attackPeriod; mStrike = false; } - + const MWWorld::Class &cls = actor.getClass(); const ESM::Weapon *weapon = NULL; MWMechanics::WeaponType weaptype; @@ -138,14 +138,14 @@ namespace MWMechanics { MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //Get weapon speed and range - MWWorld::ContainerStoreIterator weaponSlot = + MWWorld::ContainerStoreIterator weaponSlot = MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); if (weaptype == WeapType_HandToHand) { - const MWWorld::Store &gmst = + const MWWorld::Store &gmst = MWBase::Environment::get().getWorld()->getStore().get(); weapRange = gmst.find("fHandToHandReach")->getFloat(); } @@ -246,12 +246,12 @@ namespace MWMechanics mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); mRotate = true; - + mMovement.mPosition[1] = 1; mReadyToAttack = false; } @@ -305,13 +305,13 @@ namespace MWMechanics float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); float targetPosThreshold; - bool isOutside = actor.getCell()->mCell->isExterior(); + bool isOutside = actor.getCell()->getCell()->isExterior(); if (isOutside) targetPosThreshold = 300; else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if(dist > targetPosThreshold) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -328,7 +328,7 @@ namespace MWMechanics PathFinder newPathFinder; newPathFinder.buildPath(start, dest, actor.getCell(), isOutside); - //TO EXPLORE: + //TO EXPLORE: //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) @@ -391,7 +391,7 @@ void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; float total = slash + chop + thrust; - + float roll = static_cast(rand())/RAND_MAX; if(roll <= static_cast(slash)/total) { diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index bac2584254..a58bfef051 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -86,25 +86,25 @@ namespace MWMechanics MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::Position pos = actor.getRefData().getPosition(); - bool cellChange = actor.getCell()->mCell->mData.mX != mCellX || actor.getCell()->mCell->mData.mY != mCellY; + bool cellChange = actor.getCell()->getCell()->mData.mX != mCellX || actor.getCell()->getCell()->mData.mY != mCellY; - if(actor.getCell()->mCell->mData.mX != player.getCell()->mCell->mData.mX) + if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(actor.getCell()->mCell->mData.mX - player.getCell()->mCell->mData.mX); + int sideX = sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX); // Check if actor is near the border of an inactive cell. If so, pause walking. - if(sideX * (pos.pos[0] - actor.getCell()->mCell->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / - 2.0 - 200)) + if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / + 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; return false; } } - if(actor.getCell()->mCell->mData.mY != player.getCell()->mCell->mData.mY) + if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(actor.getCell()->mCell->mData.mY - player.getCell()->mCell->mData.mY); + int sideY = sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY); // Check if actor is near the border of an inactive cell. If so, pause walking. - if(sideY*(pos.pos[1] - actor.getCell()->mCell->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / - 2.0 - 200)) + if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / + 2.0 - 200)) { MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; return false; @@ -114,8 +114,8 @@ namespace MWMechanics if(!mPathFinder.isPathConstructed() || cellChange) { - mCellX = actor.getCell()->mCell->mData.mX; - mCellY = actor.getCell()->mCell->mData.mY; + mCellX = actor.getCell()->getCell()->mData.mX; + mCellY = actor.getCell()->getCell()->mData.mY; ESM::Pathgrid::Point dest; dest.mX = mX; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index cf5291fd35..ef6ead9d74 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -33,16 +33,16 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + - (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) { if(actor.getCell()->isExterior()) { - if(mCellId == "") + if(mCellId == "") return true; } else { - if(mCellId == actor.getCell()->mCell->mName) + if(mCellId == actor.getCell()->getCell()->mName) return true; } } @@ -67,7 +67,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); - if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX) + if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX) +(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY) +(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ) > 100*100) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 03052f327b..41226ad796 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -36,12 +36,12 @@ namespace MWMechanics MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); Movement &movement = actor.getClass().getMovementSettings(actor); - const ESM::Cell *cell = actor.getCell()->mCell; + const ESM::Cell *cell = actor.getCell()->getCell(); MWWorld::Ptr player = world->getPlayerPtr(); - if(cell->mData.mX != player.getCell()->mCell->mData.mX) + if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX); + int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) @@ -50,9 +50,9 @@ namespace MWMechanics return false; } } - if(cell->mData.mY != player.getCell()->mCell->mData.mY) + if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY); + int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 77316fedf9..012f71b729 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -103,10 +103,10 @@ namespace MWMechanics if(!mStoredAvailableNodes) { mStoredAvailableNodes = true; - mPathgrid = world->getStore().get().search(*actor.getCell()->mCell); + mPathgrid = world->getStore().get().search(*actor.getCell()->getCell()); - mCellX = actor.getCell()->mCell->mData.mX; - mCellY = actor.getCell()->mCell->mData.mY; + mCellX = actor.getCell()->getCell()->mData.mX; + mCellY = actor.getCell()->getCell()->mData.mY; if(!mPathgrid) mDistance = 0; @@ -117,7 +117,7 @@ namespace MWMechanics { mXCell = 0; mYCell = 0; - if(actor.getCell()->mCell->isExterior()) + if(actor.getCell()->getCell()->isExterior()) { mXCell = mCellX * ESM::Land::REAL_SIZE; mYCell = mCellY * ESM::Land::REAL_SIZE; @@ -157,7 +157,7 @@ namespace MWMechanics mDistance = 0; // Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles. - if(mDistance && (mCellX != actor.getCell()->mCell->mData.mX || mCellY != actor.getCell()->mCell->mData.mY)) + if(mDistance && (mCellX != actor.getCell()->getCell()->mData.mX || mCellY != actor.getCell()->getCell()->mData.mY)) mDistance = 0; if(mChooseAction) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4407363a61..2503181aeb 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -299,14 +299,14 @@ namespace MWMechanics if(!allowShortcuts) { const ESM::Pathgrid *pathGrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->mCell); + MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); float xCell = 0; float yCell = 0; if (mCell->isExterior()) { - xCell = mCell->mCell->mData.mX * ESM::Land::REAL_SIZE; - yCell = mCell->mCell->mData.mY * ESM::Land::REAL_SIZE; + xCell = mCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; + yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; } int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); @@ -393,7 +393,7 @@ namespace MWMechanics { std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); - + if( (*iter).mX == oldStart->mX && (*iter).mY == oldStart->mY && (*iter).mZ == oldStart->mZ diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index fe395e566c..30072a8cca 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -503,7 +503,7 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition); if (markedCell) { - MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->mCell->mName, + MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName, markedPosition); action.execute(target); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 44bba90d07..820857137a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -334,7 +334,7 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene )); objlist->mControllers.push_back(Ogre::Controller(src, dest, func)); - bool interior = !(mPtr.isInCell() && mPtr.getCell()->mCell->isExterior()); + bool interior = !(mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior()); bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ? !interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); @@ -1070,7 +1070,7 @@ bool Animation::allowSwitchViewMode() const { for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter) { - if(stateiter->second.mPriority > MWMechanics::Priority_Movement + if(stateiter->second.mPriority > MWMechanics::Priority_Movement && stateiter->second.mPriority < MWMechanics::Priority_Torch) return false; } diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 2b61e109b6..0cfcf826f5 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -230,22 +230,22 @@ void Debugging::togglePathgrid() void Debugging::enableCellPathgrid(MWWorld::CellStore *store) { const ESM::Pathgrid *pathgrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*store->mCell); + MWBase::Environment::get().getWorld()->getStore().get().search(*store->getCell()); if (!pathgrid) return; Vector3 cellPathGridPos(0, 0, 0); - if (store->mCell->isExterior()) + if (store->getCell()->isExterior()) { - cellPathGridPos.x = store->mCell->mData.mX * ESM::Land::REAL_SIZE; - cellPathGridPos.y = store->mCell->mData.mY * ESM::Land::REAL_SIZE; + cellPathGridPos.x = store->getCell()->mData.mX * ESM::Land::REAL_SIZE; + cellPathGridPos.y = store->getCell()->mData.mY * ESM::Land::REAL_SIZE; } SceneNode *cellPathGrid = mPathGridRoot->createChildSceneNode(cellPathGridPos); cellPathGrid->attachObject(createPathgridLines(pathgrid)); cellPathGrid->attachObject(createPathgridPoints(pathgrid)); - if (store->mCell->isExterior()) + if (store->getCell()->isExterior()) { - mExteriorPathgridNodes[std::make_pair(store->mCell->getGridX(), store->mCell->getGridY())] = cellPathGrid; + mExteriorPathgridNodes[std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())] = cellPathGrid; } else { @@ -256,10 +256,10 @@ void Debugging::enableCellPathgrid(MWWorld::CellStore *store) void Debugging::disableCellPathgrid(MWWorld::CellStore *store) { - if (store->mCell->isExterior()) + if (store->getCell()->isExterior()) { ExteriorPathgridNodes::iterator it = - mExteriorPathgridNodes.find(std::make_pair(store->mCell->getGridX(), store->mCell->getGridY())); + mExteriorPathgridNodes.find(std::make_pair(store->getCell()->getGridX(), store->getCell()->getGridY())); if (it != mExteriorPathgridNodes.end()) { destroyCellPathgridNode(it->second); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index f147ae7b70..554ba22faf 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -115,8 +115,8 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax) mCameraRotNode->setOrientation(Quaternion::IDENTITY); mCellCamera->setOrientation(Quaternion(Ogre::Math::Cos(Ogre::Degree(0)/2.f), 0, 0, -Ogre::Math::Sin(Ogre::Degree(0)/2.f))); - int x = cell->mCell->getGridX(); - int y = cell->mCell->getGridY(); + int x = cell->getCell()->getGridX(); + int y = cell->getCell()->getGridY(); std::string name = "Cell_"+coordStr(x, y); @@ -182,7 +182,7 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, const int segsX = std::ceil( length.x / sSize ); const int segsY = std::ceil( length.y / sSize ); - mInteriorName = cell->mCell->mName; + mInteriorName = cell->getCell()->mName; for (int x=0; xmCell->mName + "_" + coordStr(x,y)); + cell->getCell()->mName + "_" + coordStr(x,y)); } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0a82d67fbb..0311c90b3a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -422,9 +422,9 @@ void RenderingManager::postRenderTargetUpdate(const RenderTargetEvent &evt) void RenderingManager::waterAdded (MWWorld::CellStore *store) { - if(store->mCell->mData.mFlags & ESM::Cell::HasWater) + if (store->getCell()->mData.mFlags & ESM::Cell::HasWater) { - mWater->changeCell(store->mCell); + mWater->changeCell (store->getCell()); mWater->setActive(true); } else @@ -501,9 +501,9 @@ bool RenderingManager::toggleRenderMode(int mode) void RenderingManager::configureFog(MWWorld::CellStore &mCell) { Ogre::ColourValue color; - color.setAsABGR (mCell.mCell->mAmbi.mFog); + color.setAsABGR (mCell.getCell()->mAmbi.mFog); - configureFog(mCell.mCell->mAmbi.mFogDensity, color); + configureFog (mCell.getCell()->mAmbi.mFogDensity, color); } void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour) @@ -553,8 +553,8 @@ void RenderingManager::setAmbientMode() void RenderingManager::configureAmbient(MWWorld::CellStore &mCell) { - if (mCell.mCell->mData.mFlags & ESM::Cell::Interior) - mAmbientColor.setAsABGR (mCell.mCell->mAmbi.mAmbient); + if (mCell.getCell()->mData.mFlags & ESM::Cell::Interior) + mAmbientColor.setAsABGR (mCell.getCell()->mAmbi.mAmbient); setAmbientMode(); // Create a "sun" that shines light downwards. It doesn't look @@ -564,10 +564,10 @@ void RenderingManager::configureAmbient(MWWorld::CellStore &mCell) mSun = mRendering.getScene()->createLight(); mSun->setType(Ogre::Light::LT_DIRECTIONAL); } - if (mCell.mCell->mData.mFlags & ESM::Cell::Interior) + if (mCell.getCell()->mData.mFlags & ESM::Cell::Interior) { Ogre::ColourValue colour; - colour.setAsABGR (mCell.mCell->mAmbi.mSunlight); + colour.setAsABGR (mCell.getCell()->mAmbi.mSunlight); mSun->setDiffuseColour (colour); mSun->setDirection(0,-1,0); } @@ -650,12 +650,12 @@ void RenderingManager::setGlare(bool glare) void RenderingManager::requestMap(MWWorld::CellStore* cell) { - if (cell->mCell->isExterior()) + if (cell->getCell()->isExterior()) { assert(mTerrain); Ogre::AxisAlignedBox dims = mObjects->getDimensions(cell); - Ogre::Vector2 center(cell->mCell->getGridX() + 0.5, cell->mCell->getGridY() + 0.5); + Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5); dims.merge(mTerrain->getWorldBoundingBox(center)); if (dims.isFinite()) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index a0acfa4daa..2140076c3b 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -88,7 +88,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { bool interior = - !MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->isExterior(); + !MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell()->isExterior(); runtime.push (interior ? 1 : 0); } @@ -103,7 +103,7 @@ namespace MWScript std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell; + const ESM::Cell *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell(); std::string current = cell->mName; @@ -131,7 +131,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - if (cell->mCell->hasWater()) + if (cell->getCell()->hasWater()) runtime.push (cell->mWaterLevel); else runtime.push (-std::numeric_limits().max()); @@ -148,7 +148,7 @@ namespace MWScript MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - if (cell->mCell->isExterior()) + if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); cell->mWaterLevel = level; @@ -166,7 +166,7 @@ namespace MWScript MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); - if (cell->mCell->isExterior()) + if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); cell->mWaterLevel +=level; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index fd3fe4c0ad..13e152e689 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -480,7 +480,7 @@ namespace MWSound static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Ptr player = world->getPlayerPtr(); - const ESM::Cell *cell = player.getCell()->mCell; + const ESM::Cell *cell = player.getCell()->getCell(); sTimePassed += duration; if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) @@ -548,7 +548,7 @@ namespace MWSound MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - const ESM::Cell *cell = player.getCell()->mCell; + const ESM::Cell *cell = player.getCell()->getCell(); Environment env = Env_Normal; if((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index ba0e1d056f..8c59247f4a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -301,7 +301,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 965c9fc5de..cdfbb7b45d 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -83,8 +83,8 @@ bool MWWorld::Cells::hasState (const CellStore& cellStore) const if (cellStore.mState==CellStore::State_Loaded) return true; - if (cellStore.mCell->mData.mFlags & ESM::Cell::Interior) - return cellStore.mCell->mData.mFlags & ESM::Cell::HasWater; + if (cellStore.getCell()->mData.mFlags & ESM::Cell::Interior) + return cellStore.getCell()->mData.mFlags & ESM::Cell::HasWater; else return false; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 42c954afb3..c3c3eb6e9f 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -145,6 +145,11 @@ namespace MWWorld mWaterLevel = cell->mWater; } + const ESM::Cell *CellStore::getCell() const + { + return mCell; + } + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index a4f2190135..b90ea72a57 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -49,19 +49,26 @@ namespace MWWorld } }; - /// A storage struct for one single cell reference. + /// \brief Mutable state of a cell class CellStore { - public: + public: - enum State - { - State_Unloaded, State_Preloaded, State_Loaded - }; + enum State + { + State_Unloaded, State_Preloaded, State_Loaded + }; - CellStore (const ESM::Cell *cell_); + private: + + const ESM::Cell *mCell; + + public: + + CellStore (const ESM::Cell *cell_); + + const ESM::Cell *getCell() const; - const ESM::Cell *mCell; State mState; std::vector mIds; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index a7103b9911..56d19c8be1 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -574,7 +574,7 @@ namespace MWWorld for(;iter != mMovementQueue.end();iter++) { float waterlevel = -std::numeric_limits::max(); - const ESM::Cell *cell = iter->first.getCell()->mCell; + const ESM::Cell *cell = iter->first.getCell()->getCell(); if(cell->hasWater()) waterlevel = cell->mWater; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index c1cce84fc1..ba816d6d53 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -191,7 +191,7 @@ namespace MWWorld ESM::Player player; mPlayer.save (player.mObject); - player.mCellId = mCellStore->mCell->getCellId(); + player.mCellId = mCellStore->getCell()->getCellId(); player.mBirthsign = mSign; @@ -203,7 +203,7 @@ namespace MWWorld { player.mHasMark = true; player.mMarkedPosition = mMarkedPosition; - player.mMarkedCell = mMarkedCell->mCell->getCellId(); + player.mMarkedCell = mMarkedCell->getCell()->getCellId(); } else player.mHasMark = false; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 167adf3014..06141db547 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -97,15 +97,15 @@ namespace MWWorld } } - if ((*iter)->mCell->isExterior()) + if ((*iter)->getCell()->isExterior()) { ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search( - (*iter)->mCell->getGridX(), - (*iter)->mCell->getGridY() + (*iter)->getCell()->getGridX(), + (*iter)->getCell()->getGridY() ); if (land) - mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() ); + mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY()); } mRendering.removeCell(*iter); @@ -128,18 +128,18 @@ namespace MWWorld float worldsize = ESM::Land::REAL_SIZE; // Load terrain physics first... - if (cell->mCell->isExterior()) + if (cell->getCell()->isExterior()) { ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search( - cell->mCell->getGridX(), - cell->mCell->getGridY() + cell->getCell()->getGridX(), + cell->getCell()->getGridY() ); if (land) { mPhysics->addHeightField ( land->mLandData->mHeights, - cell->mCell->getGridX(), - cell->mCell->getGridY(), + cell->getCell()->getGridX(), + cell->getCell()->getGridY(), 0, worldsize / (verts-1), verts) @@ -218,10 +218,10 @@ namespace MWWorld int numUnload = 0; while (active!=mActiveCells.end()) { - if ((*active)->mCell->isExterior()) + if ((*active)->getCell()->isExterior()) { - if (std::abs (X-(*active)->mCell->getGridX())<=1 && - std::abs (Y-(*active)->mCell->getGridY())<=1) + if (std::abs (X-(*active)->getCell()->getGridX())<=1 && + std::abs (Y-(*active)->getCell()->getGridY())<=1) { // keep cells within the new 3x3 grid ++active; @@ -235,10 +235,10 @@ namespace MWWorld active = mActiveCells.begin(); while (active!=mActiveCells.end()) { - if ((*active)->mCell->isExterior()) + if ((*active)->getCell()->isExterior()) { - if (std::abs (X-(*active)->mCell->getGridX())<=1 && - std::abs (Y-(*active)->mCell->getGridY())<=1) + if (std::abs (X-(*active)->getCell()->getGridX())<=1 && + std::abs (Y-(*active)->getCell()->getGridY())<=1) { // keep cells within the new 3x3 grid ++active; @@ -257,10 +257,10 @@ namespace MWWorld while (iter!=mActiveCells.end()) { - assert ((*iter)->mCell->isExterior()); + assert ((*iter)->getCell()->isExterior()); - if (x==(*iter)->mCell->getGridX() && - y==(*iter)->mCell->getGridY()) + if (x==(*iter)->getCell()->getGridX() && + y==(*iter)->getCell()->getGridY()) break; ++iter; @@ -280,10 +280,10 @@ namespace MWWorld while (iter!=mActiveCells.end()) { - assert ((*iter)->mCell->isExterior()); + assert ((*iter)->getCell()->isExterior()); - if (x==(*iter)->mCell->getGridX() && - y==(*iter)->mCell->getGridY()) + if (x==(*iter)->getCell()->getGridX() && + y==(*iter)->getCell()->getGridY()) break; ++iter; @@ -302,10 +302,10 @@ namespace MWWorld while (iter!=mActiveCells.end()) { - assert ((*iter)->mCell->isExterior()); + assert ((*iter)->getCell()->isExterior()); - if (X==(*iter)->mCell->getGridX() && - Y==(*iter)->mCell->getGridY()) + if (X==(*iter)->getCell()->getGridX() && + Y==(*iter)->getCell()->getGridY()) break; ++iter; @@ -407,7 +407,7 @@ namespace MWWorld loadingListener->setProgressRange(refsToLoad); // Load cell. - std::cout << "cellName: " << cell->mCell->mName << std::endl; + std::cout << "cellName: " << cell->getCell()->mName << std::endl; //Loading Interior loading text diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b00ad15ca3..c79d7ee4fb 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -664,7 +664,7 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int mRegionOverrides[Misc::StringUtils::lowerCase(region)] = weather; - std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->mCell->mRegion; + std::string playerRegion = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell()->mRegion; if (Misc::StringUtils::ciEqual(region, playerRegion)) setWeather(weather); } @@ -695,7 +695,7 @@ void WeatherManager::switchToNextWeather(bool instantly) MWBase::World* world = MWBase::Environment::get().getWorld(); if (world->isCellExterior() || world->isCellQuasiExterior()) { - std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->mCell->mRegion); + std::string regionstr = Misc::StringUtils::lowerCase(world->getPlayerPtr().getCell()->getCell()->mRegion); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index be6c0b338f..4c97c594d5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -506,10 +506,10 @@ namespace MWWorld if (!cell) cell = mWorldScene->getCurrentCell(); - if (!cell->mCell->isExterior() || !cell->mCell->mName.empty()) - return cell->mCell->mName; + if (!cell->getCell()->isExterior() || !cell->getCell()->mName.empty()) + return cell->getCell()->mName; - if (const ESM::Region* region = getStore().get().search (cell->mCell->mRegion)) + if (const ESM::Region* region = getStore().get().search (cell->getCell()->mRegion)) return region->mName; return getStore().get().find ("sDefaultCellname")->mValue.getString(); @@ -946,11 +946,11 @@ namespace MWWorld if (isPlayer) { if (!newCell.isExterior()) - changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos); + changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.getCell()->mName), pos); else { - int cellX = newCell.mCell->getGridX(); - int cellY = newCell.mCell->getGridY(); + int cellX = newCell.getCell()->getGridX(); + int cellY = newCell.getCell()->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } addContainerScripts (getPlayerPtr(), &newCell); @@ -1465,7 +1465,7 @@ namespace MWWorld CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { - return currentCell->mCell->isExterior(); + return currentCell->getCell()->isExterior(); } return false; } @@ -1475,7 +1475,7 @@ namespace MWWorld CellStore *currentCell = mWorldScene->getCurrentCell(); if (currentCell) { - if (!(currentCell->mCell->mData.mFlags & ESM::Cell::QuasiEx)) + if (!(currentCell->getCell()->mData.mFlags & ESM::Cell::QuasiEx)) return false; else return true; @@ -1757,7 +1757,7 @@ namespace MWWorld bool World::isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const { - if (!(cell->mCell->mData.mFlags & ESM::Cell::HasWater)) { + if (!(cell->getCell()->mData.mFlags & ESM::Cell::HasWater)) { return false; } return pos.z < cell->mWaterLevel; @@ -1816,7 +1816,7 @@ namespace MWWorld const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos)) return 2; - if((currentCell->mCell->mData.mFlags&ESM::Cell::NoSleep) || + if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || Class::get(player).getNpcStats(player).isWerewolf()) return 1; From 59508e914fc0f7766a915d992781841327c1c4bb Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 22 Feb 2014 01:17:58 +0400 Subject: [PATCH 002/240] Fixed OpenCS build on OS X --- apps/opencs/view/filter/editwidget.hpp | 2 +- apps/opencs/view/filter/filterbox.hpp | 2 +- apps/opencs/view/filter/recordfilterbox.hpp | 2 +- apps/opencs/view/world/tablesubview.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index 555b6d3602..e7e34b8e9a 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 3817d5e709..5954035fcb 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 3638dc6c34..fa5c9c3c28 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1f67e0262a..b3c253919b 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -#include +#include class QModelIndex; From 1ab5948f196618c76e839834d0c047f9a34941d2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 23 Feb 2014 19:11:37 +1100 Subject: [PATCH 003/240] merge upstream changes --- apps/opencs/model/world/columnbase.hpp | 254 +-- apps/opencs/model/world/tablemimedata.cpp | 890 ++++----- apps/opencs/model/world/tablemimedata.hpp | 124 +- apps/opencs/view/filter/editwidget.cpp | 406 ++-- apps/opencs/view/filter/editwidget.hpp | 114 +- apps/opencs/view/filter/filterbox.cpp | 100 +- apps/opencs/view/filter/filterbox.hpp | 90 +- apps/opencs/view/filter/recordfilterbox.cpp | 64 +- apps/opencs/view/filter/recordfilterbox.hpp | 74 +- apps/opencs/view/world/table.cpp | 1090 +++++------ apps/opencs/view/world/table.hpp | 254 +-- apps/opencs/view/world/tablesubview.cpp | 262 +-- apps/opencs/view/world/tablesubview.hpp | 126 +- apps/openmw/mwstate/statemanagerimp.cpp | 704 +++---- components/esm/esmwriter.cpp | 406 ++-- components/esm/esmwriter.hpp | 228 +-- readme.txt | 1902 +++++++++---------- 17 files changed, 3544 insertions(+), 3544 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index e043336080..4ce45ffe86 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -1,127 +1,127 @@ -#ifndef CSM_WOLRD_COLUMNBASE_H -#define CSM_WOLRD_COLUMNBASE_H - -#include - -#include -#include - -#include "record.hpp" - -namespace CSMWorld -{ - struct ColumnBase - { - enum Roles - { - Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 - }; - - enum Flags - { - Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view - }; - - enum Display - { - Display_None, //Do not use - Display_String, - - //CONCRETE TYPES STARTS HERE - Display_Skill, - Display_Class, - Display_Faction, - Display_Race, - Display_Sound, - Display_Region, - Display_Birthsign, - Display_Spell, - Display_Cell, - Display_Referenceable, - Display_Activator, - Display_Potion, - Display_Apparatus, - Display_Armor, - Display_Book, - Display_Clothing, - Display_Container, - Display_Creature, - Display_Door, - Display_Ingredient, - Display_CreatureLevelledList, - Display_ItemLevelledList, - Display_Light, - Display_Lockpick, - Display_Miscellaneous, - Display_Npc, - Display_Probe, - Display_Repair, - Display_Static, - Display_Weapon, - Display_Reference, - Display_Filter, - Display_Topic, - Display_Journal, - Display_TopicInfo, - Display_JournalInfo, - Display_Scene, - //CONCRETE TYPES ENDS HERE - - Display_Integer, - Display_Float, - Display_Var, - Display_GmstVarType, - Display_GlobalVarType, - Display_Specialisation, - Display_Attribute, - Display_Boolean, - Display_SpellType, - Display_Script, - Display_ApparatusType, - Display_ArmorType, - Display_ClothingType, - Display_CreatureType, - Display_WeaponType, - Display_RecordState, - Display_RefRecordType, - Display_DialogueType, - Display_QuestStatusType, - Display_Gender - }; - - int mColumnId; - int mFlags; - Display mDisplayType; - - ColumnBase (int columnId, Display displayType, int flag); - - virtual ~ColumnBase(); - - virtual bool isEditable() const = 0; - - virtual bool isUserEditable() const; - ///< Can this column be edited directly by the user? - - virtual std::string getTitle() const; - }; - - template - struct Column : public ColumnBase - { - int mFlags; - - Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (columnId, displayType, flags) {} - - virtual QVariant get (const Record& record) const = 0; - - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + getTitle() + " is not editable"); - } - }; -} - -#endif +#ifndef CSM_WOLRD_COLUMNBASE_H +#define CSM_WOLRD_COLUMNBASE_H + +#include + +#include +#include + +#include "record.hpp" + +namespace CSMWorld +{ + struct ColumnBase + { + enum Roles + { + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 + }; + + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; + + enum Display + { + Display_None, //Do not use + Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Skill, + Display_Class, + Display_Faction, + Display_Race, + Display_Sound, + Display_Region, + Display_Birthsign, + Display_Spell, + Display_Cell, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_Reference, + Display_Filter, + Display_Topic, + Display_Journal, + Display_TopicInfo, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + + Display_Integer, + Display_Float, + Display_Var, + Display_GmstVarType, + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType, + Display_RecordState, + Display_RefRecordType, + Display_DialogueType, + Display_QuestStatusType, + Display_Gender + }; + + int mColumnId; + int mFlags; + Display mDisplayType; + + ColumnBase (int columnId, Display displayType, int flag); + + virtual ~ColumnBase(); + + virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? + + virtual std::string getTitle() const; + }; + + template + struct Column : public ColumnBase + { + int mFlags; + + Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (columnId, displayType, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + getTitle() + " is not editable"); + } + }; +} + +#endif diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b56c9c8c25..f5e1acfb4a 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,446 +1,446 @@ -#include "tablemimedata.hpp" -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : -mDocument(document) -{ - mUniversalId.push_back (id); - mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); -} - -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : - mUniversalId (id), mDocument(document) -{ - for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) - { - mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); - } -} - -QStringList CSMWorld::TableMimeData::formats() const -{ - return mObjectsFormats; -} - -CSMWorld::TableMimeData::~TableMimeData() -{ -} - -std::string CSMWorld::TableMimeData::getIcon() const -{ - if (mUniversalId.empty()) - { - throw ("TableMimeData holds no UniversalId"); - } - - std::string tmpIcon; - bool firstIteration = true; - - for (unsigned i = 0; i < mUniversalId.size(); ++i) - { - if (firstIteration) - { - firstIteration = false; - tmpIcon = mUniversalId[i].getIcon(); - continue; - } - - if (tmpIcon != mUniversalId[i].getIcon()) - { - return ":/multitype.png"; //icon stolen from gnome - } - - tmpIcon = mUniversalId[i].getIcon(); - } - - return mUniversalId.begin()->getIcon(); //All objects are of the same type; -} - -std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const -{ - return mUniversalId; -} - - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return true; - } - } - - return false; -} - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return true; - } - } - - return false; -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const -{ - return &document == &mDocument; -} - -CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) -{ - switch (type) - { - case CSMWorld::ColumnBase::Display_Race: - return CSMWorld::UniversalId::Type_Race; - - - case CSMWorld::ColumnBase::Display_Skill: - return CSMWorld::UniversalId::Type_Skill; - - - case CSMWorld::ColumnBase::Display_Class: - return CSMWorld::UniversalId::Type_Class; - - - case CSMWorld::ColumnBase::Display_Faction: - return CSMWorld::UniversalId::Type_Faction; - - - case CSMWorld::ColumnBase::Display_Sound: - return CSMWorld::UniversalId::Type_Sound; - - - case CSMWorld::ColumnBase::Display_Region: - return CSMWorld::UniversalId::Type_Region; - - - case CSMWorld::ColumnBase::Display_Birthsign: - return CSMWorld::UniversalId::Type_Birthsign; - - - case CSMWorld::ColumnBase::Display_Spell: - return CSMWorld::UniversalId::Type_Spell; - - - case CSMWorld::ColumnBase::Display_Cell: - return CSMWorld::UniversalId::Type_Cell; - - - case CSMWorld::ColumnBase::Display_Referenceable: - return CSMWorld::UniversalId::Type_Referenceable; - - - case CSMWorld::ColumnBase::Display_Activator: - return CSMWorld::UniversalId::Type_Activator; - - - case CSMWorld::ColumnBase::Display_Potion: - return CSMWorld::UniversalId::Type_Potion; - - - case CSMWorld::ColumnBase::Display_Apparatus: - return CSMWorld::UniversalId::Type_Apparatus; - - - case CSMWorld::ColumnBase::Display_Armor: - return CSMWorld::UniversalId::Type_Armor; - - - case CSMWorld::ColumnBase::Display_Book: - return CSMWorld::UniversalId::Type_Book; - - - case CSMWorld::ColumnBase::Display_Clothing: - return CSMWorld::UniversalId::Type_Clothing; - - - case CSMWorld::ColumnBase::Display_Container: - return CSMWorld::UniversalId::Type_Container; - - - case CSMWorld::ColumnBase::Display_Creature: - return CSMWorld::UniversalId::Type_Creature; - - - case CSMWorld::ColumnBase::Display_Door: - return CSMWorld::UniversalId::Type_Door; - - - case CSMWorld::ColumnBase::Display_Ingredient: - return CSMWorld::UniversalId::Type_Ingredient; - - - case CSMWorld::ColumnBase::Display_CreatureLevelledList: - return CSMWorld::UniversalId::Type_CreatureLevelledList; - - - case CSMWorld::ColumnBase::Display_ItemLevelledList: - return CSMWorld::UniversalId::Type_ItemLevelledList; - - - case CSMWorld::ColumnBase::Display_Light: - return CSMWorld::UniversalId::Type_Light; - - - case CSMWorld::ColumnBase::Display_Lockpick: - return CSMWorld::UniversalId::Type_Lockpick; - - - case CSMWorld::ColumnBase::Display_Miscellaneous: - return CSMWorld::UniversalId::Type_Miscellaneous; - - - case CSMWorld::ColumnBase::Display_Npc: - return CSMWorld::UniversalId::Type_Npc; - - - case CSMWorld::ColumnBase::Display_Probe: - return CSMWorld::UniversalId::Type_Probe; - - - case CSMWorld::ColumnBase::Display_Repair: - return CSMWorld::UniversalId::Type_Repair; - - - case CSMWorld::ColumnBase::Display_Static: - return CSMWorld::UniversalId::Type_Static; - - - case CSMWorld::ColumnBase::Display_Weapon: - return CSMWorld::UniversalId::Type_Weapon; - - - case CSMWorld::ColumnBase::Display_Reference: - return CSMWorld::UniversalId::Type_Reference; - - - case CSMWorld::ColumnBase::Display_Filter: - return CSMWorld::UniversalId::Type_Filter; - - - case CSMWorld::ColumnBase::Display_Topic: - return CSMWorld::UniversalId::Type_Topic; - - - case CSMWorld::ColumnBase::Display_Journal: - return CSMWorld::UniversalId::Type_Journal; - - - case CSMWorld::ColumnBase::Display_TopicInfo: - return CSMWorld::UniversalId::Type_TopicInfo; - - - case CSMWorld::ColumnBase::Display_JournalInfo: - return CSMWorld::UniversalId::Type_JournalInfo; - - - case CSMWorld::ColumnBase::Display_Scene: - return CSMWorld::UniversalId::Type_Scene; - - - case CSMWorld::ColumnBase::Display_Script: - return CSMWorld::UniversalId::Type_Script; - - - default: - return CSMWorld::UniversalId::Type_None; - - } -} - -CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) -{ - switch (type) - { - case CSMWorld::UniversalId::Type_Race: - return CSMWorld::ColumnBase::Display_Race; - - - case CSMWorld::UniversalId::Type_Skill: - return CSMWorld::ColumnBase::Display_Skill; - - - case CSMWorld::UniversalId::Type_Class: - return CSMWorld::ColumnBase::Display_Class; - - - case CSMWorld::UniversalId::Type_Faction: - return CSMWorld::ColumnBase::Display_Faction; - - - case CSMWorld::UniversalId::Type_Sound: - return CSMWorld::ColumnBase::Display_Sound; - - - case CSMWorld::UniversalId::Type_Region: - return CSMWorld::ColumnBase::Display_Region; - - - case CSMWorld::UniversalId::Type_Birthsign: - return CSMWorld::ColumnBase::Display_Birthsign; - - - case CSMWorld::UniversalId::Type_Spell: - return CSMWorld::ColumnBase::Display_Spell; - - - case CSMWorld::UniversalId::Type_Cell: - return CSMWorld::ColumnBase::Display_Cell; - - - case CSMWorld::UniversalId::Type_Referenceable: - return CSMWorld::ColumnBase::Display_Referenceable; - - - case CSMWorld::UniversalId::Type_Activator: - return CSMWorld::ColumnBase::Display_Activator; - - - case CSMWorld::UniversalId::Type_Potion: - return CSMWorld::ColumnBase::Display_Potion; - - - case CSMWorld::UniversalId::Type_Apparatus: - return CSMWorld::ColumnBase::Display_Apparatus; - - - case CSMWorld::UniversalId::Type_Armor: - return CSMWorld::ColumnBase::Display_Armor; - - - case CSMWorld::UniversalId::Type_Book: - return CSMWorld::ColumnBase::Display_Book; - - - case CSMWorld::UniversalId::Type_Clothing: - return CSMWorld::ColumnBase::Display_Clothing; - - - case CSMWorld::UniversalId::Type_Container: - return CSMWorld::ColumnBase::Display_Container; - - - case CSMWorld::UniversalId::Type_Creature: - return CSMWorld::ColumnBase::Display_Creature; - - - case CSMWorld::UniversalId::Type_Door: - return CSMWorld::ColumnBase::Display_Door; - - - case CSMWorld::UniversalId::Type_Ingredient: - return CSMWorld::ColumnBase::Display_Ingredient; - - - case CSMWorld::UniversalId::Type_CreatureLevelledList: - return CSMWorld::ColumnBase::Display_CreatureLevelledList; - - - case CSMWorld::UniversalId::Type_ItemLevelledList: - return CSMWorld::ColumnBase::Display_ItemLevelledList; - - - case CSMWorld::UniversalId::Type_Light: - return CSMWorld::ColumnBase::Display_Light; - - - case CSMWorld::UniversalId::Type_Lockpick: - return CSMWorld::ColumnBase::Display_Lockpick; - - - case CSMWorld::UniversalId::Type_Miscellaneous: - return CSMWorld::ColumnBase::Display_Miscellaneous; - - - case CSMWorld::UniversalId::Type_Npc: - return CSMWorld::ColumnBase::Display_Npc; - - - case CSMWorld::UniversalId::Type_Probe: - return CSMWorld::ColumnBase::Display_Probe; - - - case CSMWorld::UniversalId::Type_Repair: - return CSMWorld::ColumnBase::Display_Repair; - - - case CSMWorld::UniversalId::Type_Static: - return CSMWorld::ColumnBase::Display_Static; - - - case CSMWorld::UniversalId::Type_Weapon: - return CSMWorld::ColumnBase::Display_Weapon; - - - case CSMWorld::UniversalId::Type_Reference: - return CSMWorld::ColumnBase::Display_Reference; - - - case CSMWorld::UniversalId::Type_Filter: - return CSMWorld::ColumnBase::Display_Filter; - - - case CSMWorld::UniversalId::Type_Topic: - return CSMWorld::ColumnBase::Display_Topic; - - - case CSMWorld::UniversalId::Type_Journal: - return CSMWorld::ColumnBase::Display_Journal; - - - case CSMWorld::UniversalId::Type_TopicInfo: - return CSMWorld::ColumnBase::Display_TopicInfo; - - - case CSMWorld::UniversalId::Type_JournalInfo: - return CSMWorld::ColumnBase::Display_JournalInfo; - - - case CSMWorld::UniversalId::Type_Scene: - return CSMWorld::ColumnBase::Display_Scene; - - - case CSMWorld::UniversalId::Type_Script: - return CSMWorld::ColumnBase::Display_Script; - - - default: - return CSMWorld::ColumnBase::Display_None; - } +#include "tablemimedata.hpp" +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) +{ + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) +{ + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + } +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return mObjectsFormats; +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw ("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ":/multitype.png"; //icon stolen from gnome + } + + tmpIcon = mUniversalId[i].getIcon(); + } + + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} + + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + + + default: + return CSMWorld::UniversalId::Type_None; + + } +} + +CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) +{ + switch (type) + { + case CSMWorld::UniversalId::Type_Race: + return CSMWorld::ColumnBase::Display_Race; + + + case CSMWorld::UniversalId::Type_Skill: + return CSMWorld::ColumnBase::Display_Skill; + + + case CSMWorld::UniversalId::Type_Class: + return CSMWorld::ColumnBase::Display_Class; + + + case CSMWorld::UniversalId::Type_Faction: + return CSMWorld::ColumnBase::Display_Faction; + + + case CSMWorld::UniversalId::Type_Sound: + return CSMWorld::ColumnBase::Display_Sound; + + + case CSMWorld::UniversalId::Type_Region: + return CSMWorld::ColumnBase::Display_Region; + + + case CSMWorld::UniversalId::Type_Birthsign: + return CSMWorld::ColumnBase::Display_Birthsign; + + + case CSMWorld::UniversalId::Type_Spell: + return CSMWorld::ColumnBase::Display_Spell; + + + case CSMWorld::UniversalId::Type_Cell: + return CSMWorld::ColumnBase::Display_Cell; + + + case CSMWorld::UniversalId::Type_Referenceable: + return CSMWorld::ColumnBase::Display_Referenceable; + + + case CSMWorld::UniversalId::Type_Activator: + return CSMWorld::ColumnBase::Display_Activator; + + + case CSMWorld::UniversalId::Type_Potion: + return CSMWorld::ColumnBase::Display_Potion; + + + case CSMWorld::UniversalId::Type_Apparatus: + return CSMWorld::ColumnBase::Display_Apparatus; + + + case CSMWorld::UniversalId::Type_Armor: + return CSMWorld::ColumnBase::Display_Armor; + + + case CSMWorld::UniversalId::Type_Book: + return CSMWorld::ColumnBase::Display_Book; + + + case CSMWorld::UniversalId::Type_Clothing: + return CSMWorld::ColumnBase::Display_Clothing; + + + case CSMWorld::UniversalId::Type_Container: + return CSMWorld::ColumnBase::Display_Container; + + + case CSMWorld::UniversalId::Type_Creature: + return CSMWorld::ColumnBase::Display_Creature; + + + case CSMWorld::UniversalId::Type_Door: + return CSMWorld::ColumnBase::Display_Door; + + + case CSMWorld::UniversalId::Type_Ingredient: + return CSMWorld::ColumnBase::Display_Ingredient; + + + case CSMWorld::UniversalId::Type_CreatureLevelledList: + return CSMWorld::ColumnBase::Display_CreatureLevelledList; + + + case CSMWorld::UniversalId::Type_ItemLevelledList: + return CSMWorld::ColumnBase::Display_ItemLevelledList; + + + case CSMWorld::UniversalId::Type_Light: + return CSMWorld::ColumnBase::Display_Light; + + + case CSMWorld::UniversalId::Type_Lockpick: + return CSMWorld::ColumnBase::Display_Lockpick; + + + case CSMWorld::UniversalId::Type_Miscellaneous: + return CSMWorld::ColumnBase::Display_Miscellaneous; + + + case CSMWorld::UniversalId::Type_Npc: + return CSMWorld::ColumnBase::Display_Npc; + + + case CSMWorld::UniversalId::Type_Probe: + return CSMWorld::ColumnBase::Display_Probe; + + + case CSMWorld::UniversalId::Type_Repair: + return CSMWorld::ColumnBase::Display_Repair; + + + case CSMWorld::UniversalId::Type_Static: + return CSMWorld::ColumnBase::Display_Static; + + + case CSMWorld::UniversalId::Type_Weapon: + return CSMWorld::ColumnBase::Display_Weapon; + + + case CSMWorld::UniversalId::Type_Reference: + return CSMWorld::ColumnBase::Display_Reference; + + + case CSMWorld::UniversalId::Type_Filter: + return CSMWorld::ColumnBase::Display_Filter; + + + case CSMWorld::UniversalId::Type_Topic: + return CSMWorld::ColumnBase::Display_Topic; + + + case CSMWorld::UniversalId::Type_Journal: + return CSMWorld::ColumnBase::Display_Journal; + + + case CSMWorld::UniversalId::Type_TopicInfo: + return CSMWorld::ColumnBase::Display_TopicInfo; + + + case CSMWorld::UniversalId::Type_JournalInfo: + return CSMWorld::ColumnBase::Display_JournalInfo; + + + case CSMWorld::UniversalId::Type_Scene: + return CSMWorld::ColumnBase::Display_Scene; + + + case CSMWorld::UniversalId::Type_Script: + return CSMWorld::ColumnBase::Display_Script; + + + default: + return CSMWorld::ColumnBase::Display_None; + } } \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 7687f3555f..0b0143abd4 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,63 +1,63 @@ - -#ifndef TABLEMIMEDATA_H -#define TABLEMIMEDATA_H - -#include - -#include -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -namespace CSMDoc -{ - class Document; -} - -namespace CSMWorld -{ - -/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. -/// -/// This class provides way to construct mimedata object holding the universalid copy -/// Universalid is used in the majority of the tables to store type, id, argument types. -/// This way universalid grants a way to retrive record from the concrete table. -/// Please note, that tablemimedata object can hold multiple universalIds in the vector. - - class TableMimeData : public QMimeData - { - public: - TableMimeData(UniversalId id, const CSMDoc::Document& document); - - TableMimeData(std::vector& id, const CSMDoc::Document& document); - - ~TableMimeData(); - - virtual QStringList formats() const; - - std::string getIcon() const; - - std::vector getData() const; - - bool holdsType(UniversalId::Type type) const; - - bool holdsType(CSMWorld::ColumnBase::Display type) const; - - bool fromDocument(const CSMDoc::Document& document) const; - - UniversalId returnMatching(UniversalId::Type type) const; - - UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; - - static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); - static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); - - private: - std::vector mUniversalId; - QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; - - }; -} + +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +#include +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + + ~TableMimeData(); + + virtual QStringList formats() const; + + std::string getIcon() const; + + std::vector getData() const; + + bool holdsType(UniversalId::Type type) const; + + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + + UniversalId returnMatching(UniversalId::Type type) const; + + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; + + static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); + + private: + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; + + }; +} #endif // TABLEMIMEDATA_H \ No newline at end of file diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index cc1578bdd9..841919a9eb 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -1,203 +1,203 @@ - -#include "editwidget.hpp" - -#include -#include -#include - -#include "../../model/world/data.hpp" - -CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) -: QLineEdit (parent), mParser (data) -{ - mPalette = palette(); - connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); - - QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); - - connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), - this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), - this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), - Qt::QueuedConnection); -} - -void CSVFilter::EditWidget::textChanged (const QString& text) -{ - if (mParser.parse (text.toUtf8().constData())) - { - setPalette (mPalette); - emit filterChanged (mParser.getFilter()); - } - else - { - QPalette palette (mPalette); - palette.setColor (QPalette::Text, Qt::red); - setPalette (palette); - - /// \todo improve error reporting; mark only the faulty part - } -} - -void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, - const QModelIndex& bottomRight) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, - Qt::DropAction action) -{ - const unsigned count = filterSource.size(); - bool multipleElements = false; - - switch (count) //setting multipleElements; - { - case 0: //empty - return; //nothing to do here - - case 1: //only single - multipleElements = false; - break; - - default: - multipleElements = true; - break; - } - - Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); - QString oldContent (text()); - - bool replaceMode = false; - std::string orAnd; - - switch (key) //setting replaceMode and string used to glue expressions - { - case Qt::ShiftModifier: - orAnd = "!or("; - replaceMode = false; - break; - - case Qt::ControlModifier: - orAnd = "!and("; - replaceMode = false; - break; - - default: - replaceMode = true; - break; - } - - if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode - { - replaceMode = true; - } - - if (!replaceMode) - { - oldContent.remove ('!'); - } - - std::stringstream ss; - - if (multipleElements) - { - if (replaceMode) - { - ss<<"!or("; - } else { - ss << orAnd << oldContent.toStdString() << ','; - } - - for (unsigned i = 0; i < count; ++i) - { - ss<4) - { - clear(); - insert (QString::fromUtf8(ss.str().c_str())); - } -} - -std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const -{ - const unsigned columns = seekedString.second.size(); - - bool multipleColumns = false; - switch (columns) - { - case 0: //empty - return ""; //no column to filter - - case 1: //one column to look for - multipleColumns = false; - break; - - default: - multipleColumns = true; - break; - } - - std::stringstream ss; - if (multipleColumns) - { - ss<<"or("; - for (unsigned i = 0; i < columns; ++i) - { - ss<<"string("<<'"'< +#include +#include + +#include "../../model/world/data.hpp" + +CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) +: QLineEdit (parent), mParser (data) +{ + mPalette = palette(); + connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); + + connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), + this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), + Qt::QueuedConnection); +} + +void CSVFilter::EditWidget::textChanged (const QString& text) +{ + if (mParser.parse (text.toUtf8().constData())) + { + setPalette (mPalette); + emit filterChanged (mParser.getFilter()); + } + else + { + QPalette palette (mPalette); + palette.setColor (QPalette::Text, Qt::red); + setPalette (palette); + + /// \todo improve error reporting; mark only the faulty part + } +} + +void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + const unsigned count = filterSource.size(); + bool multipleElements = false; + + switch (count) //setting multipleElements; + { + case 0: //empty + return; //nothing to do here + + case 1: //only single + multipleElements = false; + break; + + default: + multipleElements = true; + break; + } + + Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); + QString oldContent (text()); + + bool replaceMode = false; + std::string orAnd; + + switch (key) //setting replaceMode and string used to glue expressions + { + case Qt::ShiftModifier: + orAnd = "!or("; + replaceMode = false; + break; + + case Qt::ControlModifier: + orAnd = "!and("; + replaceMode = false; + break; + + default: + replaceMode = true; + break; + } + + if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode + { + replaceMode = true; + } + + if (!replaceMode) + { + oldContent.remove ('!'); + } + + std::stringstream ss; + + if (multipleElements) + { + if (replaceMode) + { + ss<<"!or("; + } else { + ss << orAnd << oldContent.toStdString() << ','; + } + + for (unsigned i = 0; i < count; ++i) + { + ss<4) + { + clear(); + insert (QString::fromUtf8(ss.str().c_str())); + } +} + +std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const +{ + const unsigned columns = seekedString.second.size(); + + bool multipleColumns = false; + switch (columns) + { + case 0: //empty + return ""; //no column to filter + + case 1: //one column to look for + multipleColumns = false; + break; + + default: + multipleColumns = true; + break; + } + + std::stringstream ss; + if (multipleColumns) + { + ss<<"or("; + for (unsigned i = 0; i < columns; ++i) + { + ss<<"string("<<'"'< - -#include -#include -#include - -#include "../../model/filter/parser.hpp" -#include "../../model/filter/node.hpp" - -class QModelIndex; - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class EditWidget : public QLineEdit - { - Q_OBJECT - - CSMFilter::Parser mParser; - QPalette mPalette; - - public: - - EditWidget (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - - private: - std::string generateFilter(std::pair >& seekedString) const; - - private slots: - - void textChanged (const QString& text); - - void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void filterRowsRemoved (const QModelIndex& parent, int start, int end); - - void filterRowsInserted (const QModelIndex& parent, int start, int end); - - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - - void useFilterRequest(const std::string& idOfFilter); - }; -} - -#endif +#ifndef CSV_FILTER_EDITWIDGET_H +#define CSV_FILTER_EDITWIDGET_H + +#include + +#include +#include +#include + +#include "../../model/filter/parser.hpp" +#include "../../model/filter/node.hpp" + +class QModelIndex; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class EditWidget : public QLineEdit + { + Q_OBJECT + + CSMFilter::Parser mParser; + QPalette mPalette; + + public: + + EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + + private: + std::string generateFilter(std::pair >& seekedString) const; + + private slots: + + void textChanged (const QString& text); + + void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void filterRowsRemoved (const QModelIndex& parent, int start, int end); + + void filterRowsInserted (const QModelIndex& parent, int start, int end); + + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + + void useFilterRequest(const std::string& idOfFilter); + }; +} + +#endif diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index a332880252..4954efefb8 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -1,50 +1,50 @@ - -#include "filterbox.hpp" - -#include -#include - -#include "recordfilterbox.hpp" - -#include - -CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); - - layout->addWidget (recordFilterBox); - - setLayout (layout); - - connect (recordFilterBox, - SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (recordFilterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); - setAcceptDrops(true); -} - -void CSVFilter::FilterBox::dropEvent (QDropEvent* event) -{ - std::vector data = dynamic_cast (event->mimeData())->getData(); - - emit recordDropped(data, event->proposedAction()); -} - -void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) -{ - event->acceptProposedAction(); -} - -void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) -{ - event->accept(); -} + +#include "filterbox.hpp" + +#include +#include + +#include "recordfilterbox.hpp" + +#include + +CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + + layout->addWidget (recordFilterBox); + + setLayout (layout); + + connect (recordFilterBox, + SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (recordFilterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + setAcceptDrops(true); +} + +void CSVFilter::FilterBox::dropEvent (QDropEvent* event) +{ + std::vector data = dynamic_cast (event->mimeData())->getData(); + + emit recordDropped(data, event->proposedAction()); +} + +void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 3817d5e709..263e89e73d 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -1,45 +1,45 @@ -#ifndef CSV_FILTER_FILTERBOX_H -#define CSV_FILTER_FILTERBOX_H - -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/universalid.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class FilterBox : public QWidget - { - Q_OBJECT - - void dragEnterEvent (QDragEnterEvent* event); - - void dropEvent (QDropEvent* event); - - void dragMoveEvent(QDragMoveEvent *event); - - public: - - FilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void recordFilterChanged (boost::shared_ptr filter); - void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - -#endif - +#ifndef CSV_FILTER_FILTERBOX_H +#define CSV_FILTER_FILTERBOX_H + +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class FilterBox : public QWidget + { + Q_OBJECT + + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent(QDragMoveEvent *event); + + public: + + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void recordFilterChanged (boost::shared_ptr filter); + void recordDropped (std::vector& types, Qt::DropAction action); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + +#endif + diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 2a1a1407fa..59c083bd89 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -1,32 +1,32 @@ - -#include "recordfilterbox.hpp" - -#include -#include - -#include "editwidget.hpp" - -CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - layout->addWidget (new QLabel ("Record Filter", this)); - - EditWidget *editWidget = new EditWidget (data, this); - - layout->addWidget (editWidget); - - setLayout (layout); - - connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (filterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); -} + +#include "recordfilterbox.hpp" + +#include +#include + +#include "editwidget.hpp" + +CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (new QLabel ("Record Filter", this)); + + EditWidget *editWidget = new EditWidget (data, this); + + layout->addWidget (editWidget); + + setLayout (layout); + + connect ( + editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (filterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 3638dc6c34..fd384a32b4 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -1,38 +1,38 @@ -#ifndef CSV_FILTER_RECORDFILTERBOX_H -#define CSV_FILTER_RECORDFILTERBOX_H - -#include - -#include -#include - -#include - -#include "../../model/filter/node.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class RecordFilterBox : public QWidget - { - Q_OBJECT - - public: - - RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - +#ifndef CSV_FILTER_RECORDFILTERBOX_H +#define CSV_FILTER_RECORDFILTERBOX_H + +#include + +#include +#include + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class RecordFilterBox : public QWidget + { + Q_OBJECT + + public: + + RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + #endif \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index edf3bc6dea..bc67c68b51 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -1,546 +1,546 @@ - -#include "table.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -#include "../../model/world/data.hpp" -#include "../../model/world/commands.hpp" -#include "../../model/world/idtableproxymodel.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/record.hpp" -#include "../../model/world/columns.hpp" -#include "../../model/world/tablemimedata.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "recordstatusdelegate.hpp" -#include "util.hpp" - -void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - QMenu menu (this); - - /// \todo add menu items for select all and clear selection - - if (!mEditLock) - { - if (selectedRows.size()==1) - { - menu.addAction (mEditAction); - if (mCreateAction) - menu.addAction(mCloneAction); - } - - if (mCreateAction) - menu.addAction (mCreateAction); - - /// \todo Reverting temporarily disabled on tables that support reordering, because - /// revert logic currently can not handle reordering. - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); - - if (listDeletableSelectedIds().size()>0) - menu.addAction (mDeleteAction); - - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) - { - /// \todo allow reordering of multiple rows - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); - - if (column==-1) - column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); - - if (column!=-1) - { - if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row-1, column))) - { - menu.addAction (mMoveUpAction); - } - - if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row+1, column))) - { - menu.addAction (mMoveDownAction); - } - } - } - } - } - - menu.exec (event->globalPos()); -} - -std::vector CSVWorld::Table::listRevertableSelectedIds() const -{ - std::vector revertableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - revertableIds.push_back (id); - } - } - } - - return revertableIds; -} - -std::vector CSVWorld::Table::listDeletableSelectedIds() const -{ - std::vector deletableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - // check record state - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state==CSMWorld::RecordBase::State_Deleted) - continue; - - // check other columns (only relevant for a subset of the tables) - int dialogueTypeIndex = - mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - - if (dialogueTypeIndex!=-1) - { - int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - - if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) - continue; - } - - // add the id to the collection - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); - } - } - - return deletableIds; -} - -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) -{ - mModel = &dynamic_cast (*data.getTableModel (id)); - - mProxyModel = new CSMWorld::IdTableProxyModel (this); - mProxyModel->setSourceModel (mModel); - - setModel (mProxyModel); - horizontalHeader()->setResizeMode (QHeaderView::Interactive); - verticalHeader()->hide(); - setSortingEnabled (sorting); - setSelectionBehavior (QAbstractItemView::SelectRows); - setSelectionMode (QAbstractItemView::ExtendedSelection); - - int columns = mModel->columnCount(); - - for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); - - if (flags & CSMWorld::ColumnBase::Flag_Table) - { - CSMWorld::ColumnBase::Display display = static_cast ( - mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); - - mDelegates.push_back (delegate); - setItemDelegateForColumn (i, delegate); - } - else - hideColumn (i); - } - - mEditAction = new QAction (tr ("Edit Record"), this); - connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); - addAction (mEditAction); - - if (createAndDelete) - { - mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); - addAction (mCreateAction); - - mCloneAction = new QAction (tr ("Clone Record"), this); - connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); - addAction(mCloneAction); - } - - mRevertAction = new QAction (tr ("Revert Record"), this); - connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); - addAction (mRevertAction); - - mDeleteAction = new QAction (tr ("Delete Record"), this); - connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); - addAction (mDeleteAction); - - mMoveUpAction = new QAction (tr ("Move Up"), this); - connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); - addAction (mMoveUpAction); - - mMoveDownAction = new QAction (tr ("Move Down"), this); - connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); - addAction (mMoveDownAction); - - connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (tableSizeUpdate())); - - /// \note This signal could instead be connected to a slot that filters out changes not affecting - /// the records status column (for permanence reasons) - connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (tableSizeUpdate())); - - connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), - this, SLOT (selectionSizeUpdate ())); - - setAcceptDrops(true); -} - -void CSVWorld::Table::setEditLock (bool locked) -{ - for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) - (*iter)->setEditLock (locked); - - mEditLock = locked; -} - -CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const -{ - return CSMWorld::UniversalId ( - static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); -} - -void CSVWorld::Table::revertRecord() -{ - if (!mEditLock) - { - std::vector revertableIds = listRevertableSelectedIds(); - - if (revertableIds.size()>0) - { - if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); - - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); - - if (revertableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::deleteRecord() -{ - if (!mEditLock) - { - std::vector deletableIds = listDeletableSelectedIds(); - - if (deletableIds.size()>0) - { - if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); - - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - - if (deletableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::editRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - emit editRequest (selectedRows.begin()->row()); - } -} - -void CSVWorld::Table::cloneRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); - if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) - { - emit cloneRequest (toClone); - } - } -} - -void CSVWorld::Table::moveUpRecord() -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - { - int row2 =selectedRows.begin()->row(); - - if (row2>0) - { - int row = row2-1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; iselectedRows(); - - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - if (rowrowCount()-1) - { - int row2 = row+1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; icolumnCount(); - - for (int i=0; i (*delegate). - updateEditorSetting (settingName, settingValue)) - emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); -} - -void CSVWorld::Table::tableSizeUpdate() -{ - int size = 0; - int deleted = 0; - int modified = 0; - - if (mProxyModel->columnCount()>0) - { - int rows = mProxyModel->rowCount(); - - for (int i=0; imapToSource (mProxyModel->index (i, 0)); - - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); - int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); - - switch (state) - { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; - } - } - } - - tableSizeChanged (size, deleted, modified); -} - -void CSVWorld::Table::selectionSizeUpdate() -{ - selectionSizeChanged (selectionModel()->selectedRows().size()); -} - -void CSVWorld::Table::requestFocus (const std::string& id) -{ - QModelIndex index = mProxyModel->getModelIndex (id, 0); - - if (index.isValid()) - scrollTo (index, QAbstractItemView::PositionAtTop); -} - -void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) -{ - mProxyModel->setFilter (filter); -} - -void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) -{ - if (event->buttons() & Qt::LeftButton) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size() == 0) - { - return; - } - - QDrag* drag = new QDrag (this); - CSMWorld::TableMimeData* mime = NULL; - - if (selectedRows.size() == 1) - { - mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); - } - else - { - std::vector idToDrag; - - foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. - { - idToDrag.push_back (getUniversalId (it.row())); - } - - mime = new CSMWorld::TableMimeData (idToDrag, mDocument); - } - - drag->setMimeData (mime); - drag->setPixmap (QString::fromStdString (mime->getIcon())); - - Qt::DropActions action = Qt::IgnoreAction; - switch (QApplication::keyboardModifiers()) - { - case Qt::ControlModifier: - action = Qt::CopyAction; - break; - - case Qt::ShiftModifier: - action = Qt::MoveAction; - break; - } - - drag->exec(action); - } - -} - -void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) -{ - event->acceptProposedAction(); -} - -void CSVWorld::Table::dropEvent(QDropEvent *event) -{ - QModelIndex index = indexAt (event->pos()); - - if (!index.isValid()) - { - return; - } - - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); - if (mime->fromDocument (mDocument)) - { - CSMWorld::ColumnBase::Display display = static_cast - (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (mime->holdsType (display)) - { - CSMWorld::UniversalId record (mime->returnMatching (display)); - - std::auto_ptr command (new CSMWorld::ModifyCommand - (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - - mUndoStack.push (command.release()); - } - } //TODO handle drops from different document -} - -void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) -{ - event->accept(); -} - -std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const -{ - const int count = mModel->columnCount(); - - std::vector titles; - for (int i = 0; i < count; ++i) - { - CSMWorld::ColumnBase::Display columndisplay = static_cast - (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (display == columndisplay) - { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); - } - } - return titles; + +#include "table.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtableproxymodel.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "recordstatusdelegate.hpp" +#include "util.hpp" + +void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu (this); + + /// \todo add menu items for select all and clear selection + + if (!mEditLock) + { + if (selectedRows.size()==1) + { + menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } + + if (mCreateAction) + menu.addAction (mCreateAction); + + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); + + if (listDeletableSelectedIds().size()>0) + menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } + } + + menu.exec (event->globalPos()); +} + +std::vector CSVWorld::Table::listRevertableSelectedIds() const +{ + std::vector revertableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } + } + + return revertableIds; +} + +std::vector CSVWorld::Table::listDeletableSelectedIds() const +{ + std::vector deletableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + // check record state + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state==CSMWorld::RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) + { + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; + } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } + + return deletableIds; +} + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) +{ + mModel = &dynamic_cast (*data.getTableModel (id)); + + mProxyModel = new CSMWorld::IdTableProxyModel (this); + mProxyModel->setSourceModel (mModel); + + setModel (mProxyModel); + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + setSortingEnabled (sorting); + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + int columns = mModel->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + + if (flags & CSMWorld::ColumnBase::Flag_Table) + { + CSMWorld::ColumnBase::Display display = static_cast ( + mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, + undoStack, this); + + mDelegates.push_back (delegate); + setItemDelegateForColumn (i, delegate); + } + else + hideColumn (i); + } + + mEditAction = new QAction (tr ("Edit Record"), this); + connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + addAction (mEditAction); + + if (createAndDelete) + { + mCreateAction = new QAction (tr ("Add Record"), this); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); + addAction (mCreateAction); + + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); + addAction(mCloneAction); + } + + mRevertAction = new QAction (tr ("Revert Record"), this); + connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + addAction (mRevertAction); + + mDeleteAction = new QAction (tr ("Delete Record"), this); + connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + addAction (mDeleteAction); + + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); +} + +void CSVWorld::Table::setEditLock (bool locked) +{ + for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) + (*iter)->setEditLock (locked); + + mEditLock = locked; +} + +CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const +{ + return CSMWorld::UniversalId ( + static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), + mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); +} + +void CSVWorld::Table::revertRecord() +{ + if (!mEditLock) + { + std::vector revertableIds = listRevertableSelectedIds(); + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::deleteRecord() +{ + if (!mEditLock) + { + std::vector deletableIds = listDeletableSelectedIds(); + + if (deletableIds.size()>0) + { + if (deletableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + + if (deletableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::editRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit editRequest (selectedRows.begin()->row()); + } +} + +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) + { + emit cloneRequest (toClone); + } + } +} + +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); + + for (int i=0; i (*delegate). + updateEditorSetting (settingName, settingValue)) + emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); +} + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mProxyModel->columnCount()>0) + { + int rows = mProxyModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); +} + +void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) +{ + mProxyModel->setFilter (filter); +} + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); + + Qt::DropActions action = Qt::IgnoreAction; + switch (QApplication::keyboardModifiers()) + { + case Qt::ControlModifier: + action = Qt::CopyAction; + break; + + case Qt::ShiftModifier: + action = Qt::MoveAction; + break; + } + + drag->exec(action); + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + QModelIndex index = indexAt (event->pos()); + + if (!index.isValid()) + { + return; + } + + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) + { + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (mime->holdsType (display)) + { + CSMWorld::UniversalId record (mime->returnMatching (display)); + + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const +{ + const int count = mModel->columnCount(); + + std::vector titles; + for (int i = 0; i < count; ++i) + { + CSMWorld::ColumnBase::Display columndisplay = static_cast + (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (display == columndisplay) + { + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + } + } + return titles; } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 615a31b4d7..04733a53e7 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -1,127 +1,127 @@ -#ifndef CSV_WORLD_TABLE_H -#define CSV_WORLD_TABLE_H - -#include -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/columnbase.hpp" - -namespace CSMDoc { - class Document; -} - -class QUndoStack; -class QAction; - -namespace CSMWorld -{ - class Data; - class UniversalId; - class IdTableProxyModel; - class IdTable; -} - -namespace CSVWorld -{ - class CommandDelegate; - - ///< Table widget - class Table : public QTableView - { - Q_OBJECT - - std::vector mDelegates; - QUndoStack& mUndoStack; - QAction *mEditAction; - QAction *mCreateAction; - QAction *mCloneAction; - QAction *mRevertAction; - QAction *mDeleteAction; - QAction *mMoveUpAction; - QAction *mMoveDownAction; - CSMWorld::IdTableProxyModel *mProxyModel; - CSMWorld::IdTable *mModel; - bool mEditLock; - int mRecordStatusDisplay; - - /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you - /// should NOT use it for anything else. - const CSMDoc::Document& mDocument; - - private: - - void contextMenuEvent (QContextMenuEvent *event); - - std::vector listRevertableSelectedIds() const; - - std::vector listDeletableSelectedIds() const; - - void mouseMoveEvent(QMouseEvent *event); - - void dragEnterEvent(QDragEnterEvent *event); - - void dragMoveEvent(QDragMoveEvent *event); - - void dropEvent(QDropEvent *event); - - public: - - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - - ///< \param createAndDelete Allow creation and deletion of records. - /// \param sorting Allow changing order of rows in the view via column headers. - - void setEditLock (bool locked); - - CSMWorld::UniversalId getUniversalId (int row) const; - - void updateEditorSetting (const QString &settingName, const QString &settingValue); - - std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; - - signals: - - void editRequest (int row); - - void selectionSizeChanged (int size); - - void tableSizeChanged (int size, int deleted, int modified); - ///< \param size Number of not deleted records - /// \param deleted Number of deleted records - /// \param modified Number of added and modified records - - void createRequest(); - void cloneRequest(const CSMWorld::UniversalId&); - - private slots: - - void revertRecord(); - - void deleteRecord(); - - void editRecord(); - - void cloneRecord(); - - void moveUpRecord(); - - void moveDownRecord(); - - public slots: - - void tableSizeUpdate(); - - void selectionSizeUpdate(); - - void requestFocus (const std::string& id); - - void recordFilterChanged (boost::shared_ptr filter); - }; -} - -#endif +#ifndef CSV_WORLD_TABLE_H +#define CSV_WORLD_TABLE_H + +#include +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/columnbase.hpp" + +namespace CSMDoc { + class Document; +} + +class QUndoStack; +class QAction; + +namespace CSMWorld +{ + class Data; + class UniversalId; + class IdTableProxyModel; + class IdTable; +} + +namespace CSVWorld +{ + class CommandDelegate; + + ///< Table widget + class Table : public QTableView + { + Q_OBJECT + + std::vector mDelegates; + QUndoStack& mUndoStack; + QAction *mEditAction; + QAction *mCreateAction; + QAction *mCloneAction; + QAction *mRevertAction; + QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; + CSMWorld::IdTableProxyModel *mProxyModel; + CSMWorld::IdTable *mModel; + bool mEditLock; + int mRecordStatusDisplay; + + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + std::vector listRevertableSelectedIds() const; + + std::vector listDeletableSelectedIds() const; + + void mouseMoveEvent(QMouseEvent *event); + + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + + public: + + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + + ///< \param createAndDelete Allow creation and deletion of records. + /// \param sorting Allow changing order of rows in the view via column headers. + + void setEditLock (bool locked); + + CSMWorld::UniversalId getUniversalId (int row) const; + + void updateEditorSetting (const QString &settingName, const QString &settingValue); + + std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; + + signals: + + void editRequest (int row); + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + void cloneRequest(const CSMWorld::UniversalId&); + + private slots: + + void revertRecord(); + + void deleteRecord(); + + void editRecord(); + + void cloneRecord(); + + void moveUpRecord(); + + void moveDownRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + + void recordFilterChanged (boost::shared_ptr filter); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e330d47758..782ccfd244 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,132 +1,132 @@ - -#include "tablesubview.hpp" - -#include -#include - -#include "../../model/doc/document.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "../filter/filterbox.hpp" -#include "table.hpp" -#include "tablebottombox.hpp" -#include "creator.hpp" - -CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting) -: SubView (id) -{ - QVBoxLayout *layout = new QVBoxLayout; - - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - - layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); - - layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); - - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); - - layout->insertWidget (0, filterBox); - - QWidget *widget = new QWidget; - - widget->setLayout (layout); - - setWidget (widget); - - connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); - - connect (mTable, SIGNAL (selectionSizeChanged (int)), - mBottom, SLOT (selectionSizeChanged (int))); - connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), - mBottom, SLOT (tableSizeChanged (int, int, int))); - - mTable->tableSizeUpdate(); - mTable->selectionSizeUpdate(); - mTable->viewport()->installEventFilter(this); - mBottom->installEventFilter(this); - filterBox->installEventFilter(this); - - if (mBottom->canCreateAndDelete()) - { - connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, - SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); - } - connect (mBottom, SIGNAL (requestFocus (const std::string&)), - mTable, SLOT (requestFocus (const std::string&))); - - connect (filterBox, - SIGNAL (recordFilterChanged (boost::shared_ptr)), - mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), - this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); -} - -void CSVWorld::TableSubView::setEditLock (bool locked) -{ - mTable->setEditLock (locked); - mBottom->setEditLock (locked); -} - -void CSVWorld::TableSubView::editRequest (int row) -{ - focusId (mTable->getUniversalId (row)); -} - -void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) -{ - mTable->updateEditorSetting(settingName, settingValue); -} - -void CSVWorld::TableSubView::setStatusBar (bool show) -{ - mBottom->setStatusBar (show); -} - -void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) -{ - emit cloneRequest(toClone.getId(), toClone.getType()); -} - -void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) -{ - std::vector > > filterSource; - - for (std::vector::iterator it = types.begin(); it != types.end(); ++it) - { - std::pair > pair( //splited long line - std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); - - filterSource.push_back(pair); - } - emit createFilterRequest(filterSource, action); -} - -bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) -{ - if (event->type() == QEvent::Drop) - { - QDropEvent* drop = dynamic_cast(event); - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); - if (handled) - { - emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); - } - return handled; - } - return false; + +#include "tablesubview.hpp" + +#include +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "../filter/filterbox.hpp" +#include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" + +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting) +: SubView (id) +{ + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + mTable->viewport()->installEventFilter(this); + mBottom->installEventFilter(this); + filterBox->installEventFilter(this); + + if (mBottom->canCreateAndDelete()) + { + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); + + connect (filterBox, + SIGNAL (recordFilterChanged (boost::shared_ptr)), + mTable, SLOT (recordFilterChanged (boost::shared_ptr))); + + connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); +} + +void CSVWorld::TableSubView::setEditLock (bool locked) +{ + mTable->setEditLock (locked); + mBottom->setEditLock (locked); +} + +void CSVWorld::TableSubView::editRequest (int row) +{ + focusId (mTable->getUniversalId (row)); +} + +void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + mTable->updateEditorSetting(settingName, settingValue); +} + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} + +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) +{ + emit cloneRequest(toClone.getId(), toClone.getType()); +} + +void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) +{ + std::vector > > filterSource; + + for (std::vector::iterator it = types.begin(); it != types.end(); ++it) + { + std::pair > pair( //splited long line + std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); + + filterSource.push_back(pair); + } + emit createFilterRequest(filterSource, action); +} + +bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Drop) + { + QDropEvent* drop = dynamic_cast(event); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; + } + return false; } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1f67e0262a..4e578b180c 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -1,63 +1,63 @@ -#ifndef CSV_WORLD_TABLESUBVIEW_H -#define CSV_WORLD_TABLESUBVIEW_H - -#include "../doc/subview.hpp" - -#include - -class QModelIndex; - -namespace CSMWorld -{ - class IdTable; -} - -namespace CSMDoc -{ - class Document; -} - -namespace CSVWorld -{ - class Table; - class TableBottomBox; - class CreatorFactoryBase; - - class TableSubView : public CSVDoc::SubView - { - Q_OBJECT - - Table *mTable; - TableBottomBox *mBottom; - - public: - - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting); - - virtual void setEditLock (bool locked); - - virtual void updateEditorSetting (const QString& key, const QString& value); - - virtual void setStatusBar (bool show); - - protected: - bool eventFilter(QObject* object, QEvent *event); - - signals: - void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - - private slots: - - void editRequest (int row); - void cloneRequest (const CSMWorld::UniversalId& toClone); - void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, - Qt::DropAction action); - }; -} - -#endif +#ifndef CSV_WORLD_TABLESUBVIEW_H +#define CSV_WORLD_TABLESUBVIEW_H + +#include "../doc/subview.hpp" + +#include + +class QModelIndex; + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class Table; + class TableBottomBox; + class CreatorFactoryBase; + + class TableSubView : public CSVDoc::SubView + { + Q_OBJECT + + Table *mTable; + TableBottomBox *mBottom; + + public: + + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting); + + virtual void setEditLock (bool locked); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + + protected: + bool eventFilter(QObject* object, QEvent *event); + + signals: + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + + private slots: + + void editRequest (int row); + void cloneRequest (const CSMWorld::UniversalId& toClone); + void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, + Qt::DropAction action); + }; +} + +#endif diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 265069dc46..f9b5b4d04a 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,352 +1,352 @@ - -#include "statemanagerimp.hpp" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/scriptmanager.hpp" -#include "../mwbase/soundmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "../mwscript/globalscripts.hpp" - -void MWState::StateManager::cleanup (bool force) -{ - if (mState!=State_NoGame || force) - { - MWBase::Environment::get().getSoundManager()->clear(); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); - MWBase::Environment::get().getWorld()->clear(); - MWBase::Environment::get().getWindowManager()->clear(); - - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } -} - -std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) - const -{ - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - const std::vector& prev = reader.getGameFiles(); - - std::map map; - - for (int iPrev = 0; iPrev (prev.size()); ++iPrev) - { - std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); - - for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) - if (id==Misc::StringUtils::lowerCase (current[iCurrent])) - { - map.insert (std::make_pair (iPrev, iCurrent)); - break; - } - } - - return map; -} - -MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) -{ - -} - -void MWState::StateManager::requestQuit() -{ - mQuitRequest = true; -} - -bool MWState::StateManager::hasQuitRequest() const -{ - return mQuitRequest; -} - -void MWState::StateManager::askLoadRecent() -{ - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) - return; - - if( !mAskLoadRecent ) - { - if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves - { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - else - { - MWState::Slot lastSave = *getCurrentCharacter()->begin(); - std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); - std::string tag("%s"); - std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); - size_t pos = message.find(tag); - message.replace(pos, tag.length(), lastSave.mProfile.mDescription); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); - mAskLoadRecent = true; - } - } -} - -MWState::StateManager::State MWState::StateManager::getState() const -{ - return mState; -} - -void MWState::StateManager::newGame (bool bypass) -{ - cleanup(); - - if (!bypass) - { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame (true); - } - else - MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); - - mState = State_Running; -} - -void MWState::StateManager::endGame() -{ - mState = State_Ended; - MWBase::Environment::get().getWorld()->useDeathCamera(); -} - -void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) -{ - ESM::SavedGame profile; - - MWBase::World& world = *MWBase::Environment::get().getWorld(); - - MWWorld::Ptr player = world.getPlayer().getPlayer(); - - profile.mContentFiles = world.getContentFiles(); - - profile.mPlayerName = player.getClass().getName (player); - profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mClass; - - profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); - profile.mTimePlayed = mTimePlayed; - profile.mDescription = description; - - int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing - Ogre::Image screenshot; - world.screenshot(screenshot, screenshotW, screenshotH); - Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); - profile.mScreenshot.resize(encoded->size()); - encoded->read(&profile.mScreenshot[0], encoded->size()); - - if (!slot) - slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); - else - slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - - std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); - - ESM::ESMWriter writer; - - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 - - writer.setFormat (ESM::Header::CurrentFormat); - writer.setRecordCount ( - 1 // saved game header - +MWBase::Environment::get().getJournal()->countSavedGameRecords() - +MWBase::Environment::get().getWorld()->countSavedGameRecords() - +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() - +1 // global map - ); - - writer.save (stream); - - writer.startRecord (ESM::REC_SAVE); - slot->mProfile.save (writer); - writer.endRecord (ESM::REC_SAVE); - - MWBase::Environment::get().getJournal()->write (writer); - MWBase::Environment::get().getDialogueManager()->write (writer); - MWBase::Environment::get().getWorld()->write (writer); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); - MWBase::Environment::get().getWindowManager()->write(writer); - - writer.close(); - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); -} - -void MWState::StateManager::loadGame (const Character *character, const Slot *slot) -{ - try - { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - std::map contentFileMap = buildContentFileIndexMap (reader); - - while (reader.hasMoreRecs()) - { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); - - switch (n.val) - { - case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); - break; - - case ESM::REC_JOUR: - case ESM::REC_QUES: - - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; - - case ESM::REC_DIAS: - - MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); - break; - - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: - case ESM::REC_CSTA: - - MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); - break; - - case ESM::REC_GSCR: - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; - - case ESM::REC_GMAP: - - MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); - break; - - default: - - // ignore invalid records - /// \todo log error - reader.skipRecord(); - } - } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWindowManager()->setNewGame(false); - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); - } - catch (const std::exception& e) - { - std::cerr << "failed to load saved game: " << e.what() << std::endl; - cleanup (true); - } -} - -MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) -{ - return mCharacterManager.getCurrentCharacter (create); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() -{ - return mCharacterManager.begin(); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() -{ - return mCharacterManager.end(); -} - -void MWState::StateManager::update (float duration) -{ - mTimePlayed += duration; - - // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. - if (mAskLoadRecent) - { - int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) - { - mAskLoadRecent = false; - //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); - MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); - } - else if(iButton==1) - { - mAskLoadRecent = false; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - } -} + +#include "statemanagerimp.hpp" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwscript/globalscripts.hpp" + +void MWState::StateManager::cleanup (bool force) +{ + if (mState!=State_NoGame || force) + { + MWBase::Environment::get().getSoundManager()->clear(); + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); + MWBase::Environment::get().getWorld()->clear(); + MWBase::Environment::get().getWindowManager()->clear(); + + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) + if (id==Misc::StringUtils::lowerCase (current[iCurrent])) + { + map.insert (std::make_pair (iPrev, iCurrent)); + break; + } + } + + return map; +} + +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +{ + +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; +} + +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); + std::string tag("%s"); + std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); + size_t pos = message.find(tag); + message.replace(pos, tag.length(), lastSave.mProfile.mDescription); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + mAskLoadRecent = true; + } + } +} + +MWState::StateManager::State MWState::StateManager::getState() const +{ + return mState; +} + +void MWState::StateManager::newGame (bool bypass) +{ + cleanup(); + + if (!bypass) + { + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + + mState = State_Running; +} + +void MWState::StateManager::endGame() +{ + mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); +} + +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) +{ + ESM::SavedGame profile; + + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + profile.mContentFiles = world.getContentFiles(); + + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mClass; + + profile.mPlayerCell = world.getCellName(); + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + profile.mInGameTime.mYear = world.getYear(); + profile.mTimePlayed = mTimePlayed; + profile.mDescription = description; + + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + Ogre::Image screenshot; + world.screenshot(screenshot, screenshotW, screenshotH); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + profile.mScreenshot.resize(encoded->size()); + encoded->read(&profile.mScreenshot[0], encoded->size()); + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); + + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); + ++iter) + writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + + writer.setFormat (ESM::Header::CurrentFormat); + writer.setRecordCount ( + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map + ); + + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); + MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); + MWBase::Environment::get().getWindowManager()->write(writer); + + writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); +} + +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + try + { + cleanup(); + + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + std::map contentFileMap = buildContentFileIndexMap (reader); + + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); + break; + + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + + case ESM::REC_GMAP: + + MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } + + mCharacterManager.setCurrentCharacter(character); + + mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWindowManager()->setNewGame(false); + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); + } +} + +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) +{ + return mCharacterManager.getCurrentCharacter (create); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } +} diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 91f123eb71..b7207486bc 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,203 +1,203 @@ -#include "esmwriter.hpp" - -#include -#include -#include - -namespace ESM -{ - ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} - - unsigned int ESMWriter::getVersion() const - { - return mHeader.mData.version; - } - - void ESMWriter::setVersion(unsigned int ver) - { - mHeader.mData.version = ver; - } - - void ESMWriter::setAuthor(const std::string& auth) - { - mHeader.mData.author.assign (auth); - } - - void ESMWriter::setDescription(const std::string& desc) - { - mHeader.mData.desc.assign (desc); - } - - void ESMWriter::setRecordCount (int count) - { - mHeader.mData.records = count; - } - - void ESMWriter::setFormat (int format) - { - mHeader.mFormat = format; - } - - void ESMWriter::clearMaster() - { - mHeader.mMaster.clear(); - } - - void ESMWriter::addMaster(const std::string& name, uint64_t size) - { - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); - } - - void ESMWriter::save(std::ostream& file) - { - mRecordCount = 0; - mRecords.clear(); - mCounting = true; - mStream = &file; - - startRecord("TES3", 0); - - mHeader.save (*this); - - endRecord("TES3"); - } - - void ESMWriter::close() - { - if (!mRecords.empty()) - throw std::runtime_error ("Unclosed record remaining"); - } - - void ESMWriter::startRecord(const std::string& name, uint32_t flags) - { - mRecordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::startRecord (uint32_t name, uint32_t flags) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - startRecord (type, flags); - } - - void ESMWriter::startSubRecord(const std::string& name) - { - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::endRecord(const std::string& name) - { - RecordData rec = mRecords.back(); - assert(rec.name == name); - mRecords.pop_back(); - - mStream->seekp(rec.position); - - mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(uint32_t)); - mCounting = true; - - mStream->seekp(0, std::ios::end); - - } - - void ESMWriter::endRecord (uint32_t name) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - endRecord (type); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHString(data); - endRecord(name); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) - { - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); - - if (data.size() < size) - { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); - } - - endRecord(name); - } - - void ESMWriter::writeHString(const std::string& data) - { - if (data.size() == 0) - write("\0", 1); - else - { - // Convert to UTF8 and return - std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - - write(string.c_str(), string.size()); - } - } - - void ESMWriter::writeHCString(const std::string& data) - { - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); - } - - void ESMWriter::writeName(const std::string& name) - { - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); - } - - void ESMWriter::write(const char* data, size_t size) - { - if (mCounting && !mRecords.empty()) - { - for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) - it->size += size; - } - - mStream->write(data, size); - } - - void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) - { - mEncoder = encoder; - } -} +#include "esmwriter.hpp" + +#include +#include +#include + +namespace ESM +{ + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} + + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } + + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } + + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } + + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } + + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } + + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } + + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } + + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } + + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; + + startRecord("TES3", 0); + + mHeader.save (*this); + + endRecord("TES3"); + } + + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } + + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); + + mStream->seekp(rec.position); + + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); + mCounting = true; + + mStream->seekp(0, std::ios::end); + + } + + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + endRecord(name); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) + { + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); + } + + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; + + write(string.c_str(), string.size()); + } + } + + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } + + void ESMWriter::writeName(const std::string& name) + { + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); + } + + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } + + mStream->write(data, size); + } + + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } +} diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 33650e6784..304876dbf6 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,114 +1,114 @@ -#ifndef OPENMW_ESM_WRITER_H -#define OPENMW_ESM_WRITER_H - -#include -#include - -#include - -#include "esmcommon.hpp" -#include "loadtes3.hpp" - -namespace ESM { - -class ESMWriter -{ - struct RecordData - { - std::string name; - std::streampos position; - uint32_t size; - }; - - public: - - ESMWriter(); - - unsigned int getVersion() const; - void setVersion(unsigned int ver = 0x3fa66666); - void setEncoder(ToUTF8::Utf8Encoder *encoding); - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void clearMaster(); - - void addMaster(const std::string& name, uint64_t size); - - void save(std::ostream& file); - ///< Start saving a file by writing the TES3 header. - - void close(); - ///< \note Does not close the stream. - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags = 0); - void startRecord(uint32_t name, uint32_t flags = 0); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void endRecord(uint32_t name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - - private: - std::list mRecords; - std::ostream* mStream; - std::streampos mHeaderPos; - ToUTF8::Utf8Encoder* mEncoder; - int mRecordCount; - bool mCounting; - - Header mHeader; - }; -} - -#endif +#ifndef OPENMW_ESM_WRITER_H +#define OPENMW_ESM_WRITER_H + +#include +#include + +#include + +#include "esmcommon.hpp" +#include "loadtes3.hpp" + +namespace ESM { + +class ESMWriter +{ + struct RecordData + { + std::string name; + std::streampos position; + uint32_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void endRecord(uint32_t name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; + }; +} + +#endif diff --git a/readme.txt b/readme.txt index a23cd1077e..4cc9c6234b 100644 --- a/readme.txt +++ b/readme.txt @@ -1,951 +1,951 @@ -OpenMW: A reimplementation of The Elder Scrolls III: Morrowind - -OpenMW is an attempt at recreating the engine for the popular role-playing game -Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. - -Version: 0.28.0 -License: GPL (see GPL3.txt for more information) -Website: http://www.openmw.org - -Font Licenses: -EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - - - -INSTALLATION - -Windows: -Run the installer. - -Linux: -Ubuntu (and most others) -Download the .deb file and install it in the usual way. - -Arch Linux -There's an OpenMW package available in the [community] Repository: -https://www.archlinux.org/packages/?sort=&q=openmw - -OS X: -Open DMG file, copy OpenMW folder anywhere, for example in /Applications - -BUILD FROM SOURCE - -https://wiki.openmw.org/index.php?title=Development_Environment_Setup - - -THE DATA PATH - -The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to -pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly -(installing Morrowind under WINE is considered a proper install). - -COMMAND LINE OPTIONS - -Syntax: openmw -Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories - have higher priority) - --data-local arg set local data directory (highest - priority) - --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later - archives have higher priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or - omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files - --no-sound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue - scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script - functionality - --script-run arg select a file containing a list of - console commands that is executed on - startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling - scripts - 0 - ignore warning - 1 - show warning but consider script as - correctly compiled anyway - 2 - treat warnings as errors - --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --fs-strict [=arg(=1)] (=0) strict file system handling (no case - folding) - --encoding arg (=win1252) Character encoding used in OpenMW game - messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and - Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic - and other languages - - win1252 - Western European (Latin) - alphabet, used by default - --fallback arg fallback values - --no-grab Don't grab mouse cursor - --activate-dist arg (=-1) activation distance override - -CHANGELOG - -0.29.0 - -Bug #556: Video soundtrack not played when music volume is set to zero -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #848: Wrong amount of footsteps playing in 1st person -Bug #888: Ascended Sleepers have movement issues -Bug #892: Explicit references are allowed on all script functions -Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly -Bug #1009: Lake Fjalding AI related slowdown. -Bug #1041: Music playback issues on OS X >= 10.9 -Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window -Bug #1060: Some message boxes are cut off at the bottom -Bug #1062: Bittercup script does not work ('end' variable) -Bug #1074: Inventory paperdoll obscures armour rating -Bug #1077: Message after killing an essential NPC disappears too fast -Bug #1078: "Clutterbane" shows empty charge bar -Bug #1083: UndoWerewolf fails -Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered -Bug #1090: Start scripts fail when going to a non-predefined cell -Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. -Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior -Bug #1105: Magicka is depleted when using uncastable spells -Bug #1106: Creatures should be able to run -Bug #1107: TR cliffs have way too huge collision boxes in OpenMW -Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. -Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) -Bug #1115: Memory leak when spying on Fargoth -Bug #1137: Script execution fails (drenSlaveOwners script) -Bug #1143: Mehra Milo quest (vivec informants) is broken -Bug #1145: Issues with moving gold between inventory and containers -Bug #1146: Issues with picking up stacks of gold -Bug #1147: Dwemer Crossbows are held incorrectly -Bug #1158: Armor rating should always stay below inventory mannequin -Bug #1159: Quick keys can be set during character generation -Bug #1160: Crash on equip lockpick when -Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file -Feature #30: Loading/Saving (still missing a few parts) -Feature #101: AI Package: Activate -Feature #103: AI Package: Follow, FollowCell -Feature #138: Editor: Drag & Drop -Feature #428: Player death -Feature #505: Editor: Record Cloning -Feature #701: Levelled creatures -Feature #708: Improved Local Variable handling -Feature #709: Editor: Script verifier -Feature #764: Missing journal backend features -Feature #777: Creature weapons/shields -Feature #789: Editor: Referenceable record verifier -Feature #924: Load/Save GUI (still missing loading screen and progress bars) -Feature #946: Knockdown -Feature #947: Decrease fatigue when running, swimming and attacking -Feature #956: Melee Combat: Blocking -Feature #957: Area magic -Feature #960: Combat/AI combat for creatures -Feature #962: Combat-Related AI instructions -Feature #1075: Damage/Restore skill/attribute magic effects -Feature #1076: Soultrap magic effect -Feature #1081: Disease contraction -Feature #1086: Blood particles -Feature #1092: Interrupt resting -Feature #1101: Inventory equip scripts -Feature #1116: Version/Build number in Launcher window -Feature #1119: Resistance/weakness to normal weapons magic effect -Feature #1123: Slow Fall magic effect -Feature #1130: Auto-calculate spells -Feature #1164: Editor: Case-insensitive sorting in tables - -0.28.0 - -Bug #399: Inventory changes are not visible immediately -Bug #417: Apply weather instantly when teleporting -Bug #566: Global Map position marker not updated for interior cells -Bug #712: Looting corpse delay -Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod -Bug #805: Two TR meshes appear black (v0.24RC) -Bug #841: Third-person activation distance taken from camera rather than head -Bug #845: NPCs hold torches during the day -Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 -Bug #856: Maormer race by Mac Kom - The heads are way up -Bug #864: Walk locks during loading in 3rd person -Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog -Bug #882: Hircine's Ring doesn't always work -Bug #909: [Tamriel Rebuilt] crashes in Akamora -Bug #922: Launcher writing merged openmw.cfg files -Bug #943: Random magnitude should be calculated per effect -Bug #948: Negative fatigue level should be allowed -Bug #949: Particles in world space -Bug #950: Hard crash on x64 Linux running --new-game (on startup) -Bug #951: setMagicka and setFatigue have no effect -Bug #954: Problem with equipping inventory items when using a keyboard shortcut -Bug #955: Issues with equipping torches -Bug #966: Shield is visible when casting spell -Bug #967: Game crashes when equipping silver candlestick -Bug #970: Segmentation fault when starting at Bal Isra -Bug #977: Pressing down key in console doesn't go forward in history -Bug #979: Tooltip disappears when changing inventory -Bug #980: Barter: item category is remembered, but not shown -Bug #981: Mod: replacing model has wrong position/orientation -Bug #982: Launcher: Addon unchecking is not saved -Bug #983: Fix controllers to affect objects attached to the base node -Bug #985: Player can talk to NPCs who are in combat -Bug #989: OpenMW crashes when trying to include mod with capital .ESP -Bug #991: Merchants equip items with harmful constant effect enchantments -Bug #994: Don't cap skills/attributes when set via console -Bug #998: Setting the max health should also set the current health -Bug #1005: Torches are visible when casting spells and during hand to hand combat. -Bug #1006: Many NPCs have 0 skill -Bug #1007: Console fills up with text -Bug #1013: Player randomly loses health or dies -Bug #1014: Persuasion window is not centered in maximized window -Bug #1015: Player status window scroll state resets on status change -Bug #1016: Notification window not big enough for all skill level ups -Bug #1020: Saved window positions are not rescaled appropriately on resolution change -Bug #1022: Messages stuck permanently on screen when they pile up -Bug #1023: Journals doesn't open -Bug #1026: Game loses track of torch usage. -Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level -Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch -Bug #1046: All damaged weaponry is worth 1 gold -Bug #1048: Links in "locked" dialogue are still clickable -Bug #1052: Using color codes when naming your character actually changes the name's color -Bug #1054: Spell effects not visible in front of water -Bug #1055: Power-Spell animation starts even though you already casted it that day -Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability -Bug #1063: Crash upon checking out game start ship area in Seyda Neen -Bug #1064: openmw binaries link to unnecessary libraries -Bug #1065: Landing from a high place in water still causes fall damage -Bug #1072: Drawing weapon increases torch brightness -Bug #1073: Merchants sell stacks of gold -Feature #43: Visuals for Magic Effects -Feature #51: Ranged Magic -Feature #52: Touch Range Magic -Feature #53: Self Range Magic -Feature #54: Spell Casting -Feature #70: Vampirism -Feature #100: Combat AI -Feature #171: Implement NIF record NiFlipController -Feature #410: Window to restore enchanted item charge -Feature #647: Enchanted item glow -Feature #723: Invisibility/Chameleon magic effects -Feature #737: Resist Magicka magic effect -Feature #758: GetLOS -Feature #926: Editor: Info-Record tables -Feature #958: Material controllers -Feature #959: Terrain bump, specular, & parallax mapping -Feature #990: Request: unlock mouse when in any menu -Feature #1018: Do not allow view mode switching while performing an action -Feature #1027: Vertex morph animation (NiGeomMorpherController) -Feature #1031: Handle NiBillboardNode -Feature #1051: Implement NIF texture slot DarkTexture -Task #873: Unify OGRE initialisation - -0.27.0 - -Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp -Bug #794: incorrect display of decimal numbers -Bug #840: First-person sneaking camera height -Bug #887: Ambient sounds playing while paused -Bug #902: Problems with Polish character encoding -Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key -Bug #910: Some CDs not working correctly with Unshield installer -Bug #917: Quick character creation plugin does not work -Bug #918: Fatigue does not refill -Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) -Feature #57: Acrobatics Skill -Feature #462: Editor: Start Dialogue -Feature #546: Modify ESX selector to handle new content file scheme -Feature #588: Editor: Adjust name/path of edited content files -Feature #644: Editor: Save -Feature #710: Editor: Configure script compiler context -Feature #790: God Mode -Feature #881: Editor: Allow only one instance of OpenCS -Feature #889: Editor: Record filtering -Feature #895: Extinguish torches -Feature #898: Breath meter enhancements -Feature #901: Editor: Default record filter -Feature #913: Merge --master and --plugin switches - -0.26.0 - -Bug #274: Inconsistencies in the terrain -Bug #557: Already-dead NPCs do not equip clothing/items. -Bug #592: Window resizing -Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) -Bug #664: Heart of lorkhan acts like a dead body (container) -Bug #767: Wonky ramp physics & water -Bug #780: Swimming out of water -Bug #792: Wrong ground alignment on actors when no clipping -Bug #796: Opening and closing door sound issue -Bug #797: No clipping hinders opening and closing of doors -Bug #799: sliders in enchanting window -Bug #838: Pressing key during startup procedure freezes the game -Bug #839: Combat/magic stances during character creation -Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment -Bug #844: Resting "until healed" option given even with full stats -Bug #846: Equipped torches are invisible. -Bug #847: Incorrect formula for autocalculated NPC initial health -Bug #850: Shealt weapon sound plays when leaving magic-ready stance -Bug #852: Some boots do not produce footstep sounds -Bug #860: FPS bar misalignment -Bug #861: Unable to print screen -Bug #863: No sneaking and jumping at the same time -Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. -Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. -Bug #868: Idle animations are repeated -Bug #874: Underwater swimming close to the ground is jerky -Bug #875: Animation problem while swimming on the surface and looking up -Bug #876: Always a starting upper case letter in the inventory -Bug #878: Active spell effects don't update the layout properly when ended -Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load -Bug #896: New game sound issue -Feature #49: Melee Combat -Feature #71: Lycanthropy -Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList -Feature #622: Multiple positions for inventory window -Feature #627: Drowning -Feature #786: Allow the 'Activate' key to close the countdialog window -Feature #798: Morrowind installation via Launcher (Linux/Max OS only) -Feature #851: First/Third person transitions with mouse wheel -Task #689: change PhysicActor::enableCollisions -Task #707: Reorganise Compiler - -0.25.0 - -Bug #411: Launcher crash on OS X < 10.8 -Bug #604: Terrible performance drop in the Census and Excise Office. -Bug #676: Start Scripts fail to load -Bug #677: OpenMW does not accept script names with - -Bug #766: Extra space in front of topic links -Bug #793: AIWander Isn't Being Passed The Repeat Parameter -Bug #795: Sound playing with drawn weapon and crossing cell-border -Bug #800: can't select weapon for enchantment -Bug #801: Player can move while over-encumbered -Bug #802: Dead Keys not working -Bug #808: mouse capture -Bug #809: ini Importer does not work without an existing cfg file -Bug #812: Launcher will run OpenMW with no ESM or ESP selected -Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected -Bug #817: Dead NPCs and Creatures still have collision boxes -Bug #820: Incorrect sorting of answers (Dialogue) -Bug #826: mwinimport dumps core when given an unknown parameter -Bug #833: getting stuck in door -Bug #835: Journals/books not showing up properly. -Feature #38: SoundGen -Feature #105: AI Package: Wander -Feature #230: 64-bit compatibility for OS X -Feature #263: Hardware mouse cursors -Feature #449: Allow mouse outside of window while paused -Feature #736: First person animations -Feature #750: Using mouse wheel in third person mode -Feature #822: Autorepeat for slider buttons - -0.24.0 - -Bug #284: Book's text misalignment -Bug #445: Camera able to get slightly below floor / terrain -Bug #582: Seam issue in Red Mountain -Bug #632: Journal Next Button shows white square -Bug #653: IndexedStore ignores index -Bug #694: Parser does not recognize float values starting with . -Bug #699: Resource handling broken with Ogre 1.9 trunk -Bug #718: components/esm/loadcell is using the mwworld subsystem -Bug #729: Levelled item list tries to add nonexistent item -Bug #730: Arrow buttons in the settings menu do not work. -Bug #732: Erroneous behavior when binding keys -Bug #733: Unclickable dialogue topic -Bug #734: Book empty line problem -Bug #738: OnDeath only works with implicit references -Bug #740: Script compiler fails on scripts with special names -Bug #742: Wait while no clipping -Bug #743: Problem with changeweather console command -Bug #744: No wait dialogue after starting a new game -Bug #748: Player is not able to unselect objects with the console -Bug #751: AddItem should only spawn a message box when called from dialogue -Bug #752: The enter button has several functions in trade and looting that is not impelemted. -Bug #753: Fargoth's Ring Quest Strange Behavior -Bug #755: Launcher writes duplicate lines into settings.cfg -Bug #759: Second quest in mages guild does not work -Bug #763: Enchantment cast cost is wrong -Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly -Bug #773: AIWander Isn't Being Passed The Correct idle Values -Bug #778: The journal can be opened at the start of a new game -Bug #779: Divayth Fyr starts as dead -Bug #787: "Batch count" on detailed FPS counter gets cut-off -Bug #788: chargen scroll layout does not match vanilla -Feature #60: Atlethics Skill -Feature #65: Security Skill -Feature #74: Interaction with non-load-doors -Feature #98: Render Weapon and Shield -Feature #102: AI Package: Escort, EscortCell -Feature #182: Advanced Journal GUI -Feature #288: Trading enhancements -Feature #405: Integrate "new game" into the menu -Feature #537: Highlight dialogue topic links -Feature #658: Rotate, RotateWorld script instructions and local rotations -Feature #690: Animation Layering -Feature #722: Night Eye/Blind magic effects -Feature #735: Move, MoveWorld script instructions. -Feature #760: Non-removable corpses - -0.23.0 - -Bug #522: Player collides with placeable items -Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open -Bug #561: Tooltip word wrapping delay -Bug #578: Bribing works incorrectly -Bug #601: PositionCell fails on negative coordinates -Bug #606: Some NPCs hairs not rendered with Better Heads addon -Bug #609: Bad rendering of bone boots -Bug #613: Messagebox causing assert to fail -Bug #631: Segfault on shutdown -Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard -Bug #635: Scale NPCs depending on race -Bug #643: Dialogue Race select function is inverted -Bug #646: Twohanded weapons don't work properly -Bug #654: Crash when dropping objects without a collision shape -Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell -Bug #660: "g" in "change" cut off in Race Menu -Bug #661: Arrille sells me the key to his upstairs room -Bug #662: Day counter starts at 2 instead of 1 -Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur -Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. -Bug #666: Looking up/down problem -Bug #667: Active effects border visible during loading -Bug #669: incorrect player position at new game start -Bug #670: race selection menu: sex, face and hair left button not totally clickable -Bug #671: new game: player is naked -Bug #674: buying or selling items doesn't change amount of gold -Bug #675: fatigue is not set to its maximum when starting a new game -Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly -Bug #680: different gold coins in Tel Mara -Bug #682: Race menu ignores playable flag for some hairs and faces -Bug #685: Script compiler does not accept ":" after a function name -Bug #688: dispose corpse makes cross-hair to disappear -Bug #691: Auto equipping ignores equipment conditions -Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder -Bug #696: Draugr incorrect head offset -Bug #697: Sail transparency issue -Bug #700: "On the rocks" mod does not load its UV coordinates correctly. -Bug #702: Some race mods don't work -Bug #711: Crash during character creation -Bug #715: Growing Tauryon -Bug #725: Auto calculate stats -Bug #728: Failure to open container and talk dialogue -Bug #731: Crash with Mush-Mere's "background" topic -Feature #55/657: Item Repairing -Feature #62/87: Enchanting -Feature #99: Pathfinding -Feature #104: AI Package: Travel -Feature #129: Levelled items -Feature #204: Texture animations -Feature #239: Fallback-Settings -Feature #535: Console object selection improvements -Feature #629: Add levelup description in levelup layout dialog -Feature #630: Optional format subrecord in (tes3) header -Feature #641: Armor rating -Feature #645: OnDeath script function -Feature #683: Companion item UI -Feature #698: Basic Particles -Task #648: Split up components/esm/loadlocks -Task #695: mwgui cleanup - -0.22.0 - -Bug #311: Potential infinite recursion in script compiler -Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. -Bug #382: Weird effect in 3rd person on water -Bug #387: Always use detailed shape for physics raycasts -Bug #420: Potion/ingredient effects do not stack -Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips -Bug #434/Bug #605: Object movement between cells not properly implemented -Bug #502: Duplicate player collision model at origin -Bug #509: Dialogue topic list shifts inappropriately -Bug #513: Sliding stairs -Bug #515: Launcher does not support non-latin strings -Bug #525: Race selection preview camera wrong position -Bug #526: Attributes / skills should not go below zero -Bug #529: Class and Birthsign menus options should be preselected -Bug #530: Lock window button graphic missing -Bug #532: Missing map menu graphics -Bug #545: ESX selector does not list ESM files properly -Bug #547: Global variables of type short are read incorrectly -Bug #550: Invisible meshes collision and tooltip -Bug #551: Performance drop when loading multiple ESM files -Bug #552: Don't list CG in options if it is not available -Bug #555: Character creation windows "OK" button broken -Bug #558: Segmentation fault when Alt-tabbing with console opened -Bug #559: Dialog window should not be available before character creation is finished -Bug #560: Tooltip borders should be stretched -Bug #562: Sound should not be played when an object cannot be picked up -Bug #565: Water animation speed + timescale -Bug #572: Better Bodies' textures don't work -Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) -Bug #574: Moving left/right should not cancel auto-run -Bug #575: Crash entering the Chamber of Song -Bug #576: Missing includes -Bug #577: Left Gloves Addon causes ESMReader exception -Bug #579: Unable to open container "Kvama Egg Sack" -Bug #581: Mimicking vanilla Morrowind water -Bug #583: Gender not recognized -Bug #586: Wrong char gen behaviour -Bug #587: "End" script statements with spaces don't work -Bug #589: Closing message boxes by pressing the activation key -Bug #590: Ugly Dagoth Ur rendering -Bug #591: Race selection issues -Bug #593: Persuasion response should be random -Bug #595: Footless guard -Bug #599: Waterfalls are invisible from a certain distance -Bug #600: Waterfalls rendered incorrectly, cut off by water -Bug #607: New beast bodies mod crashes -Bug #608: Crash in cell "Mournhold, Royal Palace" -Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt -Bug #613: Messagebox causing assert to fail -Bug #615: Meshes invisible from above water -Bug #617: Potion effects should be hidden until discovered -Bug #619: certain moss hanging from tree has rendering bug -Bug #621: Batching bloodmoon's trees -Bug #623: NiMaterialProperty alpha unhandled -Bug #628: Launcher in latest master crashes the game -Bug #633: Crash on startup: Better Heads -Bug #636: Incorrect Char Gen Menu Behavior -Feature #29: Allow ESPs and multiple ESMs -Feature #94: Finish class selection-dialogue -Feature #149: Texture Alphas -Feature #237: Run Morrowind-ini importer from launcher -Feature #286: Update Active Spell Icons -Feature #334: Swimming animation -Feature #335: Walking animation -Feature #360: Proper collision shapes for NPCs and creatures -Feature #367: Lights that behave more like original morrowind implementation -Feature #477: Special local scripting variables -Feature #528: Message boxes should close when enter is pressed under certain conditions. -Feature #543: Add bsa files to the settings imported by the ini importer -Feature #594: coordinate space and utility functions -Feature #625: Zoom in vanity mode -Task #464: Refactor launcher ESX selector into a re-usable component -Task #624: Unified implementation of type-variable sub-records - -0.21.0 - -Bug #253: Dialogs don't work for Russian version of Morrowind -Bug #267: Activating creatures without dialogue can still activate the dialogue GUI -Bug #354: True flickering lights -Bug #386: The main menu's first entry is wrong (in french) -Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations -Bug #495: Activation Range -Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned -Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available -Bug #500: Disposition for most NPCs is 0/100 -Bug #501: Getdisposition command wrongly returns base disposition -Bug #506: Journal UI doesn't update anymore -Bug #507: EnableRestMenu is not a valid command - change it to EnableRest -Bug #508: Crash in Ald Daedroth Shrine -Bug #517: Wrong price calculation when untrading an item -Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin -Bug #524: Beast races are able to wear shoes -Bug #527: Background music fails to play -Bug #533: The arch at Gnisis entrance is not displayed -Bug #534: Terrain gets its correct shape only some time after the cell is loaded -Bug #536: The same entry can be added multiple times to the journal -Bug #539: Race selection is broken -Bug #544: Terrain normal map corrupt when the map is rendered -Feature #39: Video Playback -Feature #151: ^-escape sequences in text output -Feature #392: Add AI related script functions -Feature #456: Determine required ini fallback values and adjust the ini importer accordingly -Feature #460: Experimental DirArchives improvements -Feature #540: Execute scripts of objects in containers/inventories in active cells -Task #401: Review GMST fixing -Task #453: Unify case smashing/folding -Task #512: Rewrite utf8 component - -0.20.0 - -Bug #366: Changing the player's race during character creation does not change the look of the player character -Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell -Bug #437: Stop animations when paused -Bug #438: Time displays as "0 a.m." when it should be "12 a.m." -Bug #439: Text in "name" field of potion/spell creation window is persistent -Bug #440: Starting date at a new game is off by one day -Bug #442: Console window doesn't close properly sometimes -Bug #448: Do not break container window formatting when item names are very long -Bug #458: Topics sometimes not automatically added to known topic list -Bug #476: Auto-Moving allows player movement after using DisablePlayerControls -Bug #478: After sleeping in a bed the rest dialogue window opens automtically again -Bug #492: On creating potions the ingredients are removed twice -Feature #63: Mercantile skill -Feature #82: Persuasion Dialogue -Feature #219: Missing dialogue filters/functions -Feature #369: Add a FailedAction -Feature #377: Select head/hair on character creation -Feature #391: Dummy AI package classes -Feature #435: Global Map, 2nd Layer -Feature #450: Persuasion -Feature #457: Add more script instructions -Feature #474: update the global variable pcrace when the player's race is changed -Task #158: Move dynamically generated classes from Player class to World Class -Task #159: ESMStore rework and cleanup -Task #163: More Component Namespace Cleanup -Task #402: Move player data from MWWorld::Player to the player's NPC record -Task #446: Fix no namespace in BulletShapeLoader - -0.19.0 - -Bug #374: Character shakes in 3rd person mode near the origin -Bug #404: Gamma correct rendering -Bug #407: Shoes of St. Rilm do not work -Bug #408: Rugs has collision even if they are not supposed to -Bug #412: Birthsign menu sorted incorrectly -Bug #413: Resolutions presented multiple times in launcher -Bug #414: launcher.cfg file stored in wrong directory -Bug #415: Wrong esm order in openmw.cfg -Bug #418: Sound listener position updates incorrectly -Bug #423: wrong usage of "Version" entry in openmw.desktop -Bug #426: Do not use hardcoded splash images -Bug #431: Don't use markers for raycast -Bug #432: Crash after picking up items from an NPC -Feature #21/#95: Sleeping/resting -Feature #61: Alchemy Skill -Feature #68: Death -Feature #69/#86: Spell Creation -Feature #72/#84: Travel -Feature #76: Global Map, 1st Layer -Feature #120: Trainer Window -Feature #152: Skill Increase from Skill Books -Feature #160: Record Saving -Task #400: Review GMST access - -0.18.0 - -Bug #310: Button of the "preferences menu" are too small -Bug #361: Hand-to-hand skill is always 100 -Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to -Bug #372: playSound3D uses original coordinates instead of current coordinates. -Bug #373: Static OGRE build faulty -Bug #375: Alt-tab toggle view -Bug #376: Screenshots are disable -Bug #378: Exception when drinking self-made potions -Bug #380: Cloth visibility problem -Bug #384: Weird character on doors tooltip. -Bug #398: Some objects do not collide in MW, but do so in OpenMW -Feature #22: Implement level-up -Feature #36: Hide Marker -Feature #88: Hotkey Window -Feature #91: Level-Up Dialogue -Feature #118: Keyboard and Mouse-Button bindings -Feature #119: Spell Buying Window -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local -Feature #292: Object Movement/Creation Script Instructions -Feature #340: AIPackage data structures -Feature #356: Ingredients use -Feature #358: Input system rewrite -Feature #370: Target handling in actions -Feature #379: Door markers on the local map -Feature #389: AI framework -Feature #395: Using keys to open doors / containers -Feature #396: Loading screens -Feature #397: Inventory avatar image and race selection head preview -Task #339: Move sounds into Action - -0.17.0 - -Bug #225: Valgrind reports about 40MB of leaked memory -Bug #241: Some physics meshes still don't match -Bug #248: Some textures are too dark -Bug #300: Dependency on proprietary CG toolkit -Bug #302: Some objects don't collide although they should -Bug #308: Freeze in Balmora, Meldor: Armorer -Bug #313: openmw without a ~/.config/openmw folder segfault. -Bug #317: adding non-existing spell via console locks game -Bug #318: Wrong character normals -Bug #341: Building with Ogre Debug libraries does not use debug version of plugins -Bug #347: Crash when running openmw with --start="XYZ" -Bug #353: FindMyGUI.cmake breaks path on Windows -Bug #359: WindowManager throws exception at destruction -Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation -Feature #33: Allow objects to cross cell-borders -Feature #59: Dropping Items (replaced stopgap implementation with a proper one) -Feature #93: Main Menu -Feature #96/329/330/331/332/333: Player Control -Feature #180: Object rotation and scaling. -Feature #272: Incorrect NIF material sharing -Feature #314: Potion usage -Feature #324: Skill Gain -Feature #342: Drain/fortify dynamic stats/attributes magic effects -Feature #350: Allow console only script instructions -Feature #352: Run scripts in console on startup -Task #107: Refactor mw*-subsystems -Task #325: Make CreatureStats into a class -Task #345: Use Ogre's animation system -Task #351: Rewrite Action class to support automatic sound playing - -0.16.0 - -Bug #250: OpenMW launcher erratic behaviour -Bug #270: Crash because of underwater effect on OS X -Bug #277: Auto-equipping in some cells not working -Bug #294: Container GUI ignores disabled inventory menu -Bug #297: Stats review dialog shows all skills and attribute values as 0 -Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses -Bug #299: Crash in World::disable -Bug #306: Non-existent ~/.config/openmw "crash" the launcher. -Bug #307: False "Data Files" location make the launcher "crash" -Feature #81: Spell Window -Feature #85: Alchemy Window -Feature #181: Support for x.y script syntax -Feature #242: Weapon and Spell icons -Feature #254: Ingame settings window -Feature #293: Allow "stacking" game modes -Feature #295: Class creation dialog tooltips -Feature #296: Clicking on the HUD elements should show/hide the respective window -Feature #301: Direction after using a Teleport Door -Feature #303: Allow object selection in the console -Feature #305: Allow the use of = as a synonym for == -Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts -Task #176: Restructure enabling/disabling of MW-references -Task #283: Integrate ogre.cfg file in settings file -Task #290: Auto-Close MW-reference related GUI windows - -0.15.0 - -Bug #5: Physics reimplementation (fixes various issues) -Bug #258: Resizing arrow's background is not transparent -Bug #268: Widening the stats window in X direction causes layout problems -Bug #269: Topic pane in dialgoue window is too small for some longer topics -Bug #271: Dialog choices are sorted incorrectly -Bug #281: The single quote character is not rendered on dialog windows -Bug #285: Terrain not handled properly in cells that are not predefined -Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs -Feature #15: Collision with Terrain -Feature #17: Inventory-, Container- and Trade-Windows -Feature #44: Floating Labels above Focussed Objects -Feature #80: Tooltips -Feature #83: Barter Dialogue -Feature #90: Book and Scroll Windows -Feature #156: Item Stacking in Containers -Feature #213: Pulsating lights -Feature #218: Feather & Burden -Feature #256: Implement magic effect bookkeeping -Feature #259: Add missing information to Stats window -Feature #260: Correct case for dialogue topics -Feature #280: GUI texture atlasing -Feature #291: Ability to use GMST strings from GUI layout files -Task #255: Make MWWorld::Environment into a singleton - -0.14.0 - -Bug #1: Meshes rendered with wrong orientation -Bug #6/Task #220: Picking up small objects doesn't always work -Bug #127: tcg doesn't work -Bug #178: Compablity problems with Ogre 1.8.0 RC 1 -Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI -Bug #227: Terrain crashes when moving away from predefined cells -Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces -Bug #235: TGA texture loading problem -Bug #246: wireframe mode does not work in water -Feature #8/#232: Water Rendering -Feature #13: Terrain Rendering -Feature #37: Render Path Grid -Feature #66: Factions -Feature #77: Local Map -Feature #78: Compass/Mini-Map -Feature #97: Render Clothing/Armour -Feature #121: Window Pinning -Feature #205: Auto equip -Feature #217: Contiainer should track changes to its content -Feature #221: NPC Dialogue Window Enhancements -Feature #233: Game settings manager -Feature #240: Spell List and selected spell (no GUI yet) -Feature #243: Draw State -Task #113: Morrowind.ini Importer -Task #215: Refactor the sound code -Task #216: Update MyGUI - -0.13.0 - -Bug #145: Fixed sound problems after cell change -Bug #179: Pressing space in console triggers activation -Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux -Bug #189: ASCII 16 character added to console on it's activation on Mac OS X -Bug #190: Case Folding fails with music files -Bug #192: Keypresses write Text into Console no matter which gui element is active -Bug #196: Collision shapes out of place -Bug #202: ESMTool doesn't not work with localised ESM files anymore -Bug #203: Torch lights only visible on short distance -Bug #207: Ogre.log not written -Bug #209: Sounds do not play -Bug #210: Ogre crash at Dren plantation -Bug #214: Unsupported file format version -Bug #222: Launcher is writing openmw.cfg file to wrong location -Feature #9: NPC Dialogue Window -Feature #16/42: New sky/weather implementation -Feature #40: Fading -Feature #48: NPC Dialogue System -Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) -Feature #161: Load REC_PGRD records -Feature #195: Wireframe-mode -Feature #198/199: Various sound effects -Feature #206: Allow picking data path from launcher if non is set -Task #108: Refactor window manager class -Task #172: Sound Manager Cleanup -Task #173: Create OpenEngine systems in the appropriate manager classes -Task #184: Adjust MSVC and gcc warning levels -Task #185: RefData rewrite -Task #201: Workaround for transparency issues -Task #208: silenced esm_reader.hpp warning - -0.12.0 - -Bug #154: FPS Drop -Bug #169: Local scripts continue running if associated object is deleted -Bug #174: OpenMW fails to start if the config directory doesn't exist -Bug #187: Missing lighting -Bug #188: Lights without a mesh are not rendered -Bug #191: Taking screenshot causes crash when running installed -Feature #28: Sort out the cell load problem -Feature #31: Allow the player to move away from pre-defined cells -Feature #35: Use alternate storage location for modified object position -Feature #45: NPC animations -Feature #46: Creature Animation -Feature #89: Basic Journal Window -Feature #110: Automatically pick up the path of existing MW-installations -Feature #183: More FPS display settings -Task #19: Refactor engine class -Task #109/Feature #162: Automate Packaging -Task #112: Catch exceptions thrown in input handling functions -Task #128/#168: Cleanup Configuration File Handling -Task #131: NPC Activation doesn't work properly -Task #144: MWRender cleanup -Task #155: cmake cleanup - -0.11.1 - -Bug #2: Resources loading doesn't work outside of bsa files -Bug #3: GUI does not render non-English characters -Bug #7: openmw.cfg location doesn't match -Bug #124: The TCL alias for ToggleCollision is missing. -Bug #125: Some command line options can't be used from a .cfg file -Bug #126: Toggle-type script instructions are less verbose compared with original MW -Bug #130: NPC-Record Loading fails for some NPCs -Bug #167: Launcher sets invalid parameters in ogre config -Feature #10: Journal -Feature #12: Rendering Optimisations -Feature #23: Change Launcher GUI to a tabbed interface -Feature #24: Integrate the OGRE settings window into the launcher -Feature #25: Determine openmw.cfg location (Launcher) -Feature #26: Launcher Profiles -Feature #79: MessageBox -Feature #116: Tab-Completion in Console -Feature #132: --data-local and multiple --data -Feature #143: Non-Rendering Performance-Optimisations -Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs -Feature #157: Version Handling -Task #14: Replace tabs with 4 spaces -Task #18: Move components from global namespace into their own namespace -Task #123: refactor header files in components/esm - -0.10.0 - -* NPC dialogue window (not functional yet) -* Collisions with objects -* Refactor the PlayerPos class -* Adjust file locations -* CMake files and test linking for Bullet -* Replace Ogre raycasting test for activation with something more precise -* Adjust player movement according to collision results -* FPS display -* Various Portability Improvements -* Mac OS X support is back! - -0.9.0 - -* Exterior cells loading, unloading and management -* Character Creation GUI -* Character creation -* Make cell names case insensitive when doing internal lookups -* Music player -* NPCs rendering - -0.8.0 - -* GUI -* Complete and working script engine -* In game console -* Sky rendering -* Sound and music -* Tons of smaller stuff - -0.7.0 - -* This release is a complete rewrite in C++. -* All D code has been culled, and all modules have been rewritten. -* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. - -0.6.0 - -* Coded a GUI system using MyGUI -* Skinned MyGUI to look like Morrowind (work in progress) -* Integrated the Monster script engine -* Rewrote some functions into script code -* Very early MyGUI < > Monster binding -* Fixed Windows sound problems (replaced old openal32.dll) - -0.5.0 - -* Collision detection with Bullet -* Experimental walk & fall character physics -* New key bindings: - * t toggle physics mode (walking, flying, ghost), - * n night eye, brightens the scene -* Fixed incompatability with DMD 1.032 and newer compilers -* * (thanks to tomqyp) -* Various minor changes and updates - -0.4.0 - -* Switched from Audiere to OpenAL -* * (BIG thanks to Chris Robinson) -* Added complete Makefile (again) as a alternative build tool -* More realistic lighting (thanks again to Chris Robinson) -* Various localization fixes tested with Russian and French versions -* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' -* Added ns option to disable sound, for debugging -* Various bug fixes -* Cosmetic changes to placate gdc Wall - -0.3.0 - -* Built and tested on Windows XP -* Partial support for FreeBSD (exceptions do not work) -* You no longer have to download Monster separately -* Made an alternative for building without DSSS (but DSSS still works) -* Renamed main program from 'morro' to 'openmw' -* Made the config system more robust -* Added oc switch for showing Ogre config window on startup -* Removed some config files, these are auto generated when missing. -* Separated plugins.cfg into linux and windows versions. -* Updated Makefile and sources for increased portability -* confirmed to work against OIS 1.0.0 (Ubuntu repository package) - -0.2.0 - -* Compiles with gdc -* Switched to DSSS for building D code -* Includes the program esmtool - -0.1.0 - -first release +OpenMW: A reimplementation of The Elder Scrolls III: Morrowind + +OpenMW is an attempt at recreating the engine for the popular role-playing game +Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +Version: 0.28.0 +License: GPL (see GPL3.txt for more information) +Website: http://www.openmw.org + +Font Licenses: +EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) + + + +INSTALLATION + +Windows: +Run the installer. + +Linux: +Ubuntu (and most others) +Download the .deb file and install it in the usual way. + +Arch Linux +There's an OpenMW package available in the [community] Repository: +https://www.archlinux.org/packages/?sort=&q=openmw + +OS X: +Open DMG file, copy OpenMW folder anywhere, for example in /Applications + +BUILD FROM SOURCE + +https://wiki.openmw.org/index.php?title=Development_Environment_Setup + + +THE DATA PATH + +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to +pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly +(installing Morrowind under WINE is considered a proper install). + +COMMAND LINE OPTIONS + +Syntax: openmw +Allowed options: + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors + --skip-menu [=arg(=1)] (=0) skip main menu on game startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override + +CHANGELOG + +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + +0.26.0 + +Bug #274: Inconsistencies in the terrain +Bug #557: Already-dead NPCs do not equip clothing/items. +Bug #592: Window resizing +Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) +Bug #664: Heart of lorkhan acts like a dead body (container) +Bug #767: Wonky ramp physics & water +Bug #780: Swimming out of water +Bug #792: Wrong ground alignment on actors when no clipping +Bug #796: Opening and closing door sound issue +Bug #797: No clipping hinders opening and closing of doors +Bug #799: sliders in enchanting window +Bug #838: Pressing key during startup procedure freezes the game +Bug #839: Combat/magic stances during character creation +Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment +Bug #844: Resting "until healed" option given even with full stats +Bug #846: Equipped torches are invisible. +Bug #847: Incorrect formula for autocalculated NPC initial health +Bug #850: Shealt weapon sound plays when leaving magic-ready stance +Bug #852: Some boots do not produce footstep sounds +Bug #860: FPS bar misalignment +Bug #861: Unable to print screen +Bug #863: No sneaking and jumping at the same time +Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. +Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. +Bug #868: Idle animations are repeated +Bug #874: Underwater swimming close to the ground is jerky +Bug #875: Animation problem while swimming on the surface and looking up +Bug #876: Always a starting upper case letter in the inventory +Bug #878: Active spell effects don't update the layout properly when ended +Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load +Bug #896: New game sound issue +Feature #49: Melee Combat +Feature #71: Lycanthropy +Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList +Feature #622: Multiple positions for inventory window +Feature #627: Drowning +Feature #786: Allow the 'Activate' key to close the countdialog window +Feature #798: Morrowind installation via Launcher (Linux/Max OS only) +Feature #851: First/Third person transitions with mouse wheel +Task #689: change PhysicActor::enableCollisions +Task #707: Reorganise Compiler + +0.25.0 + +Bug #411: Launcher crash on OS X < 10.8 +Bug #604: Terrible performance drop in the Census and Excise Office. +Bug #676: Start Scripts fail to load +Bug #677: OpenMW does not accept script names with - +Bug #766: Extra space in front of topic links +Bug #793: AIWander Isn't Being Passed The Repeat Parameter +Bug #795: Sound playing with drawn weapon and crossing cell-border +Bug #800: can't select weapon for enchantment +Bug #801: Player can move while over-encumbered +Bug #802: Dead Keys not working +Bug #808: mouse capture +Bug #809: ini Importer does not work without an existing cfg file +Bug #812: Launcher will run OpenMW with no ESM or ESP selected +Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected +Bug #817: Dead NPCs and Creatures still have collision boxes +Bug #820: Incorrect sorting of answers (Dialogue) +Bug #826: mwinimport dumps core when given an unknown parameter +Bug #833: getting stuck in door +Bug #835: Journals/books not showing up properly. +Feature #38: SoundGen +Feature #105: AI Package: Wander +Feature #230: 64-bit compatibility for OS X +Feature #263: Hardware mouse cursors +Feature #449: Allow mouse outside of window while paused +Feature #736: First person animations +Feature #750: Using mouse wheel in third person mode +Feature #822: Autorepeat for slider buttons + +0.24.0 + +Bug #284: Book's text misalignment +Bug #445: Camera able to get slightly below floor / terrain +Bug #582: Seam issue in Red Mountain +Bug #632: Journal Next Button shows white square +Bug #653: IndexedStore ignores index +Bug #694: Parser does not recognize float values starting with . +Bug #699: Resource handling broken with Ogre 1.9 trunk +Bug #718: components/esm/loadcell is using the mwworld subsystem +Bug #729: Levelled item list tries to add nonexistent item +Bug #730: Arrow buttons in the settings menu do not work. +Bug #732: Erroneous behavior when binding keys +Bug #733: Unclickable dialogue topic +Bug #734: Book empty line problem +Bug #738: OnDeath only works with implicit references +Bug #740: Script compiler fails on scripts with special names +Bug #742: Wait while no clipping +Bug #743: Problem with changeweather console command +Bug #744: No wait dialogue after starting a new game +Bug #748: Player is not able to unselect objects with the console +Bug #751: AddItem should only spawn a message box when called from dialogue +Bug #752: The enter button has several functions in trade and looting that is not impelemted. +Bug #753: Fargoth's Ring Quest Strange Behavior +Bug #755: Launcher writes duplicate lines into settings.cfg +Bug #759: Second quest in mages guild does not work +Bug #763: Enchantment cast cost is wrong +Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly +Bug #773: AIWander Isn't Being Passed The Correct idle Values +Bug #778: The journal can be opened at the start of a new game +Bug #779: Divayth Fyr starts as dead +Bug #787: "Batch count" on detailed FPS counter gets cut-off +Bug #788: chargen scroll layout does not match vanilla +Feature #60: Atlethics Skill +Feature #65: Security Skill +Feature #74: Interaction with non-load-doors +Feature #98: Render Weapon and Shield +Feature #102: AI Package: Escort, EscortCell +Feature #182: Advanced Journal GUI +Feature #288: Trading enhancements +Feature #405: Integrate "new game" into the menu +Feature #537: Highlight dialogue topic links +Feature #658: Rotate, RotateWorld script instructions and local rotations +Feature #690: Animation Layering +Feature #722: Night Eye/Blind magic effects +Feature #735: Move, MoveWorld script instructions. +Feature #760: Non-removable corpses + +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + +0.22.0 + +Bug #311: Potential infinite recursion in script compiler +Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. +Bug #382: Weird effect in 3rd person on water +Bug #387: Always use detailed shape for physics raycasts +Bug #420: Potion/ingredient effects do not stack +Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips +Bug #434/Bug #605: Object movement between cells not properly implemented +Bug #502: Duplicate player collision model at origin +Bug #509: Dialogue topic list shifts inappropriately +Bug #513: Sliding stairs +Bug #515: Launcher does not support non-latin strings +Bug #525: Race selection preview camera wrong position +Bug #526: Attributes / skills should not go below zero +Bug #529: Class and Birthsign menus options should be preselected +Bug #530: Lock window button graphic missing +Bug #532: Missing map menu graphics +Bug #545: ESX selector does not list ESM files properly +Bug #547: Global variables of type short are read incorrectly +Bug #550: Invisible meshes collision and tooltip +Bug #551: Performance drop when loading multiple ESM files +Bug #552: Don't list CG in options if it is not available +Bug #555: Character creation windows "OK" button broken +Bug #558: Segmentation fault when Alt-tabbing with console opened +Bug #559: Dialog window should not be available before character creation is finished +Bug #560: Tooltip borders should be stretched +Bug #562: Sound should not be played when an object cannot be picked up +Bug #565: Water animation speed + timescale +Bug #572: Better Bodies' textures don't work +Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) +Bug #574: Moving left/right should not cancel auto-run +Bug #575: Crash entering the Chamber of Song +Bug #576: Missing includes +Bug #577: Left Gloves Addon causes ESMReader exception +Bug #579: Unable to open container "Kvama Egg Sack" +Bug #581: Mimicking vanilla Morrowind water +Bug #583: Gender not recognized +Bug #586: Wrong char gen behaviour +Bug #587: "End" script statements with spaces don't work +Bug #589: Closing message boxes by pressing the activation key +Bug #590: Ugly Dagoth Ur rendering +Bug #591: Race selection issues +Bug #593: Persuasion response should be random +Bug #595: Footless guard +Bug #599: Waterfalls are invisible from a certain distance +Bug #600: Waterfalls rendered incorrectly, cut off by water +Bug #607: New beast bodies mod crashes +Bug #608: Crash in cell "Mournhold, Royal Palace" +Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt +Bug #613: Messagebox causing assert to fail +Bug #615: Meshes invisible from above water +Bug #617: Potion effects should be hidden until discovered +Bug #619: certain moss hanging from tree has rendering bug +Bug #621: Batching bloodmoon's trees +Bug #623: NiMaterialProperty alpha unhandled +Bug #628: Launcher in latest master crashes the game +Bug #633: Crash on startup: Better Heads +Bug #636: Incorrect Char Gen Menu Behavior +Feature #29: Allow ESPs and multiple ESMs +Feature #94: Finish class selection-dialogue +Feature #149: Texture Alphas +Feature #237: Run Morrowind-ini importer from launcher +Feature #286: Update Active Spell Icons +Feature #334: Swimming animation +Feature #335: Walking animation +Feature #360: Proper collision shapes for NPCs and creatures +Feature #367: Lights that behave more like original morrowind implementation +Feature #477: Special local scripting variables +Feature #528: Message boxes should close when enter is pressed under certain conditions. +Feature #543: Add bsa files to the settings imported by the ini importer +Feature #594: coordinate space and utility functions +Feature #625: Zoom in vanity mode +Task #464: Refactor launcher ESX selector into a re-usable component +Task #624: Unified implementation of type-variable sub-records + +0.21.0 + +Bug #253: Dialogs don't work for Russian version of Morrowind +Bug #267: Activating creatures without dialogue can still activate the dialogue GUI +Bug #354: True flickering lights +Bug #386: The main menu's first entry is wrong (in french) +Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations +Bug #495: Activation Range +Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned +Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available +Bug #500: Disposition for most NPCs is 0/100 +Bug #501: Getdisposition command wrongly returns base disposition +Bug #506: Journal UI doesn't update anymore +Bug #507: EnableRestMenu is not a valid command - change it to EnableRest +Bug #508: Crash in Ald Daedroth Shrine +Bug #517: Wrong price calculation when untrading an item +Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin +Bug #524: Beast races are able to wear shoes +Bug #527: Background music fails to play +Bug #533: The arch at Gnisis entrance is not displayed +Bug #534: Terrain gets its correct shape only some time after the cell is loaded +Bug #536: The same entry can be added multiple times to the journal +Bug #539: Race selection is broken +Bug #544: Terrain normal map corrupt when the map is rendered +Feature #39: Video Playback +Feature #151: ^-escape sequences in text output +Feature #392: Add AI related script functions +Feature #456: Determine required ini fallback values and adjust the ini importer accordingly +Feature #460: Experimental DirArchives improvements +Feature #540: Execute scripts of objects in containers/inventories in active cells +Task #401: Review GMST fixing +Task #453: Unify case smashing/folding +Task #512: Rewrite utf8 component + +0.20.0 + +Bug #366: Changing the player's race during character creation does not change the look of the player character +Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell +Bug #437: Stop animations when paused +Bug #438: Time displays as "0 a.m." when it should be "12 a.m." +Bug #439: Text in "name" field of potion/spell creation window is persistent +Bug #440: Starting date at a new game is off by one day +Bug #442: Console window doesn't close properly sometimes +Bug #448: Do not break container window formatting when item names are very long +Bug #458: Topics sometimes not automatically added to known topic list +Bug #476: Auto-Moving allows player movement after using DisablePlayerControls +Bug #478: After sleeping in a bed the rest dialogue window opens automtically again +Bug #492: On creating potions the ingredients are removed twice +Feature #63: Mercantile skill +Feature #82: Persuasion Dialogue +Feature #219: Missing dialogue filters/functions +Feature #369: Add a FailedAction +Feature #377: Select head/hair on character creation +Feature #391: Dummy AI package classes +Feature #435: Global Map, 2nd Layer +Feature #450: Persuasion +Feature #457: Add more script instructions +Feature #474: update the global variable pcrace when the player's race is changed +Task #158: Move dynamically generated classes from Player class to World Class +Task #159: ESMStore rework and cleanup +Task #163: More Component Namespace Cleanup +Task #402: Move player data from MWWorld::Player to the player's NPC record +Task #446: Fix no namespace in BulletShapeLoader + +0.19.0 + +Bug #374: Character shakes in 3rd person mode near the origin +Bug #404: Gamma correct rendering +Bug #407: Shoes of St. Rilm do not work +Bug #408: Rugs has collision even if they are not supposed to +Bug #412: Birthsign menu sorted incorrectly +Bug #413: Resolutions presented multiple times in launcher +Bug #414: launcher.cfg file stored in wrong directory +Bug #415: Wrong esm order in openmw.cfg +Bug #418: Sound listener position updates incorrectly +Bug #423: wrong usage of "Version" entry in openmw.desktop +Bug #426: Do not use hardcoded splash images +Bug #431: Don't use markers for raycast +Bug #432: Crash after picking up items from an NPC +Feature #21/#95: Sleeping/resting +Feature #61: Alchemy Skill +Feature #68: Death +Feature #69/#86: Spell Creation +Feature #72/#84: Travel +Feature #76: Global Map, 1st Layer +Feature #120: Trainer Window +Feature #152: Skill Increase from Skill Books +Feature #160: Record Saving +Task #400: Review GMST access + +0.18.0 + +Bug #310: Button of the "preferences menu" are too small +Bug #361: Hand-to-hand skill is always 100 +Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to +Bug #372: playSound3D uses original coordinates instead of current coordinates. +Bug #373: Static OGRE build faulty +Bug #375: Alt-tab toggle view +Bug #376: Screenshots are disable +Bug #378: Exception when drinking self-made potions +Bug #380: Cloth visibility problem +Bug #384: Weird character on doors tooltip. +Bug #398: Some objects do not collide in MW, but do so in OpenMW +Feature #22: Implement level-up +Feature #36: Hide Marker +Feature #88: Hotkey Window +Feature #91: Level-Up Dialogue +Feature #118: Keyboard and Mouse-Button bindings +Feature #119: Spell Buying Window +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #292: Object Movement/Creation Script Instructions +Feature #340: AIPackage data structures +Feature #356: Ingredients use +Feature #358: Input system rewrite +Feature #370: Target handling in actions +Feature #379: Door markers on the local map +Feature #389: AI framework +Feature #395: Using keys to open doors / containers +Feature #396: Loading screens +Feature #397: Inventory avatar image and race selection head preview +Task #339: Move sounds into Action + +0.17.0 + +Bug #225: Valgrind reports about 40MB of leaked memory +Bug #241: Some physics meshes still don't match +Bug #248: Some textures are too dark +Bug #300: Dependency on proprietary CG toolkit +Bug #302: Some objects don't collide although they should +Bug #308: Freeze in Balmora, Meldor: Armorer +Bug #313: openmw without a ~/.config/openmw folder segfault. +Bug #317: adding non-existing spell via console locks game +Bug #318: Wrong character normals +Bug #341: Building with Ogre Debug libraries does not use debug version of plugins +Bug #347: Crash when running openmw with --start="XYZ" +Bug #353: FindMyGUI.cmake breaks path on Windows +Bug #359: WindowManager throws exception at destruction +Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation +Feature #33: Allow objects to cross cell-borders +Feature #59: Dropping Items (replaced stopgap implementation with a proper one) +Feature #93: Main Menu +Feature #96/329/330/331/332/333: Player Control +Feature #180: Object rotation and scaling. +Feature #272: Incorrect NIF material sharing +Feature #314: Potion usage +Feature #324: Skill Gain +Feature #342: Drain/fortify dynamic stats/attributes magic effects +Feature #350: Allow console only script instructions +Feature #352: Run scripts in console on startup +Task #107: Refactor mw*-subsystems +Task #325: Make CreatureStats into a class +Task #345: Use Ogre's animation system +Task #351: Rewrite Action class to support automatic sound playing + +0.16.0 + +Bug #250: OpenMW launcher erratic behaviour +Bug #270: Crash because of underwater effect on OS X +Bug #277: Auto-equipping in some cells not working +Bug #294: Container GUI ignores disabled inventory menu +Bug #297: Stats review dialog shows all skills and attribute values as 0 +Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses +Bug #299: Crash in World::disable +Bug #306: Non-existent ~/.config/openmw "crash" the launcher. +Bug #307: False "Data Files" location make the launcher "crash" +Feature #81: Spell Window +Feature #85: Alchemy Window +Feature #181: Support for x.y script syntax +Feature #242: Weapon and Spell icons +Feature #254: Ingame settings window +Feature #293: Allow "stacking" game modes +Feature #295: Class creation dialog tooltips +Feature #296: Clicking on the HUD elements should show/hide the respective window +Feature #301: Direction after using a Teleport Door +Feature #303: Allow object selection in the console +Feature #305: Allow the use of = as a synonym for == +Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts +Task #176: Restructure enabling/disabling of MW-references +Task #283: Integrate ogre.cfg file in settings file +Task #290: Auto-Close MW-reference related GUI windows + +0.15.0 + +Bug #5: Physics reimplementation (fixes various issues) +Bug #258: Resizing arrow's background is not transparent +Bug #268: Widening the stats window in X direction causes layout problems +Bug #269: Topic pane in dialgoue window is too small for some longer topics +Bug #271: Dialog choices are sorted incorrectly +Bug #281: The single quote character is not rendered on dialog windows +Bug #285: Terrain not handled properly in cells that are not predefined +Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs +Feature #15: Collision with Terrain +Feature #17: Inventory-, Container- and Trade-Windows +Feature #44: Floating Labels above Focussed Objects +Feature #80: Tooltips +Feature #83: Barter Dialogue +Feature #90: Book and Scroll Windows +Feature #156: Item Stacking in Containers +Feature #213: Pulsating lights +Feature #218: Feather & Burden +Feature #256: Implement magic effect bookkeeping +Feature #259: Add missing information to Stats window +Feature #260: Correct case for dialogue topics +Feature #280: GUI texture atlasing +Feature #291: Ability to use GMST strings from GUI layout files +Task #255: Make MWWorld::Environment into a singleton + +0.14.0 + +Bug #1: Meshes rendered with wrong orientation +Bug #6/Task #220: Picking up small objects doesn't always work +Bug #127: tcg doesn't work +Bug #178: Compablity problems with Ogre 1.8.0 RC 1 +Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI +Bug #227: Terrain crashes when moving away from predefined cells +Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces +Bug #235: TGA texture loading problem +Bug #246: wireframe mode does not work in water +Feature #8/#232: Water Rendering +Feature #13: Terrain Rendering +Feature #37: Render Path Grid +Feature #66: Factions +Feature #77: Local Map +Feature #78: Compass/Mini-Map +Feature #97: Render Clothing/Armour +Feature #121: Window Pinning +Feature #205: Auto equip +Feature #217: Contiainer should track changes to its content +Feature #221: NPC Dialogue Window Enhancements +Feature #233: Game settings manager +Feature #240: Spell List and selected spell (no GUI yet) +Feature #243: Draw State +Task #113: Morrowind.ini Importer +Task #215: Refactor the sound code +Task #216: Update MyGUI + +0.13.0 + +Bug #145: Fixed sound problems after cell change +Bug #179: Pressing space in console triggers activation +Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux +Bug #189: ASCII 16 character added to console on it's activation on Mac OS X +Bug #190: Case Folding fails with music files +Bug #192: Keypresses write Text into Console no matter which gui element is active +Bug #196: Collision shapes out of place +Bug #202: ESMTool doesn't not work with localised ESM files anymore +Bug #203: Torch lights only visible on short distance +Bug #207: Ogre.log not written +Bug #209: Sounds do not play +Bug #210: Ogre crash at Dren plantation +Bug #214: Unsupported file format version +Bug #222: Launcher is writing openmw.cfg file to wrong location +Feature #9: NPC Dialogue Window +Feature #16/42: New sky/weather implementation +Feature #40: Fading +Feature #48: NPC Dialogue System +Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) +Feature #161: Load REC_PGRD records +Feature #195: Wireframe-mode +Feature #198/199: Various sound effects +Feature #206: Allow picking data path from launcher if non is set +Task #108: Refactor window manager class +Task #172: Sound Manager Cleanup +Task #173: Create OpenEngine systems in the appropriate manager classes +Task #184: Adjust MSVC and gcc warning levels +Task #185: RefData rewrite +Task #201: Workaround for transparency issues +Task #208: silenced esm_reader.hpp warning + +0.12.0 + +Bug #154: FPS Drop +Bug #169: Local scripts continue running if associated object is deleted +Bug #174: OpenMW fails to start if the config directory doesn't exist +Bug #187: Missing lighting +Bug #188: Lights without a mesh are not rendered +Bug #191: Taking screenshot causes crash when running installed +Feature #28: Sort out the cell load problem +Feature #31: Allow the player to move away from pre-defined cells +Feature #35: Use alternate storage location for modified object position +Feature #45: NPC animations +Feature #46: Creature Animation +Feature #89: Basic Journal Window +Feature #110: Automatically pick up the path of existing MW-installations +Feature #183: More FPS display settings +Task #19: Refactor engine class +Task #109/Feature #162: Automate Packaging +Task #112: Catch exceptions thrown in input handling functions +Task #128/#168: Cleanup Configuration File Handling +Task #131: NPC Activation doesn't work properly +Task #144: MWRender cleanup +Task #155: cmake cleanup + +0.11.1 + +Bug #2: Resources loading doesn't work outside of bsa files +Bug #3: GUI does not render non-English characters +Bug #7: openmw.cfg location doesn't match +Bug #124: The TCL alias for ToggleCollision is missing. +Bug #125: Some command line options can't be used from a .cfg file +Bug #126: Toggle-type script instructions are less verbose compared with original MW +Bug #130: NPC-Record Loading fails for some NPCs +Bug #167: Launcher sets invalid parameters in ogre config +Feature #10: Journal +Feature #12: Rendering Optimisations +Feature #23: Change Launcher GUI to a tabbed interface +Feature #24: Integrate the OGRE settings window into the launcher +Feature #25: Determine openmw.cfg location (Launcher) +Feature #26: Launcher Profiles +Feature #79: MessageBox +Feature #116: Tab-Completion in Console +Feature #132: --data-local and multiple --data +Feature #143: Non-Rendering Performance-Optimisations +Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs +Feature #157: Version Handling +Task #14: Replace tabs with 4 spaces +Task #18: Move components from global namespace into their own namespace +Task #123: refactor header files in components/esm + +0.10.0 + +* NPC dialogue window (not functional yet) +* Collisions with objects +* Refactor the PlayerPos class +* Adjust file locations +* CMake files and test linking for Bullet +* Replace Ogre raycasting test for activation with something more precise +* Adjust player movement according to collision results +* FPS display +* Various Portability Improvements +* Mac OS X support is back! + +0.9.0 + +* Exterior cells loading, unloading and management +* Character Creation GUI +* Character creation +* Make cell names case insensitive when doing internal lookups +* Music player +* NPCs rendering + +0.8.0 + +* GUI +* Complete and working script engine +* In game console +* Sky rendering +* Sound and music +* Tons of smaller stuff + +0.7.0 + +* This release is a complete rewrite in C++. +* All D code has been culled, and all modules have been rewritten. +* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. + +0.6.0 + +* Coded a GUI system using MyGUI +* Skinned MyGUI to look like Morrowind (work in progress) +* Integrated the Monster script engine +* Rewrote some functions into script code +* Very early MyGUI < > Monster binding +* Fixed Windows sound problems (replaced old openal32.dll) + +0.5.0 + +* Collision detection with Bullet +* Experimental walk & fall character physics +* New key bindings: + * t toggle physics mode (walking, flying, ghost), + * n night eye, brightens the scene +* Fixed incompatability with DMD 1.032 and newer compilers +* * (thanks to tomqyp) +* Various minor changes and updates + +0.4.0 + +* Switched from Audiere to OpenAL +* * (BIG thanks to Chris Robinson) +* Added complete Makefile (again) as a alternative build tool +* More realistic lighting (thanks again to Chris Robinson) +* Various localization fixes tested with Russian and French versions +* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' +* Added ns option to disable sound, for debugging +* Various bug fixes +* Cosmetic changes to placate gdc Wall + +0.3.0 + +* Built and tested on Windows XP +* Partial support for FreeBSD (exceptions do not work) +* You no longer have to download Monster separately +* Made an alternative for building without DSSS (but DSSS still works) +* Renamed main program from 'morro' to 'openmw' +* Made the config system more robust +* Added oc switch for showing Ogre config window on startup +* Removed some config files, these are auto generated when missing. +* Separated plugins.cfg into linux and windows versions. +* Updated Makefile and sources for increased portability +* confirmed to work against OIS 1.0.0 (Ubuntu repository package) + +0.2.0 + +* Compiles with gdc +* Switched to DSSS for building D code +* Includes the program esmtool + +0.1.0 + +first release From 8ea686c6c8be5ce8105194c135f92fc2cffc9919 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 14:26:36 +0100 Subject: [PATCH 004/240] encapsulated mState --- apps/openmw/mwworld/cells.cpp | 12 ++++++------ apps/openmw/mwworld/cellstore.cpp | 5 +++++ apps/openmw/mwworld/cellstore.hpp | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index cdfbb7b45d..c3bc135a1c 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -80,7 +80,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) c bool MWWorld::Cells::hasState (const CellStore& cellStore) const { - if (cellStore.mState==CellStore::State_Loaded) + if (cellStore.getState()==CellStore::State_Loaded) return true; if (cellStore.getCell()->mData.mFlags & ESM::Cell::Interior) @@ -122,7 +122,7 @@ MWWorld::CellStore *MWWorld::Cells::getExterior (int x, int y) std::make_pair (x, y), CellStore (cell))).first; } - if (result->second.mState!=CellStore::State_Loaded) + if (result->second.getState()!=CellStore::State_Loaded) { // Multiple plugin support for landscape data is much easier than for references. The last plugin wins. result->second.load (mStore, mReader); @@ -143,7 +143,7 @@ MWWorld::CellStore *MWWorld::Cells::getInterior (const std::string& name) result = mInteriors.insert (std::make_pair (lowerName, CellStore (cell))).first; } - if (result->second.mState!=CellStore::State_Loaded) + if (result->second.getState()!=CellStore::State_Loaded) { result->second.load (mStore, mReader); } @@ -162,10 +162,10 @@ MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id) MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, bool searchInContainers) { - if (cell.mState==CellStore::State_Unloaded) + if (cell.getState()==CellStore::State_Unloaded) cell.preload (mStore, mReader); - if (cell.mState==CellStore::State_Preloaded) + if (cell.getState()==CellStore::State_Preloaded) { if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name)) { @@ -375,7 +375,7 @@ bool MWWorld::Cells::readRecord (ESM::ESMReader& reader, int32_t type, state.load (reader); cellStore->loadState (state); - if (cellStore->mState!=CellStore::State_Loaded) + if (cellStore->getState()!=CellStore::State_Loaded) cellStore->load (mStore, mReader); cellStore->readReferences (reader, contentFileMap); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index c3c3eb6e9f..16f317f086 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -150,6 +150,11 @@ namespace MWWorld return mCell; } + CellStore::State CellStore::getState() const + { + return mState; + } + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index b90ea72a57..ef16c64c25 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -62,6 +62,7 @@ namespace MWWorld private: const ESM::Cell *mCell; + State mState; public: @@ -69,7 +70,8 @@ namespace MWWorld const ESM::Cell *getCell() const; - State mState; + State getState() const; + std::vector mIds; float mWaterLevel; From 79e723ad06eaeca8d073eb0d78e6c5bc756dc029 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 16:46:07 +0100 Subject: [PATCH 005/240] encapsulated mIds --- apps/openmw/mwworld/cells.cpp | 63 ++----------------------- apps/openmw/mwworld/cellstore.cpp | 78 +++++++++++++++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 10 +++- 3 files changed, 91 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index c3bc135a1c..8ce7d4fe80 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -167,7 +167,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, if (cell.getState()==CellStore::State_Preloaded) { - if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), name)) + if (cell.hasId (name)) { cell.load (mStore, mReader); } @@ -175,65 +175,10 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, CellStore& cell, return Ptr(); } - if (MWWorld::LiveCellRef *ref = cell.mActivators.find (name)) - return Ptr (ref, &cell); + Ptr ptr = cell.search (name); - if (MWWorld::LiveCellRef *ref = cell.mPotions.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mAppas.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mArmors.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mBooks.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mClothes.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mContainers.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mCreatures.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mDoors.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mIngreds.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mCreatureLists.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mItemLists.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mLights.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mLockpicks.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mMiscItems.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mNpcs.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mProbes.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mRepairs.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mStatics.find (name)) - return Ptr (ref, &cell); - - if (MWWorld::LiveCellRef *ref = cell.mWeapons.find (name)) - return Ptr (ref, &cell); + if (!ptr.isEmpty()) + return ptr; if (searchInContainers) return cell.searchInContainer (name); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 16f317f086..e95fd226b0 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -1,6 +1,7 @@ #include "cellstore.hpp" #include +#include #include #include @@ -155,6 +156,83 @@ namespace MWWorld return mState; } + bool CellStore::hasId (const std::string& id) const + { + if (mState==State_Unloaded) + return false; + + if (mState==State_Preloaded) + return std::binary_search (mIds.begin(), mIds.end(), id); + + /// \todo address const-issues + return const_cast (this)->search (id).isEmpty(); + } + + Ptr CellStore::search (const std::string& id) + { + if (LiveCellRef *ref = mActivators.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mPotions.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mAppas.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mArmors.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mBooks.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mClothes.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mContainers.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mCreatures.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mDoors.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mIngreds.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mCreatureLists.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mItemLists.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mLights.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mLockpicks.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mMiscItems.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mNpcs.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mProbes.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mRepairs.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mStatics.find (id)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mWeapons.find (id)) + return Ptr (ref, this); + + return Ptr(); + } + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index ef16c64c25..66f5bbf8da 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -14,6 +14,7 @@ namespace ESM namespace MWWorld { + class Ptr; /// A list of cell references template @@ -63,6 +64,7 @@ namespace MWWorld const ESM::Cell *mCell; State mState; + std::vector mIds; public: @@ -72,7 +74,13 @@ namespace MWWorld State getState() const; - std::vector mIds; + bool hasId (const std::string& id) const; + ///< May return true for deleted IDs when in preload state. Will return false, if cell is + /// unloaded. + + Ptr search (const std::string& id); + ///< Will return an empty Ptr if cell is not loaded. Does not check references in + /// containers. float mWaterLevel; From b72c1c2c58531d6bcd1179eeeb4eb278043ee987 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 17:34:18 +0100 Subject: [PATCH 006/240] encapsulated water level --- apps/openmw/mwscript/cellextensions.cpp | 10 +++++----- apps/openmw/mwworld/cellstore.cpp | 10 ++++++++++ apps/openmw/mwworld/cellstore.hpp | 5 ++++- apps/openmw/mwworld/worldimp.cpp | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 2140076c3b..4d1c15f1ea 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -132,7 +132,7 @@ namespace MWScript { MWWorld::CellStore *cell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(); if (cell->getCell()->hasWater()) - runtime.push (cell->mWaterLevel); + runtime.push (cell->getWaterLevel()); else runtime.push (-std::numeric_limits().max()); } @@ -151,8 +151,8 @@ namespace MWScript if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); - cell->mWaterLevel = level; - MWBase::Environment::get().getWorld()->setWaterHeight(cell->mWaterLevel); + cell->setWaterLevel (level); + MWBase::Environment::get().getWorld()->setWaterHeight (cell->getWaterLevel()); } }; @@ -169,8 +169,8 @@ namespace MWScript if (cell->getCell()->isExterior()) throw std::runtime_error("Can't set water level in exterior cell"); - cell->mWaterLevel +=level; - MWBase::Environment::get().getWorld()->setWaterHeight(cell->mWaterLevel); + cell->setWaterLevel (cell->getWaterLevel()+level); + MWBase::Environment::get().getWorld()->setWaterHeight(cell->getWaterLevel()); } }; diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index e95fd226b0..ceb27274e3 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -233,6 +233,16 @@ namespace MWWorld return Ptr(); } + float CellStore::getWaterLevel() const + { + return mWaterLevel; + } + + void CellStore::setWaterLevel (float level) + { + mWaterLevel = level; + } + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 66f5bbf8da..428890764b 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -65,6 +65,7 @@ namespace MWWorld const ESM::Cell *mCell; State mState; std::vector mIds; + float mWaterLevel; public: @@ -82,7 +83,9 @@ namespace MWWorld ///< Will return an empty Ptr if cell is not loaded. Does not check references in /// containers. - float mWaterLevel; + float getWaterLevel() const; + + void setWaterLevel (float level); // Lists for each individual object type CellRefList mActivators; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4c97c594d5..e258d8503d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1760,7 +1760,7 @@ namespace MWWorld if (!(cell->getCell()->mData.mFlags & ESM::Cell::HasWater)) { return false; } - return pos.z < cell->mWaterLevel; + return pos.z < cell->getWaterLevel(); } bool World::isOnGround(const MWWorld::Ptr &ptr) const From f2208671446d3a89276c0d5fa4f28c8ce2d764f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 18:17:41 +0100 Subject: [PATCH 007/240] make Ptr a bit more pointer-like --- apps/openmw/mwworld/cellstore.cpp | 24 ++++++------------------ apps/openmw/mwworld/ptr.cpp | 5 +++++ apps/openmw/mwworld/ptr.hpp | 3 +++ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index ceb27274e3..91bda79af2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -342,26 +342,14 @@ namespace MWWorld Ptr CellStore::searchInContainer (const std::string& id) { - { - Ptr ptr = searchInContainerList (mContainers, id); + if (Ptr ptr = searchInContainerList (mContainers, id)) + return ptr; - if (!ptr.isEmpty()) - return ptr; - } + if (Ptr ptr = searchInContainerList (mCreatures, id)) + return ptr; - { - Ptr ptr = searchInContainerList (mCreatures, id); - - if (!ptr.isEmpty()) - return ptr; - } - - { - Ptr ptr = searchInContainerList (mNpcs, id); - - if (!ptr.isEmpty()) - return ptr; - } + if (Ptr ptr = searchInContainerList (mNpcs, id)) + return ptr; return Ptr(); } diff --git a/apps/openmw/mwworld/ptr.cpp b/apps/openmw/mwworld/ptr.cpp index 67bfe49007..3920a3e794 100644 --- a/apps/openmw/mwworld/ptr.cpp +++ b/apps/openmw/mwworld/ptr.cpp @@ -55,3 +55,8 @@ MWWorld::ContainerStore *MWWorld::Ptr::getContainerStore() const { return mContainerStore; } + +MWWorld::Ptr::operator const void *() +{ + return mRef; +} \ No newline at end of file diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 1212619d03..4529ae2798 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -74,6 +74,9 @@ namespace MWWorld ContainerStore *getContainerStore() const; ///< May return a 0-pointer, if reference is not in a container. + + operator const void *(); + ///< Return a 0-pointer, if Ptr is empty; return a non-0-pointer, if Ptr is not empty }; inline bool operator== (const Ptr& left, const Ptr& right) From 367919200fea6be12f3db2e47b83316ac6bcfa27 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 20:11:05 +0100 Subject: [PATCH 008/240] moved CellRefList into a separate file --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 6 +++ apps/openmw/mwclass/creature.cpp | 1 + apps/openmw/mwclass/creature.hpp | 5 ++ apps/openmw/mwclass/door.hpp | 2 + apps/openmw/mwclass/npc.cpp | 1 + apps/openmw/mwclass/static.cpp | 3 +- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 5 ++ apps/openmw/mwdialogue/filter.cpp | 3 +- apps/openmw/mwgui/bookwindow.cpp | 6 ++- apps/openmw/mwgui/charactercreation.hpp | 3 ++ apps/openmw/mwgui/console.cpp | 2 + apps/openmw/mwgui/levelupdialog.cpp | 2 + apps/openmw/mwgui/mapwindow.cpp | 2 + apps/openmw/mwgui/merchantrepair.cpp | 3 ++ apps/openmw/mwgui/scrollwindow.cpp | 2 + apps/openmw/mwgui/sortfilteritemmodel.cpp | 13 +++++ apps/openmw/mwgui/spellbuyingwindow.cpp | 2 + apps/openmw/mwgui/tradeitemmodel.cpp | 2 + apps/openmw/mwgui/travelwindow.cpp | 2 + apps/openmw/mwgui/waitdialog.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 13 +++-- apps/openmw/mwmechanics/activespells.cpp | 10 +++- apps/openmw/mwmechanics/aiactivate.cpp | 6 +-- apps/openmw/mwmechanics/aicombat.cpp | 2 + apps/openmw/mwmechanics/aiescort.cpp | 9 ++-- apps/openmw/mwmechanics/aifollow.cpp | 12 +++-- apps/openmw/mwmechanics/aitravel.cpp | 2 + apps/openmw/mwmechanics/aiwander.cpp | 10 ++-- apps/openmw/mwmechanics/alchemy.hpp | 53 ++++++++++--------- apps/openmw/mwmechanics/character.cpp | 11 ++-- apps/openmw/mwmechanics/combat.cpp | 1 + apps/openmw/mwmechanics/disease.hpp | 1 + apps/openmw/mwmechanics/pathfinding.cpp | 8 +-- apps/openmw/mwmechanics/pickpocket.cpp | 3 ++ apps/openmw/mwmechanics/repair.cpp | 1 + apps/openmw/mwmechanics/security.cpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 1 + apps/openmw/mwmechanics/spellcasting.hpp | 10 ++++ apps/openmw/mwmechanics/spells.hpp | 2 + apps/openmw/mwrender/activatoranimation.cpp | 4 +- apps/openmw/mwrender/animation.cpp | 12 ++++- apps/openmw/mwrender/animation.hpp | 4 ++ apps/openmw/mwrender/creatureanimation.cpp | 4 +- apps/openmw/mwrender/debugging.cpp | 4 +- apps/openmw/mwrender/localmap.cpp | 5 +- apps/openmw/mwrender/objects.cpp | 4 ++ apps/openmw/mwrender/renderingmanager.cpp | 1 + apps/openmw/mwscript/cellextensions.cpp | 1 + apps/openmw/mwscript/containerextensions.cpp | 4 +- apps/openmw/mwscript/miscextensions.cpp | 4 ++ apps/openmw/mwsound/soundmanagerimp.cpp | 1 + apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/actioneat.cpp | 6 ++- apps/openmw/mwworld/cellreflist.hpp | 44 +++++++++++++++ apps/openmw/mwworld/cells.cpp | 1 + apps/openmw/mwworld/cells.hpp | 1 + apps/openmw/mwworld/cellstore.hpp | 35 +----------- apps/openmw/mwworld/containerstore.hpp | 13 +++++ apps/openmw/mwworld/inventorystore.hpp | 5 ++ apps/openmw/mwworld/physicssystem.cpp | 4 +- apps/openmw/mwworld/player.cpp | 7 +-- apps/openmw/mwworld/ptr.hpp | 8 ++- apps/openmw/mwworld/scene.cpp | 2 +- apps/openmw/mwworld/scene.hpp | 5 ++ apps/openmw/mwworld/weather.cpp | 1 + 67 files changed, 295 insertions(+), 111 deletions(-) create mode 100644 apps/openmw/mwworld/cellreflist.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 84d116848e..be583ea749 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -57,7 +57,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor - contentloader esmloader omwloader actiontrap + contentloader esmloader omwloader actiontrap cellreflist ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3d033838cc..fd39789430 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -39,6 +39,12 @@ namespace ESM struct Spell; struct NPC; struct CellId; + struct Armor; + struct Weapon; + struct Clothing; + struct Enchantment; + struct Book; + struct EffectList; } namespace MWRender diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 81ca0ce2bf..516a4c5100 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -22,6 +22,7 @@ #include "../mwworld/customdata.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/actors.hpp" diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index adaf62a96e..6df6db297f 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -3,6 +3,11 @@ #include "../mwworld/class.hpp" +namespace ESM +{ + struct GameSetting; +} + namespace MWClass { class Creature : public MWWorld::Class diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 05ba0248b5..2ac342a61e 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWCLASS_DOOR_H #define GAME_MWCLASS_DOOR_H +#include + #include "../mwworld/class.hpp" namespace MWClass diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3a95f3c294..e00c3b7205 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -30,6 +30,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index bd7deca889..f8677dc20d 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -5,6 +5,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -25,7 +26,7 @@ namespace MWClass if(!model.empty()) physics.addObject(ptr); } - + std::string Static::getModel(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index cf8ea11764..b9284dc1af 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -13,6 +13,11 @@ #include "../mwscript/compilercontext.hpp" +namespace ESM +{ + struct Dialogue; +} + namespace MWDialogue { class DialogueManager : public MWBase::DialogueManager diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 846cff1141..18ae7dd1b0 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -8,8 +8,9 @@ #include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index 98d963b229..884d567c51 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -169,7 +171,7 @@ namespace MWGui } ++i; } - + //If it is the last page, hide the button "Next Page" if ( (mCurrentPage+1)*2 == mPages.size() || (mCurrentPage+1)*2 == mPages.size() + 1) @@ -194,7 +196,7 @@ namespace MWGui if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); } - + void BookWindow::nextPage() { if ((mCurrentPage+1)*2 < mPages.size()) diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index 924f40c282..03898093d7 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -1,6 +1,9 @@ #ifndef CHARACTER_CREATION_HPP #define CHARACTER_CREATION_HPP +#include +#include + #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index f3805b255a..01eb770f73 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -8,6 +8,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" + namespace MWGui { class ConsoleInterpreterContext : public MWScript::InterpreterContext diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index f56d80883a..2f11a4d125 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -8,6 +8,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 267c761e2d..51e160d26f 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -7,7 +7,9 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + #include "../mwworld/player.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/globalmap.hpp" diff --git a/apps/openmw/mwgui/merchantrepair.cpp b/apps/openmw/mwgui/merchantrepair.cpp index 3c3335d8b0..2a48e62a94 100644 --- a/apps/openmw/mwgui/merchantrepair.cpp +++ b/apps/openmw/mwgui/merchantrepair.cpp @@ -1,5 +1,7 @@ #include "merchantrepair.hpp" +#include + #include #include "../mwbase/world.hpp" @@ -10,6 +12,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/scrollwindow.cpp b/apps/openmw/mwgui/scrollwindow.cpp index e1970004ce..a084e6453a 100644 --- a/apps/openmw/mwgui/scrollwindow.cpp +++ b/apps/openmw/mwgui/scrollwindow.cpp @@ -1,5 +1,7 @@ #include "scrollwindow.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 3cf514dc5c..74a4f88e79 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -1,5 +1,18 @@ #include "sortfilteritemmodel.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "../mwworld/class.hpp" namespace diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index 68aecf28dd..77df465146 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -10,6 +10,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index 88c14b7913..c9c65a1524 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -1,5 +1,7 @@ #include "tradeitemmodel.hpp" +#include + #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 348178d81b..89d73215d0 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -13,6 +13,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" namespace MWGui { diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index fd487c87fb..9417d2a4b6 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -10,6 +10,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ba96845898..f440782569 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -18,6 +18,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/cellstore.hpp" #include "console.hpp" #include "journalwindow.hpp" diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 66d93469c7..3140537996 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -15,13 +15,16 @@ #include "../engine.hpp" -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" -#include "../mwworld/inventorystore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" + #include "../mwmechanics/creaturestats.hpp" using namespace ICS; @@ -169,7 +172,7 @@ namespace MWInput switch (action) { case A_GameMenu: - if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running + if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)) toggleMainMenu (); break; @@ -283,7 +286,7 @@ namespace MWInput // Disable movement in Gui mode if (MWBase::Environment::get().getWindowManager()->isGuiMode() - || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) + || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) return; diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 994798b0bb..0c5bd9afab 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -1,8 +1,14 @@ #include "activespells.hpp" +#include + +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + namespace MWMechanics { void ActiveSpells::update() const @@ -40,7 +46,7 @@ namespace MWMechanics void ActiveSpells::rebuildEffects() const { MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp(); - + mEffects = MagicEffects(); for (TIterator iter (begin()); iter!=end(); ++iter) @@ -59,7 +65,7 @@ namespace MWMechanics if (end>now) mEffects.add(effectIt->mKey, MWMechanics::EffectParam(effectIt->mMagnitude)); } - } + } } ActiveSpells::ActiveSpells() diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index ea146625a7..bdd7c8f3be 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -1,14 +1,14 @@ #include "aiactivate.hpp" -#include - -#include "movement.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/action.hpp" +#include "../mwworld/cellstore.hpp" #include "steering.hpp" +#include "movement.hpp" namespace { diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index b0e0c2f7b3..5be90bf2c7 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -7,6 +7,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index a58bfef051..b54f1d39ca 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -1,14 +1,15 @@ #include "aiescort.hpp" -#include "movement.hpp" - -#include "../mwworld/class.hpp" -#include "../mwworld/timestamp.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/timestamp.hpp" + #include "steering.hpp" +#include "movement.hpp" namespace { diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index ef6ead9d74..2849375466 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,12 +1,14 @@ #include "aifollow.hpp" -#include -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" -#include "../mwworld/class.hpp" -#include "movement.hpp" #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" + +#include "movement.hpp" #include "steering.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 41226ad796..1fd719c608 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -2,7 +2,9 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "steering.hpp" #include "movement.hpp" diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 012f71b729..9a95822f5e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,17 +1,19 @@ #include "aiwander.hpp" -#include "movement.hpp" +#include -#include "../mwworld/class.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "creaturestats.hpp" -#include +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" +#include "creaturestats.hpp" #include "steering.hpp" +#include "movement.hpp" namespace { diff --git a/apps/openmw/mwmechanics/alchemy.hpp b/apps/openmw/mwmechanics/alchemy.hpp index 957e3122c7..31cafa4dc7 100644 --- a/apps/openmw/mwmechanics/alchemy.hpp +++ b/apps/openmw/mwmechanics/alchemy.hpp @@ -8,6 +8,11 @@ #include "../mwworld/ptr.hpp" +namespace ESM +{ + struct Potion; +} + namespace MWMechanics { struct EffectKey; @@ -16,29 +21,29 @@ namespace MWMechanics class Alchemy { public: - + typedef std::vector TToolsContainer; typedef TToolsContainer::const_iterator TToolsIterator; - + typedef std::vector TIngredientsContainer; typedef TIngredientsContainer::const_iterator TIngredientsIterator; typedef std::vector TEffectsContainer; typedef TEffectsContainer::const_iterator TEffectsIterator; - + enum Result { Result_Success, - + Result_NoMortarAndPestle, Result_LessThanTwoIngredients, Result_NoName, Result_NoEffects, Result_RandomFailure }; - + private: - + MWWorld::Ptr mAlchemist; TToolsContainer mTools; TIngredientsContainer mIngredients; @@ -51,61 +56,61 @@ namespace MWMechanics void applyTools (int flags, float& value) const; void updateEffects(); - + const ESM::Potion *getRecord() const; ///< Return existing recrod for created potion (may return 0) - + void removeIngredients(); ///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and /// update effect list accordingly. void addPotion (const std::string& name); ///< Add a potion to the alchemist's inventory. - + void increaseSkill(); ///< Increase alchemist's skill. - + float getChance() const; ///< Return chance of success. - + int countIngredients() const; - + public: - + void setAlchemist (const MWWorld::Ptr& npc); ///< Set alchemist and configure alchemy setup accordingly. \a npc may be empty to indicate that /// there is no alchemist (alchemy session has ended). - + TToolsIterator beginTools() const; ///< \attention Iterates over tool slots, not over tools. Some of the slots may be empty. - + TToolsIterator endTools() const; - + TIngredientsIterator beginIngredients() const; ///< \attention Iterates over ingredient slots, not over ingredients. Some of the slots may be empty. - + TIngredientsIterator endIngredients() const; - + void clear(); ///< Remove alchemist, tools and ingredients. - + int addIngredient (const MWWorld::Ptr& ingredient); ///< Add ingredient into the next free slot. /// /// \return Slot index or -1, if adding failed because of no free slot or the ingredient type being /// listed already. - + void removeIngredient (int index); ///< Remove ingredient from slot (calling this function on an empty slot is a no-op). - + TEffectsIterator beginEffects() const; - + TEffectsIterator endEffects() const; - + std::string getPotionName() const; ///< Return the name of the potion that would be created when calling create (if a record for such /// a potion already exists) or return an empty string. - + Result create (const std::string& name); ///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and /// adjust the skills of the alchemist accordingly. diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c2a26ced39..d19f32507d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -36,6 +36,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" namespace { @@ -562,7 +563,7 @@ bool CharacterController::updateWeaponState() getWeaponGroup(weaptype, weapgroup); mAnimation->showWeapons(false); mAnimation->setWeaponGroup(weapgroup); - + mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "equip start", "equip stop", 0.0f, 0); @@ -751,7 +752,7 @@ bool CharacterController::updateWeaponState() MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; + mUpperBodyState = UpperCharState_StartToMinAttack; } } @@ -855,7 +856,7 @@ bool CharacterController::updateWeaponState() mUpperBodyState = UpperCharState_WeapEquiped; //don't allow to continue playing hit animation on UpperBody after actor had attacked during it - if(mHitState == CharState_Hit) + if(mHitState == CharState_Hit) { mAnimation->changeGroups(mCurrentHit, MWRender::Animation::Group_LowerBody); //commenting out following 2 lines will give a bit different combat dynamics(slower) @@ -932,7 +933,7 @@ bool CharacterController::updateWeaponState() weapSpeed, start, stop, 0.0f, 0); } } - + //if playing combat animation and lowerbody is not busy switch to whole body animation if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) { @@ -1431,7 +1432,7 @@ void CharacterController::updateVisibility() void CharacterController::determineAttackType() { float * move = mPtr.getClass().getMovementSettings(mPtr).mPosition; - + if(mPtr.getClass().hasInventoryStore(mPtr)) { if (move[0] && !move[1]) //sideway diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 2042641065..6aeb90dad3 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwbase/windowmanager.hpp" diff --git a/apps/openmw/mwmechanics/disease.hpp b/apps/openmw/mwmechanics/disease.hpp index d3ea825cf5..5f73e8acd9 100644 --- a/apps/openmw/mwmechanics/disease.hpp +++ b/apps/openmw/mwmechanics/disease.hpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/spells.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 2503181aeb..efa2bdf691 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -1,13 +1,15 @@ #include "pathfinding.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/environment.hpp" +#include #include "OgreMath.h" #include "OgreVector3.h" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" -#include +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" namespace { diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp index 53681caf8b..14abcd643e 100644 --- a/apps/openmw/mwmechanics/pickpocket.cpp +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -1,8 +1,11 @@ #include "pickpocket.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" + #include "npcstats.hpp" namespace MWMechanics diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 1b17f83056..48179d3442 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -10,6 +10,7 @@ #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index 2e5eaecfde..edec45e15f 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -2,6 +2,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 30072a8cca..9745cdebe8 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -14,6 +14,7 @@ #include "../mwworld/actionteleport.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwrender/animation.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 74dc490ea9..44402fe7bb 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -5,6 +5,16 @@ #include +#include + +namespace ESM +{ + struct Spell; + struct Ingredient; + struct Potion; + struct EffectList; +} + namespace MWMechanics { class EffectKey; diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index cc239a650f..354b1fd0bd 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -4,6 +4,8 @@ #include #include +#include + #include "../mwworld/ptr.hpp" #include "magiceffects.hpp" diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index 7f4be9a688..de0457e578 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -1,9 +1,11 @@ #include "activatoranimation.hpp" -#include "renderconst.hpp" +#include #include "../mwbase/world.hpp" +#include "renderconst.hpp" + namespace MWRender { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 820857137a..2ddea76822 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -11,18 +11,26 @@ #include #include +#include +#include +#include +#include + #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" -#include - #include "../mwmechanics/character.hpp" #include "../mwmechanics/creaturestats.hpp" + #include "../mwworld/class.hpp" #include "../mwworld/fallback.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index c0cb18010c..564bb73ef1 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -8,6 +8,10 @@ #include "../mwworld/ptr.hpp" +namespace ESM +{ + struct Light; +} namespace MWRender { diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index e2aa9a2b83..8ef5841549 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -4,12 +4,14 @@ #include #include -#include "renderconst.hpp" +#include #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" +#include "renderconst.hpp" + namespace MWRender { diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 0cfcf826f5..241f7e470c 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -13,12 +13,12 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" #include "../mwworld/ptr.hpp" +#include "../mwworld/cellstore.hpp" +#include "../mwworld/esmstore.hpp" #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 554ba22faf..772813c73d 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -7,12 +7,13 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" + #include "renderconst.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index e721477ee0..c97e5279a0 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -11,11 +11,15 @@ #include #include +#include +#include + #include #include #include "../mwworld/ptr.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "renderconst.hpp" #include "animation.hpp" diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0311c90b3a..844be52d80 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -24,6 +24,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 4d1c15f1ea..903612258e 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -13,6 +13,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/cellstore.hpp" #include "interpretercontext.hpp" diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index fc21c57122..d489bfaf15 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -284,7 +286,7 @@ namespace MWScript virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); - + const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 0c60e1c940..fa8441aa57 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -13,12 +13,16 @@ #include #include +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 13e152e689..0720e798a5 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -9,6 +9,7 @@ #include "../mwbase/statemanager.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" #include "sound_output.hpp" #include "sound_decoder.hpp" diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8c59247f4a..2ca7d76b37 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -23,6 +23,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwmechanics/npcstats.hpp" diff --git a/apps/openmw/mwworld/actioneat.cpp b/apps/openmw/mwworld/actioneat.cpp index f5d7e26361..020bdb0ff7 100644 --- a/apps/openmw/mwworld/actioneat.cpp +++ b/apps/openmw/mwworld/actioneat.cpp @@ -3,6 +3,8 @@ #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -19,10 +21,10 @@ namespace MWWorld // apply to actor std::string id = Class::get (getTarget()).getId (getTarget()); - + if (Class::get (actor).apply (actor, id, actor)) Class::get (actor).skillUsageSucceeded (actor, ESM::Skill::Alchemy, 1); - } + } ActionEat::ActionEat (const MWWorld::Ptr& object) : Action (false, object) {} } diff --git a/apps/openmw/mwworld/cellreflist.hpp b/apps/openmw/mwworld/cellreflist.hpp new file mode 100644 index 0000000000..8240fbd2db --- /dev/null +++ b/apps/openmw/mwworld/cellreflist.hpp @@ -0,0 +1,44 @@ +#ifndef GAME_MWWORLD_CELLREFLIST_H +#define GAME_MWWORLD_CELLREFLIST_H + +#include + +#include "livecellref.hpp" + +namespace MWWorld +{ + /// \brief Collection of references of one type + template + struct CellRefList + { + typedef LiveCellRef LiveRef; + typedef std::list List; + List mList; + + /// Search for the given reference in the given reclist from + /// ESMStore. Insert the reference into the list if a match is + /// found. If not, throw an exception. + /// Moved to cpp file, as we require a custom compare operator for it, + /// and the build will fail with an ugly three-way cyclic header dependence + /// so we need to pass the instantiation of the method to the linker, when + /// all methods are known. + void load (ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore); + + LiveRef *find (const std::string& name) + { + for (typename List::iterator iter (mList.begin()); iter!=mList.end(); ++iter) + if (iter->mData.getCount() > 0 && iter->mRef.mRefID == name) + return &*iter; + + return 0; + } + + LiveRef &insert (const LiveRef &item) + { + mList.push_back(item); + return mList.back(); + } + }; +} + +#endif diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 8ce7d4fe80..74df0f79d1 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -11,6 +11,7 @@ #include "class.hpp" #include "esmstore.hpp" #include "containerstore.hpp" +#include "cellstore.hpp" MWWorld::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 7ee8a3f6c2..0df55b586b 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -12,6 +12,7 @@ namespace ESM class ESMReader; class ESMWriter; struct CellId; + struct Cell; } namespace MWWorld diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 428890764b..c576a0609f 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -6,6 +6,7 @@ #include "livecellref.hpp" #include "esmstore.hpp" +#include "cellreflist.hpp" namespace ESM { @@ -16,40 +17,6 @@ namespace MWWorld { class Ptr; - /// A list of cell references - template - struct CellRefList - { - typedef LiveCellRef LiveRef; - typedef std::list List; - List mList; - - // Search for the given reference in the given reclist from - // ESMStore. Insert the reference into the list if a match is - // found. If not, throw an exception. - // Moved to cpp file, as we require a custom compare operator for it, - // and the build will fail with an ugly three-way cyclic header dependence - // so we need to pass the instantiation of the method to the lnker, when - // all methods are known. - void load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore); - - LiveRef *find (const std::string& name) - { - for (typename std::list::iterator iter (mList.begin()); iter!=mList.end(); ++iter) - { - if (iter->mData.getCount() > 0 && iter->mRef.mRefID == name) - return &*iter; - } - - return 0; - } - - LiveRef &insert(const LiveRef &item) { - mList.push_back(item); - return mList.back(); - } - }; - /// \brief Mutable state of a cell class CellStore { diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index acf4298918..dd0c1b0024 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -3,6 +3,19 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "ptr.hpp" namespace ESM diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 714ba47daa..b4b275b6ad 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -5,6 +5,11 @@ #include "../mwmechanics/magiceffects.hpp" +namespace ESM +{ + struct MagicEffect; +} + namespace MWMechanics { class NpcStats; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 56d19c8be1..1a16870daa 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -15,13 +15,15 @@ #include +#include + #include "../mwbase/world.hpp" // FIXME #include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" -#include #include "../mwworld/esmstore.hpp" +#include "../mwworld/cellstore.hpp" #include "ptr.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index ba816d6d53..6d551ecf1e 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -7,19 +7,20 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" -#include "../mwworld/ptr.hpp" -#include "../mwworld/inventorystore.hpp" - #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" #include "class.hpp" +#include "ptr.hpp" +#include "inventorystore.hpp" +#include "cellstore.hpp" namespace MWWorld { diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 4529ae2798..b830692831 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -1,12 +1,18 @@ #ifndef GAME_MWWORLD_PTR_H #define GAME_MWWORLD_PTR_H -#include "cellstore.hpp" +#include + +#include +#include + +#include "cellreflist.hpp" #include "livecellref.hpp" namespace MWWorld { class ContainerStore; + class CellStore; /// \brief Pointer to a LiveCellRef diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 06141db547..248d452c1a 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -17,8 +17,8 @@ #include "localscripts.hpp" #include "esmstore.hpp" #include "class.hpp" - #include "cellfunctors.hpp" +#include "cellstore.hpp" namespace { diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 6652748318..af833f3314 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -21,6 +21,11 @@ namespace Files class Collections; } +namespace Loading +{ + class Listener; +} + namespace Render { class OgreRenderer; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c79d7ee4fb..5f38ee2863 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -9,6 +9,7 @@ #include "player.hpp" #include "esmstore.hpp" #include "fallback.hpp" +#include "cellstore.hpp" using namespace Ogre; using namespace MWWorld; From 83ded18af0f3cec9fa23ca2e41a6d04078b4601d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 21:21:27 +0100 Subject: [PATCH 009/240] encapsulated reference collections --- apps/openmw/mwclass/activator.cpp | 2 +- apps/openmw/mwclass/apparatus.cpp | 2 +- apps/openmw/mwclass/armor.cpp | 10 +- apps/openmw/mwclass/book.cpp | 2 +- apps/openmw/mwclass/clothing.cpp | 2 +- apps/openmw/mwclass/container.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/door.cpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 10 +- apps/openmw/mwclass/light.cpp | 2 +- apps/openmw/mwclass/lockpick.cpp | 2 +- apps/openmw/mwclass/misc.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/potion.cpp | 2 +- apps/openmw/mwclass/probe.cpp | 2 +- apps/openmw/mwclass/repair.cpp | 2 +- apps/openmw/mwclass/static.cpp | 2 +- apps/openmw/mwclass/weapon.cpp | 2 +- apps/openmw/mwworld/cellreflist.hpp | 10 ++ apps/openmw/mwworld/cellstore.cpp | 90 +++++++++++++ apps/openmw/mwworld/cellstore.hpp | 184 +++++++++++++++++++++++---- apps/openmw/mwworld/localscripts.cpp | 40 +++--- apps/openmw/mwworld/scene.cpp | 110 +++++++++------- apps/openmw/mwworld/scene.hpp | 2 - apps/openmw/mwworld/worldimp.cpp | 81 +----------- 25 files changed, 374 insertions(+), 197 deletions(-) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 8bc104d254..16ab6321d5 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -128,6 +128,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mActivators.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index b3a1af288f..8580d61ce0 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -148,7 +148,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mAppas.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Apparatus::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e3974f243a..550151e43b 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -333,11 +333,11 @@ namespace MWClass if(weapon->getTypeName() == typeid(ESM::Weapon).name() && (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) { return std::make_pair(3,""); @@ -363,7 +363,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 0e6506514c..ebc3b18e6f 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -186,7 +186,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index ab98d05ae8..18a40d5d30 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -276,7 +276,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 546d6538c9..715784b7cf 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -257,7 +257,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 516a4c5100..2939a5431e 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -680,7 +680,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Creature::isFlying(const MWWorld::Ptr &ptr) const diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index e9ac39f1d1..3cd8237e70 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -258,6 +258,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mDoors.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index faf29bc83c..e15424c388 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -30,7 +30,7 @@ namespace MWClass return ref->mBase->mId; } - + void Ingredient::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); @@ -89,16 +89,16 @@ namespace MWClass return ref->mBase->mData.mValue; } - + boost::shared_ptr Ingredient::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action (new MWWorld::ActionEat (ptr)); action->setSound ("Swallow"); - return action; + return action; } - + void Ingredient::registerSelf() { boost::shared_ptr instance (new Ingredient); @@ -189,7 +189,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mIngreds.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Ingredient::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ddb2c16d66..72de620e44 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -227,7 +227,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mLights.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } void Light::ensureCustomData (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 795b660527..60ffec7b9b 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -169,7 +169,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mLockpicks.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Lockpick::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index e58716f1cf..e568bf8697 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -218,13 +218,13 @@ namespace MWClass MWWorld::ManualRef newRef(store, base); MWWorld::LiveCellRef *ref = newRef.getPtr().get(); - newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); + newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); newPtr.getCellRef().mGoldValue = goldAmount; newPtr.getRefData().setCount(1); } else { MWWorld::LiveCellRef *ref = ptr.get(); - newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); + newPtr = MWWorld::Ptr(&cell.get().insert(*ref), &cell); } return newPtr; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e00c3b7205..20c7d9a0e0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1253,7 +1253,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index d68db4e454..216f815cd6 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -185,7 +185,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mPotions.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Potion::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index 4209c1431b..d376270cb3 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -168,7 +168,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mProbes.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } bool Probe::canSell (const MWWorld::Ptr& item, int npcServices) const diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 5f2065c3cc..af79a96915 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -163,7 +163,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mRepairs.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } boost::shared_ptr Repair::use (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index f8677dc20d..4ac41350fd 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -58,6 +58,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mStatics.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index af0234cd54..e9b0c8f3cb 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -426,7 +426,7 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); + return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwworld/cellreflist.hpp b/apps/openmw/mwworld/cellreflist.hpp index 8240fbd2db..264929bfb5 100644 --- a/apps/openmw/mwworld/cellreflist.hpp +++ b/apps/openmw/mwworld/cellreflist.hpp @@ -38,6 +38,16 @@ namespace MWWorld mList.push_back(item); return mList.back(); } + + LiveCellRef *searchViaHandle (const std::string& handle) + { + for (typename List::iterator iter (mList.begin()); iter!=mList.end(); ++iter) + if (iter->mData.getCount()>0 && iter->mData.getBaseNode() && + iter->mData.getHandle()==handle) + return &*iter; + + return 0; + } }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 91bda79af2..8b0740d3b1 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -233,6 +233,71 @@ namespace MWWorld return Ptr(); } + Ptr CellStore::searchViaHandle (const std::string& handle) + { + if (LiveCellRef *ref = mActivators.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mPotions.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mAppas.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mArmors.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mBooks.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mClothes.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mContainers.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mCreatures.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mDoors.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mIngreds.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mCreatureLists.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mItemLists.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mLights.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mLockpicks.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mMiscItems.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mNpcs.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mProbes.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mRepairs.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mStatics.searchViaHandle (handle)) + return Ptr (ref, this); + + if (LiveCellRef *ref = mWeapons.searchViaHandle (handle)) + return Ptr (ref, this); + + return Ptr(); + } + float CellStore::getWaterLevel() const { return mWaterLevel; @@ -243,6 +308,31 @@ namespace MWWorld mWaterLevel = level; } + int CellStore::count() const + { + return + mActivators.mList.size() + + mPotions.mList.size() + + mAppas.mList.size() + + mArmors.mList.size() + + mBooks.mList.size() + + mClothes.mList.size() + + mContainers.mList.size() + + mDoors.mList.size() + + mIngreds.mList.size() + + mCreatureLists.mList.size() + + mItemLists.mList.size() + + mLights.mList.size() + + mLockpicks.mList.size() + + mMiscItems.mList.size() + + mProbes.mList.size() + + mRepairs.mList.size() + + mStatics.mList.size() + + mWeapons.mList.size() + + mCreatures.mList.size() + + mNpcs.mList.size(); + } + void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) { if (mState!=State_Loaded) diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index c576a0609f..5ff63c5820 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "livecellref.hpp" #include "esmstore.hpp" @@ -34,6 +35,27 @@ namespace MWWorld std::vector mIds; float mWaterLevel; + CellRefList mActivators; + CellRefList mPotions; + CellRefList mAppas; + CellRefList mArmors; + CellRefList mBooks; + CellRefList mClothes; + CellRefList mContainers; + CellRefList mCreatures; + CellRefList mDoors; + CellRefList mIngreds; + CellRefList mCreatureLists; + CellRefList mItemLists; + CellRefList mLights; + CellRefList mLockpicks; + CellRefList mMiscItems; + CellRefList mNpcs; + CellRefList mProbes; + CellRefList mRepairs; + CellRefList mStatics; + CellRefList mWeapons; + public: CellStore (const ESM::Cell *cell_); @@ -50,31 +72,16 @@ namespace MWWorld ///< Will return an empty Ptr if cell is not loaded. Does not check references in /// containers. + Ptr searchViaHandle (const std::string& handle); + ///< Will return an empty Ptr if cell is not loaded. + float getWaterLevel() const; void setWaterLevel (float level); - // Lists for each individual object type - CellRefList mActivators; - CellRefList mPotions; - CellRefList mAppas; - CellRefList mArmors; - CellRefList mBooks; - CellRefList mClothes; - CellRefList mContainers; - CellRefList mCreatures; - CellRefList mDoors; - CellRefList mIngreds; - CellRefList mCreatureLists; - CellRefList mItemLists; - CellRefList mLights; - CellRefList mLockpicks; - CellRefList mMiscItems; - CellRefList mNpcs; - CellRefList mProbes; - CellRefList mRepairs; - CellRefList mStatics; - CellRefList mWeapons; + int count() const; + ///< Return total number of references, including deleted ones. + void load (const MWWorld::ESMStore &store, std::vector &esm); @@ -83,6 +90,8 @@ namespace MWWorld /// Call functor (ref) for each reference. functor must return a bool. Returning /// false will abort the iteration. /// \return Iteration completed? + /// + /// \note Creatures and NPCs are handled last. template bool forEach (Functor& functor) { @@ -94,19 +103,19 @@ namespace MWWorld forEachImp (functor, mBooks) && forEachImp (functor, mClothes) && forEachImp (functor, mContainers) && - forEachImp (functor, mCreatures) && forEachImp (functor, mDoors) && forEachImp (functor, mIngreds) && - forEachImp (functor, mCreatureLists) && forEachImp (functor, mItemLists) && forEachImp (functor, mLights) && forEachImp (functor, mLockpicks) && forEachImp (functor, mMiscItems) && - forEachImp (functor, mNpcs) && forEachImp (functor, mProbes) && forEachImp (functor, mRepairs) && forEachImp (functor, mStatics) && - forEachImp (functor, mWeapons); + forEachImp (functor, mWeapons) && + forEachImp (functor, mCreatures) && + forEachImp (functor, mNpcs) && + forEachImp (functor, mCreatureLists); } bool operator==(const CellStore &cell) { @@ -133,6 +142,11 @@ namespace MWWorld void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + template + CellRefList& get() { + throw std::runtime_error ("Storage for this type not exist in cells"); + } + private: template @@ -159,6 +173,126 @@ namespace MWWorld /// /// Invalid \a ref objects are silently dropped. }; + + template<> + inline CellRefList& CellStore::get() + { + return mActivators; + } + + template<> + inline CellRefList& CellStore::get() + { + return mPotions; + } + + template<> + inline CellRefList& CellStore::get() + { + return mAppas; + } + + template<> + inline CellRefList& CellStore::get() + { + return mArmors; + } + + template<> + inline CellRefList& CellStore::get() + { + return mBooks; + } + + template<> + inline CellRefList& CellStore::get() + { + return mClothes; + } + + template<> + inline CellRefList& CellStore::get() + { + return mContainers; + } + + template<> + inline CellRefList& CellStore::get() + { + return mCreatures; + } + + template<> + inline CellRefList& CellStore::get() + { + return mDoors; + } + + template<> + inline CellRefList& CellStore::get() + { + return mIngreds; + } + + template<> + inline CellRefList& CellStore::get() + { + return mCreatureLists; + } + + template<> + inline CellRefList& CellStore::get() + { + return mItemLists; + } + + template<> + inline CellRefList& CellStore::get() + { + return mLights; + } + + template<> + inline CellRefList& CellStore::get() + { + return mLockpicks; + } + + template<> + inline CellRefList& CellStore::get() + { + return mMiscItems; + } + + template<> + inline CellRefList& CellStore::get() + { + return mNpcs; + } + + template<> + inline CellRefList& CellStore::get() + { + return mProbes; + } + + template<> + inline CellRefList& CellStore::get() + { + return mRepairs; + } + + template<> + inline CellRefList& CellStore::get() + { + return mStatics; + } + + template<> + inline CellRefList& CellStore::get() + { + return mWeapons; + } } #endif diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 997e9e32c1..844e2b18ba 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -101,26 +101,26 @@ void MWWorld::LocalScripts::add (const std::string& scriptName, const Ptr& ptr) void MWWorld::LocalScripts::addCell (CellStore *cell) { - listCellScripts (*this, cell->mActivators, cell); - listCellScripts (*this, cell->mPotions, cell); - listCellScripts (*this, cell->mAppas, cell); - listCellScripts (*this, cell->mArmors, cell); - listCellScripts (*this, cell->mBooks, cell); - listCellScripts (*this, cell->mClothes, cell); - listCellScripts (*this, cell->mContainers, cell); - listCellScriptsCont (*this, cell->mContainers, cell); - listCellScripts (*this, cell->mCreatures, cell); - listCellScriptsCont (*this, cell->mCreatures, cell); - listCellScripts (*this, cell->mDoors, cell); - listCellScripts (*this, cell->mIngreds, cell); - listCellScripts (*this, cell->mLights, cell); - listCellScripts (*this, cell->mLockpicks, cell); - listCellScripts (*this, cell->mMiscItems, cell); - listCellScripts (*this, cell->mNpcs, cell); - listCellScriptsCont (*this, cell->mNpcs, cell); - listCellScripts (*this, cell->mProbes, cell); - listCellScripts (*this, cell->mRepairs, cell); - listCellScripts (*this, cell->mWeapons, cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScriptsCont (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScriptsCont (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScriptsCont (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); + listCellScripts (*this, cell->get(), cell); } void MWWorld::LocalScripts::clear() diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 248d452c1a..10afbc675c 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -22,6 +22,63 @@ namespace { + struct InsertFunctor + { + MWWorld::CellStore& mCell; + bool mRescale; + Loading::Listener& mLoadingListener; + MWWorld::PhysicsSystem& mPhysics; + MWRender::RenderingManager& mRendering; + + InsertFunctor (MWWorld::CellStore& cell, bool rescale, Loading::Listener& loadingListener, + MWWorld::PhysicsSystem& physics, MWRender::RenderingManager& rendering); + + bool operator() (const MWWorld::Ptr& ptr); + }; + + InsertFunctor::InsertFunctor (MWWorld::CellStore& cell, bool rescale, + Loading::Listener& loadingListener, MWWorld::PhysicsSystem& physics, + MWRender::RenderingManager& rendering) + : mCell (cell), mRescale (rescale), mLoadingListener (loadingListener), + mPhysics (physics), mRendering (rendering) + {} + + bool InsertFunctor::InsertFunctor::operator() (const MWWorld::Ptr& ptr) + { + if (mRescale) + { + if (ptr.getCellRef().mScale<0.5) + ptr.getCellRef().mScale = 0.5; + else if (ptr.getCellRef().mScale>2) + ptr.getCellRef().mScale = 2; + } + + if (ptr.getRefData().getCount() && ptr.getRefData().isEnabled()) + { + try + { + mRendering.addObject (ptr); + ptr.getClass().insertObject (ptr, mPhysics); + + float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); + MWBase::Environment::get().getWorld()->localRotateObject (ptr, ax, ay, az); + + MWBase::Environment::get().getWorld()->scaleObject (ptr, ptr.getCellRef().mScale); + ptr.getClass().adjustPosition (ptr); + } + catch (const std::exception& e) + { + std::string error ("error during rendering: "); + std::cerr << error + e.what() << std::endl; + } + } + + mLoadingListener.increaseProgress (1); + + return true; + } template void insertCellRefList(MWRender::RenderingManager& rendering, @@ -267,7 +324,7 @@ namespace MWWorld } if (iter==mActiveCells.end()) - refsToLoad += countRefs(*MWBase::Environment::get().getWorld()->getExterior(x, y)); + refsToLoad += MWBase::Environment::get().getWorld()->getExterior(x, y)->count(); } loadingListener->setProgressRange(refsToLoad); @@ -403,7 +460,7 @@ namespace MWWorld ++current; } - int refsToLoad = countRefs(*cell); + int refsToLoad = cell->count(); loadingListener->setProgressRange(refsToLoad); // Load cell. @@ -451,55 +508,10 @@ namespace MWWorld mCellChanged = false; } - int Scene::countRefs (const CellStore& cell) - { - return cell.mActivators.mList.size() - + cell.mPotions.mList.size() - + cell.mAppas.mList.size() - + cell.mArmors.mList.size() - + cell.mBooks.mList.size() - + cell.mClothes.mList.size() - + cell.mContainers.mList.size() - + cell.mDoors.mList.size() - + cell.mIngreds.mList.size() - + cell.mCreatureLists.mList.size() - + cell.mItemLists.mList.size() - + cell.mLights.mList.size() - + cell.mLockpicks.mList.size() - + cell.mMiscItems.mList.size() - + cell.mProbes.mList.size() - + cell.mRepairs.mList.size() - + cell.mStatics.mList.size() - + cell.mWeapons.mList.size() - + cell.mCreatures.mList.size() - + cell.mNpcs.mList.size(); - } - void Scene::insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener) { - // Loop through all references in the cell - insertCellRefList(mRendering, cell.mActivators, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mPotions, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mAppas, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mArmors, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mBooks, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mClothes, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mContainers, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mDoors, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mIngreds, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mItemLists, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mLights, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mLockpicks, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mMiscItems, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mProbes, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mRepairs, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mStatics, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mWeapons, cell, *mPhysics, rescale, loadingListener); - // Load NPCs and creatures _after_ everything else (important for adjustPosition to work correctly) - insertCellRefList(mRendering, cell.mCreatures, cell, *mPhysics, rescale, loadingListener); - insertCellRefList(mRendering, cell.mNpcs, cell, *mPhysics, rescale, loadingListener); - // Since this adds additional creatures, load afterwards, or they would be loaded twice - insertCellRefList(mRendering, cell.mCreatureLists, cell, *mPhysics, rescale, loadingListener); + InsertFunctor functor (cell, rescale, *loadingListener, *mPhysics, mRendering); + cell.forEach (functor); } void Scene::addObjectToScene (const Ptr& ptr) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index af833f3314..16d4877a92 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -63,8 +63,6 @@ namespace MWWorld void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener); - int countRefs (const CellStore& cell); - public: Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e258d8503d..b51e224f84 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -48,27 +48,6 @@ using namespace Ogre; -namespace -{ - template - MWWorld::LiveCellRef *searchViaHandle (const std::string& handle, - MWWorld::CellRefList& refList) - { - typedef typename MWWorld::CellRefList::List::iterator iterator; - - for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter) - { - if (iter->mData.getCount() > 0 && iter->mData.getBaseNode()){ - if (iter->mData.getHandle()==handle) - { - return &*iter; - } - } - } - return 0; - } -} - namespace MWWorld { struct GameContentLoader : public ContentLoader @@ -103,52 +82,6 @@ namespace MWWorld LoadersContainer mLoaders; }; - Ptr World::getPtrViaHandle (const std::string& handle, CellStore& cell) - { - if (MWWorld::LiveCellRef *ref = - searchViaHandle (handle, cell.mActivators)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mPotions)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mAppas)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mArmors)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mBooks)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mClothes)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = - searchViaHandle (handle, cell.mContainers)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = - searchViaHandle (handle, cell.mCreatures)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mDoors)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = - searchViaHandle (handle, cell.mIngreds)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLights)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mLockpicks)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mMiscItems)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mNpcs)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mProbes)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mRepairs)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mStatics)) - return Ptr (ref, &cell); - if (MWWorld::LiveCellRef *ref = searchViaHandle (handle, cell.mWeapons)) - return Ptr (ref, &cell); - return Ptr(); - } - - int World::getDaysPerMonth (int month) const { switch (month) @@ -579,7 +512,7 @@ namespace MWWorld iter!=mWorldScene->getActiveCells().end(); ++iter) { CellStore* cellstore = *iter; - Ptr ptr = getPtrViaHandle (handle, *cellstore); + Ptr ptr = cellstore->searchViaHandle (handle); if (!ptr.isEmpty()) return ptr; @@ -1505,7 +1438,7 @@ namespace MWWorld Ogre::Vector2 World::getNorthVector (CellStore* cell) { - MWWorld::CellRefList& statics = cell->mStatics; + MWWorld::CellRefList& statics = cell->get(); MWWorld::LiveCellRef* ref = statics.find("northmarker"); if (!ref) return Vector2(0, 1); @@ -1517,7 +1450,7 @@ namespace MWWorld void World::getDoorMarkers (CellStore* cell, std::vector& out) { - MWWorld::CellRefList& doors = cell->mDoors; + MWWorld::CellRefList& doors = cell->get(); CellRefList::List& refList = doors.mList; for (CellRefList::List::iterator it = refList.begin(); it != refList.end(); ++it) { @@ -1900,7 +1833,7 @@ namespace MWWorld const Scene::CellStoreCollection& collection = mWorldScene->getActiveCells(); for (Scene::CellStoreCollection::const_iterator cellIt = collection.begin(); cellIt != collection.end(); ++cellIt) { - MWWorld::CellRefList& containers = (*cellIt)->mContainers; + MWWorld::CellRefList& containers = (*cellIt)->get(); CellRefList::List& refList = containers.mList; for (CellRefList::List::iterator container = refList.begin(); container != refList.end(); ++container) { @@ -1972,7 +1905,7 @@ namespace MWWorld if (0 == cellStore) { return false; } - const DoorList &doors = cellStore->mDoors.mList; + const DoorList &doors = cellStore->get().mList; for (DoorList::const_iterator it = doors.begin(); it != doors.end(); ++it) { if (!it->mRef.mTeleport) { continue; @@ -1994,7 +1927,7 @@ namespace MWWorld if (0 != source) { // Find door leading to our current teleport door // and use it destination to position inside cell. - const DoorList &doors = source->mDoors.mList; + const DoorList &doors = source->get().mList; for (DoorList::const_iterator jt = doors.begin(); jt != doors.end(); ++jt) { if (it->mRef.mTeleport && Misc::StringUtils::ciEqual(name, jt->mRef.mDestCell)) @@ -2402,7 +2335,7 @@ namespace MWWorld { if (cell->isExterior()) return false; - MWWorld::CellRefList& doors = cell->mDoors; + MWWorld::CellRefList& doors = cell->get(); CellRefList::List& refList = doors.mList; // Check if any door in the cell leads to an exterior directly From bfcd768078ea8d0d74ce8fdcafc04b2d48e13b52 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Feb 2014 21:39:18 +0100 Subject: [PATCH 010/240] various bits of cleanup --- apps/openmw/mwworld/cellstore.cpp | 15 ++ apps/openmw/mwworld/cellstore.hpp | 273 +++++++++++++++--------------- components/esm/cellid.cpp | 13 +- components/esm/cellid.hpp | 3 + 4 files changed, 162 insertions(+), 142 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 8b0740d3b1..f84b35b580 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -430,6 +430,11 @@ namespace MWWorld } } + bool CellStore::isExterior() const + { + return mCell->isExterior(); + } + Ptr CellStore::searchInContainer (const std::string& id) { if (Ptr ptr = searchInContainerList (mContainers, id)) @@ -637,4 +642,14 @@ namespace MWWorld } } } + + bool operator== (const CellStore& left, const CellStore& right) + { + return left.getCell()->getCellId()==right.getCell()->getCellId(); + } + + bool operator!= (const CellStore& left, const CellStore& right) + { + return !(left==right); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 5ff63c5820..6c18d1ce14 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -1,7 +1,6 @@ #ifndef GAME_MWWORLD_CELLSTORE_H #define GAME_MWWORLD_CELLSTORE_H -#include #include #include @@ -18,161 +17,150 @@ namespace MWWorld { class Ptr; - /// \brief Mutable state of a cell - class CellStore - { - public: - - enum State - { - State_Unloaded, State_Preloaded, State_Loaded - }; - - private: - - const ESM::Cell *mCell; - State mState; - std::vector mIds; - float mWaterLevel; - - CellRefList mActivators; - CellRefList mPotions; - CellRefList mAppas; - CellRefList mArmors; - CellRefList mBooks; - CellRefList mClothes; - CellRefList mContainers; - CellRefList mCreatures; - CellRefList mDoors; - CellRefList mIngreds; - CellRefList mCreatureLists; - CellRefList mItemLists; - CellRefList mLights; - CellRefList mLockpicks; - CellRefList mMiscItems; - CellRefList mNpcs; - CellRefList mProbes; - CellRefList mRepairs; - CellRefList mStatics; - CellRefList mWeapons; - - public: - - CellStore (const ESM::Cell *cell_); - - const ESM::Cell *getCell() const; - - State getState() const; - - bool hasId (const std::string& id) const; - ///< May return true for deleted IDs when in preload state. Will return false, if cell is - /// unloaded. - - Ptr search (const std::string& id); - ///< Will return an empty Ptr if cell is not loaded. Does not check references in - /// containers. - - Ptr searchViaHandle (const std::string& handle); - ///< Will return an empty Ptr if cell is not loaded. - - float getWaterLevel() const; - - void setWaterLevel (float level); - - int count() const; - ///< Return total number of references, including deleted ones. - - - void load (const MWWorld::ESMStore &store, std::vector &esm); - - void preload (const MWWorld::ESMStore &store, std::vector &esm); - - /// Call functor (ref) for each reference. functor must return a bool. Returning - /// false will abort the iteration. - /// \return Iteration completed? - /// - /// \note Creatures and NPCs are handled last. - template - bool forEach (Functor& functor) + /// \brief Mutable state of a cell + class CellStore { - return - forEachImp (functor, mActivators) && - forEachImp (functor, mPotions) && - forEachImp (functor, mAppas) && - forEachImp (functor, mArmors) && - forEachImp (functor, mBooks) && - forEachImp (functor, mClothes) && - forEachImp (functor, mContainers) && - forEachImp (functor, mDoors) && - forEachImp (functor, mIngreds) && - forEachImp (functor, mItemLists) && - forEachImp (functor, mLights) && - forEachImp (functor, mLockpicks) && - forEachImp (functor, mMiscItems) && - forEachImp (functor, mProbes) && - forEachImp (functor, mRepairs) && - forEachImp (functor, mStatics) && - forEachImp (functor, mWeapons) && - forEachImp (functor, mCreatures) && - forEachImp (functor, mNpcs) && - forEachImp (functor, mCreatureLists); - } + public: - bool operator==(const CellStore &cell) { - return mCell->mName == cell.mCell->mName && - mCell->mData.mX == cell.mCell->mData.mX && - mCell->mData.mY == cell.mCell->mData.mY; - } + enum State + { + State_Unloaded, State_Preloaded, State_Loaded + }; - bool operator!=(const CellStore &cell) { - return !(*this == cell); - } + private: - bool isExterior() const { - return mCell->isExterior(); - } + const ESM::Cell *mCell; + State mState; + std::vector mIds; + float mWaterLevel; - Ptr searchInContainer (const std::string& id); + CellRefList mActivators; + CellRefList mPotions; + CellRefList mAppas; + CellRefList mArmors; + CellRefList mBooks; + CellRefList mClothes; + CellRefList mContainers; + CellRefList mCreatures; + CellRefList mDoors; + CellRefList mIngreds; + CellRefList mCreatureLists; + CellRefList mItemLists; + CellRefList mLights; + CellRefList mLockpicks; + CellRefList mMiscItems; + CellRefList mNpcs; + CellRefList mProbes; + CellRefList mRepairs; + CellRefList mStatics; + CellRefList mWeapons; - void loadState (const ESM::CellState& state); + public: - void saveState (ESM::CellState& state) const; + CellStore (const ESM::Cell *cell_); - void writeReferences (ESM::ESMWriter& writer) const; + const ESM::Cell *getCell() const; - void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + State getState() const; - template - CellRefList& get() { - throw std::runtime_error ("Storage for this type not exist in cells"); - } + bool hasId (const std::string& id) const; + ///< May return true for deleted IDs when in preload state. Will return false, if cell is + /// unloaded. - private: + Ptr search (const std::string& id); + ///< Will return an empty Ptr if cell is not loaded. Does not check references in + /// containers. - template - bool forEachImp (Functor& functor, List& list) - { - for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); - ++iter) - { - if (!iter->mData.getCount()) - continue; - if (!functor (MWWorld::Ptr(&*iter, this))) - return false; - } - return true; - } + Ptr searchViaHandle (const std::string& handle); + ///< Will return an empty Ptr if cell is not loaded. - /// Run through references and store IDs - void listRefs(const MWWorld::ESMStore &store, std::vector &esm); + float getWaterLevel() const; - void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + void setWaterLevel (float level); - void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); - ///< Make case-adjustments to \a ref and insert it into the respective container. - /// - /// Invalid \a ref objects are silently dropped. - }; + int count() const; + ///< Return total number of references, including deleted ones. + + void load (const MWWorld::ESMStore &store, std::vector &esm); + ///< Load references from content file. + + void preload (const MWWorld::ESMStore &store, std::vector &esm); + ///< Build ID list from content file. + + /// Call functor (ref) for each reference. functor must return a bool. Returning + /// false will abort the iteration. + /// \return Iteration completed? + /// + /// \note Creatures and NPCs are handled last. + template + bool forEach (Functor& functor) + { + return + forEachImp (functor, mActivators) && + forEachImp (functor, mPotions) && + forEachImp (functor, mAppas) && + forEachImp (functor, mArmors) && + forEachImp (functor, mBooks) && + forEachImp (functor, mClothes) && + forEachImp (functor, mContainers) && + forEachImp (functor, mDoors) && + forEachImp (functor, mIngreds) && + forEachImp (functor, mItemLists) && + forEachImp (functor, mLights) && + forEachImp (functor, mLockpicks) && + forEachImp (functor, mMiscItems) && + forEachImp (functor, mProbes) && + forEachImp (functor, mRepairs) && + forEachImp (functor, mStatics) && + forEachImp (functor, mWeapons) && + forEachImp (functor, mCreatures) && + forEachImp (functor, mNpcs) && + forEachImp (functor, mCreatureLists); + } + + bool isExterior() const; + + Ptr searchInContainer (const std::string& id); + + void loadState (const ESM::CellState& state); + + void saveState (ESM::CellState& state) const; + + void writeReferences (ESM::ESMWriter& writer) const; + + void readReferences (ESM::ESMReader& reader, const std::map& contentFileMap); + + template + CellRefList& get() { + throw std::runtime_error ("Storage for this type not exist in cells"); + } + + private: + + template + bool forEachImp (Functor& functor, List& list) + { + for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); + ++iter) + { + if (!iter->mData.getCount()) + continue; + if (!functor (MWWorld::Ptr(&*iter, this))) + return false; + } + return true; + } + + /// Run through references and store IDs + void listRefs(const MWWorld::ESMStore &store, std::vector &esm); + + void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + + void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); + ///< Make case-adjustments to \a ref and insert it into the respective container. + /// + /// Invalid \a ref objects are silently dropped. + }; template<> inline CellRefList& CellStore::get() @@ -293,6 +281,9 @@ namespace MWWorld { return mWeapons; } + + bool operator== (const CellStore& left, const CellStore& right); + bool operator!= (const CellStore& left, const CellStore& right); } #endif diff --git a/components/esm/cellid.cpp b/components/esm/cellid.cpp index 5bc8b7aef3..3c6e23ffd9 100644 --- a/components/esm/cellid.cpp +++ b/components/esm/cellid.cpp @@ -23,4 +23,15 @@ void ESM::CellId::save (ESMWriter &esm) const if (mPaged) esm.writeHNT ("CIDX", mIndex, 8); -} \ No newline at end of file +} + +bool ESM::operator== (const CellId& left, const CellId& right) +{ + return left.mWorldspace==right.mWorldspace && left.mPaged==right.mPaged && + (!left.mPaged || (left.mIndex.mX==right.mIndex.mX && left.mIndex.mY==right.mIndex.mY)); +} + +bool ESM::operator!= (const CellId& left, const CellId& right) +{ + return !(left==right); +} diff --git a/components/esm/cellid.hpp b/components/esm/cellid.hpp index 54dbdae780..40bc552e00 100644 --- a/components/esm/cellid.hpp +++ b/components/esm/cellid.hpp @@ -23,6 +23,9 @@ namespace ESM void load (ESMReader &esm); void save (ESMWriter &esm) const; }; + + bool operator== (const CellId& left, const CellId& right); + bool operator!= (const CellId& left, const CellId& right); } #endif \ No newline at end of file From 9b18e0150708fc32875d13991723d4e8b05290c4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 24 Feb 2014 10:03:04 +0100 Subject: [PATCH 011/240] keep track of which cells have state that needs to be saved --- apps/openmw/mwworld/cells.cpp | 28 ++++++++++------------------ apps/openmw/mwworld/cells.hpp | 9 +++------ apps/openmw/mwworld/cellstore.cpp | 30 +++++++++++++++++++++++++++++- apps/openmw/mwworld/cellstore.hpp | 26 ++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 74df0f79d1..acffe20f36 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -66,8 +66,11 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, CellStore& return ptr; } -void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) const +void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const { + if (cell.getState()!=CellStore::State_Loaded) + cell.load (mStore, mReader); + ESM::CellState cellState; cell.saveState (cellState); @@ -79,17 +82,6 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, const CellStore& cell) c writer.endRecord (ESM::REC_CSTA); } -bool MWWorld::Cells::hasState (const CellStore& cellStore) const -{ - if (cellStore.getState()==CellStore::State_Loaded) - return true; - - if (cellStore.getCell()->mData.mFlags & ESM::Cell::Interior) - return cellStore.getCell()->mData.mFlags & ESM::Cell::HasWater; - else - return false; -} - MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector& reader) : mStore (store), mReader (reader), mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable @@ -274,12 +266,12 @@ int MWWorld::Cells::countSavedGameRecords() const for (std::map::const_iterator iter (mInteriors.begin()); iter!=mInteriors.end(); ++iter) - if (hasState (iter->second)) + if (iter->second.hasState()) ++count; for (std::map, CellStore>::const_iterator iter (mExteriors.begin()); iter!=mExteriors.end(); ++iter) - if (hasState (iter->second)) + if (iter->second.hasState()) ++count; return count; @@ -287,14 +279,14 @@ int MWWorld::Cells::countSavedGameRecords() const void MWWorld::Cells::write (ESM::ESMWriter& writer) const { - for (std::map, CellStore>::const_iterator iter (mExteriors.begin()); + for (std::map, CellStore>::iterator iter (mExteriors.begin()); iter!=mExteriors.end(); ++iter) - if (hasState (iter->second)) + if (iter->second.hasState()) writeCell (writer, iter->second); - for (std::map::const_iterator iter (mInteriors.begin()); + for (std::map::iterator iter (mInteriors.begin()); iter!=mInteriors.end(); ++iter) - if (hasState (iter->second)) + if (iter->second.hasState()) writeCell (writer, iter->second); } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0df55b586b..5209aa51aa 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -24,8 +24,8 @@ namespace MWWorld { const MWWorld::ESMStore& mStore; std::vector& mReader; - std::map mInteriors; - std::map, CellStore> mExteriors; + mutable std::map mInteriors; + mutable std::map, CellStore> mExteriors; std::vector > mIdCache; std::size_t mIdCacheIndex; @@ -36,10 +36,7 @@ namespace MWWorld Ptr getPtrAndCache (const std::string& name, CellStore& cellStore); - void writeCell (ESM::ESMWriter& writer, const CellStore& cell) const; - - bool hasState (const CellStore& cellStore) const; - ///< Check if cell has state that needs to be included in a saved game file. + void writeCell (ESM::ESMWriter& writer, CellStore& cell) const; public: diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index f84b35b580..3f1ef8ab24 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -141,7 +141,7 @@ namespace MWWorld } CellStore::CellStore (const ESM::Cell *cell) - : mCell (cell), mState (State_Unloaded) + : mCell (cell), mState (State_Unloaded), mHasState (false) { mWaterLevel = cell->mWater; } @@ -156,6 +156,11 @@ namespace MWWorld return mState; } + bool CellStore::hasState() const + { + return mHasState; + } + bool CellStore::hasId (const std::string& id) const { if (mState==State_Unloaded) @@ -170,6 +175,10 @@ namespace MWWorld Ptr CellStore::search (const std::string& id) { + bool oldState = mHasState; + + mHasState = true; + if (LiveCellRef *ref = mActivators.find (id)) return Ptr (ref, this); @@ -230,11 +239,17 @@ namespace MWWorld if (LiveCellRef *ref = mWeapons.find (id)) return Ptr (ref, this); + mHasState = oldState; + return Ptr(); } Ptr CellStore::searchViaHandle (const std::string& handle) { + bool oldState = mHasState; + + mHasState = true; + if (LiveCellRef *ref = mActivators.searchViaHandle (handle)) return Ptr (ref, this); @@ -295,6 +310,8 @@ namespace MWWorld if (LiveCellRef *ref = mWeapons.searchViaHandle (handle)) return Ptr (ref, this); + mHasState = oldState; + return Ptr(); } @@ -306,6 +323,7 @@ namespace MWWorld void CellStore::setWaterLevel (float level) { mWaterLevel = level; + mHasState = true; } int CellStore::count() const @@ -437,6 +455,10 @@ namespace MWWorld Ptr CellStore::searchInContainer (const std::string& id) { + bool oldState = mHasState; + + mHasState = true; + if (Ptr ptr = searchInContainerList (mContainers, id)) return ptr; @@ -446,6 +468,8 @@ namespace MWWorld if (Ptr ptr = searchInContainerList (mNpcs, id)) return ptr; + mHasState = oldState; + return Ptr(); } @@ -486,6 +510,8 @@ namespace MWWorld void CellStore::loadState (const ESM::CellState& state) { + mHasState = true; + if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) mWaterLevel = state.mWaterLevel; @@ -529,6 +555,8 @@ namespace MWWorld void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap) { + mHasState = true; + while (reader.isNextSub ("OBJE")) { unsigned int id = 0; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 6c18d1ce14..4b7c0011b5 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -31,6 +31,7 @@ namespace MWWorld const ESM::Cell *mCell; State mState; + bool mHasState; std::vector mIds; float mWaterLevel; @@ -63,6 +64,9 @@ namespace MWWorld State getState() const; + bool hasState() const; + ///< Does this cell have state that needs to be stored in a saved game file? + bool hasId (const std::string& id) const; ///< May return true for deleted IDs when in preload state. Will return false, if cell is /// unloaded. @@ -95,6 +99,8 @@ namespace MWWorld template bool forEach (Functor& functor) { + mHasState = true; + return forEachImp (functor, mActivators) && forEachImp (functor, mPotions) && @@ -165,120 +171,140 @@ namespace MWWorld template<> inline CellRefList& CellStore::get() { + mHasState = true; return mActivators; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mPotions; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mAppas; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mArmors; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mBooks; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mClothes; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mContainers; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mCreatures; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mDoors; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mIngreds; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mCreatureLists; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mItemLists; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mLights; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mLockpicks; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mMiscItems; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mNpcs; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mProbes; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mRepairs; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mStatics; } template<> inline CellRefList& CellStore::get() { + mHasState = true; return mWeapons; } From 76c1d60d2395ecd2ef8d670e2f71f54f3d57eef0 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 22 Feb 2014 01:17:58 +0400 Subject: [PATCH 012/240] Fixed OpenCS build on OS X --- apps/opencs/view/filter/editwidget.hpp | 2 +- apps/opencs/view/filter/filterbox.hpp | 2 +- apps/opencs/view/filter/recordfilterbox.hpp | 2 +- apps/opencs/view/world/tablesubview.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index 555b6d3602..e7e34b8e9a 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 3817d5e709..5954035fcb 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 3638dc6c34..fa5c9c3c28 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1f67e0262a..b3c253919b 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -#include +#include class QModelIndex; From 85c467f00f01e14b8a3cecffe1d722eaed7df3bf Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 24 Feb 2014 18:47:09 +1100 Subject: [PATCH 013/240] Minor cleanup for aicombat pathfinding workaround. --- apps/openmw/mwmechanics/aicombat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 91c77702e2..17a624f0ff 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,12 +240,12 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); //not guaranteed, check before use + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //if buildNewPath() failed leave mTargetAngle unchanged + //if no new path leave mTargetAngle unchanged if(!mPathFinder.getPath().empty()) { //try shortcut @@ -253,8 +253,8 @@ namespace MWMechanics mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); else mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; } - mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; From 5b48ca114fee7742ad8bc8f18f0baaa9ced7e945 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 25 Feb 2014 07:41:10 +1100 Subject: [PATCH 014/240] aicombat pathfinding fix - check the correct list --- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5996eb0793..8dbdd87707 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,7 +391,7 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { - if (path.size() < 2) + if (mPath.size() < 2) return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); From 6ec86564d169c0b656f797066786a2a891f608ba Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 11:58:32 +0100 Subject: [PATCH 015/240] re-enabled OGRE in OpenCS; cleaned up startup --- apps/opencs/editor.cpp | 45 +++++++++++++------------ apps/opencs/editor.hpp | 14 ++++++-- apps/opencs/main.cpp | 16 +++------ apps/opencs/view/world/scenesubview.cpp | 15 --------- 4 files changed, 41 insertions(+), 49 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 5d5ac4c551..2b2f41754b 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -9,15 +9,22 @@ #include #include +#include + #include "model/doc/document.hpp" #include "model/world/data.hpp" -CS::Editor::Editor() - : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) +CS::Editor::Editor (OgreInit::OgreInit& ogreInit) +: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), + mIpcServerName ("org.openmw.OpenCS") { - mIpcServerName = "org.openmw.OpenCS"; + Files::PathContainer dataDirs = readConfig(); - setupDataFiles(); + setupDataFiles (dataDirs); + + CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg"); + + ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); mNewGame.setLocalData (mLocal); mFileDialog.setLocalData (mLocal); @@ -42,7 +49,16 @@ CS::Editor::Editor() this, SLOT (createNewGame (const boost::filesystem::path&))); } -void CS::Editor::setupDataFiles() +void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) +{ + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + { + QString path = QString::fromStdString(iter->string()); + mFileDialog.addFiles(path); + } +} + +Files::PathContainer CS::Editor::readConfig() { boost::program_options::variables_map variables; boost::program_options::options_description desc("Syntax: opencs \nAllowed options"); @@ -58,6 +74,8 @@ void CS::Editor::setupDataFiles() mCfgMgr.readConfiguration(variables, desc); + mDocumentManager.setResourceDir (variables["resources"].as()); + Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); @@ -83,23 +101,11 @@ void CS::Editor::setupDataFiles() messageBox.exec(); QApplication::exit (1); - return; } dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - mDocumentManager.setResourceDir (variables["resources"].as()); - - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) - { - - QString path = QString::fromStdString(iter->string()); - mFileDialog.addFiles(path); - } - - //load the settings into the userSettings instance. - const QString settingFileName = "opencs.cfg"; - CSMSettings::UserSettings::instance().loadSettings(settingFileName); + return dataDirs; } void CS::Editor::createGame() @@ -210,8 +216,6 @@ int CS::Editor::run() if (mLocal.empty()) return 1; -// temporarily disable OGRE-integration (need to fix path problem first) -#if 0 // TODO: setting Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); @@ -228,7 +232,6 @@ int CS::Editor::run() #endif Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); -#endif mStartup.show(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 930aa9d643..0f1c7a682d 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -10,6 +10,8 @@ #include #endif +#include + #include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" @@ -20,6 +22,11 @@ #include "view/settings/usersettingsdialog.hpp" +namespace OgreInit +{ + class OgreInit; +} + namespace CS { class Editor : public QObject @@ -37,7 +44,10 @@ namespace CS boost::filesystem::path mLocal; - void setupDataFiles(); + void setupDataFiles (const Files::PathContainer& dataDirs); + + Files::PathContainer readConfig(); + ///< \return data paths // not implemented Editor (const Editor&); @@ -45,7 +55,7 @@ namespace CS public: - Editor(); + Editor (OgreInit::OgreInit& ogreInit); bool makeIPCServer(); void connectToIPCServer(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 931d633127..212ed08367 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -40,15 +40,9 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE (resources); - // TODO: Ogre startup shouldn't be here, but it currently has to: - // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( - - Application mApplication (argc, argv); -// temporarily disable OGRE-integration (need to fix path problem first) -#if 0 OgreInit::OgreInit ogreInit; - ogreInit.init("./opencsOgre.log"); // TODO log path? -#endif + + Application application (argc, argv); #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); @@ -66,12 +60,12 @@ int main(int argc, char *argv[]) QStringList libraryPaths; libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); - mApplication.setLibraryPaths(libraryPaths); + application.setLibraryPaths(libraryPaths); #endif - mApplication.setWindowIcon (QIcon (":./opencs.png")); + application.setWindowIcon (QIcon (":./opencs.png")); - CS::Editor editor; + CS::Editor editor (ogreInit); if(!editor.makeIPCServer()) { diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 33ae327a0d..705adf2ad0 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -43,26 +43,11 @@ toolbar->addTool (new SceneToolMode (toolbar)); toolbar->addTool (new SceneToolMode (toolbar)); layout2->addWidget (toolbar, 0); -// temporarily disable OGRE-integration (need to fix path problem first) -#if 0 CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); layout2->addWidget (sceneWidget, 1); layout->insertLayout (0, layout2, 1); -#endif - /// \todo replace with rendering widget - QPalette palette2 (palette()); - palette2.setColor (QPalette::Background, Qt::white); - QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); - placeholder->setAutoFillBackground (true); - placeholder->setPalette (palette2); - placeholder->setAlignment (Qt::AlignHCenter); - - layout2->addWidget (placeholder, 1); - - layout->insertLayout (0, layout2, 1); - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); From 4ec4599be31bc1b0b1426d31367ef7f051e13aa3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 12:30:45 +0100 Subject: [PATCH 016/240] added navigation mode enum to SceneWidget --- apps/opencs/view/render/scenewidget.cpp | 2 +- apps/opencs/view/render/scenewidget.hpp | 33 +++++++++++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6e327bb6e0..88493fd76f 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -15,7 +15,7 @@ namespace CSVRender : QWidget(parent) , mWindow(NULL) , mCamera(NULL) - , mSceneMgr(NULL) + , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 355a6331e9..deba47fbb4 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -17,24 +17,31 @@ namespace CSVRender { Q_OBJECT - public: - SceneWidget(QWidget *parent); - virtual ~SceneWidget(void); + public: - QPaintEngine* paintEngine() const; + enum NavigationMode + { + NavigationMode_Free + }; - private: - void paintEvent(QPaintEvent* e); - void resizeEvent(QResizeEvent* e); - bool event(QEvent* e); + SceneWidget(QWidget *parent); + virtual ~SceneWidget(void); - void updateOgreWindow(); + QPaintEngine* paintEngine() const; - Ogre::Camera* mCamera; - Ogre::SceneManager* mSceneMgr; - Ogre::RenderWindow* mWindow; + private: + void paintEvent(QPaintEvent* e); + void resizeEvent(QResizeEvent* e); + bool event(QEvent* e); + + void updateOgreWindow(); + + Ogre::Camera* mCamera; + Ogre::SceneManager* mSceneMgr; + Ogre::RenderWindow* mWindow; + + NavigationMode mNavigationMode; }; - } #endif From 8081c067a611b147b9bde00ce1b7aec92fabaa69 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 13:09:37 +0100 Subject: [PATCH 017/240] moving the camera with W and S keys --- apps/opencs/view/render/scenewidget.cpp | 61 +++++++++++++++++++++++-- apps/opencs/view/render/scenewidget.hpp | 14 +++++- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 88493fd76f..280a85fd4f 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -10,16 +11,18 @@ namespace CSVRender { - SceneWidget::SceneWidget(QWidget *parent) : QWidget(parent) , mWindow(NULL) , mCamera(NULL) - , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free) + , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) + , mKeyForward (false), mKeyBackward (false) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); + setFocusPolicy (Qt::StrongFocus); + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); // Throw in a random color just to make sure multiple scenes work @@ -44,6 +47,11 @@ namespace CSVRender mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); mCamera->setFarClipDistance(3000); + + QTimer *timer = new QTimer (this); + + connect (timer, SIGNAL (timeout()), this, SLOT (update())); + timer->start (20); /// \todo make this configurable } void SceneWidget::updateOgreWindow() @@ -93,7 +101,6 @@ namespace CSVRender e->accept(); } - QPaintEngine* SceneWidget::paintEngine() const { // We don't want another paint engine to get in the way. @@ -130,4 +137,52 @@ namespace CSVRender return QWidget::event(e); } + void SceneWidget::keyPressEvent (QKeyEvent *event) + { + switch (event->key()) + { + case Qt::Key_W: mKeyForward = true; break; + case Qt::Key_S: mKeyBackward = true; break; + default: QWidget::keyPressEvent (event); + } + } + + void SceneWidget::keyReleaseEvent (QKeyEvent *event) + { + switch (event->key()) + { + case Qt::Key_W: mKeyForward = false; break; + case Qt::Key_S: mKeyBackward = false; break; + default: QWidget::keyReleaseEvent (event); + } + } + + void SceneWidget::focusOutEvent (QFocusEvent *event) + { + mKeyForward = false; + mKeyBackward = false; + + QWidget::focusOutEvent (event); + } + + void SceneWidget::update() + { + if (mKeyForward && !mKeyBackward) + { + mCamera->move (mCamera->getDirection()); + mUpdate = true; + } + + if (!mKeyForward && mKeyBackward) + { + mCamera->move (-mCamera->getDirection()); + mUpdate = true; + } + + if (mUpdate) + { + mUpdate = false; + mWindow->update(); + } + } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index deba47fbb4..7773983535 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -12,7 +12,6 @@ namespace Ogre namespace CSVRender { - class SceneWidget : public QWidget { Q_OBJECT @@ -34,6 +33,12 @@ namespace CSVRender void resizeEvent(QResizeEvent* e); bool event(QEvent* e); + void keyPressEvent (QKeyEvent *event); + + void keyReleaseEvent (QKeyEvent *event); + + void focusOutEvent (QFocusEvent *event); + void updateOgreWindow(); Ogre::Camera* mCamera; @@ -41,6 +46,13 @@ namespace CSVRender Ogre::RenderWindow* mWindow; NavigationMode mNavigationMode; + bool mUpdate; + int mKeyForward; + int mKeyBackward; + + private slots: + + void update(); }; } From af50575259f2484a21d5e7e5656430a57ab2acd7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 13:17:15 +0100 Subject: [PATCH 018/240] use shift key to speed up camera movement --- apps/opencs/view/render/scenewidget.cpp | 11 ++++++++--- apps/opencs/view/render/scenewidget.hpp | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 280a85fd4f..4702b0f720 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -16,7 +16,7 @@ namespace CSVRender , mWindow(NULL) , mCamera(NULL) , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) - , mKeyForward (false), mKeyBackward (false) + , mKeyForward (false), mKeyBackward (false), mFast (false) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -143,6 +143,7 @@ namespace CSVRender { case Qt::Key_W: mKeyForward = true; break; case Qt::Key_S: mKeyBackward = true; break; + case Qt::Key_Shift: mFast = true; break; default: QWidget::keyPressEvent (event); } } @@ -153,6 +154,7 @@ namespace CSVRender { case Qt::Key_W: mKeyForward = false; break; case Qt::Key_S: mKeyBackward = false; break; + case Qt::Key_Shift: mFast = false; break; default: QWidget::keyReleaseEvent (event); } } @@ -161,6 +163,7 @@ namespace CSVRender { mKeyForward = false; mKeyBackward = false; + mFast = false; QWidget::focusOutEvent (event); } @@ -169,13 +172,15 @@ namespace CSVRender { if (mKeyForward && !mKeyBackward) { - mCamera->move (mCamera->getDirection()); + int factor = mFast ? 4 : 1; + mCamera->move (factor * mCamera->getDirection()); mUpdate = true; } if (!mKeyForward && mKeyBackward) { - mCamera->move (-mCamera->getDirection()); + int factor = mFast ? 4 : 1; + mCamera->move (factor * -mCamera->getDirection()); mUpdate = true; } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 7773983535..0b1817682f 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -49,6 +49,7 @@ namespace CSVRender bool mUpdate; int mKeyForward; int mKeyBackward; + bool mFast; private slots: From b6ae521aa5deaf3f0f9b99ccd160bb149f75bb02 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 13:31:15 +0100 Subject: [PATCH 019/240] moving the camera with A and D keys --- apps/opencs/view/render/scenewidget.cpp | 27 ++++++++++++++++++++++--- apps/opencs/view/render/scenewidget.hpp | 2 ++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 4702b0f720..4f75750246 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -16,7 +16,8 @@ namespace CSVRender , mWindow(NULL) , mCamera(NULL) , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) - , mKeyForward (false), mKeyBackward (false), mFast (false) + , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) + , mFast (false) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -143,6 +144,8 @@ namespace CSVRender { case Qt::Key_W: mKeyForward = true; break; case Qt::Key_S: mKeyBackward = true; break; + case Qt::Key_A: mKeyLeft = true; break; + case Qt::Key_D: mKeyRight = true; break; case Qt::Key_Shift: mFast = true; break; default: QWidget::keyPressEvent (event); } @@ -154,6 +157,8 @@ namespace CSVRender { case Qt::Key_W: mKeyForward = false; break; case Qt::Key_S: mKeyBackward = false; break; + case Qt::Key_A: mKeyLeft = false; break; + case Qt::Key_D: mKeyRight = false; break; case Qt::Key_Shift: mFast = false; break; default: QWidget::keyReleaseEvent (event); } @@ -163,6 +168,8 @@ namespace CSVRender { mKeyForward = false; mKeyBackward = false; + mKeyLeft = false; + mKeyRight = false; mFast = false; QWidget::focusOutEvent (event); @@ -170,20 +177,34 @@ namespace CSVRender void SceneWidget::update() { + int factor = mFast ? 4 : 1; + if (mKeyForward && !mKeyBackward) { - int factor = mFast ? 4 : 1; mCamera->move (factor * mCamera->getDirection()); mUpdate = true; } if (!mKeyForward && mKeyBackward) { - int factor = mFast ? 4 : 1; mCamera->move (factor * -mCamera->getDirection()); mUpdate = true; } + if (mKeyLeft && !mKeyRight) + { + Ogre::Vector3 direction = mCamera->getDerivedRight(); + mCamera->move (factor * -direction); + mUpdate = true; + } + + if (!mKeyLeft && mKeyRight) + { + Ogre::Vector3 direction = mCamera->getDerivedRight(); + mCamera->move (factor * direction); + mUpdate = true; + } + if (mUpdate) { mUpdate = false; diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 0b1817682f..d5042d97b4 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -49,6 +49,8 @@ namespace CSVRender bool mUpdate; int mKeyForward; int mKeyBackward; + int mKeyLeft; + int mKeyRight; bool mFast; private slots: From b9107addc09bf817f711586274bdd2e39278827b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 13:39:49 +0100 Subject: [PATCH 020/240] mouse wheel zooming --- apps/opencs/view/render/scenewidget.cpp | 13 ++++++++++++- apps/opencs/view/render/scenewidget.hpp | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 4f75750246..2cc9c93b54 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -164,6 +164,17 @@ namespace CSVRender } } + void SceneWidget::wheelEvent (QWheelEvent *event) + { + if (int delta = event->delta()) + { + int factor = mFast ? 4 : 1; /// \todo make this configurable + /// \todo make mouse sensitivity configurable (the factor 2) + mCamera->move ((factor * mCamera->getDirection() * delta)/2); + mUpdate = true; + } + } + void SceneWidget::focusOutEvent (QFocusEvent *event) { mKeyForward = false; @@ -177,7 +188,7 @@ namespace CSVRender void SceneWidget::update() { - int factor = mFast ? 4 : 1; + int factor = mFast ? 4 : 1; /// \todo make this configurable if (mKeyForward && !mKeyBackward) { diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index d5042d97b4..6b6a8486d1 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -39,6 +39,8 @@ namespace CSVRender void focusOutEvent (QFocusEvent *event); + void wheelEvent (QWheelEvent *event); + void updateOgreWindow(); Ogre::Camera* mCamera; From 07d20a20131b652a009c3def4aa9af70da7fe30f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:09:07 +0100 Subject: [PATCH 021/240] camera panning via left button drag --- apps/opencs/view/render/scenewidget.cpp | 48 ++++++++++++++++++++++++- apps/opencs/view/render/scenewidget.hpp | 8 +++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 2cc9c93b54..ffe867ae53 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -17,7 +17,7 @@ namespace CSVRender , mCamera(NULL) , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) - , mFast (false) + , mFast (false), mDragging (false) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -175,6 +175,52 @@ namespace CSVRender } } + void SceneWidget::leaveEvent (QEvent *event) + { + mDragging = false; + } + + void SceneWidget::mouseMoveEvent (QMouseEvent *event) + { + int factor = mFast ? 4 : 1; /// \todo make this configurable + + if (event->buttons() & Qt::LeftButton) + { + if (mDragging) + { + QPoint diff = mOldPos-event->pos(); + mOldPos = event->pos(); + + if (diff.x()) + { + Ogre::Vector3 direction = mCamera->getDerivedRight(); + /// \todo make mouse sensitivity configurable (the factor 2) + mCamera->move ((factor * direction * diff.x())/2); + mUpdate = true; + } + + if (diff.y()) + { + Ogre::Vector3 direction = mCamera->getDerivedUp(); + /// \todo make mouse sensitivity configurable (the factor 2) + mCamera->move ((factor * -direction * diff.y())/2); + mUpdate = true; + } + } + else + { + mDragging = true; + mOldPos = event->pos(); + } + } + } + + void SceneWidget::mouseReleaseEvent (QMouseEvent *event) + { + if (event->buttons() & Qt::LeftButton) + mDragging = false; + } + void SceneWidget::focusOutEvent (QFocusEvent *event) { mKeyForward = false; diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 6b6a8486d1..1b78346f74 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -41,6 +41,12 @@ namespace CSVRender void wheelEvent (QWheelEvent *event); + void leaveEvent (QEvent *event); + + void mouseMoveEvent (QMouseEvent *event); + + void mouseReleaseEvent (QMouseEvent *event); + void updateOgreWindow(); Ogre::Camera* mCamera; @@ -54,6 +60,8 @@ namespace CSVRender int mKeyLeft; int mKeyRight; bool mFast; + bool mDragging; + QPoint mOldPos; private slots: From e9871999495d90622d18406981bbec8d7091209b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:14:53 +0100 Subject: [PATCH 022/240] getting rid of some magic numbers --- apps/opencs/view/render/scenewidget.cpp | 28 ++++++++++++------------- apps/opencs/view/render/scenewidget.hpp | 4 ++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index ffe867ae53..35a29c6687 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -18,6 +18,7 @@ namespace CSVRender , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) , mFast (false), mDragging (false) + , mMouseSensitivity (2), mFastFactor (4) /// \todo make these configurable { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -168,9 +169,7 @@ namespace CSVRender { if (int delta = event->delta()) { - int factor = mFast ? 4 : 1; /// \todo make this configurable - /// \todo make mouse sensitivity configurable (the factor 2) - mCamera->move ((factor * mCamera->getDirection() * delta)/2); + mCamera->move ((getFastFactor() * mCamera->getDirection() * delta)/mMouseSensitivity); mUpdate = true; } } @@ -182,8 +181,6 @@ namespace CSVRender void SceneWidget::mouseMoveEvent (QMouseEvent *event) { - int factor = mFast ? 4 : 1; /// \todo make this configurable - if (event->buttons() & Qt::LeftButton) { if (mDragging) @@ -194,16 +191,14 @@ namespace CSVRender if (diff.x()) { Ogre::Vector3 direction = mCamera->getDerivedRight(); - /// \todo make mouse sensitivity configurable (the factor 2) - mCamera->move ((factor * direction * diff.x())/2); + mCamera->move ((getFastFactor() * direction * diff.x())/mMouseSensitivity); mUpdate = true; } if (diff.y()) { Ogre::Vector3 direction = mCamera->getDerivedUp(); - /// \todo make mouse sensitivity configurable (the factor 2) - mCamera->move ((factor * -direction * diff.y())/2); + mCamera->move ((getFastFactor() * -direction * diff.y())/mMouseSensitivity); mUpdate = true; } } @@ -234,31 +229,29 @@ namespace CSVRender void SceneWidget::update() { - int factor = mFast ? 4 : 1; /// \todo make this configurable - if (mKeyForward && !mKeyBackward) { - mCamera->move (factor * mCamera->getDirection()); + mCamera->move (getFastFactor() * mCamera->getDirection()); mUpdate = true; } if (!mKeyForward && mKeyBackward) { - mCamera->move (factor * -mCamera->getDirection()); + mCamera->move (getFastFactor() * -mCamera->getDirection()); mUpdate = true; } if (mKeyLeft && !mKeyRight) { Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (factor * -direction); + mCamera->move (getFastFactor() * -direction); mUpdate = true; } if (!mKeyLeft && mKeyRight) { Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (factor * direction); + mCamera->move (getFastFactor() * direction); mUpdate = true; } @@ -268,4 +261,9 @@ namespace CSVRender mWindow->update(); } } + + int SceneWidget::getFastFactor() const + { + return mFast ? mFastFactor : 1; + } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 1b78346f74..37ef3cb94c 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -49,6 +49,8 @@ namespace CSVRender void updateOgreWindow(); + int getFastFactor() const; + Ogre::Camera* mCamera; Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; @@ -62,6 +64,8 @@ namespace CSVRender bool mFast; bool mDragging; QPoint mOldPos; + int mMouseSensitivity; + int mFastFactor; private slots: From e33bd4d50841fa14812882fde150435ed3063a98 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:24:35 +0100 Subject: [PATCH 023/240] camera rotation via ctrl-left button drag --- apps/opencs/view/render/scenewidget.cpp | 44 +++++++++++++++++++------ apps/opencs/view/render/scenewidget.hpp | 1 + 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 35a29c6687..b498fba061 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -17,7 +17,7 @@ namespace CSVRender , mCamera(NULL) , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) - , mFast (false), mDragging (false) + , mFast (false), mDragging (false), mMod1 (false) , mMouseSensitivity (2), mFastFactor (4) /// \todo make these configurable { setAttribute(Qt::WA_PaintOnScreen); @@ -148,6 +148,7 @@ namespace CSVRender case Qt::Key_A: mKeyLeft = true; break; case Qt::Key_D: mKeyRight = true; break; case Qt::Key_Shift: mFast = true; break; + case Qt::Key_Control: mMod1 = true; break; default: QWidget::keyPressEvent (event); } } @@ -161,6 +162,7 @@ namespace CSVRender case Qt::Key_A: mKeyLeft = false; break; case Qt::Key_D: mKeyRight = false; break; case Qt::Key_Shift: mFast = false; break; + case Qt::Key_Control: mMod1 = false; break; default: QWidget::keyReleaseEvent (event); } } @@ -188,18 +190,39 @@ namespace CSVRender QPoint diff = mOldPos-event->pos(); mOldPos = event->pos(); - if (diff.x()) + if (mMod1) { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move ((getFastFactor() * direction * diff.x())/mMouseSensitivity); - mUpdate = true; - } + // turn camera + if (diff.x()) + { + mCamera->yaw ( + Ogre::Degree ((getFastFactor() * diff.x())/mMouseSensitivity)); + mUpdate = true; + } - if (diff.y()) + if (diff.y()) + { + mCamera->pitch ( + Ogre::Degree ((getFastFactor() * diff.y())/mMouseSensitivity)); + mUpdate = true; + } + } + else { - Ogre::Vector3 direction = mCamera->getDerivedUp(); - mCamera->move ((getFastFactor() * -direction * diff.y())/mMouseSensitivity); - mUpdate = true; + // pan camera + if (diff.x()) + { + Ogre::Vector3 direction = mCamera->getDerivedRight(); + mCamera->move ((getFastFactor() * direction * diff.x())/mMouseSensitivity); + mUpdate = true; + } + + if (diff.y()) + { + Ogre::Vector3 direction = mCamera->getDerivedUp(); + mCamera->move ((getFastFactor() * -direction * diff.y())/mMouseSensitivity); + mUpdate = true; + } } } else @@ -223,6 +246,7 @@ namespace CSVRender mKeyLeft = false; mKeyRight = false; mFast = false; + mMod1 = false; QWidget::focusOutEvent (event); } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 37ef3cb94c..58b122deea 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -63,6 +63,7 @@ namespace CSVRender int mKeyRight; bool mFast; bool mDragging; + bool mMod1; QPoint mOldPos; int mMouseSensitivity; int mFastFactor; From 644f5cb8de97add530f34a02968b5fe09e91ba50 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:28:55 +0100 Subject: [PATCH 024/240] fixed mouse button release detection --- apps/opencs/view/render/scenewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index b498fba061..d6f12b5298 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -235,7 +235,7 @@ namespace CSVRender void SceneWidget::mouseReleaseEvent (QMouseEvent *event) { - if (event->buttons() & Qt::LeftButton) + if (!(event->buttons() & Qt::LeftButton)) mDragging = false; } From 526d75df516cf72ec43ec29d4dc449bc015f30c4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:30:38 +0100 Subject: [PATCH 025/240] inverted ctrl key --- apps/opencs/view/render/scenewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index d6f12b5298..c608df3765 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -190,7 +190,7 @@ namespace CSVRender QPoint diff = mOldPos-event->pos(); mOldPos = event->pos(); - if (mMod1) + if (!mMod1) { // turn camera if (diff.x()) From 8f73cc9268cee9bcf4fabb85ea0ef9656bc20b8c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 25 Feb 2014 14:59:46 +0100 Subject: [PATCH 026/240] added navigation mode selection --- apps/opencs/view/render/scenewidget.cpp | 11 ++++++++- apps/opencs/view/render/scenewidget.hpp | 8 ++++-- apps/opencs/view/world/scenesubview.cpp | 33 ++++++++++++++++--------- apps/opencs/view/world/scenesubview.hpp | 10 ++++++++ 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c608df3765..07fe56f8fa 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -15,7 +15,7 @@ namespace CSVRender : QWidget(parent) , mWindow(NULL) , mCamera(NULL) - , mSceneMgr(NULL), mNavigationMode (NavigationMode_Free), mUpdate (false) + , mSceneMgr(NULL), mNavigationMode (NavigationMode_1stPerson), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) , mFast (false), mDragging (false), mMod1 (false) , mMouseSensitivity (2), mFastFactor (4) /// \todo make these configurable @@ -94,6 +94,15 @@ namespace CSVRender Ogre::Root::getSingleton().destroyRenderTarget(mWindow); } + void SceneWidget::setNavigationMode (NavigationMode mode) + { + if (mode!=mNavigationMode) + { + mNavigationMode = mode; + + } + } + void SceneWidget::paintEvent(QPaintEvent* e) { if (!mWindow) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 58b122deea..55f62d6619 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -20,14 +20,18 @@ namespace CSVRender enum NavigationMode { - NavigationMode_Free + NavigationMode_1stPerson, + NavigationMode_Free, + NavigationMode_Orbit }; SceneWidget(QWidget *parent); - virtual ~SceneWidget(void); + virtual ~SceneWidget(); QPaintEngine* paintEngine() const; + void setNavigationMode (NavigationMode mode); + private: void paintEvent(QPaintEvent* e); void resizeEvent(QResizeEvent* e); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 705adf2ad0..339a885191 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -32,20 +32,21 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout2->setContentsMargins (QMargins (0, 0, 0, 0)); SceneToolbar *toolbar = new SceneToolbar (48, this); -// test -SceneToolMode *tool = new SceneToolMode (toolbar); -tool->addButton (":door.png", "a"); -tool->addButton (":GMST.png", "b"); -tool->addButton (":Info.png", "c"); -toolbar->addTool (tool); -toolbar->addTool (new SceneToolMode (toolbar)); -toolbar->addTool (new SceneToolMode (toolbar)); -toolbar->addTool (new SceneToolMode (toolbar)); + + // navigation mode + SceneToolMode *tool = new SceneToolMode (toolbar); + tool->addButton (":door.png", "1st"); /// \todo replace icons + tool->addButton (":GMST.png", "free"); + tool->addButton (":Info.png", "orbit"); + toolbar->addTool (tool); + connect (tool, SIGNAL (modeChanged (const std::string&)), + this, SLOT (selectNavigationMode (const std::string&))); + layout2->addWidget (toolbar, 0); - CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); + mScene = new CSVRender::SceneWidget(this); - layout2->addWidget (sceneWidget, 1); + layout2->addWidget (mScene, 1); layout->insertLayout (0, layout2, 1); @@ -76,3 +77,13 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } + +void CSVWorld::SceneSubView::selectNavigationMode (const std::string& mode) +{ + if (mode=="1st") + mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_1stPerson); + else if (mode=="free") + mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_Free); + else if (mode=="orbit") + mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_Orbit); +} diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index a0fed908df..65e1614c11 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -10,6 +10,11 @@ namespace CSMDoc class Document; } +namespace CSVRender +{ + class SceneWidget; +} + namespace CSVWorld { class Table; @@ -21,6 +26,7 @@ namespace CSVWorld Q_OBJECT TableBottomBox *mBottom; + CSVRender::SceneWidget *mScene; public: @@ -31,6 +37,10 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); + + private slots: + + void selectNavigationMode (const std::string& mode); }; } From 2421f23c2f83ab7d8d2d2b153fcfb43cb761aaee Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 27 Feb 2014 23:59:23 +1100 Subject: [PATCH 027/240] cleanup the mess... --- apps/opencs/model/world/columnbase.hpp | 254 +-- apps/opencs/model/world/tablemimedata.cpp | 890 ++++----- apps/opencs/model/world/tablemimedata.hpp | 124 +- apps/opencs/view/filter/editwidget.cpp | 406 ++-- apps/opencs/view/filter/editwidget.hpp | 114 +- apps/opencs/view/filter/filterbox.cpp | 100 +- apps/opencs/view/filter/filterbox.hpp | 90 +- apps/opencs/view/filter/recordfilterbox.cpp | 64 +- apps/opencs/view/filter/recordfilterbox.hpp | 74 +- apps/opencs/view/world/table.cpp | 1090 +++++------ apps/opencs/view/world/table.hpp | 254 +-- apps/opencs/view/world/tablesubview.cpp | 262 +-- apps/opencs/view/world/tablesubview.hpp | 126 +- apps/openmw/mwmechanics/aicombat.cpp | 37 +- apps/openmw/mwmechanics/pathfinding.cpp | 2 + apps/openmw/mwstate/statemanagerimp.cpp | 704 +++---- components/esm/esmwriter.cpp | 406 ++-- components/esm/esmwriter.hpp | 228 +-- credits.txt | 1 + readme.txt | 1902 +++++++++---------- 20 files changed, 3571 insertions(+), 3557 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 4ce45ffe86..e043336080 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -1,127 +1,127 @@ -#ifndef CSM_WOLRD_COLUMNBASE_H -#define CSM_WOLRD_COLUMNBASE_H - -#include - -#include -#include - -#include "record.hpp" - -namespace CSMWorld -{ - struct ColumnBase - { - enum Roles - { - Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 - }; - - enum Flags - { - Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view - }; - - enum Display - { - Display_None, //Do not use - Display_String, - - //CONCRETE TYPES STARTS HERE - Display_Skill, - Display_Class, - Display_Faction, - Display_Race, - Display_Sound, - Display_Region, - Display_Birthsign, - Display_Spell, - Display_Cell, - Display_Referenceable, - Display_Activator, - Display_Potion, - Display_Apparatus, - Display_Armor, - Display_Book, - Display_Clothing, - Display_Container, - Display_Creature, - Display_Door, - Display_Ingredient, - Display_CreatureLevelledList, - Display_ItemLevelledList, - Display_Light, - Display_Lockpick, - Display_Miscellaneous, - Display_Npc, - Display_Probe, - Display_Repair, - Display_Static, - Display_Weapon, - Display_Reference, - Display_Filter, - Display_Topic, - Display_Journal, - Display_TopicInfo, - Display_JournalInfo, - Display_Scene, - //CONCRETE TYPES ENDS HERE - - Display_Integer, - Display_Float, - Display_Var, - Display_GmstVarType, - Display_GlobalVarType, - Display_Specialisation, - Display_Attribute, - Display_Boolean, - Display_SpellType, - Display_Script, - Display_ApparatusType, - Display_ArmorType, - Display_ClothingType, - Display_CreatureType, - Display_WeaponType, - Display_RecordState, - Display_RefRecordType, - Display_DialogueType, - Display_QuestStatusType, - Display_Gender - }; - - int mColumnId; - int mFlags; - Display mDisplayType; - - ColumnBase (int columnId, Display displayType, int flag); - - virtual ~ColumnBase(); - - virtual bool isEditable() const = 0; - - virtual bool isUserEditable() const; - ///< Can this column be edited directly by the user? - - virtual std::string getTitle() const; - }; - - template - struct Column : public ColumnBase - { - int mFlags; - - Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (columnId, displayType, flags) {} - - virtual QVariant get (const Record& record) const = 0; - - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + getTitle() + " is not editable"); - } - }; -} - -#endif +#ifndef CSM_WOLRD_COLUMNBASE_H +#define CSM_WOLRD_COLUMNBASE_H + +#include + +#include +#include + +#include "record.hpp" + +namespace CSMWorld +{ + struct ColumnBase + { + enum Roles + { + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 + }; + + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; + + enum Display + { + Display_None, //Do not use + Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Skill, + Display_Class, + Display_Faction, + Display_Race, + Display_Sound, + Display_Region, + Display_Birthsign, + Display_Spell, + Display_Cell, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_Reference, + Display_Filter, + Display_Topic, + Display_Journal, + Display_TopicInfo, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + + Display_Integer, + Display_Float, + Display_Var, + Display_GmstVarType, + Display_GlobalVarType, + Display_Specialisation, + Display_Attribute, + Display_Boolean, + Display_SpellType, + Display_Script, + Display_ApparatusType, + Display_ArmorType, + Display_ClothingType, + Display_CreatureType, + Display_WeaponType, + Display_RecordState, + Display_RefRecordType, + Display_DialogueType, + Display_QuestStatusType, + Display_Gender + }; + + int mColumnId; + int mFlags; + Display mDisplayType; + + ColumnBase (int columnId, Display displayType, int flag); + + virtual ~ColumnBase(); + + virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? + + virtual std::string getTitle() const; + }; + + template + struct Column : public ColumnBase + { + int mFlags; + + Column (int columnId, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (columnId, displayType, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + getTitle() + " is not editable"); + } + }; +} + +#endif diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index f5e1acfb4a..b56c9c8c25 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -1,446 +1,446 @@ -#include "tablemimedata.hpp" -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : -mDocument(document) -{ - mUniversalId.push_back (id); - mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); -} - -CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : - mUniversalId (id), mDocument(document) -{ - for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) - { - mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); - } -} - -QStringList CSMWorld::TableMimeData::formats() const -{ - return mObjectsFormats; -} - -CSMWorld::TableMimeData::~TableMimeData() -{ -} - -std::string CSMWorld::TableMimeData::getIcon() const -{ - if (mUniversalId.empty()) - { - throw ("TableMimeData holds no UniversalId"); - } - - std::string tmpIcon; - bool firstIteration = true; - - for (unsigned i = 0; i < mUniversalId.size(); ++i) - { - if (firstIteration) - { - firstIteration = false; - tmpIcon = mUniversalId[i].getIcon(); - continue; - } - - if (tmpIcon != mUniversalId[i].getIcon()) - { - return ":/multitype.png"; //icon stolen from gnome - } - - tmpIcon = mUniversalId[i].getIcon(); - } - - return mUniversalId.begin()->getIcon(); //All objects are of the same type; -} - -std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const -{ - return mUniversalId; -} - - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return true; - } - } - - return false; -} - -bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return true; - } - } - - return false; -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == type) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const -{ - for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) - { - if (it->getType() == convertEnums (type)) - { - return *it; - } - } - - throw ("TableMimeData object does not hold object of the seeked type"); -} - -bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const -{ - return &document == &mDocument; -} - -CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) -{ - switch (type) - { - case CSMWorld::ColumnBase::Display_Race: - return CSMWorld::UniversalId::Type_Race; - - - case CSMWorld::ColumnBase::Display_Skill: - return CSMWorld::UniversalId::Type_Skill; - - - case CSMWorld::ColumnBase::Display_Class: - return CSMWorld::UniversalId::Type_Class; - - - case CSMWorld::ColumnBase::Display_Faction: - return CSMWorld::UniversalId::Type_Faction; - - - case CSMWorld::ColumnBase::Display_Sound: - return CSMWorld::UniversalId::Type_Sound; - - - case CSMWorld::ColumnBase::Display_Region: - return CSMWorld::UniversalId::Type_Region; - - - case CSMWorld::ColumnBase::Display_Birthsign: - return CSMWorld::UniversalId::Type_Birthsign; - - - case CSMWorld::ColumnBase::Display_Spell: - return CSMWorld::UniversalId::Type_Spell; - - - case CSMWorld::ColumnBase::Display_Cell: - return CSMWorld::UniversalId::Type_Cell; - - - case CSMWorld::ColumnBase::Display_Referenceable: - return CSMWorld::UniversalId::Type_Referenceable; - - - case CSMWorld::ColumnBase::Display_Activator: - return CSMWorld::UniversalId::Type_Activator; - - - case CSMWorld::ColumnBase::Display_Potion: - return CSMWorld::UniversalId::Type_Potion; - - - case CSMWorld::ColumnBase::Display_Apparatus: - return CSMWorld::UniversalId::Type_Apparatus; - - - case CSMWorld::ColumnBase::Display_Armor: - return CSMWorld::UniversalId::Type_Armor; - - - case CSMWorld::ColumnBase::Display_Book: - return CSMWorld::UniversalId::Type_Book; - - - case CSMWorld::ColumnBase::Display_Clothing: - return CSMWorld::UniversalId::Type_Clothing; - - - case CSMWorld::ColumnBase::Display_Container: - return CSMWorld::UniversalId::Type_Container; - - - case CSMWorld::ColumnBase::Display_Creature: - return CSMWorld::UniversalId::Type_Creature; - - - case CSMWorld::ColumnBase::Display_Door: - return CSMWorld::UniversalId::Type_Door; - - - case CSMWorld::ColumnBase::Display_Ingredient: - return CSMWorld::UniversalId::Type_Ingredient; - - - case CSMWorld::ColumnBase::Display_CreatureLevelledList: - return CSMWorld::UniversalId::Type_CreatureLevelledList; - - - case CSMWorld::ColumnBase::Display_ItemLevelledList: - return CSMWorld::UniversalId::Type_ItemLevelledList; - - - case CSMWorld::ColumnBase::Display_Light: - return CSMWorld::UniversalId::Type_Light; - - - case CSMWorld::ColumnBase::Display_Lockpick: - return CSMWorld::UniversalId::Type_Lockpick; - - - case CSMWorld::ColumnBase::Display_Miscellaneous: - return CSMWorld::UniversalId::Type_Miscellaneous; - - - case CSMWorld::ColumnBase::Display_Npc: - return CSMWorld::UniversalId::Type_Npc; - - - case CSMWorld::ColumnBase::Display_Probe: - return CSMWorld::UniversalId::Type_Probe; - - - case CSMWorld::ColumnBase::Display_Repair: - return CSMWorld::UniversalId::Type_Repair; - - - case CSMWorld::ColumnBase::Display_Static: - return CSMWorld::UniversalId::Type_Static; - - - case CSMWorld::ColumnBase::Display_Weapon: - return CSMWorld::UniversalId::Type_Weapon; - - - case CSMWorld::ColumnBase::Display_Reference: - return CSMWorld::UniversalId::Type_Reference; - - - case CSMWorld::ColumnBase::Display_Filter: - return CSMWorld::UniversalId::Type_Filter; - - - case CSMWorld::ColumnBase::Display_Topic: - return CSMWorld::UniversalId::Type_Topic; - - - case CSMWorld::ColumnBase::Display_Journal: - return CSMWorld::UniversalId::Type_Journal; - - - case CSMWorld::ColumnBase::Display_TopicInfo: - return CSMWorld::UniversalId::Type_TopicInfo; - - - case CSMWorld::ColumnBase::Display_JournalInfo: - return CSMWorld::UniversalId::Type_JournalInfo; - - - case CSMWorld::ColumnBase::Display_Scene: - return CSMWorld::UniversalId::Type_Scene; - - - case CSMWorld::ColumnBase::Display_Script: - return CSMWorld::UniversalId::Type_Script; - - - default: - return CSMWorld::UniversalId::Type_None; - - } -} - -CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) -{ - switch (type) - { - case CSMWorld::UniversalId::Type_Race: - return CSMWorld::ColumnBase::Display_Race; - - - case CSMWorld::UniversalId::Type_Skill: - return CSMWorld::ColumnBase::Display_Skill; - - - case CSMWorld::UniversalId::Type_Class: - return CSMWorld::ColumnBase::Display_Class; - - - case CSMWorld::UniversalId::Type_Faction: - return CSMWorld::ColumnBase::Display_Faction; - - - case CSMWorld::UniversalId::Type_Sound: - return CSMWorld::ColumnBase::Display_Sound; - - - case CSMWorld::UniversalId::Type_Region: - return CSMWorld::ColumnBase::Display_Region; - - - case CSMWorld::UniversalId::Type_Birthsign: - return CSMWorld::ColumnBase::Display_Birthsign; - - - case CSMWorld::UniversalId::Type_Spell: - return CSMWorld::ColumnBase::Display_Spell; - - - case CSMWorld::UniversalId::Type_Cell: - return CSMWorld::ColumnBase::Display_Cell; - - - case CSMWorld::UniversalId::Type_Referenceable: - return CSMWorld::ColumnBase::Display_Referenceable; - - - case CSMWorld::UniversalId::Type_Activator: - return CSMWorld::ColumnBase::Display_Activator; - - - case CSMWorld::UniversalId::Type_Potion: - return CSMWorld::ColumnBase::Display_Potion; - - - case CSMWorld::UniversalId::Type_Apparatus: - return CSMWorld::ColumnBase::Display_Apparatus; - - - case CSMWorld::UniversalId::Type_Armor: - return CSMWorld::ColumnBase::Display_Armor; - - - case CSMWorld::UniversalId::Type_Book: - return CSMWorld::ColumnBase::Display_Book; - - - case CSMWorld::UniversalId::Type_Clothing: - return CSMWorld::ColumnBase::Display_Clothing; - - - case CSMWorld::UniversalId::Type_Container: - return CSMWorld::ColumnBase::Display_Container; - - - case CSMWorld::UniversalId::Type_Creature: - return CSMWorld::ColumnBase::Display_Creature; - - - case CSMWorld::UniversalId::Type_Door: - return CSMWorld::ColumnBase::Display_Door; - - - case CSMWorld::UniversalId::Type_Ingredient: - return CSMWorld::ColumnBase::Display_Ingredient; - - - case CSMWorld::UniversalId::Type_CreatureLevelledList: - return CSMWorld::ColumnBase::Display_CreatureLevelledList; - - - case CSMWorld::UniversalId::Type_ItemLevelledList: - return CSMWorld::ColumnBase::Display_ItemLevelledList; - - - case CSMWorld::UniversalId::Type_Light: - return CSMWorld::ColumnBase::Display_Light; - - - case CSMWorld::UniversalId::Type_Lockpick: - return CSMWorld::ColumnBase::Display_Lockpick; - - - case CSMWorld::UniversalId::Type_Miscellaneous: - return CSMWorld::ColumnBase::Display_Miscellaneous; - - - case CSMWorld::UniversalId::Type_Npc: - return CSMWorld::ColumnBase::Display_Npc; - - - case CSMWorld::UniversalId::Type_Probe: - return CSMWorld::ColumnBase::Display_Probe; - - - case CSMWorld::UniversalId::Type_Repair: - return CSMWorld::ColumnBase::Display_Repair; - - - case CSMWorld::UniversalId::Type_Static: - return CSMWorld::ColumnBase::Display_Static; - - - case CSMWorld::UniversalId::Type_Weapon: - return CSMWorld::ColumnBase::Display_Weapon; - - - case CSMWorld::UniversalId::Type_Reference: - return CSMWorld::ColumnBase::Display_Reference; - - - case CSMWorld::UniversalId::Type_Filter: - return CSMWorld::ColumnBase::Display_Filter; - - - case CSMWorld::UniversalId::Type_Topic: - return CSMWorld::ColumnBase::Display_Topic; - - - case CSMWorld::UniversalId::Type_Journal: - return CSMWorld::ColumnBase::Display_Journal; - - - case CSMWorld::UniversalId::Type_TopicInfo: - return CSMWorld::ColumnBase::Display_TopicInfo; - - - case CSMWorld::UniversalId::Type_JournalInfo: - return CSMWorld::ColumnBase::Display_JournalInfo; - - - case CSMWorld::UniversalId::Type_Scene: - return CSMWorld::ColumnBase::Display_Scene; - - - case CSMWorld::UniversalId::Type_Script: - return CSMWorld::ColumnBase::Display_Script; - - - default: - return CSMWorld::ColumnBase::Display_None; - } +#include "tablemimedata.hpp" +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) +{ + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) +{ + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + } +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return mObjectsFormats; +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw ("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ":/multitype.png"; //icon stolen from gnome + } + + tmpIcon = mUniversalId[i].getIcon(); + } + + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} + + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + + + default: + return CSMWorld::UniversalId::Type_None; + + } +} + +CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) +{ + switch (type) + { + case CSMWorld::UniversalId::Type_Race: + return CSMWorld::ColumnBase::Display_Race; + + + case CSMWorld::UniversalId::Type_Skill: + return CSMWorld::ColumnBase::Display_Skill; + + + case CSMWorld::UniversalId::Type_Class: + return CSMWorld::ColumnBase::Display_Class; + + + case CSMWorld::UniversalId::Type_Faction: + return CSMWorld::ColumnBase::Display_Faction; + + + case CSMWorld::UniversalId::Type_Sound: + return CSMWorld::ColumnBase::Display_Sound; + + + case CSMWorld::UniversalId::Type_Region: + return CSMWorld::ColumnBase::Display_Region; + + + case CSMWorld::UniversalId::Type_Birthsign: + return CSMWorld::ColumnBase::Display_Birthsign; + + + case CSMWorld::UniversalId::Type_Spell: + return CSMWorld::ColumnBase::Display_Spell; + + + case CSMWorld::UniversalId::Type_Cell: + return CSMWorld::ColumnBase::Display_Cell; + + + case CSMWorld::UniversalId::Type_Referenceable: + return CSMWorld::ColumnBase::Display_Referenceable; + + + case CSMWorld::UniversalId::Type_Activator: + return CSMWorld::ColumnBase::Display_Activator; + + + case CSMWorld::UniversalId::Type_Potion: + return CSMWorld::ColumnBase::Display_Potion; + + + case CSMWorld::UniversalId::Type_Apparatus: + return CSMWorld::ColumnBase::Display_Apparatus; + + + case CSMWorld::UniversalId::Type_Armor: + return CSMWorld::ColumnBase::Display_Armor; + + + case CSMWorld::UniversalId::Type_Book: + return CSMWorld::ColumnBase::Display_Book; + + + case CSMWorld::UniversalId::Type_Clothing: + return CSMWorld::ColumnBase::Display_Clothing; + + + case CSMWorld::UniversalId::Type_Container: + return CSMWorld::ColumnBase::Display_Container; + + + case CSMWorld::UniversalId::Type_Creature: + return CSMWorld::ColumnBase::Display_Creature; + + + case CSMWorld::UniversalId::Type_Door: + return CSMWorld::ColumnBase::Display_Door; + + + case CSMWorld::UniversalId::Type_Ingredient: + return CSMWorld::ColumnBase::Display_Ingredient; + + + case CSMWorld::UniversalId::Type_CreatureLevelledList: + return CSMWorld::ColumnBase::Display_CreatureLevelledList; + + + case CSMWorld::UniversalId::Type_ItemLevelledList: + return CSMWorld::ColumnBase::Display_ItemLevelledList; + + + case CSMWorld::UniversalId::Type_Light: + return CSMWorld::ColumnBase::Display_Light; + + + case CSMWorld::UniversalId::Type_Lockpick: + return CSMWorld::ColumnBase::Display_Lockpick; + + + case CSMWorld::UniversalId::Type_Miscellaneous: + return CSMWorld::ColumnBase::Display_Miscellaneous; + + + case CSMWorld::UniversalId::Type_Npc: + return CSMWorld::ColumnBase::Display_Npc; + + + case CSMWorld::UniversalId::Type_Probe: + return CSMWorld::ColumnBase::Display_Probe; + + + case CSMWorld::UniversalId::Type_Repair: + return CSMWorld::ColumnBase::Display_Repair; + + + case CSMWorld::UniversalId::Type_Static: + return CSMWorld::ColumnBase::Display_Static; + + + case CSMWorld::UniversalId::Type_Weapon: + return CSMWorld::ColumnBase::Display_Weapon; + + + case CSMWorld::UniversalId::Type_Reference: + return CSMWorld::ColumnBase::Display_Reference; + + + case CSMWorld::UniversalId::Type_Filter: + return CSMWorld::ColumnBase::Display_Filter; + + + case CSMWorld::UniversalId::Type_Topic: + return CSMWorld::ColumnBase::Display_Topic; + + + case CSMWorld::UniversalId::Type_Journal: + return CSMWorld::ColumnBase::Display_Journal; + + + case CSMWorld::UniversalId::Type_TopicInfo: + return CSMWorld::ColumnBase::Display_TopicInfo; + + + case CSMWorld::UniversalId::Type_JournalInfo: + return CSMWorld::ColumnBase::Display_JournalInfo; + + + case CSMWorld::UniversalId::Type_Scene: + return CSMWorld::ColumnBase::Display_Scene; + + + case CSMWorld::UniversalId::Type_Script: + return CSMWorld::ColumnBase::Display_Script; + + + default: + return CSMWorld::ColumnBase::Display_None; + } } \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 0b0143abd4..7687f3555f 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -1,63 +1,63 @@ - -#ifndef TABLEMIMEDATA_H -#define TABLEMIMEDATA_H - -#include - -#include -#include - -#include "universalid.hpp" -#include "columnbase.hpp" - -namespace CSMDoc -{ - class Document; -} - -namespace CSMWorld -{ - -/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. -/// -/// This class provides way to construct mimedata object holding the universalid copy -/// Universalid is used in the majority of the tables to store type, id, argument types. -/// This way universalid grants a way to retrive record from the concrete table. -/// Please note, that tablemimedata object can hold multiple universalIds in the vector. - - class TableMimeData : public QMimeData - { - public: - TableMimeData(UniversalId id, const CSMDoc::Document& document); - - TableMimeData(std::vector& id, const CSMDoc::Document& document); - - ~TableMimeData(); - - virtual QStringList formats() const; - - std::string getIcon() const; - - std::vector getData() const; - - bool holdsType(UniversalId::Type type) const; - - bool holdsType(CSMWorld::ColumnBase::Display type) const; - - bool fromDocument(const CSMDoc::Document& document) const; - - UniversalId returnMatching(UniversalId::Type type) const; - - UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; - - static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); - static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); - - private: - std::vector mUniversalId; - QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; - - }; -} + +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +#include +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + + ~TableMimeData(); + + virtual QStringList formats() const; + + std::string getIcon() const; + + std::vector getData() const; + + bool holdsType(UniversalId::Type type) const; + + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + + UniversalId returnMatching(UniversalId::Type type) const; + + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; + + static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); + + private: + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; + + }; +} #endif // TABLEMIMEDATA_H \ No newline at end of file diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index 841919a9eb..cc1578bdd9 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -1,203 +1,203 @@ - -#include "editwidget.hpp" - -#include -#include -#include - -#include "../../model/world/data.hpp" - -CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) -: QLineEdit (parent), mParser (data) -{ - mPalette = palette(); - connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); - - QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); - - connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), - this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), - this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), - Qt::QueuedConnection); - connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), - Qt::QueuedConnection); -} - -void CSVFilter::EditWidget::textChanged (const QString& text) -{ - if (mParser.parse (text.toUtf8().constData())) - { - setPalette (mPalette); - emit filterChanged (mParser.getFilter()); - } - else - { - QPalette palette (mPalette); - palette.setColor (QPalette::Text, Qt::red); - setPalette (palette); - - /// \todo improve error reporting; mark only the faulty part - } -} - -void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, - const QModelIndex& bottomRight) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) -{ - textChanged (text()); -} - -void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, - Qt::DropAction action) -{ - const unsigned count = filterSource.size(); - bool multipleElements = false; - - switch (count) //setting multipleElements; - { - case 0: //empty - return; //nothing to do here - - case 1: //only single - multipleElements = false; - break; - - default: - multipleElements = true; - break; - } - - Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); - QString oldContent (text()); - - bool replaceMode = false; - std::string orAnd; - - switch (key) //setting replaceMode and string used to glue expressions - { - case Qt::ShiftModifier: - orAnd = "!or("; - replaceMode = false; - break; - - case Qt::ControlModifier: - orAnd = "!and("; - replaceMode = false; - break; - - default: - replaceMode = true; - break; - } - - if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode - { - replaceMode = true; - } - - if (!replaceMode) - { - oldContent.remove ('!'); - } - - std::stringstream ss; - - if (multipleElements) - { - if (replaceMode) - { - ss<<"!or("; - } else { - ss << orAnd << oldContent.toStdString() << ','; - } - - for (unsigned i = 0; i < count; ++i) - { - ss<4) - { - clear(); - insert (QString::fromUtf8(ss.str().c_str())); - } -} - -std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const -{ - const unsigned columns = seekedString.second.size(); - - bool multipleColumns = false; - switch (columns) - { - case 0: //empty - return ""; //no column to filter - - case 1: //one column to look for - multipleColumns = false; - break; - - default: - multipleColumns = true; - break; - } - - std::stringstream ss; - if (multipleColumns) - { - ss<<"or("; - for (unsigned i = 0; i < columns; ++i) - { - ss<<"string("<<'"'< +#include +#include + +#include "../../model/world/data.hpp" + +CSVFilter::EditWidget::EditWidget (CSMWorld::Data& data, QWidget *parent) +: QLineEdit (parent), mParser (data) +{ + mPalette = palette(); + connect (this, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + QAbstractItemModel *model = data.getTableModel (CSMWorld::UniversalId::Type_Filters); + + connect (model, SIGNAL (dataChanged (const QModelIndex &, const QModelIndex&)), + this, SLOT (filterDataChanged (const QModelIndex &, const QModelIndex&)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (filterRowsRemoved (const QModelIndex&, int, int)), + Qt::QueuedConnection); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (filterRowsInserted (const QModelIndex&, int, int)), + Qt::QueuedConnection); +} + +void CSVFilter::EditWidget::textChanged (const QString& text) +{ + if (mParser.parse (text.toUtf8().constData())) + { + setPalette (mPalette); + emit filterChanged (mParser.getFilter()); + } + else + { + QPalette palette (mPalette); + palette.setColor (QPalette::Text, Qt::red); + setPalette (palette); + + /// \todo improve error reporting; mark only the faulty part + } +} + +void CSVFilter::EditWidget::filterDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsRemoved (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int start, int end) +{ + textChanged (text()); +} + +void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + const unsigned count = filterSource.size(); + bool multipleElements = false; + + switch (count) //setting multipleElements; + { + case 0: //empty + return; //nothing to do here + + case 1: //only single + multipleElements = false; + break; + + default: + multipleElements = true; + break; + } + + Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); + QString oldContent (text()); + + bool replaceMode = false; + std::string orAnd; + + switch (key) //setting replaceMode and string used to glue expressions + { + case Qt::ShiftModifier: + orAnd = "!or("; + replaceMode = false; + break; + + case Qt::ControlModifier: + orAnd = "!and("; + replaceMode = false; + break; + + default: + replaceMode = true; + break; + } + + if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode + { + replaceMode = true; + } + + if (!replaceMode) + { + oldContent.remove ('!'); + } + + std::stringstream ss; + + if (multipleElements) + { + if (replaceMode) + { + ss<<"!or("; + } else { + ss << orAnd << oldContent.toStdString() << ','; + } + + for (unsigned i = 0; i < count; ++i) + { + ss<4) + { + clear(); + insert (QString::fromUtf8(ss.str().c_str())); + } +} + +std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const +{ + const unsigned columns = seekedString.second.size(); + + bool multipleColumns = false; + switch (columns) + { + case 0: //empty + return ""; //no column to filter + + case 1: //one column to look for + multipleColumns = false; + break; + + default: + multipleColumns = true; + break; + } + + std::stringstream ss; + if (multipleColumns) + { + ss<<"or("; + for (unsigned i = 0; i < columns; ++i) + { + ss<<"string("<<'"'< - -#include -#include -#include - -#include "../../model/filter/parser.hpp" -#include "../../model/filter/node.hpp" - -class QModelIndex; - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class EditWidget : public QLineEdit - { - Q_OBJECT - - CSMFilter::Parser mParser; - QPalette mPalette; - - public: - - EditWidget (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - - private: - std::string generateFilter(std::pair >& seekedString) const; - - private slots: - - void textChanged (const QString& text); - - void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void filterRowsRemoved (const QModelIndex& parent, int start, int end); - - void filterRowsInserted (const QModelIndex& parent, int start, int end); - - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - - void useFilterRequest(const std::string& idOfFilter); - }; -} - -#endif +#ifndef CSV_FILTER_EDITWIDGET_H +#define CSV_FILTER_EDITWIDGET_H + +#include + +#include +#include +#include + +#include "../../model/filter/parser.hpp" +#include "../../model/filter/node.hpp" + +class QModelIndex; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class EditWidget : public QLineEdit + { + Q_OBJECT + + CSMFilter::Parser mParser; + QPalette mPalette; + + public: + + EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + + private: + std::string generateFilter(std::pair >& seekedString) const; + + private slots: + + void textChanged (const QString& text); + + void filterDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void filterRowsRemoved (const QModelIndex& parent, int start, int end); + + void filterRowsInserted (const QModelIndex& parent, int start, int end); + + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + + void useFilterRequest(const std::string& idOfFilter); + }; +} + +#endif diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index 4954efefb8..a332880252 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -1,50 +1,50 @@ - -#include "filterbox.hpp" - -#include -#include - -#include "recordfilterbox.hpp" - -#include - -CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); - - layout->addWidget (recordFilterBox); - - setLayout (layout); - - connect (recordFilterBox, - SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (recordFilterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); - setAcceptDrops(true); -} - -void CSVFilter::FilterBox::dropEvent (QDropEvent* event) -{ - std::vector data = dynamic_cast (event->mimeData())->getData(); - - emit recordDropped(data, event->proposedAction()); -} - -void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) -{ - event->acceptProposedAction(); -} - -void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) -{ - event->accept(); -} + +#include "filterbox.hpp" + +#include +#include + +#include "recordfilterbox.hpp" + +#include + +CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + + layout->addWidget (recordFilterBox); + + setLayout (layout); + + connect (recordFilterBox, + SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (recordFilterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + setAcceptDrops(true); +} + +void CSVFilter::FilterBox::dropEvent (QDropEvent* event) +{ + std::vector data = dynamic_cast (event->mimeData())->getData(); + + emit recordDropped(data, event->proposedAction()); +} + +void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 263e89e73d..5954035fcb 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -1,45 +1,45 @@ -#ifndef CSV_FILTER_FILTERBOX_H -#define CSV_FILTER_FILTERBOX_H - -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/universalid.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class FilterBox : public QWidget - { - Q_OBJECT - - void dragEnterEvent (QDragEnterEvent* event); - - void dropEvent (QDropEvent* event); - - void dragMoveEvent(QDragMoveEvent *event); - - public: - - FilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void recordFilterChanged (boost::shared_ptr filter); - void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - -#endif - +#ifndef CSV_FILTER_FILTERBOX_H +#define CSV_FILTER_FILTERBOX_H + +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/universalid.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class FilterBox : public QWidget + { + Q_OBJECT + + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent(QDragMoveEvent *event); + + public: + + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void recordFilterChanged (boost::shared_ptr filter); + void recordDropped (std::vector& types, Qt::DropAction action); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + +#endif + diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 59c083bd89..2a1a1407fa 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -1,32 +1,32 @@ - -#include "recordfilterbox.hpp" - -#include -#include - -#include "editwidget.hpp" - -CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) -: QWidget (parent) -{ - QHBoxLayout *layout = new QHBoxLayout (this); - - layout->setContentsMargins (0, 0, 0, 0); - - layout->addWidget (new QLabel ("Record Filter", this)); - - EditWidget *editWidget = new EditWidget (data, this); - - layout->addWidget (editWidget); - - setLayout (layout); - - connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), - this, SIGNAL (filterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); -} + +#include "recordfilterbox.hpp" + +#include +#include + +#include "editwidget.hpp" + +CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *parent) +: QWidget (parent) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (new QLabel ("Record Filter", this)); + + EditWidget *editWidget = new EditWidget (data, this); + + layout->addWidget (editWidget); + + setLayout (layout); + + connect ( + editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + this, SIGNAL (filterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fd384a32b4..fa5c9c3c28 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -1,38 +1,38 @@ -#ifndef CSV_FILTER_RECORDFILTERBOX_H -#define CSV_FILTER_RECORDFILTERBOX_H - -#include - -#include -#include - -#include - -#include "../../model/filter/node.hpp" - -namespace CSMWorld -{ - class Data; -} - -namespace CSVFilter -{ - class RecordFilterBox : public QWidget - { - Q_OBJECT - - public: - - RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); - - signals: - - void filterChanged (boost::shared_ptr filter); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - }; - -} - +#ifndef CSV_FILTER_RECORDFILTERBOX_H +#define CSV_FILTER_RECORDFILTERBOX_H + +#include + +#include +#include + +#include + +#include "../../model/filter/node.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVFilter +{ + class RecordFilterBox : public QWidget + { + Q_OBJECT + + public: + + RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + + signals: + + void filterChanged (boost::shared_ptr filter); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + }; + +} + #endif \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index bc67c68b51..edf3bc6dea 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -1,546 +1,546 @@ - -#include "table.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -#include "../../model/world/data.hpp" -#include "../../model/world/commands.hpp" -#include "../../model/world/idtableproxymodel.hpp" -#include "../../model/world/idtable.hpp" -#include "../../model/world/record.hpp" -#include "../../model/world/columns.hpp" -#include "../../model/world/tablemimedata.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "recordstatusdelegate.hpp" -#include "util.hpp" - -void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - QMenu menu (this); - - /// \todo add menu items for select all and clear selection - - if (!mEditLock) - { - if (selectedRows.size()==1) - { - menu.addAction (mEditAction); - if (mCreateAction) - menu.addAction(mCloneAction); - } - - if (mCreateAction) - menu.addAction (mCreateAction); - - /// \todo Reverting temporarily disabled on tables that support reordering, because - /// revert logic currently can not handle reordering. - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); - - if (listDeletableSelectedIds().size()>0) - menu.addAction (mDeleteAction); - - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) - { - /// \todo allow reordering of multiple rows - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); - - if (column==-1) - column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); - - if (column!=-1) - { - if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row-1, column))) - { - menu.addAction (mMoveUpAction); - } - - if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row+1, column))) - { - menu.addAction (mMoveDownAction); - } - } - } - } - } - - menu.exec (event->globalPos()); -} - -std::vector CSVWorld::Table::listRevertableSelectedIds() const -{ - std::vector revertableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - revertableIds.push_back (id); - } - } - } - - return revertableIds; -} - -std::vector CSVWorld::Table::listDeletableSelectedIds() const -{ - std::vector deletableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - // check record state - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state==CSMWorld::RecordBase::State_Deleted) - continue; - - // check other columns (only relevant for a subset of the tables) - int dialogueTypeIndex = - mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - - if (dialogueTypeIndex!=-1) - { - int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - - if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) - continue; - } - - // add the id to the collection - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); - } - } - - return deletableIds; -} - -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) -{ - mModel = &dynamic_cast (*data.getTableModel (id)); - - mProxyModel = new CSMWorld::IdTableProxyModel (this); - mProxyModel->setSourceModel (mModel); - - setModel (mProxyModel); - horizontalHeader()->setResizeMode (QHeaderView::Interactive); - verticalHeader()->hide(); - setSortingEnabled (sorting); - setSelectionBehavior (QAbstractItemView::SelectRows); - setSelectionMode (QAbstractItemView::ExtendedSelection); - - int columns = mModel->columnCount(); - - for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); - - if (flags & CSMWorld::ColumnBase::Flag_Table) - { - CSMWorld::ColumnBase::Display display = static_cast ( - mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); - - mDelegates.push_back (delegate); - setItemDelegateForColumn (i, delegate); - } - else - hideColumn (i); - } - - mEditAction = new QAction (tr ("Edit Record"), this); - connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); - addAction (mEditAction); - - if (createAndDelete) - { - mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); - addAction (mCreateAction); - - mCloneAction = new QAction (tr ("Clone Record"), this); - connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); - addAction(mCloneAction); - } - - mRevertAction = new QAction (tr ("Revert Record"), this); - connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); - addAction (mRevertAction); - - mDeleteAction = new QAction (tr ("Delete Record"), this); - connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); - addAction (mDeleteAction); - - mMoveUpAction = new QAction (tr ("Move Up"), this); - connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); - addAction (mMoveUpAction); - - mMoveDownAction = new QAction (tr ("Move Down"), this); - connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); - addAction (mMoveDownAction); - - connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (tableSizeUpdate())); - - /// \note This signal could instead be connected to a slot that filters out changes not affecting - /// the records status column (for permanence reasons) - connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (tableSizeUpdate())); - - connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), - this, SLOT (selectionSizeUpdate ())); - - setAcceptDrops(true); -} - -void CSVWorld::Table::setEditLock (bool locked) -{ - for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) - (*iter)->setEditLock (locked); - - mEditLock = locked; -} - -CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const -{ - return CSMWorld::UniversalId ( - static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); -} - -void CSVWorld::Table::revertRecord() -{ - if (!mEditLock) - { - std::vector revertableIds = listRevertableSelectedIds(); - - if (revertableIds.size()>0) - { - if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); - - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); - - if (revertableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::deleteRecord() -{ - if (!mEditLock) - { - std::vector deletableIds = listDeletableSelectedIds(); - - if (deletableIds.size()>0) - { - if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); - - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - - if (deletableIds.size()>1) - mUndoStack.endMacro(); - } - } -} - -void CSVWorld::Table::editRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - emit editRequest (selectedRows.begin()->row()); - } -} - -void CSVWorld::Table::cloneRecord() -{ - if (!mEditLock) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); - if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) - { - emit cloneRequest (toClone); - } - } -} - -void CSVWorld::Table::moveUpRecord() -{ - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size()==1) - { - int row2 =selectedRows.begin()->row(); - - if (row2>0) - { - int row = row2-1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; iselectedRows(); - - if (selectedRows.size()==1) - { - int row =selectedRows.begin()->row(); - - if (rowrowCount()-1) - { - int row2 = row+1; - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); - - if (row2<=row) - throw std::runtime_error ("Inconsistent row order"); - - std::vector newOrder (row2-row+1); - newOrder[0] = row2-row; - newOrder[row2-row] = 0; - for (int i=1; icolumnCount(); - - for (int i=0; i (*delegate). - updateEditorSetting (settingName, settingValue)) - emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); -} - -void CSVWorld::Table::tableSizeUpdate() -{ - int size = 0; - int deleted = 0; - int modified = 0; - - if (mProxyModel->columnCount()>0) - { - int rows = mProxyModel->rowCount(); - - for (int i=0; imapToSource (mProxyModel->index (i, 0)); - - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); - int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); - - switch (state) - { - case CSMWorld::RecordBase::State_BaseOnly: ++size; break; - case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; - case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; - case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; - } - } - } - - tableSizeChanged (size, deleted, modified); -} - -void CSVWorld::Table::selectionSizeUpdate() -{ - selectionSizeChanged (selectionModel()->selectedRows().size()); -} - -void CSVWorld::Table::requestFocus (const std::string& id) -{ - QModelIndex index = mProxyModel->getModelIndex (id, 0); - - if (index.isValid()) - scrollTo (index, QAbstractItemView::PositionAtTop); -} - -void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) -{ - mProxyModel->setFilter (filter); -} - -void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) -{ - if (event->buttons() & Qt::LeftButton) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - if (selectedRows.size() == 0) - { - return; - } - - QDrag* drag = new QDrag (this); - CSMWorld::TableMimeData* mime = NULL; - - if (selectedRows.size() == 1) - { - mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); - } - else - { - std::vector idToDrag; - - foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. - { - idToDrag.push_back (getUniversalId (it.row())); - } - - mime = new CSMWorld::TableMimeData (idToDrag, mDocument); - } - - drag->setMimeData (mime); - drag->setPixmap (QString::fromStdString (mime->getIcon())); - - Qt::DropActions action = Qt::IgnoreAction; - switch (QApplication::keyboardModifiers()) - { - case Qt::ControlModifier: - action = Qt::CopyAction; - break; - - case Qt::ShiftModifier: - action = Qt::MoveAction; - break; - } - - drag->exec(action); - } - -} - -void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) -{ - event->acceptProposedAction(); -} - -void CSVWorld::Table::dropEvent(QDropEvent *event) -{ - QModelIndex index = indexAt (event->pos()); - - if (!index.isValid()) - { - return; - } - - const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); - if (mime->fromDocument (mDocument)) - { - CSMWorld::ColumnBase::Display display = static_cast - (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (mime->holdsType (display)) - { - CSMWorld::UniversalId record (mime->returnMatching (display)); - - std::auto_ptr command (new CSMWorld::ModifyCommand - (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - - mUndoStack.push (command.release()); - } - } //TODO handle drops from different document -} - -void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) -{ - event->accept(); -} - -std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const -{ - const int count = mModel->columnCount(); - - std::vector titles; - for (int i = 0; i < count; ++i) - { - CSMWorld::ColumnBase::Display columndisplay = static_cast - (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - if (display == columndisplay) - { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); - } - } - return titles; + +#include "table.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/idtableproxymodel.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "recordstatusdelegate.hpp" +#include "util.hpp" + +void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu (this); + + /// \todo add menu items for select all and clear selection + + if (!mEditLock) + { + if (selectedRows.size()==1) + { + menu.addAction (mEditAction); + if (mCreateAction) + menu.addAction(mCloneAction); + } + + if (mCreateAction) + menu.addAction (mCreateAction); + + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); + + if (listDeletableSelectedIds().size()>0) + menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } + } + + menu.exec (event->globalPos()); +} + +std::vector CSVWorld::Table::listRevertableSelectedIds() const +{ + std::vector revertableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } + } + + return revertableIds; +} + +std::vector CSVWorld::Table::listDeletableSelectedIds() const +{ + std::vector deletableIds; + + if (mProxyModel->columnCount()>0) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + + // check record state + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); + + if (state==CSMWorld::RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) + { + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; + } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } + + return deletableIds; +} + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) +{ + mModel = &dynamic_cast (*data.getTableModel (id)); + + mProxyModel = new CSMWorld::IdTableProxyModel (this); + mProxyModel->setSourceModel (mModel); + + setModel (mProxyModel); + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + setSortingEnabled (sorting); + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + int columns = mModel->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + + if (flags & CSMWorld::ColumnBase::Flag_Table) + { + CSMWorld::ColumnBase::Display display = static_cast ( + mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, + undoStack, this); + + mDelegates.push_back (delegate); + setItemDelegateForColumn (i, delegate); + } + else + hideColumn (i); + } + + mEditAction = new QAction (tr ("Edit Record"), this); + connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); + addAction (mEditAction); + + if (createAndDelete) + { + mCreateAction = new QAction (tr ("Add Record"), this); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); + addAction (mCreateAction); + + mCloneAction = new QAction (tr ("Clone Record"), this); + connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord())); + addAction(mCloneAction); + } + + mRevertAction = new QAction (tr ("Revert Record"), this); + connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + addAction (mRevertAction); + + mDeleteAction = new QAction (tr ("Delete Record"), this); + connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + addAction (mDeleteAction); + + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); +} + +void CSVWorld::Table::setEditLock (bool locked) +{ + for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) + (*iter)->setEditLock (locked); + + mEditLock = locked; +} + +CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const +{ + return CSMWorld::UniversalId ( + static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), + mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); +} + +void CSVWorld::Table::revertRecord() +{ + if (!mEditLock) + { + std::vector revertableIds = listRevertableSelectedIds(); + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::deleteRecord() +{ + if (!mEditLock) + { + std::vector deletableIds = listDeletableSelectedIds(); + + if (deletableIds.size()>0) + { + if (deletableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + + if (deletableIds.size()>1) + mUndoStack.endMacro(); + } + } +} + +void CSVWorld::Table::editRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + emit editRequest (selectedRows.begin()->row()); + } +} + +void CSVWorld::Table::cloneRecord() +{ + if (!mEditLock) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row()); + if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted()) + { + emit cloneRequest (toClone); + } + } +} + +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); + + for (int i=0; i (*delegate). + updateEditorSetting (settingName, settingValue)) + emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); +} + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mProxyModel->columnCount()>0) + { + int rows = mProxyModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + int state = mModel->data (mModel->index (index.row(), columnIndex)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); +} + +void CSVWorld::Table::recordFilterChanged (boost::shared_ptr filter) +{ + mProxyModel->setFilter (filter); +} + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); + + Qt::DropActions action = Qt::IgnoreAction; + switch (QApplication::keyboardModifiers()) + { + case Qt::ControlModifier: + action = Qt::CopyAction; + break; + + case Qt::ShiftModifier: + action = Qt::MoveAction; + break; + } + + drag->exec(action); + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + QModelIndex index = indexAt (event->pos()); + + if (!index.isValid()) + { + return; + } + + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) + { + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (mime->holdsType (display)) + { + CSMWorld::UniversalId record (mime->returnMatching (display)); + + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const +{ + const int count = mModel->columnCount(); + + std::vector titles; + for (int i = 0; i < count; ++i) + { + CSMWorld::ColumnBase::Display columndisplay = static_cast + (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (display == columndisplay) + { + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + } + } + return titles; } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 04733a53e7..615a31b4d7 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -1,127 +1,127 @@ -#ifndef CSV_WORLD_TABLE_H -#define CSV_WORLD_TABLE_H - -#include -#include - -#include -#include - -#include "../../model/filter/node.hpp" -#include "../../model/world/columnbase.hpp" - -namespace CSMDoc { - class Document; -} - -class QUndoStack; -class QAction; - -namespace CSMWorld -{ - class Data; - class UniversalId; - class IdTableProxyModel; - class IdTable; -} - -namespace CSVWorld -{ - class CommandDelegate; - - ///< Table widget - class Table : public QTableView - { - Q_OBJECT - - std::vector mDelegates; - QUndoStack& mUndoStack; - QAction *mEditAction; - QAction *mCreateAction; - QAction *mCloneAction; - QAction *mRevertAction; - QAction *mDeleteAction; - QAction *mMoveUpAction; - QAction *mMoveDownAction; - CSMWorld::IdTableProxyModel *mProxyModel; - CSMWorld::IdTable *mModel; - bool mEditLock; - int mRecordStatusDisplay; - - /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you - /// should NOT use it for anything else. - const CSMDoc::Document& mDocument; - - private: - - void contextMenuEvent (QContextMenuEvent *event); - - std::vector listRevertableSelectedIds() const; - - std::vector listDeletableSelectedIds() const; - - void mouseMoveEvent(QMouseEvent *event); - - void dragEnterEvent(QDragEnterEvent *event); - - void dragMoveEvent(QDragMoveEvent *event); - - void dropEvent(QDropEvent *event); - - public: - - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - - ///< \param createAndDelete Allow creation and deletion of records. - /// \param sorting Allow changing order of rows in the view via column headers. - - void setEditLock (bool locked); - - CSMWorld::UniversalId getUniversalId (int row) const; - - void updateEditorSetting (const QString &settingName, const QString &settingValue); - - std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; - - signals: - - void editRequest (int row); - - void selectionSizeChanged (int size); - - void tableSizeChanged (int size, int deleted, int modified); - ///< \param size Number of not deleted records - /// \param deleted Number of deleted records - /// \param modified Number of added and modified records - - void createRequest(); - void cloneRequest(const CSMWorld::UniversalId&); - - private slots: - - void revertRecord(); - - void deleteRecord(); - - void editRecord(); - - void cloneRecord(); - - void moveUpRecord(); - - void moveDownRecord(); - - public slots: - - void tableSizeUpdate(); - - void selectionSizeUpdate(); - - void requestFocus (const std::string& id); - - void recordFilterChanged (boost::shared_ptr filter); - }; -} - -#endif +#ifndef CSV_WORLD_TABLE_H +#define CSV_WORLD_TABLE_H + +#include +#include + +#include +#include + +#include "../../model/filter/node.hpp" +#include "../../model/world/columnbase.hpp" + +namespace CSMDoc { + class Document; +} + +class QUndoStack; +class QAction; + +namespace CSMWorld +{ + class Data; + class UniversalId; + class IdTableProxyModel; + class IdTable; +} + +namespace CSVWorld +{ + class CommandDelegate; + + ///< Table widget + class Table : public QTableView + { + Q_OBJECT + + std::vector mDelegates; + QUndoStack& mUndoStack; + QAction *mEditAction; + QAction *mCreateAction; + QAction *mCloneAction; + QAction *mRevertAction; + QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; + CSMWorld::IdTableProxyModel *mProxyModel; + CSMWorld::IdTable *mModel; + bool mEditLock; + int mRecordStatusDisplay; + + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + std::vector listRevertableSelectedIds() const; + + std::vector listDeletableSelectedIds() const; + + void mouseMoveEvent(QMouseEvent *event); + + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + + public: + + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + + ///< \param createAndDelete Allow creation and deletion of records. + /// \param sorting Allow changing order of rows in the view via column headers. + + void setEditLock (bool locked); + + CSMWorld::UniversalId getUniversalId (int row) const; + + void updateEditorSetting (const QString &settingName, const QString &settingValue); + + std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; + + signals: + + void editRequest (int row); + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + + void createRequest(); + void cloneRequest(const CSMWorld::UniversalId&); + + private slots: + + void revertRecord(); + + void deleteRecord(); + + void editRecord(); + + void cloneRecord(); + + void moveUpRecord(); + + void moveDownRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); + + void requestFocus (const std::string& id); + + void recordFilterChanged (boost::shared_ptr filter); + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 782ccfd244..e330d47758 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,132 +1,132 @@ - -#include "tablesubview.hpp" - -#include -#include - -#include "../../model/doc/document.hpp" -#include "../../model/world/tablemimedata.hpp" - -#include "../filter/filterbox.hpp" -#include "table.hpp" -#include "tablebottombox.hpp" -#include "creator.hpp" - -CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting) -: SubView (id) -{ - QVBoxLayout *layout = new QVBoxLayout; - - layout->setContentsMargins (QMargins (0, 0, 0, 0)); - - layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); - - layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); - - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); - - layout->insertWidget (0, filterBox); - - QWidget *widget = new QWidget; - - widget->setLayout (layout); - - setWidget (widget); - - connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); - - connect (mTable, SIGNAL (selectionSizeChanged (int)), - mBottom, SLOT (selectionSizeChanged (int))); - connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), - mBottom, SLOT (tableSizeChanged (int, int, int))); - - mTable->tableSizeUpdate(); - mTable->selectionSizeUpdate(); - mTable->viewport()->installEventFilter(this); - mBottom->installEventFilter(this); - filterBox->installEventFilter(this); - - if (mBottom->canCreateAndDelete()) - { - connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); - - connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, - SLOT(cloneRequest(const CSMWorld::UniversalId&))); - - connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), - mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); - } - connect (mBottom, SIGNAL (requestFocus (const std::string&)), - mTable, SLOT (requestFocus (const std::string&))); - - connect (filterBox, - SIGNAL (recordFilterChanged (boost::shared_ptr)), - mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), - this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); -} - -void CSVWorld::TableSubView::setEditLock (bool locked) -{ - mTable->setEditLock (locked); - mBottom->setEditLock (locked); -} - -void CSVWorld::TableSubView::editRequest (int row) -{ - focusId (mTable->getUniversalId (row)); -} - -void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) -{ - mTable->updateEditorSetting(settingName, settingValue); -} - -void CSVWorld::TableSubView::setStatusBar (bool show) -{ - mBottom->setStatusBar (show); -} - -void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) -{ - emit cloneRequest(toClone.getId(), toClone.getType()); -} - -void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) -{ - std::vector > > filterSource; - - for (std::vector::iterator it = types.begin(); it != types.end(); ++it) - { - std::pair > pair( //splited long line - std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); - - filterSource.push_back(pair); - } - emit createFilterRequest(filterSource, action); -} - -bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) -{ - if (event->type() == QEvent::Drop) - { - QDropEvent* drop = dynamic_cast(event); - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); - if (handled) - { - emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); - } - return handled; - } - return false; + +#include "tablesubview.hpp" + +#include +#include + +#include "../../model/doc/document.hpp" +#include "../../model/world/tablemimedata.hpp" + +#include "../filter/filterbox.hpp" +#include "table.hpp" +#include "tablebottombox.hpp" +#include "creator.hpp" + +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting) +: SubView (id) +{ + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); + + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); + mTable->viewport()->installEventFilter(this); + mBottom->installEventFilter(this); + filterBox->installEventFilter(this); + + if (mBottom->canCreateAndDelete()) + { + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this, + SLOT(cloneRequest(const CSMWorld::UniversalId&))); + + connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), + mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + } + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); + + connect (filterBox, + SIGNAL (recordFilterChanged (boost::shared_ptr)), + mTable, SLOT (recordFilterChanged (boost::shared_ptr))); + + connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); +} + +void CSVWorld::TableSubView::setEditLock (bool locked) +{ + mTable->setEditLock (locked); + mBottom->setEditLock (locked); +} + +void CSVWorld::TableSubView::editRequest (int row) +{ + focusId (mTable->getUniversalId (row)); +} + +void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + mTable->updateEditorSetting(settingName, settingValue); +} + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} + +void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) +{ + emit cloneRequest(toClone.getId(), toClone.getType()); +} + +void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) +{ + std::vector > > filterSource; + + for (std::vector::iterator it = types.begin(); it != types.end(); ++it) + { + std::pair > pair( //splited long line + std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); + + filterSource.push_back(pair); + } + emit createFilterRequest(filterSource, action); +} + +bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Drop) + { + QDropEvent* drop = dynamic_cast(event); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; + } + return false; } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 4e578b180c..b3c253919b 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -1,63 +1,63 @@ -#ifndef CSV_WORLD_TABLESUBVIEW_H -#define CSV_WORLD_TABLESUBVIEW_H - -#include "../doc/subview.hpp" - -#include - -class QModelIndex; - -namespace CSMWorld -{ - class IdTable; -} - -namespace CSMDoc -{ - class Document; -} - -namespace CSVWorld -{ - class Table; - class TableBottomBox; - class CreatorFactoryBase; - - class TableSubView : public CSVDoc::SubView - { - Q_OBJECT - - Table *mTable; - TableBottomBox *mBottom; - - public: - - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - const CreatorFactoryBase& creatorFactory, bool sorting); - - virtual void setEditLock (bool locked); - - virtual void updateEditorSetting (const QString& key, const QString& value); - - virtual void setStatusBar (bool show); - - protected: - bool eventFilter(QObject* object, QEvent *event); - - signals: - void cloneRequest(const std::string&, - const CSMWorld::UniversalId::Type); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); - - private slots: - - void editRequest (int row); - void cloneRequest (const CSMWorld::UniversalId& toClone); - void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, - Qt::DropAction action); - }; -} - -#endif +#ifndef CSV_WORLD_TABLESUBVIEW_H +#define CSV_WORLD_TABLESUBVIEW_H + +#include "../doc/subview.hpp" + +#include + +class QModelIndex; + +namespace CSMWorld +{ + class IdTable; +} + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class Table; + class TableBottomBox; + class CreatorFactoryBase; + + class TableSubView : public CSVDoc::SubView + { + Q_OBJECT + + Table *mTable; + TableBottomBox *mBottom; + + public: + + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, bool sorting); + + virtual void setEditLock (bool locked); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + + protected: + bool eventFilter(QObject* object, QEvent *event); + + signals: + void cloneRequest(const std::string&, + const CSMWorld::UniversalId::Type); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); + + private slots: + + void editRequest (int row); + void cloneRequest (const CSMWorld::UniversalId& toClone); + void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, + Qt::DropAction action); + }; +} + +#endif diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 6a49f2f5e0..17a624f0ff 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,17 +240,21 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); + buildNewPath(actor); //may fail to build a path, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - mRotate = true; + //if no new path leave mTargetAngle unchanged + if(!mPathFinder.getPath().empty()) + { + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; + } mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -300,9 +304,13 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float dist = -1; //hack to indicate first time, to construct a new path + if(!mPathFinder.getPath().empty()) + { + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + } float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -311,7 +319,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -332,8 +340,11 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + if(!mPathFinder.getPath().empty()) + { + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; + } } } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4407363a61..8dbdd87707 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,6 +391,8 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { + if (mPath.size() < 2) + return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index f9b5b4d04a..265069dc46 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -1,352 +1,352 @@ - -#include "statemanagerimp.hpp" - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/journal.hpp" -#include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/windowmanager.hpp" -#include "../mwbase/mechanicsmanager.hpp" -#include "../mwbase/scriptmanager.hpp" -#include "../mwbase/soundmanager.hpp" - -#include "../mwworld/player.hpp" -#include "../mwworld/class.hpp" - -#include "../mwmechanics/npcstats.hpp" - -#include "../mwscript/globalscripts.hpp" - -void MWState::StateManager::cleanup (bool force) -{ - if (mState!=State_NoGame || force) - { - MWBase::Environment::get().getSoundManager()->clear(); - MWBase::Environment::get().getDialogueManager()->clear(); - MWBase::Environment::get().getJournal()->clear(); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); - MWBase::Environment::get().getWorld()->clear(); - MWBase::Environment::get().getWindowManager()->clear(); - - mState = State_NoGame; - mCharacterManager.clearCurrentCharacter(); - mTimePlayed = 0; - } -} - -std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) - const -{ - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - const std::vector& prev = reader.getGameFiles(); - - std::map map; - - for (int iPrev = 0; iPrev (prev.size()); ++iPrev) - { - std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); - - for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) - if (id==Misc::StringUtils::lowerCase (current[iCurrent])) - { - map.insert (std::make_pair (iPrev, iCurrent)); - break; - } - } - - return map; -} - -MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) -: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) -{ - -} - -void MWState::StateManager::requestQuit() -{ - mQuitRequest = true; -} - -bool MWState::StateManager::hasQuitRequest() const -{ - return mQuitRequest; -} - -void MWState::StateManager::askLoadRecent() -{ - if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) - return; - - if( !mAskLoadRecent ) - { - if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves - { - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - else - { - MWState::Slot lastSave = *getCurrentCharacter()->begin(); - std::vector buttons; - buttons.push_back("#{sYes}"); - buttons.push_back("#{sNo}"); - std::string tag("%s"); - std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); - size_t pos = message.find(tag); - message.replace(pos, tag.length(), lastSave.mProfile.mDescription); - MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); - mAskLoadRecent = true; - } - } -} - -MWState::StateManager::State MWState::StateManager::getState() const -{ - return mState; -} - -void MWState::StateManager::newGame (bool bypass) -{ - cleanup(); - - if (!bypass) - { - MWBase::Environment::get().getWorld()->startNewGame(); - MWBase::Environment::get().getWindowManager()->setNewGame (true); - } - else - MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); - - mState = State_Running; -} - -void MWState::StateManager::endGame() -{ - mState = State_Ended; - MWBase::Environment::get().getWorld()->useDeathCamera(); -} - -void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) -{ - ESM::SavedGame profile; - - MWBase::World& world = *MWBase::Environment::get().getWorld(); - - MWWorld::Ptr player = world.getPlayer().getPlayer(); - - profile.mContentFiles = world.getContentFiles(); - - profile.mPlayerName = player.getClass().getName (player); - profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mClass; - - profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); - profile.mTimePlayed = mTimePlayed; - profile.mDescription = description; - - int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing - Ogre::Image screenshot; - world.screenshot(screenshot, screenshotW, screenshotH); - Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); - profile.mScreenshot.resize(encoded->size()); - encoded->read(&profile.mScreenshot[0], encoded->size()); - - if (!slot) - slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); - else - slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); - - std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); - - ESM::ESMWriter writer; - - const std::vector& current = - MWBase::Environment::get().getWorld()->getContentFiles(); - - for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); - ++iter) - writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 - - writer.setFormat (ESM::Header::CurrentFormat); - writer.setRecordCount ( - 1 // saved game header - +MWBase::Environment::get().getJournal()->countSavedGameRecords() - +MWBase::Environment::get().getWorld()->countSavedGameRecords() - +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() - +1 // global map - ); - - writer.save (stream); - - writer.startRecord (ESM::REC_SAVE); - slot->mProfile.save (writer); - writer.endRecord (ESM::REC_SAVE); - - MWBase::Environment::get().getJournal()->write (writer); - MWBase::Environment::get().getDialogueManager()->write (writer); - MWBase::Environment::get().getWorld()->write (writer); - MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); - MWBase::Environment::get().getWindowManager()->write(writer); - - writer.close(); - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); -} - -void MWState::StateManager::loadGame (const Character *character, const Slot *slot) -{ - try - { - cleanup(); - - mTimePlayed = slot->mProfile.mTimePlayed; - - ESM::ESMReader reader; - reader.open (slot->mPath.string()); - - std::map contentFileMap = buildContentFileIndexMap (reader); - - while (reader.hasMoreRecs()) - { - ESM::NAME n = reader.getRecName(); - reader.getRecHeader(); - - switch (n.val) - { - case ESM::REC_SAVE: - - // don't need to read that here - reader.skipRecord(); - break; - - case ESM::REC_JOUR: - case ESM::REC_QUES: - - MWBase::Environment::get().getJournal()->readRecord (reader, n.val); - break; - - case ESM::REC_DIAS: - - MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); - break; - - case ESM::REC_ALCH: - case ESM::REC_ARMO: - case ESM::REC_BOOK: - case ESM::REC_CLAS: - case ESM::REC_CLOT: - case ESM::REC_ENCH: - case ESM::REC_NPC_: - case ESM::REC_SPEL: - case ESM::REC_WEAP: - case ESM::REC_GLOB: - case ESM::REC_PLAY: - case ESM::REC_CSTA: - - MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); - break; - - case ESM::REC_GSCR: - - MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); - break; - - case ESM::REC_GMAP: - - MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); - break; - - default: - - // ignore invalid records - /// \todo log error - reader.skipRecord(); - } - } - - mCharacterManager.setCurrentCharacter(character); - - mState = State_Running; - - Settings::Manager::setString ("character", "Saves", - slot->mPath.parent_path().filename().string()); - - MWBase::Environment::get().getWindowManager()->setNewGame(false); - MWBase::Environment::get().getWorld()->setupPlayer(); - MWBase::Environment::get().getWorld()->renderPlayer(); - MWBase::Environment::get().getWindowManager()->updatePlayer(); - MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); - - MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); - } - catch (const std::exception& e) - { - std::cerr << "failed to load saved game: " << e.what() << std::endl; - cleanup (true); - } -} - -MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) -{ - return mCharacterManager.getCurrentCharacter (create); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() -{ - return mCharacterManager.begin(); -} - -MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() -{ - return mCharacterManager.end(); -} - -void MWState::StateManager::update (float duration) -{ - mTimePlayed += duration; - - // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. - if (mAskLoadRecent) - { - int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); - if(iButton==0) - { - mAskLoadRecent = false; - //Load last saved game for current character - MWState::Character *curCharacter = getCurrentCharacter(); - MWState::Slot lastSave = *curCharacter->begin(); - loadGame(curCharacter, &lastSave); - } - else if(iButton==1) - { - mAskLoadRecent = false; - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); - } - } -} + +#include "statemanagerimp.hpp" + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" +#include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" + +#include "../mwscript/globalscripts.hpp" + +void MWState::StateManager::cleanup (bool force) +{ + if (mState!=State_NoGame || force) + { + MWBase::Environment::get().getSoundManager()->clear(); + MWBase::Environment::get().getDialogueManager()->clear(); + MWBase::Environment::get().getJournal()->clear(); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); + MWBase::Environment::get().getWorld()->clear(); + MWBase::Environment::get().getWindowManager()->clear(); + + mState = State_NoGame; + mCharacterManager.clearCurrentCharacter(); + mTimePlayed = 0; + } +} + +std::map MWState::StateManager::buildContentFileIndexMap (const ESM::ESMReader& reader) + const +{ + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + const std::vector& prev = reader.getGameFiles(); + + std::map map; + + for (int iPrev = 0; iPrev (prev.size()); ++iPrev) + { + std::string id = Misc::StringUtils::lowerCase (prev[iPrev].name); + + for (int iCurrent = 0; iCurrent (current.size()); ++iCurrent) + if (id==Misc::StringUtils::lowerCase (current[iCurrent])) + { + map.insert (std::make_pair (iPrev, iCurrent)); + break; + } + } + + return map; +} + +MWState::StateManager::StateManager (const boost::filesystem::path& saves, const std::string& game) +: mQuitRequest (false), mAskLoadRecent(false), mState (State_NoGame), mCharacterManager (saves, game), mTimePlayed (0) +{ + +} + +void MWState::StateManager::requestQuit() +{ + mQuitRequest = true; +} + +bool MWState::StateManager::hasQuitRequest() const +{ + return mQuitRequest; +} + +void MWState::StateManager::askLoadRecent() +{ + if (MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu) + return; + + if( !mAskLoadRecent ) + { + if(getCurrentCharacter()->begin() == getCurrentCharacter()->end() )//no saves + { + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + else + { + MWState::Slot lastSave = *getCurrentCharacter()->begin(); + std::vector buttons; + buttons.push_back("#{sYes}"); + buttons.push_back("#{sNo}"); + std::string tag("%s"); + std::string message = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLoadLastSaveMsg", tag); + size_t pos = message.find(tag); + message.replace(pos, tag.length(), lastSave.mProfile.mDescription); + MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); + mAskLoadRecent = true; + } + } +} + +MWState::StateManager::State MWState::StateManager::getState() const +{ + return mState; +} + +void MWState::StateManager::newGame (bool bypass) +{ + cleanup(); + + if (!bypass) + { + MWBase::Environment::get().getWorld()->startNewGame(); + MWBase::Environment::get().getWindowManager()->setNewGame (true); + } + else + MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup(); + + mState = State_Running; +} + +void MWState::StateManager::endGame() +{ + mState = State_Ended; + MWBase::Environment::get().getWorld()->useDeathCamera(); +} + +void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) +{ + ESM::SavedGame profile; + + MWBase::World& world = *MWBase::Environment::get().getWorld(); + + MWWorld::Ptr player = world.getPlayer().getPlayer(); + + profile.mContentFiles = world.getContentFiles(); + + profile.mPlayerName = player.getClass().getName (player); + profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); + profile.mPlayerClass = player.get()->mBase->mClass; + + profile.mPlayerCell = world.getCellName(); + + profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); + profile.mInGameTime.mDay = world.getDay(); + profile.mInGameTime.mMonth = world.getMonth(); + profile.mInGameTime.mYear = world.getYear(); + profile.mTimePlayed = mTimePlayed; + profile.mDescription = description; + + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + Ogre::Image screenshot; + world.screenshot(screenshot, screenshotW, screenshotH); + Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); + profile.mScreenshot.resize(encoded->size()); + encoded->read(&profile.mScreenshot[0], encoded->size()); + + if (!slot) + slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); + else + slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); + + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); + + ESM::ESMWriter writer; + + const std::vector& current = + MWBase::Environment::get().getWorld()->getContentFiles(); + + for (std::vector::const_iterator iter (current.begin()); iter!=current.end(); + ++iter) + writer.addMaster (*iter, 0); // not using the size information anyway -> use value of 0 + + writer.setFormat (ESM::Header::CurrentFormat); + writer.setRecordCount ( + 1 // saved game header + +MWBase::Environment::get().getJournal()->countSavedGameRecords() + +MWBase::Environment::get().getWorld()->countSavedGameRecords() + +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map + ); + + writer.save (stream); + + writer.startRecord (ESM::REC_SAVE); + slot->mProfile.save (writer); + writer.endRecord (ESM::REC_SAVE); + + MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); + MWBase::Environment::get().getWorld()->write (writer); + MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); + MWBase::Environment::get().getWindowManager()->write(writer); + + writer.close(); + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); +} + +void MWState::StateManager::loadGame (const Character *character, const Slot *slot) +{ + try + { + cleanup(); + + mTimePlayed = slot->mProfile.mTimePlayed; + + ESM::ESMReader reader; + reader.open (slot->mPath.string()); + + std::map contentFileMap = buildContentFileIndexMap (reader); + + while (reader.hasMoreRecs()) + { + ESM::NAME n = reader.getRecName(); + reader.getRecHeader(); + + switch (n.val) + { + case ESM::REC_SAVE: + + // don't need to read that here + reader.skipRecord(); + break; + + case ESM::REC_JOUR: + case ESM::REC_QUES: + + MWBase::Environment::get().getJournal()->readRecord (reader, n.val); + break; + + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + + case ESM::REC_ALCH: + case ESM::REC_ARMO: + case ESM::REC_BOOK: + case ESM::REC_CLAS: + case ESM::REC_CLOT: + case ESM::REC_ENCH: + case ESM::REC_NPC_: + case ESM::REC_SPEL: + case ESM::REC_WEAP: + case ESM::REC_GLOB: + case ESM::REC_PLAY: + case ESM::REC_CSTA: + + MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); + break; + + case ESM::REC_GSCR: + + MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord (reader, n.val); + break; + + case ESM::REC_GMAP: + + MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); + break; + + default: + + // ignore invalid records + /// \todo log error + reader.skipRecord(); + } + } + + mCharacterManager.setCurrentCharacter(character); + + mState = State_Running; + + Settings::Manager::setString ("character", "Saves", + slot->mPath.parent_path().filename().string()); + + MWBase::Environment::get().getWindowManager()->setNewGame(false); + MWBase::Environment::get().getWorld()->setupPlayer(); + MWBase::Environment::get().getWorld()->renderPlayer(); + MWBase::Environment::get().getWindowManager()->updatePlayer(); + MWBase::Environment::get().getMechanicsManager()->playerLoaded(); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + ESM::CellId cellId = ptr.getCell()->mCell->getCellId(); + + MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); + } + catch (const std::exception& e) + { + std::cerr << "failed to load saved game: " << e.what() << std::endl; + cleanup (true); + } +} + +MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) +{ + return mCharacterManager.getCurrentCharacter (create); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() +{ + return mCharacterManager.begin(); +} + +MWState::StateManager::CharacterIterator MWState::StateManager::characterEnd() +{ + return mCharacterManager.end(); +} + +void MWState::StateManager::update (float duration) +{ + mTimePlayed += duration; + + // Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update. + if (mAskLoadRecent) + { + int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); + if(iButton==0) + { + mAskLoadRecent = false; + //Load last saved game for current character + MWState::Character *curCharacter = getCurrentCharacter(); + MWState::Slot lastSave = *curCharacter->begin(); + loadGame(curCharacter, &lastSave); + } + else if(iButton==1) + { + mAskLoadRecent = false; + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + } + } +} diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index b7207486bc..91f123eb71 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -1,203 +1,203 @@ -#include "esmwriter.hpp" - -#include -#include -#include - -namespace ESM -{ - ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} - - unsigned int ESMWriter::getVersion() const - { - return mHeader.mData.version; - } - - void ESMWriter::setVersion(unsigned int ver) - { - mHeader.mData.version = ver; - } - - void ESMWriter::setAuthor(const std::string& auth) - { - mHeader.mData.author.assign (auth); - } - - void ESMWriter::setDescription(const std::string& desc) - { - mHeader.mData.desc.assign (desc); - } - - void ESMWriter::setRecordCount (int count) - { - mHeader.mData.records = count; - } - - void ESMWriter::setFormat (int format) - { - mHeader.mFormat = format; - } - - void ESMWriter::clearMaster() - { - mHeader.mMaster.clear(); - } - - void ESMWriter::addMaster(const std::string& name, uint64_t size) - { - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); - } - - void ESMWriter::save(std::ostream& file) - { - mRecordCount = 0; - mRecords.clear(); - mCounting = true; - mStream = &file; - - startRecord("TES3", 0); - - mHeader.save (*this); - - endRecord("TES3"); - } - - void ESMWriter::close() - { - if (!mRecords.empty()) - throw std::runtime_error ("Unclosed record remaining"); - } - - void ESMWriter::startRecord(const std::string& name, uint32_t flags) - { - mRecordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::startRecord (uint32_t name, uint32_t flags) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - startRecord (type, flags); - } - - void ESMWriter::startSubRecord(const std::string& name) - { - writeName(name); - RecordData rec; - rec.name = name; - rec.position = mStream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - mRecords.push_back(rec); - - assert(mRecords.back().size == 0); - } - - void ESMWriter::endRecord(const std::string& name) - { - RecordData rec = mRecords.back(); - assert(rec.name == name); - mRecords.pop_back(); - - mStream->seekp(rec.position); - - mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(uint32_t)); - mCounting = true; - - mStream->seekp(0, std::ios::end); - - } - - void ESMWriter::endRecord (uint32_t name) - { - std::string type; - for (int i=0; i<4; ++i) - /// \todo make endianess agnostic - type += reinterpret_cast (&name)[i]; - - endRecord (type); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHString(data); - endRecord(name); - } - - void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) - { - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); - - if (data.size() < size) - { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); - } - - endRecord(name); - } - - void ESMWriter::writeHString(const std::string& data) - { - if (data.size() == 0) - write("\0", 1); - else - { - // Convert to UTF8 and return - std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; - - write(string.c_str(), string.size()); - } - } - - void ESMWriter::writeHCString(const std::string& data) - { - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); - } - - void ESMWriter::writeName(const std::string& name) - { - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); - } - - void ESMWriter::write(const char* data, size_t size) - { - if (mCounting && !mRecords.empty()) - { - for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) - it->size += size; - } - - mStream->write(data, size); - } - - void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) - { - mEncoder = encoder; - } -} +#include "esmwriter.hpp" + +#include +#include +#include + +namespace ESM +{ + ESMWriter::ESMWriter() : mEncoder (0), mRecordCount (0), mCounting (true) {} + + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } + + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } + + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } + + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } + + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } + + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } + + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } + + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } + + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; + + startRecord("TES3", 0); + + mHeader.save (*this); + + endRecord("TES3"); + } + + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } + + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::startRecord (uint32_t name, uint32_t flags) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + startRecord (type, flags); + } + + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); + + mStream->seekp(rec.position); + + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); + mCounting = true; + + mStream->seekp(0, std::ios::end); + + } + + void ESMWriter::endRecord (uint32_t name) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic + type += reinterpret_cast (&name)[i]; + + endRecord (type); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + endRecord(name); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) + { + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); + } + + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data; + + write(string.c_str(), string.size()); + } + } + + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } + + void ESMWriter::writeName(const std::string& name) + { + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); + } + + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } + + mStream->write(data, size); + } + + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } +} diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 304876dbf6..33650e6784 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -1,114 +1,114 @@ -#ifndef OPENMW_ESM_WRITER_H -#define OPENMW_ESM_WRITER_H - -#include -#include - -#include - -#include "esmcommon.hpp" -#include "loadtes3.hpp" - -namespace ESM { - -class ESMWriter -{ - struct RecordData - { - std::string name; - std::streampos position; - uint32_t size; - }; - - public: - - ESMWriter(); - - unsigned int getVersion() const; - void setVersion(unsigned int ver = 0x3fa66666); - void setEncoder(ToUTF8::Utf8Encoder *encoding); - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void clearMaster(); - - void addMaster(const std::string& name, uint64_t size); - - void save(std::ostream& file); - ///< Start saving a file by writing the TES3 header. - - void close(); - ///< \note Does not close the stream. - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags = 0); - void startRecord(uint32_t name, uint32_t flags = 0); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void endRecord(uint32_t name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - - private: - std::list mRecords; - std::ostream* mStream; - std::streampos mHeaderPos; - ToUTF8::Utf8Encoder* mEncoder; - int mRecordCount; - bool mCounting; - - Header mHeader; - }; -} - -#endif +#ifndef OPENMW_ESM_WRITER_H +#define OPENMW_ESM_WRITER_H + +#include +#include + +#include + +#include "esmcommon.hpp" +#include "loadtes3.hpp" + +namespace ESM { + +class ESMWriter +{ + struct RecordData + { + std::string name; + std::streampos position; + uint32_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 0); + void startRecord(uint32_t name, uint32_t flags = 0); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void endRecord(uint32_t name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; + }; +} + +#endif diff --git a/credits.txt b/credits.txt index 561931cde9..6012557638 100644 --- a/credits.txt +++ b/credits.txt @@ -20,6 +20,7 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick +cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) diff --git a/readme.txt b/readme.txt index 4cc9c6234b..a23cd1077e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,951 +1,951 @@ -OpenMW: A reimplementation of The Elder Scrolls III: Morrowind - -OpenMW is an attempt at recreating the engine for the popular role-playing game -Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. - -Version: 0.28.0 -License: GPL (see GPL3.txt for more information) -Website: http://www.openmw.org - -Font Licenses: -EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) -DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) - - - -INSTALLATION - -Windows: -Run the installer. - -Linux: -Ubuntu (and most others) -Download the .deb file and install it in the usual way. - -Arch Linux -There's an OpenMW package available in the [community] Repository: -https://www.archlinux.org/packages/?sort=&q=openmw - -OS X: -Open DMG file, copy OpenMW folder anywhere, for example in /Applications - -BUILD FROM SOURCE - -https://wiki.openmw.org/index.php?title=Development_Environment_Setup - - -THE DATA PATH - -The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to -pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly -(installing Morrowind under WINE is considered a proper install). - -COMMAND LINE OPTIONS - -Syntax: openmw -Allowed options: - --help print help message - --version print version information and quit - --data arg (=data) set data directories (later directories - have higher priority) - --data-local arg set local data directory (highest - priority) - --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later - archives have higher priority) - --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or - omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files - --no-sound [=arg(=1)] (=0) disable all sounds - --script-verbose [=arg(=1)] (=0) verbose script output - --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue - scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script - functionality - --script-run arg select a file containing a list of - console commands that is executed on - startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling - scripts - 0 - ignore warning - 1 - show warning but consider script as - correctly compiled anyway - 2 - treat warnings as errors - --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --fs-strict [=arg(=1)] (=0) strict file system handling (no case - folding) - --encoding arg (=win1252) Character encoding used in OpenMW game - messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and - Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic - and other languages - - win1252 - Western European (Latin) - alphabet, used by default - --fallback arg fallback values - --no-grab Don't grab mouse cursor - --activate-dist arg (=-1) activation distance override - -CHANGELOG - -0.29.0 - -Bug #556: Video soundtrack not played when music volume is set to zero -Bug #829: OpenMW uses up all available vram, when playing for extended time -Bug #848: Wrong amount of footsteps playing in 1st person -Bug #888: Ascended Sleepers have movement issues -Bug #892: Explicit references are allowed on all script functions -Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly -Bug #1009: Lake Fjalding AI related slowdown. -Bug #1041: Music playback issues on OS X >= 10.9 -Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window -Bug #1060: Some message boxes are cut off at the bottom -Bug #1062: Bittercup script does not work ('end' variable) -Bug #1074: Inventory paperdoll obscures armour rating -Bug #1077: Message after killing an essential NPC disappears too fast -Bug #1078: "Clutterbane" shows empty charge bar -Bug #1083: UndoWerewolf fails -Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered -Bug #1090: Start scripts fail when going to a non-predefined cell -Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. -Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior -Bug #1105: Magicka is depleted when using uncastable spells -Bug #1106: Creatures should be able to run -Bug #1107: TR cliffs have way too huge collision boxes in OpenMW -Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. -Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) -Bug #1115: Memory leak when spying on Fargoth -Bug #1137: Script execution fails (drenSlaveOwners script) -Bug #1143: Mehra Milo quest (vivec informants) is broken -Bug #1145: Issues with moving gold between inventory and containers -Bug #1146: Issues with picking up stacks of gold -Bug #1147: Dwemer Crossbows are held incorrectly -Bug #1158: Armor rating should always stay below inventory mannequin -Bug #1159: Quick keys can be set during character generation -Bug #1160: Crash on equip lockpick when -Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file -Feature #30: Loading/Saving (still missing a few parts) -Feature #101: AI Package: Activate -Feature #103: AI Package: Follow, FollowCell -Feature #138: Editor: Drag & Drop -Feature #428: Player death -Feature #505: Editor: Record Cloning -Feature #701: Levelled creatures -Feature #708: Improved Local Variable handling -Feature #709: Editor: Script verifier -Feature #764: Missing journal backend features -Feature #777: Creature weapons/shields -Feature #789: Editor: Referenceable record verifier -Feature #924: Load/Save GUI (still missing loading screen and progress bars) -Feature #946: Knockdown -Feature #947: Decrease fatigue when running, swimming and attacking -Feature #956: Melee Combat: Blocking -Feature #957: Area magic -Feature #960: Combat/AI combat for creatures -Feature #962: Combat-Related AI instructions -Feature #1075: Damage/Restore skill/attribute magic effects -Feature #1076: Soultrap magic effect -Feature #1081: Disease contraction -Feature #1086: Blood particles -Feature #1092: Interrupt resting -Feature #1101: Inventory equip scripts -Feature #1116: Version/Build number in Launcher window -Feature #1119: Resistance/weakness to normal weapons magic effect -Feature #1123: Slow Fall magic effect -Feature #1130: Auto-calculate spells -Feature #1164: Editor: Case-insensitive sorting in tables - -0.28.0 - -Bug #399: Inventory changes are not visible immediately -Bug #417: Apply weather instantly when teleporting -Bug #566: Global Map position marker not updated for interior cells -Bug #712: Looting corpse delay -Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod -Bug #805: Two TR meshes appear black (v0.24RC) -Bug #841: Third-person activation distance taken from camera rather than head -Bug #845: NPCs hold torches during the day -Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 -Bug #856: Maormer race by Mac Kom - The heads are way up -Bug #864: Walk locks during loading in 3rd person -Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog -Bug #882: Hircine's Ring doesn't always work -Bug #909: [Tamriel Rebuilt] crashes in Akamora -Bug #922: Launcher writing merged openmw.cfg files -Bug #943: Random magnitude should be calculated per effect -Bug #948: Negative fatigue level should be allowed -Bug #949: Particles in world space -Bug #950: Hard crash on x64 Linux running --new-game (on startup) -Bug #951: setMagicka and setFatigue have no effect -Bug #954: Problem with equipping inventory items when using a keyboard shortcut -Bug #955: Issues with equipping torches -Bug #966: Shield is visible when casting spell -Bug #967: Game crashes when equipping silver candlestick -Bug #970: Segmentation fault when starting at Bal Isra -Bug #977: Pressing down key in console doesn't go forward in history -Bug #979: Tooltip disappears when changing inventory -Bug #980: Barter: item category is remembered, but not shown -Bug #981: Mod: replacing model has wrong position/orientation -Bug #982: Launcher: Addon unchecking is not saved -Bug #983: Fix controllers to affect objects attached to the base node -Bug #985: Player can talk to NPCs who are in combat -Bug #989: OpenMW crashes when trying to include mod with capital .ESP -Bug #991: Merchants equip items with harmful constant effect enchantments -Bug #994: Don't cap skills/attributes when set via console -Bug #998: Setting the max health should also set the current health -Bug #1005: Torches are visible when casting spells and during hand to hand combat. -Bug #1006: Many NPCs have 0 skill -Bug #1007: Console fills up with text -Bug #1013: Player randomly loses health or dies -Bug #1014: Persuasion window is not centered in maximized window -Bug #1015: Player status window scroll state resets on status change -Bug #1016: Notification window not big enough for all skill level ups -Bug #1020: Saved window positions are not rescaled appropriately on resolution change -Bug #1022: Messages stuck permanently on screen when they pile up -Bug #1023: Journals doesn't open -Bug #1026: Game loses track of torch usage. -Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level -Bug #1029: Quick keys menu: Select compatible replacement when tool used up -Bug #1042: TES3 header data wrong encoding -Bug #1045: OS X: deployed OpenCS won't launch -Bug #1046: All damaged weaponry is worth 1 gold -Bug #1048: Links in "locked" dialogue are still clickable -Bug #1052: Using color codes when naming your character actually changes the name's color -Bug #1054: Spell effects not visible in front of water -Bug #1055: Power-Spell animation starts even though you already casted it that day -Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability -Bug #1063: Crash upon checking out game start ship area in Seyda Neen -Bug #1064: openmw binaries link to unnecessary libraries -Bug #1065: Landing from a high place in water still causes fall damage -Bug #1072: Drawing weapon increases torch brightness -Bug #1073: Merchants sell stacks of gold -Feature #43: Visuals for Magic Effects -Feature #51: Ranged Magic -Feature #52: Touch Range Magic -Feature #53: Self Range Magic -Feature #54: Spell Casting -Feature #70: Vampirism -Feature #100: Combat AI -Feature #171: Implement NIF record NiFlipController -Feature #410: Window to restore enchanted item charge -Feature #647: Enchanted item glow -Feature #723: Invisibility/Chameleon magic effects -Feature #737: Resist Magicka magic effect -Feature #758: GetLOS -Feature #926: Editor: Info-Record tables -Feature #958: Material controllers -Feature #959: Terrain bump, specular, & parallax mapping -Feature #990: Request: unlock mouse when in any menu -Feature #1018: Do not allow view mode switching while performing an action -Feature #1027: Vertex morph animation (NiGeomMorpherController) -Feature #1031: Handle NiBillboardNode -Feature #1051: Implement NIF texture slot DarkTexture -Task #873: Unify OGRE initialisation - -0.27.0 - -Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp -Bug #794: incorrect display of decimal numbers -Bug #840: First-person sneaking camera height -Bug #887: Ambient sounds playing while paused -Bug #902: Problems with Polish character encoding -Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key -Bug #910: Some CDs not working correctly with Unshield installer -Bug #917: Quick character creation plugin does not work -Bug #918: Fatigue does not refill -Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) -Feature #57: Acrobatics Skill -Feature #462: Editor: Start Dialogue -Feature #546: Modify ESX selector to handle new content file scheme -Feature #588: Editor: Adjust name/path of edited content files -Feature #644: Editor: Save -Feature #710: Editor: Configure script compiler context -Feature #790: God Mode -Feature #881: Editor: Allow only one instance of OpenCS -Feature #889: Editor: Record filtering -Feature #895: Extinguish torches -Feature #898: Breath meter enhancements -Feature #901: Editor: Default record filter -Feature #913: Merge --master and --plugin switches - -0.26.0 - -Bug #274: Inconsistencies in the terrain -Bug #557: Already-dead NPCs do not equip clothing/items. -Bug #592: Window resizing -Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) -Bug #664: Heart of lorkhan acts like a dead body (container) -Bug #767: Wonky ramp physics & water -Bug #780: Swimming out of water -Bug #792: Wrong ground alignment on actors when no clipping -Bug #796: Opening and closing door sound issue -Bug #797: No clipping hinders opening and closing of doors -Bug #799: sliders in enchanting window -Bug #838: Pressing key during startup procedure freezes the game -Bug #839: Combat/magic stances during character creation -Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment -Bug #844: Resting "until healed" option given even with full stats -Bug #846: Equipped torches are invisible. -Bug #847: Incorrect formula for autocalculated NPC initial health -Bug #850: Shealt weapon sound plays when leaving magic-ready stance -Bug #852: Some boots do not produce footstep sounds -Bug #860: FPS bar misalignment -Bug #861: Unable to print screen -Bug #863: No sneaking and jumping at the same time -Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. -Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. -Bug #868: Idle animations are repeated -Bug #874: Underwater swimming close to the ground is jerky -Bug #875: Animation problem while swimming on the surface and looking up -Bug #876: Always a starting upper case letter in the inventory -Bug #878: Active spell effects don't update the layout properly when ended -Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load -Bug #896: New game sound issue -Feature #49: Melee Combat -Feature #71: Lycanthropy -Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList -Feature #622: Multiple positions for inventory window -Feature #627: Drowning -Feature #786: Allow the 'Activate' key to close the countdialog window -Feature #798: Morrowind installation via Launcher (Linux/Max OS only) -Feature #851: First/Third person transitions with mouse wheel -Task #689: change PhysicActor::enableCollisions -Task #707: Reorganise Compiler - -0.25.0 - -Bug #411: Launcher crash on OS X < 10.8 -Bug #604: Terrible performance drop in the Census and Excise Office. -Bug #676: Start Scripts fail to load -Bug #677: OpenMW does not accept script names with - -Bug #766: Extra space in front of topic links -Bug #793: AIWander Isn't Being Passed The Repeat Parameter -Bug #795: Sound playing with drawn weapon and crossing cell-border -Bug #800: can't select weapon for enchantment -Bug #801: Player can move while over-encumbered -Bug #802: Dead Keys not working -Bug #808: mouse capture -Bug #809: ini Importer does not work without an existing cfg file -Bug #812: Launcher will run OpenMW with no ESM or ESP selected -Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected -Bug #817: Dead NPCs and Creatures still have collision boxes -Bug #820: Incorrect sorting of answers (Dialogue) -Bug #826: mwinimport dumps core when given an unknown parameter -Bug #833: getting stuck in door -Bug #835: Journals/books not showing up properly. -Feature #38: SoundGen -Feature #105: AI Package: Wander -Feature #230: 64-bit compatibility for OS X -Feature #263: Hardware mouse cursors -Feature #449: Allow mouse outside of window while paused -Feature #736: First person animations -Feature #750: Using mouse wheel in third person mode -Feature #822: Autorepeat for slider buttons - -0.24.0 - -Bug #284: Book's text misalignment -Bug #445: Camera able to get slightly below floor / terrain -Bug #582: Seam issue in Red Mountain -Bug #632: Journal Next Button shows white square -Bug #653: IndexedStore ignores index -Bug #694: Parser does not recognize float values starting with . -Bug #699: Resource handling broken with Ogre 1.9 trunk -Bug #718: components/esm/loadcell is using the mwworld subsystem -Bug #729: Levelled item list tries to add nonexistent item -Bug #730: Arrow buttons in the settings menu do not work. -Bug #732: Erroneous behavior when binding keys -Bug #733: Unclickable dialogue topic -Bug #734: Book empty line problem -Bug #738: OnDeath only works with implicit references -Bug #740: Script compiler fails on scripts with special names -Bug #742: Wait while no clipping -Bug #743: Problem with changeweather console command -Bug #744: No wait dialogue after starting a new game -Bug #748: Player is not able to unselect objects with the console -Bug #751: AddItem should only spawn a message box when called from dialogue -Bug #752: The enter button has several functions in trade and looting that is not impelemted. -Bug #753: Fargoth's Ring Quest Strange Behavior -Bug #755: Launcher writes duplicate lines into settings.cfg -Bug #759: Second quest in mages guild does not work -Bug #763: Enchantment cast cost is wrong -Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly -Bug #773: AIWander Isn't Being Passed The Correct idle Values -Bug #778: The journal can be opened at the start of a new game -Bug #779: Divayth Fyr starts as dead -Bug #787: "Batch count" on detailed FPS counter gets cut-off -Bug #788: chargen scroll layout does not match vanilla -Feature #60: Atlethics Skill -Feature #65: Security Skill -Feature #74: Interaction with non-load-doors -Feature #98: Render Weapon and Shield -Feature #102: AI Package: Escort, EscortCell -Feature #182: Advanced Journal GUI -Feature #288: Trading enhancements -Feature #405: Integrate "new game" into the menu -Feature #537: Highlight dialogue topic links -Feature #658: Rotate, RotateWorld script instructions and local rotations -Feature #690: Animation Layering -Feature #722: Night Eye/Blind magic effects -Feature #735: Move, MoveWorld script instructions. -Feature #760: Non-removable corpses - -0.23.0 - -Bug #522: Player collides with placeable items -Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open -Bug #561: Tooltip word wrapping delay -Bug #578: Bribing works incorrectly -Bug #601: PositionCell fails on negative coordinates -Bug #606: Some NPCs hairs not rendered with Better Heads addon -Bug #609: Bad rendering of bone boots -Bug #613: Messagebox causing assert to fail -Bug #631: Segfault on shutdown -Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard -Bug #635: Scale NPCs depending on race -Bug #643: Dialogue Race select function is inverted -Bug #646: Twohanded weapons don't work properly -Bug #654: Crash when dropping objects without a collision shape -Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell -Bug #660: "g" in "change" cut off in Race Menu -Bug #661: Arrille sells me the key to his upstairs room -Bug #662: Day counter starts at 2 instead of 1 -Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur -Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. -Bug #666: Looking up/down problem -Bug #667: Active effects border visible during loading -Bug #669: incorrect player position at new game start -Bug #670: race selection menu: sex, face and hair left button not totally clickable -Bug #671: new game: player is naked -Bug #674: buying or selling items doesn't change amount of gold -Bug #675: fatigue is not set to its maximum when starting a new game -Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly -Bug #680: different gold coins in Tel Mara -Bug #682: Race menu ignores playable flag for some hairs and faces -Bug #685: Script compiler does not accept ":" after a function name -Bug #688: dispose corpse makes cross-hair to disappear -Bug #691: Auto equipping ignores equipment conditions -Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder -Bug #696: Draugr incorrect head offset -Bug #697: Sail transparency issue -Bug #700: "On the rocks" mod does not load its UV coordinates correctly. -Bug #702: Some race mods don't work -Bug #711: Crash during character creation -Bug #715: Growing Tauryon -Bug #725: Auto calculate stats -Bug #728: Failure to open container and talk dialogue -Bug #731: Crash with Mush-Mere's "background" topic -Feature #55/657: Item Repairing -Feature #62/87: Enchanting -Feature #99: Pathfinding -Feature #104: AI Package: Travel -Feature #129: Levelled items -Feature #204: Texture animations -Feature #239: Fallback-Settings -Feature #535: Console object selection improvements -Feature #629: Add levelup description in levelup layout dialog -Feature #630: Optional format subrecord in (tes3) header -Feature #641: Armor rating -Feature #645: OnDeath script function -Feature #683: Companion item UI -Feature #698: Basic Particles -Task #648: Split up components/esm/loadlocks -Task #695: mwgui cleanup - -0.22.0 - -Bug #311: Potential infinite recursion in script compiler -Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. -Bug #382: Weird effect in 3rd person on water -Bug #387: Always use detailed shape for physics raycasts -Bug #420: Potion/ingredient effects do not stack -Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips -Bug #434/Bug #605: Object movement between cells not properly implemented -Bug #502: Duplicate player collision model at origin -Bug #509: Dialogue topic list shifts inappropriately -Bug #513: Sliding stairs -Bug #515: Launcher does not support non-latin strings -Bug #525: Race selection preview camera wrong position -Bug #526: Attributes / skills should not go below zero -Bug #529: Class and Birthsign menus options should be preselected -Bug #530: Lock window button graphic missing -Bug #532: Missing map menu graphics -Bug #545: ESX selector does not list ESM files properly -Bug #547: Global variables of type short are read incorrectly -Bug #550: Invisible meshes collision and tooltip -Bug #551: Performance drop when loading multiple ESM files -Bug #552: Don't list CG in options if it is not available -Bug #555: Character creation windows "OK" button broken -Bug #558: Segmentation fault when Alt-tabbing with console opened -Bug #559: Dialog window should not be available before character creation is finished -Bug #560: Tooltip borders should be stretched -Bug #562: Sound should not be played when an object cannot be picked up -Bug #565: Water animation speed + timescale -Bug #572: Better Bodies' textures don't work -Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) -Bug #574: Moving left/right should not cancel auto-run -Bug #575: Crash entering the Chamber of Song -Bug #576: Missing includes -Bug #577: Left Gloves Addon causes ESMReader exception -Bug #579: Unable to open container "Kvama Egg Sack" -Bug #581: Mimicking vanilla Morrowind water -Bug #583: Gender not recognized -Bug #586: Wrong char gen behaviour -Bug #587: "End" script statements with spaces don't work -Bug #589: Closing message boxes by pressing the activation key -Bug #590: Ugly Dagoth Ur rendering -Bug #591: Race selection issues -Bug #593: Persuasion response should be random -Bug #595: Footless guard -Bug #599: Waterfalls are invisible from a certain distance -Bug #600: Waterfalls rendered incorrectly, cut off by water -Bug #607: New beast bodies mod crashes -Bug #608: Crash in cell "Mournhold, Royal Palace" -Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt -Bug #613: Messagebox causing assert to fail -Bug #615: Meshes invisible from above water -Bug #617: Potion effects should be hidden until discovered -Bug #619: certain moss hanging from tree has rendering bug -Bug #621: Batching bloodmoon's trees -Bug #623: NiMaterialProperty alpha unhandled -Bug #628: Launcher in latest master crashes the game -Bug #633: Crash on startup: Better Heads -Bug #636: Incorrect Char Gen Menu Behavior -Feature #29: Allow ESPs and multiple ESMs -Feature #94: Finish class selection-dialogue -Feature #149: Texture Alphas -Feature #237: Run Morrowind-ini importer from launcher -Feature #286: Update Active Spell Icons -Feature #334: Swimming animation -Feature #335: Walking animation -Feature #360: Proper collision shapes for NPCs and creatures -Feature #367: Lights that behave more like original morrowind implementation -Feature #477: Special local scripting variables -Feature #528: Message boxes should close when enter is pressed under certain conditions. -Feature #543: Add bsa files to the settings imported by the ini importer -Feature #594: coordinate space and utility functions -Feature #625: Zoom in vanity mode -Task #464: Refactor launcher ESX selector into a re-usable component -Task #624: Unified implementation of type-variable sub-records - -0.21.0 - -Bug #253: Dialogs don't work for Russian version of Morrowind -Bug #267: Activating creatures without dialogue can still activate the dialogue GUI -Bug #354: True flickering lights -Bug #386: The main menu's first entry is wrong (in french) -Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations -Bug #495: Activation Range -Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned -Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available -Bug #500: Disposition for most NPCs is 0/100 -Bug #501: Getdisposition command wrongly returns base disposition -Bug #506: Journal UI doesn't update anymore -Bug #507: EnableRestMenu is not a valid command - change it to EnableRest -Bug #508: Crash in Ald Daedroth Shrine -Bug #517: Wrong price calculation when untrading an item -Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin -Bug #524: Beast races are able to wear shoes -Bug #527: Background music fails to play -Bug #533: The arch at Gnisis entrance is not displayed -Bug #534: Terrain gets its correct shape only some time after the cell is loaded -Bug #536: The same entry can be added multiple times to the journal -Bug #539: Race selection is broken -Bug #544: Terrain normal map corrupt when the map is rendered -Feature #39: Video Playback -Feature #151: ^-escape sequences in text output -Feature #392: Add AI related script functions -Feature #456: Determine required ini fallback values and adjust the ini importer accordingly -Feature #460: Experimental DirArchives improvements -Feature #540: Execute scripts of objects in containers/inventories in active cells -Task #401: Review GMST fixing -Task #453: Unify case smashing/folding -Task #512: Rewrite utf8 component - -0.20.0 - -Bug #366: Changing the player's race during character creation does not change the look of the player character -Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell -Bug #437: Stop animations when paused -Bug #438: Time displays as "0 a.m." when it should be "12 a.m." -Bug #439: Text in "name" field of potion/spell creation window is persistent -Bug #440: Starting date at a new game is off by one day -Bug #442: Console window doesn't close properly sometimes -Bug #448: Do not break container window formatting when item names are very long -Bug #458: Topics sometimes not automatically added to known topic list -Bug #476: Auto-Moving allows player movement after using DisablePlayerControls -Bug #478: After sleeping in a bed the rest dialogue window opens automtically again -Bug #492: On creating potions the ingredients are removed twice -Feature #63: Mercantile skill -Feature #82: Persuasion Dialogue -Feature #219: Missing dialogue filters/functions -Feature #369: Add a FailedAction -Feature #377: Select head/hair on character creation -Feature #391: Dummy AI package classes -Feature #435: Global Map, 2nd Layer -Feature #450: Persuasion -Feature #457: Add more script instructions -Feature #474: update the global variable pcrace when the player's race is changed -Task #158: Move dynamically generated classes from Player class to World Class -Task #159: ESMStore rework and cleanup -Task #163: More Component Namespace Cleanup -Task #402: Move player data from MWWorld::Player to the player's NPC record -Task #446: Fix no namespace in BulletShapeLoader - -0.19.0 - -Bug #374: Character shakes in 3rd person mode near the origin -Bug #404: Gamma correct rendering -Bug #407: Shoes of St. Rilm do not work -Bug #408: Rugs has collision even if they are not supposed to -Bug #412: Birthsign menu sorted incorrectly -Bug #413: Resolutions presented multiple times in launcher -Bug #414: launcher.cfg file stored in wrong directory -Bug #415: Wrong esm order in openmw.cfg -Bug #418: Sound listener position updates incorrectly -Bug #423: wrong usage of "Version" entry in openmw.desktop -Bug #426: Do not use hardcoded splash images -Bug #431: Don't use markers for raycast -Bug #432: Crash after picking up items from an NPC -Feature #21/#95: Sleeping/resting -Feature #61: Alchemy Skill -Feature #68: Death -Feature #69/#86: Spell Creation -Feature #72/#84: Travel -Feature #76: Global Map, 1st Layer -Feature #120: Trainer Window -Feature #152: Skill Increase from Skill Books -Feature #160: Record Saving -Task #400: Review GMST access - -0.18.0 - -Bug #310: Button of the "preferences menu" are too small -Bug #361: Hand-to-hand skill is always 100 -Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to -Bug #372: playSound3D uses original coordinates instead of current coordinates. -Bug #373: Static OGRE build faulty -Bug #375: Alt-tab toggle view -Bug #376: Screenshots are disable -Bug #378: Exception when drinking self-made potions -Bug #380: Cloth visibility problem -Bug #384: Weird character on doors tooltip. -Bug #398: Some objects do not collide in MW, but do so in OpenMW -Feature #22: Implement level-up -Feature #36: Hide Marker -Feature #88: Hotkey Window -Feature #91: Level-Up Dialogue -Feature #118: Keyboard and Mouse-Button bindings -Feature #119: Spell Buying Window -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local -Feature #292: Object Movement/Creation Script Instructions -Feature #340: AIPackage data structures -Feature #356: Ingredients use -Feature #358: Input system rewrite -Feature #370: Target handling in actions -Feature #379: Door markers on the local map -Feature #389: AI framework -Feature #395: Using keys to open doors / containers -Feature #396: Loading screens -Feature #397: Inventory avatar image and race selection head preview -Task #339: Move sounds into Action - -0.17.0 - -Bug #225: Valgrind reports about 40MB of leaked memory -Bug #241: Some physics meshes still don't match -Bug #248: Some textures are too dark -Bug #300: Dependency on proprietary CG toolkit -Bug #302: Some objects don't collide although they should -Bug #308: Freeze in Balmora, Meldor: Armorer -Bug #313: openmw without a ~/.config/openmw folder segfault. -Bug #317: adding non-existing spell via console locks game -Bug #318: Wrong character normals -Bug #341: Building with Ogre Debug libraries does not use debug version of plugins -Bug #347: Crash when running openmw with --start="XYZ" -Bug #353: FindMyGUI.cmake breaks path on Windows -Bug #359: WindowManager throws exception at destruction -Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation -Feature #33: Allow objects to cross cell-borders -Feature #59: Dropping Items (replaced stopgap implementation with a proper one) -Feature #93: Main Menu -Feature #96/329/330/331/332/333: Player Control -Feature #180: Object rotation and scaling. -Feature #272: Incorrect NIF material sharing -Feature #314: Potion usage -Feature #324: Skill Gain -Feature #342: Drain/fortify dynamic stats/attributes magic effects -Feature #350: Allow console only script instructions -Feature #352: Run scripts in console on startup -Task #107: Refactor mw*-subsystems -Task #325: Make CreatureStats into a class -Task #345: Use Ogre's animation system -Task #351: Rewrite Action class to support automatic sound playing - -0.16.0 - -Bug #250: OpenMW launcher erratic behaviour -Bug #270: Crash because of underwater effect on OS X -Bug #277: Auto-equipping in some cells not working -Bug #294: Container GUI ignores disabled inventory menu -Bug #297: Stats review dialog shows all skills and attribute values as 0 -Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses -Bug #299: Crash in World::disable -Bug #306: Non-existent ~/.config/openmw "crash" the launcher. -Bug #307: False "Data Files" location make the launcher "crash" -Feature #81: Spell Window -Feature #85: Alchemy Window -Feature #181: Support for x.y script syntax -Feature #242: Weapon and Spell icons -Feature #254: Ingame settings window -Feature #293: Allow "stacking" game modes -Feature #295: Class creation dialog tooltips -Feature #296: Clicking on the HUD elements should show/hide the respective window -Feature #301: Direction after using a Teleport Door -Feature #303: Allow object selection in the console -Feature #305: Allow the use of = as a synonym for == -Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts -Task #176: Restructure enabling/disabling of MW-references -Task #283: Integrate ogre.cfg file in settings file -Task #290: Auto-Close MW-reference related GUI windows - -0.15.0 - -Bug #5: Physics reimplementation (fixes various issues) -Bug #258: Resizing arrow's background is not transparent -Bug #268: Widening the stats window in X direction causes layout problems -Bug #269: Topic pane in dialgoue window is too small for some longer topics -Bug #271: Dialog choices are sorted incorrectly -Bug #281: The single quote character is not rendered on dialog windows -Bug #285: Terrain not handled properly in cells that are not predefined -Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs -Feature #15: Collision with Terrain -Feature #17: Inventory-, Container- and Trade-Windows -Feature #44: Floating Labels above Focussed Objects -Feature #80: Tooltips -Feature #83: Barter Dialogue -Feature #90: Book and Scroll Windows -Feature #156: Item Stacking in Containers -Feature #213: Pulsating lights -Feature #218: Feather & Burden -Feature #256: Implement magic effect bookkeeping -Feature #259: Add missing information to Stats window -Feature #260: Correct case for dialogue topics -Feature #280: GUI texture atlasing -Feature #291: Ability to use GMST strings from GUI layout files -Task #255: Make MWWorld::Environment into a singleton - -0.14.0 - -Bug #1: Meshes rendered with wrong orientation -Bug #6/Task #220: Picking up small objects doesn't always work -Bug #127: tcg doesn't work -Bug #178: Compablity problems with Ogre 1.8.0 RC 1 -Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI -Bug #227: Terrain crashes when moving away from predefined cells -Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces -Bug #235: TGA texture loading problem -Bug #246: wireframe mode does not work in water -Feature #8/#232: Water Rendering -Feature #13: Terrain Rendering -Feature #37: Render Path Grid -Feature #66: Factions -Feature #77: Local Map -Feature #78: Compass/Mini-Map -Feature #97: Render Clothing/Armour -Feature #121: Window Pinning -Feature #205: Auto equip -Feature #217: Contiainer should track changes to its content -Feature #221: NPC Dialogue Window Enhancements -Feature #233: Game settings manager -Feature #240: Spell List and selected spell (no GUI yet) -Feature #243: Draw State -Task #113: Morrowind.ini Importer -Task #215: Refactor the sound code -Task #216: Update MyGUI - -0.13.0 - -Bug #145: Fixed sound problems after cell change -Bug #179: Pressing space in console triggers activation -Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux -Bug #189: ASCII 16 character added to console on it's activation on Mac OS X -Bug #190: Case Folding fails with music files -Bug #192: Keypresses write Text into Console no matter which gui element is active -Bug #196: Collision shapes out of place -Bug #202: ESMTool doesn't not work with localised ESM files anymore -Bug #203: Torch lights only visible on short distance -Bug #207: Ogre.log not written -Bug #209: Sounds do not play -Bug #210: Ogre crash at Dren plantation -Bug #214: Unsupported file format version -Bug #222: Launcher is writing openmw.cfg file to wrong location -Feature #9: NPC Dialogue Window -Feature #16/42: New sky/weather implementation -Feature #40: Fading -Feature #48: NPC Dialogue System -Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) -Feature #161: Load REC_PGRD records -Feature #195: Wireframe-mode -Feature #198/199: Various sound effects -Feature #206: Allow picking data path from launcher if non is set -Task #108: Refactor window manager class -Task #172: Sound Manager Cleanup -Task #173: Create OpenEngine systems in the appropriate manager classes -Task #184: Adjust MSVC and gcc warning levels -Task #185: RefData rewrite -Task #201: Workaround for transparency issues -Task #208: silenced esm_reader.hpp warning - -0.12.0 - -Bug #154: FPS Drop -Bug #169: Local scripts continue running if associated object is deleted -Bug #174: OpenMW fails to start if the config directory doesn't exist -Bug #187: Missing lighting -Bug #188: Lights without a mesh are not rendered -Bug #191: Taking screenshot causes crash when running installed -Feature #28: Sort out the cell load problem -Feature #31: Allow the player to move away from pre-defined cells -Feature #35: Use alternate storage location for modified object position -Feature #45: NPC animations -Feature #46: Creature Animation -Feature #89: Basic Journal Window -Feature #110: Automatically pick up the path of existing MW-installations -Feature #183: More FPS display settings -Task #19: Refactor engine class -Task #109/Feature #162: Automate Packaging -Task #112: Catch exceptions thrown in input handling functions -Task #128/#168: Cleanup Configuration File Handling -Task #131: NPC Activation doesn't work properly -Task #144: MWRender cleanup -Task #155: cmake cleanup - -0.11.1 - -Bug #2: Resources loading doesn't work outside of bsa files -Bug #3: GUI does not render non-English characters -Bug #7: openmw.cfg location doesn't match -Bug #124: The TCL alias for ToggleCollision is missing. -Bug #125: Some command line options can't be used from a .cfg file -Bug #126: Toggle-type script instructions are less verbose compared with original MW -Bug #130: NPC-Record Loading fails for some NPCs -Bug #167: Launcher sets invalid parameters in ogre config -Feature #10: Journal -Feature #12: Rendering Optimisations -Feature #23: Change Launcher GUI to a tabbed interface -Feature #24: Integrate the OGRE settings window into the launcher -Feature #25: Determine openmw.cfg location (Launcher) -Feature #26: Launcher Profiles -Feature #79: MessageBox -Feature #116: Tab-Completion in Console -Feature #132: --data-local and multiple --data -Feature #143: Non-Rendering Performance-Optimisations -Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs -Feature #157: Version Handling -Task #14: Replace tabs with 4 spaces -Task #18: Move components from global namespace into their own namespace -Task #123: refactor header files in components/esm - -0.10.0 - -* NPC dialogue window (not functional yet) -* Collisions with objects -* Refactor the PlayerPos class -* Adjust file locations -* CMake files and test linking for Bullet -* Replace Ogre raycasting test for activation with something more precise -* Adjust player movement according to collision results -* FPS display -* Various Portability Improvements -* Mac OS X support is back! - -0.9.0 - -* Exterior cells loading, unloading and management -* Character Creation GUI -* Character creation -* Make cell names case insensitive when doing internal lookups -* Music player -* NPCs rendering - -0.8.0 - -* GUI -* Complete and working script engine -* In game console -* Sky rendering -* Sound and music -* Tons of smaller stuff - -0.7.0 - -* This release is a complete rewrite in C++. -* All D code has been culled, and all modules have been rewritten. -* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. - -0.6.0 - -* Coded a GUI system using MyGUI -* Skinned MyGUI to look like Morrowind (work in progress) -* Integrated the Monster script engine -* Rewrote some functions into script code -* Very early MyGUI < > Monster binding -* Fixed Windows sound problems (replaced old openal32.dll) - -0.5.0 - -* Collision detection with Bullet -* Experimental walk & fall character physics -* New key bindings: - * t toggle physics mode (walking, flying, ghost), - * n night eye, brightens the scene -* Fixed incompatability with DMD 1.032 and newer compilers -* * (thanks to tomqyp) -* Various minor changes and updates - -0.4.0 - -* Switched from Audiere to OpenAL -* * (BIG thanks to Chris Robinson) -* Added complete Makefile (again) as a alternative build tool -* More realistic lighting (thanks again to Chris Robinson) -* Various localization fixes tested with Russian and French versions -* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' -* Added ns option to disable sound, for debugging -* Various bug fixes -* Cosmetic changes to placate gdc Wall - -0.3.0 - -* Built and tested on Windows XP -* Partial support for FreeBSD (exceptions do not work) -* You no longer have to download Monster separately -* Made an alternative for building without DSSS (but DSSS still works) -* Renamed main program from 'morro' to 'openmw' -* Made the config system more robust -* Added oc switch for showing Ogre config window on startup -* Removed some config files, these are auto generated when missing. -* Separated plugins.cfg into linux and windows versions. -* Updated Makefile and sources for increased portability -* confirmed to work against OIS 1.0.0 (Ubuntu repository package) - -0.2.0 - -* Compiles with gdc -* Switched to DSSS for building D code -* Includes the program esmtool - -0.1.0 - -first release +OpenMW: A reimplementation of The Elder Scrolls III: Morrowind + +OpenMW is an attempt at recreating the engine for the popular role-playing game +Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +Version: 0.28.0 +License: GPL (see GPL3.txt for more information) +Website: http://www.openmw.org + +Font Licenses: +EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) +DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) + + + +INSTALLATION + +Windows: +Run the installer. + +Linux: +Ubuntu (and most others) +Download the .deb file and install it in the usual way. + +Arch Linux +There's an OpenMW package available in the [community] Repository: +https://www.archlinux.org/packages/?sort=&q=openmw + +OS X: +Open DMG file, copy OpenMW folder anywhere, for example in /Applications + +BUILD FROM SOURCE + +https://wiki.openmw.org/index.php?title=Development_Environment_Setup + + +THE DATA PATH + +The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to +pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly +(installing Morrowind under WINE is considered a proper install). + +COMMAND LINE OPTIONS + +Syntax: openmw +Allowed options: + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories + have higher priority) + --data-local arg set local data directory (highest + priority) + --fallback-archive arg (=fallback-archive) + set fallback BSA archives (later + archives have higher priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --content arg content file(s): esm/esp, or + omwgame/omwaddon + --anim-verbose [=arg(=1)] (=0) output animation indices files + --no-sound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue + scripts) at startup + --script-console [=arg(=1)] (=0) enable console-only script + functionality + --script-run arg select a file containing a list of + console commands that is executed on + startup + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors + --skip-menu [=arg(=1)] (=0) skip main menu on game startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case + folding) + --encoding arg (=win1252) Character encoding used in OpenMW game + messages: + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and + Albanian languages + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic + and other languages + + win1252 - Western European (Latin) + alphabet, used by default + --fallback arg fallback values + --no-grab Don't grab mouse cursor + --activate-dist arg (=-1) activation distance override + +CHANGELOG + +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + +0.26.0 + +Bug #274: Inconsistencies in the terrain +Bug #557: Already-dead NPCs do not equip clothing/items. +Bug #592: Window resizing +Bug #612: [Tamriel Rebuilt] Missing terrain (South of Tel Oren) +Bug #664: Heart of lorkhan acts like a dead body (container) +Bug #767: Wonky ramp physics & water +Bug #780: Swimming out of water +Bug #792: Wrong ground alignment on actors when no clipping +Bug #796: Opening and closing door sound issue +Bug #797: No clipping hinders opening and closing of doors +Bug #799: sliders in enchanting window +Bug #838: Pressing key during startup procedure freezes the game +Bug #839: Combat/magic stances during character creation +Bug #843: [Tribunal] Dark Brotherhood assassin appears without equipment +Bug #844: Resting "until healed" option given even with full stats +Bug #846: Equipped torches are invisible. +Bug #847: Incorrect formula for autocalculated NPC initial health +Bug #850: Shealt weapon sound plays when leaving magic-ready stance +Bug #852: Some boots do not produce footstep sounds +Bug #860: FPS bar misalignment +Bug #861: Unable to print screen +Bug #863: No sneaking and jumping at the same time +Bug #866: Empty variables in [Movies] section of Morrowind.ini gets imported into OpenMW.cfg as blank fallback option and crashes game on start. +Bug #867: Dancing girls in "Suran, Desele's House of Earthly Delights" don't dance. +Bug #868: Idle animations are repeated +Bug #874: Underwater swimming close to the ground is jerky +Bug #875: Animation problem while swimming on the surface and looking up +Bug #876: Always a starting upper case letter in the inventory +Bug #878: Active spell effects don't update the layout properly when ended +Bug #891: Cell 24,-12 (Tamriel Rebuilt) crashes on load +Bug #896: New game sound issue +Feature #49: Melee Combat +Feature #71: Lycanthropy +Feature #393: Initialise MWMechanics::AiSequence from ESM::AIPackageList +Feature #622: Multiple positions for inventory window +Feature #627: Drowning +Feature #786: Allow the 'Activate' key to close the countdialog window +Feature #798: Morrowind installation via Launcher (Linux/Max OS only) +Feature #851: First/Third person transitions with mouse wheel +Task #689: change PhysicActor::enableCollisions +Task #707: Reorganise Compiler + +0.25.0 + +Bug #411: Launcher crash on OS X < 10.8 +Bug #604: Terrible performance drop in the Census and Excise Office. +Bug #676: Start Scripts fail to load +Bug #677: OpenMW does not accept script names with - +Bug #766: Extra space in front of topic links +Bug #793: AIWander Isn't Being Passed The Repeat Parameter +Bug #795: Sound playing with drawn weapon and crossing cell-border +Bug #800: can't select weapon for enchantment +Bug #801: Player can move while over-encumbered +Bug #802: Dead Keys not working +Bug #808: mouse capture +Bug #809: ini Importer does not work without an existing cfg file +Bug #812: Launcher will run OpenMW with no ESM or ESP selected +Bug #813: OpenMW defaults to Morrowind.ESM with no ESM or ESP selected +Bug #817: Dead NPCs and Creatures still have collision boxes +Bug #820: Incorrect sorting of answers (Dialogue) +Bug #826: mwinimport dumps core when given an unknown parameter +Bug #833: getting stuck in door +Bug #835: Journals/books not showing up properly. +Feature #38: SoundGen +Feature #105: AI Package: Wander +Feature #230: 64-bit compatibility for OS X +Feature #263: Hardware mouse cursors +Feature #449: Allow mouse outside of window while paused +Feature #736: First person animations +Feature #750: Using mouse wheel in third person mode +Feature #822: Autorepeat for slider buttons + +0.24.0 + +Bug #284: Book's text misalignment +Bug #445: Camera able to get slightly below floor / terrain +Bug #582: Seam issue in Red Mountain +Bug #632: Journal Next Button shows white square +Bug #653: IndexedStore ignores index +Bug #694: Parser does not recognize float values starting with . +Bug #699: Resource handling broken with Ogre 1.9 trunk +Bug #718: components/esm/loadcell is using the mwworld subsystem +Bug #729: Levelled item list tries to add nonexistent item +Bug #730: Arrow buttons in the settings menu do not work. +Bug #732: Erroneous behavior when binding keys +Bug #733: Unclickable dialogue topic +Bug #734: Book empty line problem +Bug #738: OnDeath only works with implicit references +Bug #740: Script compiler fails on scripts with special names +Bug #742: Wait while no clipping +Bug #743: Problem with changeweather console command +Bug #744: No wait dialogue after starting a new game +Bug #748: Player is not able to unselect objects with the console +Bug #751: AddItem should only spawn a message box when called from dialogue +Bug #752: The enter button has several functions in trade and looting that is not impelemted. +Bug #753: Fargoth's Ring Quest Strange Behavior +Bug #755: Launcher writes duplicate lines into settings.cfg +Bug #759: Second quest in mages guild does not work +Bug #763: Enchantment cast cost is wrong +Bug #770: The "Take" and "Close" buttons in the scroll GUI are stretched incorrectly +Bug #773: AIWander Isn't Being Passed The Correct idle Values +Bug #778: The journal can be opened at the start of a new game +Bug #779: Divayth Fyr starts as dead +Bug #787: "Batch count" on detailed FPS counter gets cut-off +Bug #788: chargen scroll layout does not match vanilla +Feature #60: Atlethics Skill +Feature #65: Security Skill +Feature #74: Interaction with non-load-doors +Feature #98: Render Weapon and Shield +Feature #102: AI Package: Escort, EscortCell +Feature #182: Advanced Journal GUI +Feature #288: Trading enhancements +Feature #405: Integrate "new game" into the menu +Feature #537: Highlight dialogue topic links +Feature #658: Rotate, RotateWorld script instructions and local rotations +Feature #690: Animation Layering +Feature #722: Night Eye/Blind magic effects +Feature #735: Move, MoveWorld script instructions. +Feature #760: Non-removable corpses + +0.23.0 + +Bug #522: Player collides with placeable items +Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open +Bug #561: Tooltip word wrapping delay +Bug #578: Bribing works incorrectly +Bug #601: PositionCell fails on negative coordinates +Bug #606: Some NPCs hairs not rendered with Better Heads addon +Bug #609: Bad rendering of bone boots +Bug #613: Messagebox causing assert to fail +Bug #631: Segfault on shutdown +Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard +Bug #635: Scale NPCs depending on race +Bug #643: Dialogue Race select function is inverted +Bug #646: Twohanded weapons don't work properly +Bug #654: Crash when dropping objects without a collision shape +Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell +Bug #660: "g" in "change" cut off in Race Menu +Bug #661: Arrille sells me the key to his upstairs room +Bug #662: Day counter starts at 2 instead of 1 +Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur +Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window. +Bug #666: Looking up/down problem +Bug #667: Active effects border visible during loading +Bug #669: incorrect player position at new game start +Bug #670: race selection menu: sex, face and hair left button not totally clickable +Bug #671: new game: player is naked +Bug #674: buying or selling items doesn't change amount of gold +Bug #675: fatigue is not set to its maximum when starting a new game +Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly +Bug #680: different gold coins in Tel Mara +Bug #682: Race menu ignores playable flag for some hairs and faces +Bug #685: Script compiler does not accept ":" after a function name +Bug #688: dispose corpse makes cross-hair to disappear +Bug #691: Auto equipping ignores equipment conditions +Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder +Bug #696: Draugr incorrect head offset +Bug #697: Sail transparency issue +Bug #700: "On the rocks" mod does not load its UV coordinates correctly. +Bug #702: Some race mods don't work +Bug #711: Crash during character creation +Bug #715: Growing Tauryon +Bug #725: Auto calculate stats +Bug #728: Failure to open container and talk dialogue +Bug #731: Crash with Mush-Mere's "background" topic +Feature #55/657: Item Repairing +Feature #62/87: Enchanting +Feature #99: Pathfinding +Feature #104: AI Package: Travel +Feature #129: Levelled items +Feature #204: Texture animations +Feature #239: Fallback-Settings +Feature #535: Console object selection improvements +Feature #629: Add levelup description in levelup layout dialog +Feature #630: Optional format subrecord in (tes3) header +Feature #641: Armor rating +Feature #645: OnDeath script function +Feature #683: Companion item UI +Feature #698: Basic Particles +Task #648: Split up components/esm/loadlocks +Task #695: mwgui cleanup + +0.22.0 + +Bug #311: Potential infinite recursion in script compiler +Bug #355: Keyboard repeat rate (in Xorg) are left disabled after game exit. +Bug #382: Weird effect in 3rd person on water +Bug #387: Always use detailed shape for physics raycasts +Bug #420: Potion/ingredient effects do not stack +Bug #429: Parts of dwemer door not picked up correctly for activation/tooltips +Bug #434/Bug #605: Object movement between cells not properly implemented +Bug #502: Duplicate player collision model at origin +Bug #509: Dialogue topic list shifts inappropriately +Bug #513: Sliding stairs +Bug #515: Launcher does not support non-latin strings +Bug #525: Race selection preview camera wrong position +Bug #526: Attributes / skills should not go below zero +Bug #529: Class and Birthsign menus options should be preselected +Bug #530: Lock window button graphic missing +Bug #532: Missing map menu graphics +Bug #545: ESX selector does not list ESM files properly +Bug #547: Global variables of type short are read incorrectly +Bug #550: Invisible meshes collision and tooltip +Bug #551: Performance drop when loading multiple ESM files +Bug #552: Don't list CG in options if it is not available +Bug #555: Character creation windows "OK" button broken +Bug #558: Segmentation fault when Alt-tabbing with console opened +Bug #559: Dialog window should not be available before character creation is finished +Bug #560: Tooltip borders should be stretched +Bug #562: Sound should not be played when an object cannot be picked up +Bug #565: Water animation speed + timescale +Bug #572: Better Bodies' textures don't work +Bug #573: OpenMW doesn't load if TR_Mainland.esm is enabled (Tamriel Rebuilt mod) +Bug #574: Moving left/right should not cancel auto-run +Bug #575: Crash entering the Chamber of Song +Bug #576: Missing includes +Bug #577: Left Gloves Addon causes ESMReader exception +Bug #579: Unable to open container "Kvama Egg Sack" +Bug #581: Mimicking vanilla Morrowind water +Bug #583: Gender not recognized +Bug #586: Wrong char gen behaviour +Bug #587: "End" script statements with spaces don't work +Bug #589: Closing message boxes by pressing the activation key +Bug #590: Ugly Dagoth Ur rendering +Bug #591: Race selection issues +Bug #593: Persuasion response should be random +Bug #595: Footless guard +Bug #599: Waterfalls are invisible from a certain distance +Bug #600: Waterfalls rendered incorrectly, cut off by water +Bug #607: New beast bodies mod crashes +Bug #608: Crash in cell "Mournhold, Royal Palace" +Bug #611: OpenMW doesn't find some of textures used in Tamriel Rebuilt +Bug #613: Messagebox causing assert to fail +Bug #615: Meshes invisible from above water +Bug #617: Potion effects should be hidden until discovered +Bug #619: certain moss hanging from tree has rendering bug +Bug #621: Batching bloodmoon's trees +Bug #623: NiMaterialProperty alpha unhandled +Bug #628: Launcher in latest master crashes the game +Bug #633: Crash on startup: Better Heads +Bug #636: Incorrect Char Gen Menu Behavior +Feature #29: Allow ESPs and multiple ESMs +Feature #94: Finish class selection-dialogue +Feature #149: Texture Alphas +Feature #237: Run Morrowind-ini importer from launcher +Feature #286: Update Active Spell Icons +Feature #334: Swimming animation +Feature #335: Walking animation +Feature #360: Proper collision shapes for NPCs and creatures +Feature #367: Lights that behave more like original morrowind implementation +Feature #477: Special local scripting variables +Feature #528: Message boxes should close when enter is pressed under certain conditions. +Feature #543: Add bsa files to the settings imported by the ini importer +Feature #594: coordinate space and utility functions +Feature #625: Zoom in vanity mode +Task #464: Refactor launcher ESX selector into a re-usable component +Task #624: Unified implementation of type-variable sub-records + +0.21.0 + +Bug #253: Dialogs don't work for Russian version of Morrowind +Bug #267: Activating creatures without dialogue can still activate the dialogue GUI +Bug #354: True flickering lights +Bug #386: The main menu's first entry is wrong (in french) +Bug #479: Adding the spell "Ash Woe Blight" to the player causes strange attribute oscillations +Bug #495: Activation Range +Bug #497: Failed Disposition check doesn't stop a dialogue entry from being returned +Bug #498: Failing a disposition check shouldn't eliminate topics from the the list of those available +Bug #500: Disposition for most NPCs is 0/100 +Bug #501: Getdisposition command wrongly returns base disposition +Bug #506: Journal UI doesn't update anymore +Bug #507: EnableRestMenu is not a valid command - change it to EnableRest +Bug #508: Crash in Ald Daedroth Shrine +Bug #517: Wrong price calculation when untrading an item +Bug #521: MWGui::InventoryWindow creates a duplicate player actor at the origin +Bug #524: Beast races are able to wear shoes +Bug #527: Background music fails to play +Bug #533: The arch at Gnisis entrance is not displayed +Bug #534: Terrain gets its correct shape only some time after the cell is loaded +Bug #536: The same entry can be added multiple times to the journal +Bug #539: Race selection is broken +Bug #544: Terrain normal map corrupt when the map is rendered +Feature #39: Video Playback +Feature #151: ^-escape sequences in text output +Feature #392: Add AI related script functions +Feature #456: Determine required ini fallback values and adjust the ini importer accordingly +Feature #460: Experimental DirArchives improvements +Feature #540: Execute scripts of objects in containers/inventories in active cells +Task #401: Review GMST fixing +Task #453: Unify case smashing/folding +Task #512: Rewrite utf8 component + +0.20.0 + +Bug #366: Changing the player's race during character creation does not change the look of the player character +Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell +Bug #437: Stop animations when paused +Bug #438: Time displays as "0 a.m." when it should be "12 a.m." +Bug #439: Text in "name" field of potion/spell creation window is persistent +Bug #440: Starting date at a new game is off by one day +Bug #442: Console window doesn't close properly sometimes +Bug #448: Do not break container window formatting when item names are very long +Bug #458: Topics sometimes not automatically added to known topic list +Bug #476: Auto-Moving allows player movement after using DisablePlayerControls +Bug #478: After sleeping in a bed the rest dialogue window opens automtically again +Bug #492: On creating potions the ingredients are removed twice +Feature #63: Mercantile skill +Feature #82: Persuasion Dialogue +Feature #219: Missing dialogue filters/functions +Feature #369: Add a FailedAction +Feature #377: Select head/hair on character creation +Feature #391: Dummy AI package classes +Feature #435: Global Map, 2nd Layer +Feature #450: Persuasion +Feature #457: Add more script instructions +Feature #474: update the global variable pcrace when the player's race is changed +Task #158: Move dynamically generated classes from Player class to World Class +Task #159: ESMStore rework and cleanup +Task #163: More Component Namespace Cleanup +Task #402: Move player data from MWWorld::Player to the player's NPC record +Task #446: Fix no namespace in BulletShapeLoader + +0.19.0 + +Bug #374: Character shakes in 3rd person mode near the origin +Bug #404: Gamma correct rendering +Bug #407: Shoes of St. Rilm do not work +Bug #408: Rugs has collision even if they are not supposed to +Bug #412: Birthsign menu sorted incorrectly +Bug #413: Resolutions presented multiple times in launcher +Bug #414: launcher.cfg file stored in wrong directory +Bug #415: Wrong esm order in openmw.cfg +Bug #418: Sound listener position updates incorrectly +Bug #423: wrong usage of "Version" entry in openmw.desktop +Bug #426: Do not use hardcoded splash images +Bug #431: Don't use markers for raycast +Bug #432: Crash after picking up items from an NPC +Feature #21/#95: Sleeping/resting +Feature #61: Alchemy Skill +Feature #68: Death +Feature #69/#86: Spell Creation +Feature #72/#84: Travel +Feature #76: Global Map, 1st Layer +Feature #120: Trainer Window +Feature #152: Skill Increase from Skill Books +Feature #160: Record Saving +Task #400: Review GMST access + +0.18.0 + +Bug #310: Button of the "preferences menu" are too small +Bug #361: Hand-to-hand skill is always 100 +Bug #365: NPC and creature animation is jerky; Characters float around when they are not supposed to +Bug #372: playSound3D uses original coordinates instead of current coordinates. +Bug #373: Static OGRE build faulty +Bug #375: Alt-tab toggle view +Bug #376: Screenshots are disable +Bug #378: Exception when drinking self-made potions +Bug #380: Cloth visibility problem +Bug #384: Weird character on doors tooltip. +Bug #398: Some objects do not collide in MW, but do so in OpenMW +Feature #22: Implement level-up +Feature #36: Hide Marker +Feature #88: Hotkey Window +Feature #91: Level-Up Dialogue +Feature #118: Keyboard and Mouse-Button bindings +Feature #119: Spell Buying Window +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #292: Object Movement/Creation Script Instructions +Feature #340: AIPackage data structures +Feature #356: Ingredients use +Feature #358: Input system rewrite +Feature #370: Target handling in actions +Feature #379: Door markers on the local map +Feature #389: AI framework +Feature #395: Using keys to open doors / containers +Feature #396: Loading screens +Feature #397: Inventory avatar image and race selection head preview +Task #339: Move sounds into Action + +0.17.0 + +Bug #225: Valgrind reports about 40MB of leaked memory +Bug #241: Some physics meshes still don't match +Bug #248: Some textures are too dark +Bug #300: Dependency on proprietary CG toolkit +Bug #302: Some objects don't collide although they should +Bug #308: Freeze in Balmora, Meldor: Armorer +Bug #313: openmw without a ~/.config/openmw folder segfault. +Bug #317: adding non-existing spell via console locks game +Bug #318: Wrong character normals +Bug #341: Building with Ogre Debug libraries does not use debug version of plugins +Bug #347: Crash when running openmw with --start="XYZ" +Bug #353: FindMyGUI.cmake breaks path on Windows +Bug #359: WindowManager throws exception at destruction +Bug #364: Laggy input on OS X due to bug in Ogre's event pump implementation +Feature #33: Allow objects to cross cell-borders +Feature #59: Dropping Items (replaced stopgap implementation with a proper one) +Feature #93: Main Menu +Feature #96/329/330/331/332/333: Player Control +Feature #180: Object rotation and scaling. +Feature #272: Incorrect NIF material sharing +Feature #314: Potion usage +Feature #324: Skill Gain +Feature #342: Drain/fortify dynamic stats/attributes magic effects +Feature #350: Allow console only script instructions +Feature #352: Run scripts in console on startup +Task #107: Refactor mw*-subsystems +Task #325: Make CreatureStats into a class +Task #345: Use Ogre's animation system +Task #351: Rewrite Action class to support automatic sound playing + +0.16.0 + +Bug #250: OpenMW launcher erratic behaviour +Bug #270: Crash because of underwater effect on OS X +Bug #277: Auto-equipping in some cells not working +Bug #294: Container GUI ignores disabled inventory menu +Bug #297: Stats review dialog shows all skills and attribute values as 0 +Bug #298: MechanicsManager::buildPlayer does not remove previous bonuses +Bug #299: Crash in World::disable +Bug #306: Non-existent ~/.config/openmw "crash" the launcher. +Bug #307: False "Data Files" location make the launcher "crash" +Feature #81: Spell Window +Feature #85: Alchemy Window +Feature #181: Support for x.y script syntax +Feature #242: Weapon and Spell icons +Feature #254: Ingame settings window +Feature #293: Allow "stacking" game modes +Feature #295: Class creation dialog tooltips +Feature #296: Clicking on the HUD elements should show/hide the respective window +Feature #301: Direction after using a Teleport Door +Feature #303: Allow object selection in the console +Feature #305: Allow the use of = as a synonym for == +Feature #312: Compensation for slow object access in poorly written Morrowind.esm scripts +Task #176: Restructure enabling/disabling of MW-references +Task #283: Integrate ogre.cfg file in settings file +Task #290: Auto-Close MW-reference related GUI windows + +0.15.0 + +Bug #5: Physics reimplementation (fixes various issues) +Bug #258: Resizing arrow's background is not transparent +Bug #268: Widening the stats window in X direction causes layout problems +Bug #269: Topic pane in dialgoue window is too small for some longer topics +Bug #271: Dialog choices are sorted incorrectly +Bug #281: The single quote character is not rendered on dialog windows +Bug #285: Terrain not handled properly in cells that are not predefined +Bug #289: Dialogue filter isn't doing case smashing/folding for item IDs +Feature #15: Collision with Terrain +Feature #17: Inventory-, Container- and Trade-Windows +Feature #44: Floating Labels above Focussed Objects +Feature #80: Tooltips +Feature #83: Barter Dialogue +Feature #90: Book and Scroll Windows +Feature #156: Item Stacking in Containers +Feature #213: Pulsating lights +Feature #218: Feather & Burden +Feature #256: Implement magic effect bookkeeping +Feature #259: Add missing information to Stats window +Feature #260: Correct case for dialogue topics +Feature #280: GUI texture atlasing +Feature #291: Ability to use GMST strings from GUI layout files +Task #255: Make MWWorld::Environment into a singleton + +0.14.0 + +Bug #1: Meshes rendered with wrong orientation +Bug #6/Task #220: Picking up small objects doesn't always work +Bug #127: tcg doesn't work +Bug #178: Compablity problems with Ogre 1.8.0 RC 1 +Bug #211: Wireframe mode (toggleWireframe command) should not apply to Console & other UI +Bug #227: Terrain crashes when moving away from predefined cells +Bug #229: On OS X Launcher cannot launch game if path to binary contains spaces +Bug #235: TGA texture loading problem +Bug #246: wireframe mode does not work in water +Feature #8/#232: Water Rendering +Feature #13: Terrain Rendering +Feature #37: Render Path Grid +Feature #66: Factions +Feature #77: Local Map +Feature #78: Compass/Mini-Map +Feature #97: Render Clothing/Armour +Feature #121: Window Pinning +Feature #205: Auto equip +Feature #217: Contiainer should track changes to its content +Feature #221: NPC Dialogue Window Enhancements +Feature #233: Game settings manager +Feature #240: Spell List and selected spell (no GUI yet) +Feature #243: Draw State +Task #113: Morrowind.ini Importer +Task #215: Refactor the sound code +Task #216: Update MyGUI + +0.13.0 + +Bug #145: Fixed sound problems after cell change +Bug #179: Pressing space in console triggers activation +Bug #186: CMake doesn't use the debug versions of Ogre libraries on Linux +Bug #189: ASCII 16 character added to console on it's activation on Mac OS X +Bug #190: Case Folding fails with music files +Bug #192: Keypresses write Text into Console no matter which gui element is active +Bug #196: Collision shapes out of place +Bug #202: ESMTool doesn't not work with localised ESM files anymore +Bug #203: Torch lights only visible on short distance +Bug #207: Ogre.log not written +Bug #209: Sounds do not play +Bug #210: Ogre crash at Dren plantation +Bug #214: Unsupported file format version +Bug #222: Launcher is writing openmw.cfg file to wrong location +Feature #9: NPC Dialogue Window +Feature #16/42: New sky/weather implementation +Feature #40: Fading +Feature #48: NPC Dialogue System +Feature #117: Equipping Items (backend only, no GUI yet, no rendering of equipped items yet) +Feature #161: Load REC_PGRD records +Feature #195: Wireframe-mode +Feature #198/199: Various sound effects +Feature #206: Allow picking data path from launcher if non is set +Task #108: Refactor window manager class +Task #172: Sound Manager Cleanup +Task #173: Create OpenEngine systems in the appropriate manager classes +Task #184: Adjust MSVC and gcc warning levels +Task #185: RefData rewrite +Task #201: Workaround for transparency issues +Task #208: silenced esm_reader.hpp warning + +0.12.0 + +Bug #154: FPS Drop +Bug #169: Local scripts continue running if associated object is deleted +Bug #174: OpenMW fails to start if the config directory doesn't exist +Bug #187: Missing lighting +Bug #188: Lights without a mesh are not rendered +Bug #191: Taking screenshot causes crash when running installed +Feature #28: Sort out the cell load problem +Feature #31: Allow the player to move away from pre-defined cells +Feature #35: Use alternate storage location for modified object position +Feature #45: NPC animations +Feature #46: Creature Animation +Feature #89: Basic Journal Window +Feature #110: Automatically pick up the path of existing MW-installations +Feature #183: More FPS display settings +Task #19: Refactor engine class +Task #109/Feature #162: Automate Packaging +Task #112: Catch exceptions thrown in input handling functions +Task #128/#168: Cleanup Configuration File Handling +Task #131: NPC Activation doesn't work properly +Task #144: MWRender cleanup +Task #155: cmake cleanup + +0.11.1 + +Bug #2: Resources loading doesn't work outside of bsa files +Bug #3: GUI does not render non-English characters +Bug #7: openmw.cfg location doesn't match +Bug #124: The TCL alias for ToggleCollision is missing. +Bug #125: Some command line options can't be used from a .cfg file +Bug #126: Toggle-type script instructions are less verbose compared with original MW +Bug #130: NPC-Record Loading fails for some NPCs +Bug #167: Launcher sets invalid parameters in ogre config +Feature #10: Journal +Feature #12: Rendering Optimisations +Feature #23: Change Launcher GUI to a tabbed interface +Feature #24: Integrate the OGRE settings window into the launcher +Feature #25: Determine openmw.cfg location (Launcher) +Feature #26: Launcher Profiles +Feature #79: MessageBox +Feature #116: Tab-Completion in Console +Feature #132: --data-local and multiple --data +Feature #143: Non-Rendering Performance-Optimisations +Feature #150: Accessing objects in cells via ID does only work for objects with all lower case IDs +Feature #157: Version Handling +Task #14: Replace tabs with 4 spaces +Task #18: Move components from global namespace into their own namespace +Task #123: refactor header files in components/esm + +0.10.0 + +* NPC dialogue window (not functional yet) +* Collisions with objects +* Refactor the PlayerPos class +* Adjust file locations +* CMake files and test linking for Bullet +* Replace Ogre raycasting test for activation with something more precise +* Adjust player movement according to collision results +* FPS display +* Various Portability Improvements +* Mac OS X support is back! + +0.9.0 + +* Exterior cells loading, unloading and management +* Character Creation GUI +* Character creation +* Make cell names case insensitive when doing internal lookups +* Music player +* NPCs rendering + +0.8.0 + +* GUI +* Complete and working script engine +* In game console +* Sky rendering +* Sound and music +* Tons of smaller stuff + +0.7.0 + +* This release is a complete rewrite in C++. +* All D code has been culled, and all modules have been rewritten. +* The game is now back up to the level of rendering interior cells and moving around, but physics, sound, GUI, and scripting still remain to be ported from the old codebase. + +0.6.0 + +* Coded a GUI system using MyGUI +* Skinned MyGUI to look like Morrowind (work in progress) +* Integrated the Monster script engine +* Rewrote some functions into script code +* Very early MyGUI < > Monster binding +* Fixed Windows sound problems (replaced old openal32.dll) + +0.5.0 + +* Collision detection with Bullet +* Experimental walk & fall character physics +* New key bindings: + * t toggle physics mode (walking, flying, ghost), + * n night eye, brightens the scene +* Fixed incompatability with DMD 1.032 and newer compilers +* * (thanks to tomqyp) +* Various minor changes and updates + +0.4.0 + +* Switched from Audiere to OpenAL +* * (BIG thanks to Chris Robinson) +* Added complete Makefile (again) as a alternative build tool +* More realistic lighting (thanks again to Chris Robinson) +* Various localization fixes tested with Russian and French versions +* Temporary workaround for the Unicode issue: invalid UTF displayed as '?' +* Added ns option to disable sound, for debugging +* Various bug fixes +* Cosmetic changes to placate gdc Wall + +0.3.0 + +* Built and tested on Windows XP +* Partial support for FreeBSD (exceptions do not work) +* You no longer have to download Monster separately +* Made an alternative for building without DSSS (but DSSS still works) +* Renamed main program from 'morro' to 'openmw' +* Made the config system more robust +* Added oc switch for showing Ogre config window on startup +* Removed some config files, these are auto generated when missing. +* Separated plugins.cfg into linux and windows versions. +* Updated Makefile and sources for increased portability +* confirmed to work against OIS 1.0.0 (Ubuntu repository package) + +0.2.0 + +* Compiles with gdc +* Switched to DSSS for building D code +* Includes the program esmtool + +0.1.0 + +first release From 8fa88f448085ee18fadced6a1beabc4bbe050d15 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:13:57 +1100 Subject: [PATCH 028/240] sync with upstream --- apps/opencs/view/filter/editwidget.hpp | 2 +- apps/opencs/view/filter/filterbox.hpp | 2 +- apps/opencs/view/filter/recordfilterbox.hpp | 2 +- apps/opencs/view/world/tablesubview.hpp | 2 +- apps/openmw/mwmechanics/aicombat.cpp | 37 ++++++++------------- apps/openmw/mwmechanics/pathfinding.cpp | 2 -- credits.txt | 1 - 7 files changed, 17 insertions(+), 31 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index e7e34b8e9a..555b6d3602 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 5954035fcb..3817d5e709 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "../../model/filter/node.hpp" #include "../../model/world/universalid.hpp" diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fa5c9c3c28..3638dc6c34 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3c253919b..1f67e0262a 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -#include +#include class QModelIndex; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 17a624f0ff..6a49f2f5e0 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -240,21 +240,17 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); //may fail to build a path, check before use + buildNewPath(actor); //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //if no new path leave mTargetAngle unchanged - if(!mPathFinder.getPath().empty()) - { - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); - mRotate = true; - } + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + mRotate = true; mMovement.mPosition[1] = 1; mReadyToAttack = false; @@ -304,13 +300,9 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - float dist = -1; //hack to indicate first time, to construct a new path - if(!mPathFinder.getPath().empty()) - { - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); - } + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -319,7 +311,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if((dist < 0) || (dist > targetPosThreshold)) + if(dist > targetPosThreshold) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -340,11 +332,8 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - if(!mPathFinder.getPath().empty()) - { - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; - } + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; } } } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 8dbdd87707..4407363a61 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,8 +391,6 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { - if (mPath.size() < 2) - return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/credits.txt b/credits.txt index 6012557638..561931cde9 100644 --- a/credits.txt +++ b/credits.txt @@ -20,7 +20,6 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick -cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) From 56ae85df0c28079b3b228d49b84079ec0e5179f0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:28:02 +1100 Subject: [PATCH 029/240] Fix 32bit Windows crash while taking the save screenshot. --- apps/openmw/mwrender/renderingmanager.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 0a82d67fbb..a4ce92347a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -980,13 +980,11 @@ void RenderingManager::screenshot(Image &image, int w, int h) Ogre::PixelFormat pf = rt->suggestPixelFormat(); - std::vector data; - data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf)); - - Ogre::PixelBox pb(w, h, 1, pf, &data[0]); - rt->copyContentsToMemory(pb); - - image.loadDynamicImage(&data[0], w, h, pf); + image.loadDynamicImage( + OGRE_ALLOC_T(Ogre::uchar, w * h * Ogre::PixelUtil::getNumElemBytes(pf), Ogre::MEMCATEGORY_GENERAL), + w, h, 1, pf, true // autoDelete=true, frees memory we allocate + ); + rt->copyContentsToMemory(image.getPixelBox()); // getPixelBox returns a box sharing the same memory as the image Ogre::TextureManager::getSingleton().remove(tempName); mRendering.getCamera()->setAspectRatio(oldAspect); From 5c11a9451184d69a801a9c5663ccc58d4d7c1082 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 28 Feb 2014 00:29:57 +1100 Subject: [PATCH 030/240] Fix 32bit Windows non-debug build with MSVC 11.0 crash during startup. --- libs/openengine/bullet/physic.cpp | 6 +++--- libs/openengine/bullet/physic.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index 481b99badb..f124abb990 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -23,7 +23,7 @@ namespace Physic mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation, true); Ogre::Quaternion inverse = mBoxRotation.Inverse(); - mBoxRotationInverse = btQuaternion(inverse.x, inverse.y, inverse.z,inverse.w); + mBoxRotationInverse = Ogre::Quaternion(inverse.w, inverse.x, inverse.y,inverse.z); mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } @@ -85,8 +85,8 @@ namespace Physic Ogre::Quaternion PhysicActor::getRotation() { assert(mBody); - btQuaternion quat = mBody->getWorldTransform().getRotation() * mBoxRotationInverse; - return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()); + btQuaternion quat = mBody->getWorldTransform().getRotation(); + return Ogre::Quaternion(quat.getW(), quat.getX(), quat.getY(), quat.getZ()) * mBoxRotationInverse; } void PhysicActor::setScale(float scale){ diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 6cd7244b85..4ef611dc87 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -162,7 +162,7 @@ namespace Physic Ogre::Vector3 mBoxScaledTranslation; Ogre::Quaternion mBoxRotation; - btQuaternion mBoxRotationInverse; + Ogre::Quaternion mBoxRotationInverse; Ogre::Vector3 mForce; bool mOnGround; From 14c3bfcf62da0b90c232f68bdde756c3552d609c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Feb 2014 14:46:06 +0100 Subject: [PATCH 031/240] added navigation class --- apps/opencs/CMakeLists.txt | 8 +++-- apps/opencs/view/render/navigation.cpp | 19 ++++++++++++ apps/opencs/view/render/navigation.hpp | 43 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 apps/opencs/view/render/navigation.cpp create mode 100644 apps/opencs/view/render/navigation.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6bcad1d08f..1ba24039bd 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -64,8 +64,12 @@ opencs_units (view/world ) opencs_units (view/render - scenewidget - ) + scenewidget + ) + +opencs_units_noqt (view/render + navigation + ) opencs_units_noqt (view/world dialoguesubview subviews diff --git a/apps/opencs/view/render/navigation.cpp b/apps/opencs/view/render/navigation.cpp new file mode 100644 index 0000000000..14ae7f0b7e --- /dev/null +++ b/apps/opencs/view/render/navigation.cpp @@ -0,0 +1,19 @@ + +#include "navigation.hpp" + +float CSVRender::Navigation::getFactor (bool mouse) const +{ + float factor = mFastModeFactor; + + if (mouse) + factor /= 2; /// \todo make this configurable + + return factor; +} + +CSVRender::Navigation::~Navigation() {} + +void CSVRender::Navigation::setFastModeFactor (float factor) +{ + mFastModeFactor = factor; +} \ No newline at end of file diff --git a/apps/opencs/view/render/navigation.hpp b/apps/opencs/view/render/navigation.hpp new file mode 100644 index 0000000000..964ceeac6e --- /dev/null +++ b/apps/opencs/view/render/navigation.hpp @@ -0,0 +1,43 @@ +#ifndef OPENCS_VIEW_NAVIGATION_H +#define OPENCS_VIEW_NAVIGATION_H + +class QPoint; + +namespace Ogre +{ + class Camera; +} + +namespace CSVRender +{ + class Navigation + { + float mFastModeFactor; + + protected: + + float getFactor (bool mouse) const; + + public: + + virtual ~Navigation(); + + void setFastModeFactor (float factor); + ///< Set currently applying fast mode factor. + + virtual bool activate (Ogre::Camera *camera) = 0; + ///< \return Update required? + + virtual bool wheelMoved (int delta) = 0; + ///< \return Update required? + + virtual bool mouseMoved (const QPoint& delta, int mode) = 0; + ///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1 + /// \return Update required? + + virtual bool handleMovementKeys (int vertical, int horizontal) = 0; + ///< \return Update required? + }; +} + +#endif From c977b2a756c4a0db93b13b48bb51a4331afe49e5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Feb 2014 15:23:14 +0100 Subject: [PATCH 032/240] moved implementation of the 1st person camera into a separate file --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/navigation1st.cpp | 60 ++++++++++++ apps/opencs/view/render/navigation1st.hpp | 32 ++++++ apps/opencs/view/render/scenewidget.cpp | 114 +++++++++------------- apps/opencs/view/render/scenewidget.hpp | 15 +-- apps/opencs/view/world/scenesubview.cpp | 12 ++- apps/opencs/view/world/scenesubview.hpp | 3 + 7 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 apps/opencs/view/render/navigation1st.cpp create mode 100644 apps/opencs/view/render/navigation1st.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1ba24039bd..bb4a9d3298 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -68,7 +68,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation + navigation navigation1st ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/navigation1st.cpp b/apps/opencs/view/render/navigation1st.cpp new file mode 100644 index 0000000000..5a3bc7b652 --- /dev/null +++ b/apps/opencs/view/render/navigation1st.cpp @@ -0,0 +1,60 @@ + +#include "navigation1st.hpp" + +#include + +#include + +CSVRender::Navigation1st::Navigation1st() : mCamera (0) {} + +bool CSVRender::Navigation1st::activate (Ogre::Camera *camera) +{ + mCamera = camera; + mCamera->setFixedYawAxis (true); + return false; +} + +bool CSVRender::Navigation1st::wheelMoved (int delta) +{ + mCamera->move (getFactor (true) * mCamera->getDirection() * delta); + return true; +} + +bool CSVRender::Navigation1st::mouseMoved (const QPoint& delta, int mode) +{ + if (mode==0) + { + // turn camera + if (delta.x()) + mCamera->yaw (Ogre::Degree (getFactor (true) * delta.x())); + + if (delta.y()) + mCamera->pitch (Ogre::Degree (getFactor (true) * delta.y())); + + return true; + } + else if (mode==1) + { + // pan camera + if (delta.x()) + mCamera->move (getFactor (true) * mCamera->getDerivedRight() * delta.x()); + + if (delta.y()) + mCamera->move (getFactor (true) * -mCamera->getDerivedUp() * delta.y()); + + return true; + } + + return false; +} + +bool CSVRender::Navigation1st::handleMovementKeys (int vertical, int horizontal) +{ + if (vertical) + mCamera->move (getFactor (false) * mCamera->getDirection() * vertical); + + if (horizontal) + mCamera->move (getFactor (true) * mCamera->getDerivedRight() * horizontal); + + return true; +} diff --git a/apps/opencs/view/render/navigation1st.hpp b/apps/opencs/view/render/navigation1st.hpp new file mode 100644 index 0000000000..815e0b1174 --- /dev/null +++ b/apps/opencs/view/render/navigation1st.hpp @@ -0,0 +1,32 @@ +#ifndef OPENCS_VIEW_NAVIGATION1ST_H +#define OPENCS_VIEW_NAVIGATION1ST_H + +#include "navigation.hpp" + +namespace CSVRender +{ + /// \brief First person-like camera controls + class Navigation1st : public Navigation + { + Ogre::Camera *mCamera; + + public: + + Navigation1st(); + + virtual bool activate (Ogre::Camera *camera); + ///< \return Update required? + + virtual bool wheelMoved (int delta); + ///< \return Update required? + + virtual bool mouseMoved (const QPoint& delta, int mode); + ///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1 + /// \return Update required? + + virtual bool handleMovementKeys (int vertical, int horizontal); + ///< \return Update required? + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 07fe56f8fa..41868dfb5b 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -9,16 +9,18 @@ #include #include +#include "navigation.hpp" + namespace CSVRender { SceneWidget::SceneWidget(QWidget *parent) : QWidget(parent) , mWindow(NULL) , mCamera(NULL) - , mSceneMgr(NULL), mNavigationMode (NavigationMode_1stPerson), mUpdate (false) + , mSceneMgr(NULL), mNavigation (0), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) , mFast (false), mDragging (false), mMod1 (false) - , mMouseSensitivity (2), mFastFactor (4) /// \todo make these configurable + , mFastFactor (4) /// \todo make this configurable { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -94,12 +96,12 @@ namespace CSVRender Ogre::Root::getSingleton().destroyRenderTarget(mWindow); } - void SceneWidget::setNavigationMode (NavigationMode mode) + void SceneWidget::setNavigation (Navigation *navigation) { - if (mode!=mNavigationMode) + if ((mNavigation = navigation)) { - mNavigationMode = mode; - + mNavigation->setFastModeFactor (mFast ? mFastFactor : 1); + mNavigation->activate (mCamera); } } @@ -156,8 +158,17 @@ namespace CSVRender case Qt::Key_S: mKeyBackward = true; break; case Qt::Key_A: mKeyLeft = true; break; case Qt::Key_D: mKeyRight = true; break; - case Qt::Key_Shift: mFast = true; break; case Qt::Key_Control: mMod1 = true; break; + + case Qt::Key_Shift: + + mFast = true; + + if (mNavigation) + mNavigation->setFastModeFactor (mFastFactor); + + break; + default: QWidget::keyPressEvent (event); } } @@ -170,19 +181,27 @@ namespace CSVRender case Qt::Key_S: mKeyBackward = false; break; case Qt::Key_A: mKeyLeft = false; break; case Qt::Key_D: mKeyRight = false; break; - case Qt::Key_Shift: mFast = false; break; case Qt::Key_Control: mMod1 = false; break; + + case Qt::Key_Shift: + + mFast = false; + + if (mNavigation) + mNavigation->setFastModeFactor (1); + + break; + default: QWidget::keyReleaseEvent (event); } } void SceneWidget::wheelEvent (QWheelEvent *event) { - if (int delta = event->delta()) - { - mCamera->move ((getFastFactor() * mCamera->getDirection() * delta)/mMouseSensitivity); - mUpdate = true; - } + if (mNavigation) + if (event->delta()) + if (mNavigation->wheelMoved (event->delta())) + mUpdate = true; } void SceneWidget::leaveEvent (QEvent *event) @@ -199,40 +218,9 @@ namespace CSVRender QPoint diff = mOldPos-event->pos(); mOldPos = event->pos(); - if (!mMod1) - { - // turn camera - if (diff.x()) - { - mCamera->yaw ( - Ogre::Degree ((getFastFactor() * diff.x())/mMouseSensitivity)); + if (mNavigation) + if (mNavigation->mouseMoved (diff, mMod1 ? 1 : 0)) mUpdate = true; - } - - if (diff.y()) - { - mCamera->pitch ( - Ogre::Degree ((getFastFactor() * diff.y())/mMouseSensitivity)); - mUpdate = true; - } - } - else - { - // pan camera - if (diff.x()) - { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move ((getFastFactor() * direction * diff.x())/mMouseSensitivity); - mUpdate = true; - } - - if (diff.y()) - { - Ogre::Vector3 direction = mCamera->getDerivedUp(); - mCamera->move ((getFastFactor() * -direction * diff.y())/mMouseSensitivity); - mUpdate = true; - } - } } else { @@ -262,30 +250,24 @@ namespace CSVRender void SceneWidget::update() { - if (mKeyForward && !mKeyBackward) + if (mNavigation) { - mCamera->move (getFastFactor() * mCamera->getDirection()); - mUpdate = true; - } + int horizontal = 0; + int vertical = 0; - if (!mKeyForward && mKeyBackward) - { - mCamera->move (getFastFactor() * -mCamera->getDirection()); - mUpdate = true; - } + if (mKeyForward && !mKeyBackward) + vertical = 1; + else if (!mKeyForward && mKeyBackward) + vertical = -1; - if (mKeyLeft && !mKeyRight) - { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (getFastFactor() * -direction); - mUpdate = true; - } + if (mKeyLeft && !mKeyRight) + horizontal = -1; + else if (!mKeyLeft && mKeyRight) + horizontal = 1; - if (!mKeyLeft && mKeyRight) - { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (getFastFactor() * direction); - mUpdate = true; + if (horizontal || vertical) + if (mNavigation->handleMovementKeys (vertical, horizontal)) + mUpdate = true; } if (mUpdate) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 55f62d6619..247dbbb1a1 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -12,25 +12,21 @@ namespace Ogre namespace CSVRender { + class Navigation; + class SceneWidget : public QWidget { Q_OBJECT public: - enum NavigationMode - { - NavigationMode_1stPerson, - NavigationMode_Free, - NavigationMode_Orbit - }; - SceneWidget(QWidget *parent); virtual ~SceneWidget(); QPaintEngine* paintEngine() const; - void setNavigationMode (NavigationMode mode); + void setNavigation (Navigation *navigation); + ///< \attention The ownership of \a navigation is not transferred to *this. private: void paintEvent(QPaintEvent* e); @@ -59,7 +55,7 @@ namespace CSVRender Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; - NavigationMode mNavigationMode; + Navigation *mNavigation; bool mUpdate; int mKeyForward; int mKeyBackward; @@ -69,7 +65,6 @@ namespace CSVRender bool mDragging; bool mMod1; QPoint mOldPos; - int mMouseSensitivity; int mFastFactor; private slots: diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 339a885191..5b7b6a587b 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -59,6 +59,8 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D widget->setLayout (layout); setWidget (widget); + + mScene->setNavigation (&m1st); } void CSVWorld::SceneSubView::setEditLock (bool locked) @@ -81,9 +83,13 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) void CSVWorld::SceneSubView::selectNavigationMode (const std::string& mode) { if (mode=="1st") - mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_1stPerson); + mScene->setNavigation (&m1st); else if (mode=="free") - mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_Free); + { + + } else if (mode=="orbit") - mScene->setNavigationMode (CSVRender::SceneWidget::NavigationMode_Orbit); + { + + } } diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index 65e1614c11..f948ecefbe 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -3,6 +3,8 @@ #include "../doc/subview.hpp" +#include "../render/navigation1st.hpp" + class QModelIndex; namespace CSMDoc @@ -27,6 +29,7 @@ namespace CSVWorld TableBottomBox *mBottom; CSVRender::SceneWidget *mScene; + CSVRender::Navigation1st m1st; public: From 4eea2c7a86dfa5df0607066e38e212cd1017a1d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Feb 2014 15:42:59 +0100 Subject: [PATCH 033/240] added free navigation mode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/navigationfree.cpp | 60 ++++++++++++++++++++++ apps/opencs/view/render/navigationfree.hpp | 32 ++++++++++++ apps/opencs/view/world/scenesubview.cpp | 2 +- apps/opencs/view/world/scenesubview.hpp | 2 + 5 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 apps/opencs/view/render/navigationfree.cpp create mode 100644 apps/opencs/view/render/navigationfree.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index bb4a9d3298..eba7a7acb8 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -68,7 +68,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation navigation1st + navigation navigation1st navigationfree ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/navigationfree.cpp b/apps/opencs/view/render/navigationfree.cpp new file mode 100644 index 0000000000..d470722bb8 --- /dev/null +++ b/apps/opencs/view/render/navigationfree.cpp @@ -0,0 +1,60 @@ + +#include "navigationfree.hpp" + +#include + +#include + +CSVRender::NavigationFree::NavigationFree() : mCamera (0) {} + +bool CSVRender::NavigationFree::activate (Ogre::Camera *camera) +{ + mCamera = camera; + mCamera->setFixedYawAxis (false); + return false; +} + +bool CSVRender::NavigationFree::wheelMoved (int delta) +{ + mCamera->move (getFactor (true) * mCamera->getDirection() * delta); + return true; +} + +bool CSVRender::NavigationFree::mouseMoved (const QPoint& delta, int mode) +{ + if (mode==0) + { + // turn camera + if (delta.x()) + mCamera->yaw (Ogre::Degree (getFactor (true) * delta.x())); + + if (delta.y()) + mCamera->pitch (Ogre::Degree (getFactor (true) * delta.y())); + + return true; + } + else if (mode==1) + { + // pan camera + if (delta.x()) + mCamera->move (getFactor (true) * mCamera->getDerivedRight() * delta.x()); + + if (delta.y()) + mCamera->move (getFactor (true) * -mCamera->getDerivedUp() * delta.y()); + + return true; + } + + return false; +} + +bool CSVRender::NavigationFree::handleMovementKeys (int vertical, int horizontal) +{ + if (vertical) + mCamera->move (getFactor (false) * mCamera->getDerivedUp() * vertical); + + if (horizontal) + mCamera->move (getFactor (true) * mCamera->getDerivedRight() * horizontal); + + return true; +} diff --git a/apps/opencs/view/render/navigationfree.hpp b/apps/opencs/view/render/navigationfree.hpp new file mode 100644 index 0000000000..fa6fdad11b --- /dev/null +++ b/apps/opencs/view/render/navigationfree.hpp @@ -0,0 +1,32 @@ +#ifndef OPENCS_VIEW_NAVIGATIONFREE_H +#define OPENCS_VIEW_NAVIGATIONFREE_H + +#include "navigation.hpp" + +namespace CSVRender +{ + /// \brief Free camera controls + class NavigationFree : public Navigation + { + Ogre::Camera *mCamera; + + public: + + NavigationFree(); + + virtual bool activate (Ogre::Camera *camera); + ///< \return Update required? + + virtual bool wheelMoved (int delta); + ///< \return Update required? + + virtual bool mouseMoved (const QPoint& delta, int mode); + ///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1 + /// \return Update required? + + virtual bool handleMovementKeys (int vertical, int horizontal); + ///< \return Update required? + }; +} + +#endif diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 5b7b6a587b..fd327b4d1b 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -86,7 +86,7 @@ void CSVWorld::SceneSubView::selectNavigationMode (const std::string& mode) mScene->setNavigation (&m1st); else if (mode=="free") { - + mScene->setNavigation (&mFree); } else if (mode=="orbit") { diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index f948ecefbe..8e42540e14 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -4,6 +4,7 @@ #include "../doc/subview.hpp" #include "../render/navigation1st.hpp" +#include "../render/navigationfree.hpp" class QModelIndex; @@ -30,6 +31,7 @@ namespace CSVWorld TableBottomBox *mBottom; CSVRender::SceneWidget *mScene; CSVRender::Navigation1st m1st; + CSVRender::NavigationFree mFree; public: From ae637d6abc712b84248330f5634182a96902757f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 28 Feb 2014 11:58:03 +0100 Subject: [PATCH 034/240] rolling --- apps/opencs/view/render/navigation.hpp | 3 +++ apps/opencs/view/render/navigation1st.cpp | 6 ++++++ apps/opencs/view/render/navigation1st.hpp | 3 +++ apps/opencs/view/render/navigationfree.cpp | 6 ++++++ apps/opencs/view/render/navigationfree.hpp | 3 +++ apps/opencs/view/render/scenewidget.cpp | 17 +++++++++++++++++ apps/opencs/view/render/scenewidget.hpp | 10 ++++++---- 7 files changed, 44 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/render/navigation.hpp b/apps/opencs/view/render/navigation.hpp index 964ceeac6e..8735196095 100644 --- a/apps/opencs/view/render/navigation.hpp +++ b/apps/opencs/view/render/navigation.hpp @@ -37,6 +37,9 @@ namespace CSVRender virtual bool handleMovementKeys (int vertical, int horizontal) = 0; ///< \return Update required? + + virtual bool handleRollKeys (int delta) = 0; + ///< \return Update required? }; } diff --git a/apps/opencs/view/render/navigation1st.cpp b/apps/opencs/view/render/navigation1st.cpp index 5a3bc7b652..b177c019b4 100644 --- a/apps/opencs/view/render/navigation1st.cpp +++ b/apps/opencs/view/render/navigation1st.cpp @@ -58,3 +58,9 @@ bool CSVRender::Navigation1st::handleMovementKeys (int vertical, int horizontal) return true; } + +bool CSVRender::Navigation1st::handleRollKeys (int delta) +{ + // we don't roll this way in 1st person mode + return false; +} diff --git a/apps/opencs/view/render/navigation1st.hpp b/apps/opencs/view/render/navigation1st.hpp index 815e0b1174..d1e09d2367 100644 --- a/apps/opencs/view/render/navigation1st.hpp +++ b/apps/opencs/view/render/navigation1st.hpp @@ -26,6 +26,9 @@ namespace CSVRender virtual bool handleMovementKeys (int vertical, int horizontal); ///< \return Update required? + + virtual bool handleRollKeys (int delta); + ///< \return Update required? }; } diff --git a/apps/opencs/view/render/navigationfree.cpp b/apps/opencs/view/render/navigationfree.cpp index d470722bb8..950e137a7d 100644 --- a/apps/opencs/view/render/navigationfree.cpp +++ b/apps/opencs/view/render/navigationfree.cpp @@ -58,3 +58,9 @@ bool CSVRender::NavigationFree::handleMovementKeys (int vertical, int horizontal return true; } + +bool CSVRender::NavigationFree::handleRollKeys (int delta) +{ + mCamera->roll (Ogre::Degree (getFactor (false) * delta)); + return true; +} diff --git a/apps/opencs/view/render/navigationfree.hpp b/apps/opencs/view/render/navigationfree.hpp index fa6fdad11b..e30722f75d 100644 --- a/apps/opencs/view/render/navigationfree.hpp +++ b/apps/opencs/view/render/navigationfree.hpp @@ -26,6 +26,9 @@ namespace CSVRender virtual bool handleMovementKeys (int vertical, int horizontal); ///< \return Update required? + + virtual bool handleRollKeys (int delta); + ///< \return Update required? }; } diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 41868dfb5b..f864e15280 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -19,6 +19,7 @@ namespace CSVRender , mCamera(NULL) , mSceneMgr(NULL), mNavigation (0), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) + , mKeyRollLeft (false), mKeyRollRight (false) , mFast (false), mDragging (false), mMod1 (false) , mFastFactor (4) /// \todo make this configurable { @@ -158,6 +159,8 @@ namespace CSVRender case Qt::Key_S: mKeyBackward = true; break; case Qt::Key_A: mKeyLeft = true; break; case Qt::Key_D: mKeyRight = true; break; + case Qt::Key_Q: mKeyRollLeft = true; break; + case Qt::Key_E: mKeyRollRight = true; break; case Qt::Key_Control: mMod1 = true; break; case Qt::Key_Shift: @@ -181,6 +184,8 @@ namespace CSVRender case Qt::Key_S: mKeyBackward = false; break; case Qt::Key_A: mKeyLeft = false; break; case Qt::Key_D: mKeyRight = false; break; + case Qt::Key_Q: mKeyRollLeft = false; break; + case Qt::Key_E: mKeyRollRight = false; break; case Qt::Key_Control: mMod1 = false; break; case Qt::Key_Shift: @@ -268,6 +273,18 @@ namespace CSVRender if (horizontal || vertical) if (mNavigation->handleMovementKeys (vertical, horizontal)) mUpdate = true; + + int roll = 0; + + if (mKeyRollLeft && !mKeyRollRight) + roll = 1; + else if (!mKeyRollLeft && mKeyRollRight) + roll = -1; + + if (roll) + if (mNavigation->handleRollKeys (roll)) + mUpdate = true; + } if (mUpdate) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 247dbbb1a1..4a18c42443 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -57,10 +57,12 @@ namespace CSVRender Navigation *mNavigation; bool mUpdate; - int mKeyForward; - int mKeyBackward; - int mKeyLeft; - int mKeyRight; + bool mKeyForward; + bool mKeyBackward; + bool mKeyLeft; + bool mKeyRight; + bool mKeyRollLeft; + bool mKeyRollRight; bool mFast; bool mDragging; bool mMod1; From 4d3abeedcbac1aaf583e16124cccf4df0618e26a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 28 Feb 2014 13:37:01 +0100 Subject: [PATCH 035/240] keep camera upright in 1st person mode --- apps/opencs/view/render/navigation1st.cpp | 23 +++++++++++++++++++++-- apps/opencs/view/render/scenewidget.cpp | 3 ++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/navigation1st.cpp b/apps/opencs/view/render/navigation1st.cpp index b177c019b4..b892d3e3e7 100644 --- a/apps/opencs/view/render/navigation1st.cpp +++ b/apps/opencs/view/render/navigation1st.cpp @@ -11,7 +11,17 @@ bool CSVRender::Navigation1st::activate (Ogre::Camera *camera) { mCamera = camera; mCamera->setFixedYawAxis (true); - return false; + + Ogre::Radian pitch = mCamera->getOrientation().getPitch(); + + Ogre::Radian limit (Ogre::Math::PI/2-0.5); + + if (pitch>limit) + mCamera->pitch (-(pitch-limit)); + else if (pitch<-limit) + mCamera->pitch (pitch-limit); + + return true; } bool CSVRender::Navigation1st::wheelMoved (int delta) @@ -29,7 +39,16 @@ bool CSVRender::Navigation1st::mouseMoved (const QPoint& delta, int mode) mCamera->yaw (Ogre::Degree (getFactor (true) * delta.x())); if (delta.y()) - mCamera->pitch (Ogre::Degree (getFactor (true) * delta.y())); + { + Ogre::Radian oldPitch = mCamera->getOrientation().getPitch(); + float deltaPitch = getFactor (true) * delta.y(); + Ogre::Radian newPitch = oldPitch + Ogre::Degree (deltaPitch); + + Ogre::Radian limit (Ogre::Math::PI/2-0.5); + + if ((deltaPitch>0 && newPitch-limit)) + mCamera->pitch (Ogre::Degree (deltaPitch)); + } return true; } diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index f864e15280..6f07c1b0d5 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -102,7 +102,8 @@ namespace CSVRender if ((mNavigation = navigation)) { mNavigation->setFastModeFactor (mFast ? mFastFactor : 1); - mNavigation->activate (mCamera); + if (mNavigation->activate (mCamera)) + mUpdate = true; } } From a99aa15d148cc85b0918955675c2d9e8cbceb13b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Feb 2014 17:23:58 +0100 Subject: [PATCH 036/240] Fix a copy&paste mistake leaving start rotation uninitialized --- apps/openmw/engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e80bd954e3..af8d494261 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -456,7 +456,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) else { pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.pos[2] = 0; + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; world->changeToExteriorCell (pos); } @@ -621,4 +621,4 @@ void OMW::Engine::setActivationDistanceOverride (int distance) void OMW::Engine::setWarningsMode (int mode) { mWarningsMode = mode; -} \ No newline at end of file +} From 46867ec0cfb2887b638f4912a59a6d49e1027a2a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 1 Mar 2014 07:24:20 +1100 Subject: [PATCH 037/240] Fix file handles being left open on windows builds --- components/files/lowlevelfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp index 71fd1b523c..06ee9fb4e8 100644 --- a/components/files/lowlevelfile.cpp +++ b/components/files/lowlevelfile.cpp @@ -219,7 +219,7 @@ LowLevelFile::LowLevelFile () LowLevelFile::~LowLevelFile () { - if (mHandle == INVALID_HANDLE_VALUE) + if (mHandle != INVALID_HANDLE_VALUE) CloseHandle (mHandle); } From 0e0d2331d60002dce43a9fd27a3eb636b62d4c89 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 28 Feb 2014 21:53:11 +0100 Subject: [PATCH 038/240] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index a23cd1077e..26abdaecc4 100644 --- a/readme.txt +++ b/readme.txt @@ -133,6 +133,7 @@ Bug #1158: Armor rating should always stay below inventory mannequin Bug #1159: Quick keys can be set during character generation Bug #1160: Crash on equip lockpick when Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Bug #1184: Game Save: overwriting an existing save does not actually overwrites the file Feature #30: Loading/Saving (still missing a few parts) Feature #101: AI Package: Activate Feature #103: AI Package: Follow, FollowCell From 536a0e0ab0a19bbedd3fd03be5374fd49497031b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Mar 2014 14:15:04 +0100 Subject: [PATCH 039/240] added orbit navigation mode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/navigationorbit.cpp | 100 ++++++++++++++++++++ apps/opencs/view/render/navigationorbit.hpp | 42 ++++++++ apps/opencs/view/world/scenesubview.cpp | 6 +- apps/opencs/view/world/scenesubview.hpp | 2 + 5 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 apps/opencs/view/render/navigationorbit.cpp create mode 100644 apps/opencs/view/render/navigationorbit.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index eba7a7acb8..119280f132 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -68,7 +68,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation navigation1st navigationfree + navigation navigation1st navigationfree navigationorbit ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/navigationorbit.cpp b/apps/opencs/view/render/navigationorbit.cpp new file mode 100644 index 0000000000..c6e729e967 --- /dev/null +++ b/apps/opencs/view/render/navigationorbit.cpp @@ -0,0 +1,100 @@ + +#include "navigationorbit.hpp" + +#include + +#include + +void CSVRender::NavigationOrbit::rotateCamera (const Ogre::Vector3& diff) +{ + Ogre::Vector3 pos = mCamera->getPosition(); + + float distance = (pos-mCentre).length(); + + Ogre::Vector3 direction = (pos+diff)-mCentre; + direction.normalise(); + + mCamera->setPosition (mCentre + direction*distance); + mCamera->lookAt (mCentre); +} + +CSVRender::NavigationOrbit::NavigationOrbit() : mCamera (0), mCentre (0, 0, 0), mDistance (100) +{} + +bool CSVRender::NavigationOrbit::activate (Ogre::Camera *camera) +{ + mCamera = camera; + mCamera->setFixedYawAxis (false); + + if ((mCamera->getPosition()-mCentre).length()getPosition(); + direction.normalise(); + + if (direction.length()==0) + direction = Ogre::Vector3 (1, 0, 0); + + mCamera->setPosition (mCentre - direction * mDistance); + } + + mCamera->lookAt (mCentre); + + return true; +} + +bool CSVRender::NavigationOrbit::wheelMoved (int delta) +{ + Ogre::Vector3 diff = getFactor (true) * mCamera->getDirection() * delta; + + Ogre::Vector3 pos = mCamera->getPosition(); + + if (delta>0 && diff.length()>=(pos-mCentre).length()-mDistance) + { + pos = mCentre-(mCamera->getDirection() * mDistance); + } + else + { + pos += diff; + } + + mCamera->setPosition (pos); + + return true; +} + +bool CSVRender::NavigationOrbit::mouseMoved (const QPoint& delta, int mode) +{ + Ogre::Vector3 diff = + getFactor (true) * -mCamera->getDerivedRight() * delta.x() + + getFactor (true) * mCamera->getDerivedUp() * delta.y(); + + if (mode==0) + { + rotateCamera (diff); + return true; + } + else if (mode==1) + { + mCamera->move (diff); + mCentre += diff; + return true; + } + + return false; +} + +bool CSVRender::NavigationOrbit::handleMovementKeys (int vertical, int horizontal) +{ + rotateCamera ( + - getFactor (false) * -mCamera->getDerivedRight() * horizontal + + getFactor (false) * mCamera->getDerivedUp() * vertical); + + return true; +} + +bool CSVRender::NavigationOrbit::handleRollKeys (int delta) +{ + mCamera->roll (Ogre::Degree (getFactor (false) * delta)); + return true; +} \ No newline at end of file diff --git a/apps/opencs/view/render/navigationorbit.hpp b/apps/opencs/view/render/navigationorbit.hpp new file mode 100644 index 0000000000..7796eb9e7c --- /dev/null +++ b/apps/opencs/view/render/navigationorbit.hpp @@ -0,0 +1,42 @@ +#ifndef OPENCS_VIEW_NAVIGATIONORBIT_H +#define OPENCS_VIEW_NAVIGATIONORBIT_H + +#include "navigation.hpp" + +#include + +namespace CSVRender +{ + /// \brief Orbiting camera controls + class NavigationOrbit : public Navigation + { + Ogre::Camera *mCamera; + Ogre::Vector3 mCentre; + int mDistance; + + void rotateCamera (const Ogre::Vector3& diff); + ///< Rotate camera around centre. + + public: + + NavigationOrbit(); + + virtual bool activate (Ogre::Camera *camera); + ///< \return Update required? + + virtual bool wheelMoved (int delta); + ///< \return Update required? + + virtual bool mouseMoved (const QPoint& delta, int mode); + ///< \param mode: 0: default mouse key, 1: default mouse key and modifier key 1 + /// \return Update required? + + virtual bool handleMovementKeys (int vertical, int horizontal); + ///< \return Update required? + + virtual bool handleRollKeys (int delta); + ///< \return Update required? + }; +} + +#endif diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index fd327b4d1b..a23931950e 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -85,11 +85,7 @@ void CSVWorld::SceneSubView::selectNavigationMode (const std::string& mode) if (mode=="1st") mScene->setNavigation (&m1st); else if (mode=="free") - { mScene->setNavigation (&mFree); - } else if (mode=="orbit") - { - - } + mScene->setNavigation (&mOrbit); } diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index 8e42540e14..f944e17c3e 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -5,6 +5,7 @@ #include "../render/navigation1st.hpp" #include "../render/navigationfree.hpp" +#include "../render/navigationorbit.hpp" class QModelIndex; @@ -32,6 +33,7 @@ namespace CSVWorld CSVRender::SceneWidget *mScene; CSVRender::Navigation1st m1st; CSVRender::NavigationFree mFree; + CSVRender::NavigationOrbit mOrbit; public: From ca30f2af3d16ce80290cb3d12c4fabc71679f701 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 11:03:37 +0100 Subject: [PATCH 040/240] minor fix --- apps/openmw/mwworld/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 10afbc675c..508127837d 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -43,7 +43,7 @@ namespace mPhysics (physics), mRendering (rendering) {} - bool InsertFunctor::InsertFunctor::operator() (const MWWorld::Ptr& ptr) + bool InsertFunctor::operator() (const MWWorld::Ptr& ptr) { if (mRescale) { From cb3994281c957225a8b93c94b2f56b44f6875572 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 13:11:15 +0100 Subject: [PATCH 041/240] changed UniversalId::Type_Scene from index to string ID argument (argument is the worldspace) --- apps/opencs/model/world/universalid.cpp | 4 ++-- apps/opencs/view/doc/view.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 8301ebfd36..1d1b3c960f 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -90,14 +90,14 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 533ca7f570..b055d34eb1 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -431,7 +431,7 @@ void CSVDoc::View::addFiltersSubView() void CSVDoc::View::addSceneSubView() { - addSubView (CSMWorld::UniversalId::Type_Scene); + addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "default")); } void CSVDoc::View::addTopicsSubView() From 797f5527ee990ba4d4499371e1ae8dc9a4817d0d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 13:29:02 +0100 Subject: [PATCH 042/240] added hint parameter for UniversalId viewing requests --- apps/opencs/view/doc/subview.cpp | 4 +++- apps/opencs/view/doc/subview.hpp | 3 +++ apps/opencs/view/doc/view.cpp | 5 ++++- apps/opencs/view/doc/view.hpp | 4 +++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 09361a1c03..6160f2673c 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -16,4 +16,6 @@ void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QSt { } -void CSVDoc::SubView::setStatusBar (bool show) {} \ No newline at end of file +void CSVDoc::SubView::setStatusBar (bool show) {} + +void CSVDoc::SubView::useHint (const std::string& hint) {} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index aa073f81dd..127cf91e62 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -40,6 +40,9 @@ namespace CSVDoc virtual void setStatusBar (bool show); ///< Default implementation: ignored + virtual void useHint (const std::string& hint); + ///< Default implementation: ignored + signals: void focusId (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index b055d34eb1..38e2bff0d4 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -310,7 +310,7 @@ void CSVDoc::View::updateProgress (int current, int max, int type, int threads) mOperations->setProgress (current, max, type, threads); } -void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) +void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::string& hint) { /// \todo add an user setting for limiting the number of sub views per top level view. Automatically open a new top level view if this /// number is exceeded @@ -322,6 +322,9 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); + if (!hint.empty()) + view->useHint (hint); + view->setStatusBar (mShowStatusBar->isChecked()); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 13c15ec9bb..a1f6bea717 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -120,7 +120,9 @@ namespace CSVDoc public slots: - void addSubView (const CSMWorld::UniversalId& id); + void addSubView (const CSMWorld::UniversalId& id, const std::string& hint = ""); + ///< \param hint Suggested view point (e.g. coordinates in a 3D scene or a line number + /// in a script). void abortOperation (int type); From 55195f819a432fece462fa32dc90315421532473 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 22:34:41 +0100 Subject: [PATCH 043/240] changed edit requests from row index format to UniversalId/hint format --- apps/opencs/view/doc/subview.hpp | 2 +- apps/opencs/view/doc/view.cpp | 4 ++-- apps/opencs/view/tools/reportsubview.cpp | 2 +- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 7 ++++--- apps/opencs/view/world/tablesubview.hpp | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 127cf91e62..59781f869c 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -45,7 +45,7 @@ namespace CSVDoc signals: - void focusId (const CSMWorld::UniversalId& universalId); + void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 38e2bff0d4..1b271fcfcf 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -329,8 +329,8 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); - connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, - SLOT (addSubView (const CSMWorld::UniversalId&))); + connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&, const std::string&)), this, + SLOT (addSubView (const CSMWorld::UniversalId&, const std::string&))); CSMSettings::UserSettings::instance().updateSettings("Display Format"); diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index 182d1cdd6c..d59f0c2349 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -40,5 +40,5 @@ void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QSt void CSVTools::ReportSubView::show (const QModelIndex& index) { - focusId (mModel->getUniversalId (index.row())); + focusId (mModel->getUniversalId (index.row()), ""); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index edf3bc6dea..2c4651dd7f 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -306,7 +306,7 @@ void CSVWorld::Table::editRecord() QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) - emit editRequest (selectedRows.begin()->row()); + emit editRequest (getUniversalId (selectedRows.begin()->row()), ""); } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 615a31b4d7..995bc00955 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -86,7 +86,7 @@ namespace CSVWorld signals: - void editRequest (int row); + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void selectionSizeChanged (int size); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index e330d47758..30a60536ae 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -36,7 +36,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D setWidget (widget); - connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + connect (mTable, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), + this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&))); connect (mTable, SIGNAL (selectionSizeChanged (int)), mBottom, SLOT (selectionSizeChanged (int))); @@ -81,9 +82,9 @@ void CSVWorld::TableSubView::setEditLock (bool locked) mBottom->setEditLock (locked); } -void CSVWorld::TableSubView::editRequest (int row) +void CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const std::string& hint) { - focusId (mTable->getUniversalId (row)); + focusId (id, hint); } void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b3c253919b..910fec325b 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -53,7 +53,7 @@ namespace CSVWorld private slots: - void editRequest (int row); + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void cloneRequest (const CSMWorld::UniversalId& toClone); void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, Qt::DropAction action); From 6e8b9c88b2f3030f36fe15b0531a9ce82e51df17 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 22:43:15 +0100 Subject: [PATCH 044/240] changed name of default worldspace (should reduce chance of a name conflict) --- components/esm/loadcell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index dd7bf3e427..55d043c8a4 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -249,7 +249,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) if (id.mPaged) { - id.mWorldspace = "default"; + id.mWorldspace = "sys::default"; id.mIndex.mX = mData.mX; id.mIndex.mY = mData.mY; } From 26c2f28879a1f89ddb26ee9ec204388e2cfd0613 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Mar 2014 22:43:44 +0100 Subject: [PATCH 045/240] replaced global world->scene menu item with individual view context menu items in cell and reference tables --- apps/opencs/model/world/collection.hpp | 4 +- apps/opencs/model/world/collectionbase.hpp | 2 +- apps/opencs/model/world/data.cpp | 8 ++-- apps/opencs/model/world/idtable.cpp | 46 +++++++++++++++++++++- apps/opencs/model/world/idtable.hpp | 23 +++++++++-- apps/opencs/view/doc/view.cpp | 9 ----- apps/opencs/view/doc/view.hpp | 2 - apps/opencs/view/world/table.cpp | 25 ++++++++++++ apps/opencs/view/world/table.hpp | 3 ++ 9 files changed, 99 insertions(+), 23 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index d342e88a47..9d97e36c76 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -98,7 +98,7 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - virtual void cloneRecord(const std::string& origin, + virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type); @@ -198,7 +198,7 @@ namespace CSMWorld } template - void Collection::cloneRecord(const std::string& origin, + void Collection::cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) { diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 408a89d923..442055d5f3 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -74,7 +74,7 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. - virtual void cloneRecord(const std::string& origin, + virtual void cloneRecord(const std::string& origin, const std::string& destination, const UniversalId::Type type) = 0; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 8d53c4e53a..d68b79ff0a 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -247,12 +247,12 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); - addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); - addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); + addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); + addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); - addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference, false); + addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell), UniversalId::Type_References, UniversalId::Type_Reference, false); addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index bea307aa21..453a7da6a3 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -4,8 +4,9 @@ #include "collectionbase.hpp" #include "columnbase.hpp" -CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering) -: mIdCollection (idCollection), mReordering (reordering) +CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering, + Viewing viewing) +: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing) {} CSMWorld::IdTable::~IdTable() @@ -188,4 +189,45 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const { return mReordering; +} + +CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const +{ + return mViewing; +} + +std::pair CSMWorld::IdTable::view (int row) const +{ + std::string id; + std::string hint; + + if (mViewing==Viewing_Cell) + { + int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell); + int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); + + if (cellColumn!=-1 && idColumn!=-1) + { + id = mIdCollection->getData (row, cellColumn).toString().toUtf8().constData(); + hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData()); + } + } + else if (mViewing==Viewing_Id) + { + int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); + + if (column!=-1) + { + id = mIdCollection->getData (row, column).toString().toUtf8().constData(); + hint = "c:" + id; + } + } + + if (id.empty()) + return std::make_pair (UniversalId::Type_None, ""); + + if (id[0]=='#') + id = "sys::default"; + + return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 74923867d6..5a271de443 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -25,10 +25,20 @@ namespace CSMWorld Reordering_WithinTopic }; + enum Viewing + { + Viewing_None, + Viewing_Id, // use ID column to generate view request (ID is transformed into + // worldspace and original ID is passed as hint with c: prefix) + Viewing_Cell // use cell column to generate view request (cell ID is transformed + // into worldspace and record ID is passed as hint with r: prefix) + }; + private: CollectionBase *mIdCollection; Reordering mReordering; + Viewing mViewing; // not implemented IdTable (const IdTable&); @@ -36,7 +46,8 @@ namespace CSMWorld public: - IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic); + IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_None, + Viewing viewing = Viewing_None); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -63,8 +74,8 @@ namespace CSMWorld void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); ///< \param type Will be ignored, unless the collection supports multiple record types - void cloneRecord(const std::string& origin, - const std::string& destination, + void cloneRecord(const std::string& origin, + const std::string& destination, UniversalId::Type type = UniversalId::Type_None); QModelIndex getModelIndex (const std::string& id, int column) const; @@ -86,6 +97,12 @@ namespace CSMWorld /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). Reordering getReordering() const; + + Viewing getViewing() const; + + std::pair view (int row) const; + ///< Return the UniversalId and the hint for viewing \a row. If viewing is not + /// supported by this table, return (UniversalId::Type_None, ""). }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 1b271fcfcf..de3f476af1 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -115,10 +115,6 @@ void CSVDoc::View::setupWorldMenu() world->addSeparator(); // items that don't represent single record lists follow here - QAction *scene = new QAction (tr ("Scene"), this); - connect (scene, SIGNAL (triggered()), this, SLOT (addSceneSubView())); - world->addAction (scene); - QAction *regionMap = new QAction (tr ("Region Map"), this); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); world->addAction (regionMap); @@ -432,11 +428,6 @@ void CSVDoc::View::addFiltersSubView() addSubView (CSMWorld::UniversalId::Type_Filters); } -void CSVDoc::View::addSceneSubView() -{ - addSubView (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "default")); -} - void CSVDoc::View::addTopicsSubView() { addSubView (CSMWorld::UniversalId::Type_Topics); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index a1f6bea717..ee7380e2b1 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -168,8 +168,6 @@ namespace CSVDoc void addFiltersSubView(); - void addSceneSubView(); - void addTopicsSubView(); void addJournalsSubView(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 2c4651dd7f..95433dbfc0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -35,8 +35,12 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (selectedRows.size()==1) { menu.addAction (mEditAction); + if (mCreateAction) menu.addAction(mCloneAction); + + if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) + menu.addAction (mViewAction); } if (mCreateAction) @@ -230,6 +234,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); addAction (mMoveDownAction); + mViewAction = new QAction (tr ("View"), this); + connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); + addAction (mViewAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); @@ -381,6 +389,23 @@ void CSVWorld::Table::moveDownRecord() } } +void CSVWorld::Table::viewRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + + std::pair params = mModel->view (row); + + if (params.first.getType()!=CSMWorld::UniversalId::Type_None) + emit editRequest (params.first, params.second); + } +} + void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) { int columns = mModel->columnCount(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 995bc00955..3bf673dac3 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -43,6 +43,7 @@ namespace CSVWorld QAction *mDeleteAction; QAction *mMoveUpAction; QAction *mMoveDownAction; + QAction *mViewAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; @@ -112,6 +113,8 @@ namespace CSVWorld void moveDownRecord(); + void viewRecord(); + public slots: void tableSizeUpdate(); From 0ae9cc0106b2f82e8583cc90815caabf8164930a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Mar 2014 08:21:16 +0100 Subject: [PATCH 046/240] removed unused function --- apps/openmw/mwworld/scene.cpp | 49 ----------------------------------- 1 file changed, 49 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 508127837d..a6493ecf84 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -79,55 +79,6 @@ namespace return true; } - - template - void insertCellRefList(MWRender::RenderingManager& rendering, - T& cellRefList, MWWorld::CellStore &cell, MWWorld::PhysicsSystem& physics, bool rescale, Loading::Listener* loadingListener) - { - if (!cellRefList.mList.empty()) - { - const MWWorld::Class& class_ = - MWWorld::Class::get (MWWorld::Ptr (&*cellRefList.mList.begin(), &cell)); - for (typename T::List::iterator it = cellRefList.mList.begin(); - it != cellRefList.mList.end(); it++) - { - if (rescale) - { - if (it->mRef.mScale<0.5) - it->mRef.mScale = 0.5; - else if (it->mRef.mScale>2) - it->mRef.mScale = 2; - } - - if (it->mData.getCount() && it->mData.isEnabled()) - { - MWWorld::Ptr ptr (&*it, &cell); - - try - { - rendering.addObject(ptr); - class_.insertObject(ptr, physics); - - float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); - MWBase::Environment::get().getWorld()->localRotateObject(ptr, ax, ay, az); - - MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().mScale); - class_.adjustPosition(ptr); - } - catch (const std::exception& e) - { - std::string error ("error during rendering: "); - std::cerr << error + e.what() << std::endl; - } - } - - loadingListener->increaseProgress(1); - } - } - } - } From 6101916b7d63f2afe5ede3d659a46f11ea9df147 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Mar 2014 08:52:48 +0100 Subject: [PATCH 047/240] bumped version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae9ec8ac02..07b8ce289d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) message(STATUS "Configuring OpenMW...") set(OPENMW_VERSION_MAJOR 0) -set(OPENMW_VERSION_MINOR 28) +set(OPENMW_VERSION_MINOR 29) set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_COMMITHASH "") diff --git a/readme.txt b/readme.txt index 26abdaecc4..bb17a68e70 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.28.0 +Version: 0.29.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From a264e86e1300135866ce7bcf942853a512ca32e2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Mar 2014 14:47:43 +0100 Subject: [PATCH 048/240] subclasses scene widget for worldspace scenes --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/worldspacewidget.cpp | 6 ++++++ apps/opencs/view/render/worldspacewidget.hpp | 18 ++++++++++++++++++ apps/opencs/view/world/scenesubview.cpp | 4 ++-- 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/view/render/worldspacewidget.cpp create mode 100644 apps/opencs/view/render/worldspacewidget.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 119280f132..72757556db 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -64,7 +64,7 @@ opencs_units (view/world ) opencs_units (view/render - scenewidget + scenewidget worldspacewidget ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp new file mode 100644 index 0000000000..c88821a629 --- /dev/null +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -0,0 +1,6 @@ + +#include "worldspacewidget.hpp" + +CSVRender::WorldspaceWidget::WorldspaceWidget (QWidget *parent) +: SceneWidget (parent) +{} \ No newline at end of file diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp new file mode 100644 index 0000000000..1c122c9353 --- /dev/null +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -0,0 +1,18 @@ +#ifndef OPENCS_VIEW_WORLDSPACEWIDGET_H +#define OPENCS_VIEW_WORLDSPACEWIDGET_H + +#include "scenewidget.hpp" + +namespace CSVRender +{ + class WorldspaceWidget : public SceneWidget + { + Q_OBJECT + + public: + + WorldspaceWidget (QWidget *parent = 0); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index a23931950e..516a5db802 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,7 +9,7 @@ #include "../filter/filterbox.hpp" -#include "../render/scenewidget.hpp" +#include "../render/worldspacewidget.hpp" #include "tablebottombox.hpp" #include "creator.hpp" @@ -44,7 +44,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout2->addWidget (toolbar, 0); - mScene = new CSVRender::SceneWidget(this); + mScene = new CSVRender::WorldspaceWidget (this); layout2->addWidget (mScene, 1); From ce8d327e8e8e1f528c6dad95f982a5b61f9edf1f Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 5 Mar 2014 22:11:10 +1100 Subject: [PATCH 049/240] Debug MSVC build of openmw needs number of sections beyond 2^16 --- apps/openmw/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index be583ea749..eb502de384 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -159,3 +159,10 @@ if (BUILD_WITH_CODE_COVERAGE) add_definitions (--coverage) target_link_libraries(openmw gcov) endif() + +if (MSVC) + # Debug version needs increased number of sections beyond 2^16 + if (CMAKE_CL_64) + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") + endif (CMAKE_CL_64) +endif (MSVC) From 5539c75ef08949ec76efe62cc9e7dfb7fdfc7906 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Wed, 5 Mar 2014 22:12:59 +1100 Subject: [PATCH 050/240] Typo fix to avoid divide by zero. --- extern/oics/ICSInputControlSystem_joystick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 21adc9f74a..35762d2b33 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -385,7 +385,7 @@ namespace ICS { ctrl->setIgnoreAutoReverse(true); - float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MAX; + float axisRange = SDL_JOY_AXIS_MAX - SDL_JOY_AXIS_MIN; float valDisplaced = (float)(evt.value - SDL_JOY_AXIS_MIN); if(joystickBinderItem.direction == Control::INCREASE) From ab224f93c917099134fa95198d3836a6fc13c256 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Wed, 5 Mar 2014 17:08:58 +0100 Subject: [PATCH 051/240] remove our stdint.h version that uses boost and force usage of system stdint.h --- CMakeLists.txt | 8 +++++++ apps/openmw/mwbase/dialoguemanager.hpp | 2 +- apps/openmw/mwbase/journal.hpp | 2 +- apps/openmw/mwgui/bookpage.cpp | 2 +- apps/openmw/mwgui/bookpage.hpp | 2 +- apps/openmw/mwgui/journalviewmodel.hpp | 2 +- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwscript/globalscripts.hpp | 2 +- apps/openmw/mwsound/openal_output.cpp | 2 ++ apps/openmw/mwworld/globals.hpp | 2 +- components/bsa/bsa_file.hpp | 2 +- components/esm/defs.hpp | 2 +- components/esm/esmcommon.hpp | 2 +- components/esm/esmreader.hpp | 2 +- components/esm/loadland.hpp | 2 +- .../files/constrainedfiledatastream.cpp | 2 +- components/nif/niffile.hpp | 2 +- extern/shiny/Main/Preprocessor.hpp | 2 ++ libs/platform/stdint.h | 21 ------------------- 19 files changed, 27 insertions(+), 36 deletions(-) delete mode 100644 libs/platform/stdint.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 07b8ce289d..954e161a1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,14 @@ if (UNIX AND NOT APPLE) find_package (Threads) endif() +# Look for stdint.h +include(CheckIncludeFile) +check_include_file(stdint.h HAVE_STDINT_H) +if(NOT HAVE_STDINT_H) + unset(HAVE_STDINT_H CACHE) + message(FATAL_ERROR "stdint.h was not found" ) +endif() + include (CheckIncludeFileCXX) check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP) if (HAVE_UNORDERED_MAP) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 33bba07e15..3d70fdc6a7 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -3,7 +3,7 @@ #include -#include +#include namespace ESM { diff --git a/apps/openmw/mwbase/journal.hpp b/apps/openmw/mwbase/journal.hpp index 56d9601fc5..8e4e9703fa 100644 --- a/apps/openmw/mwbase/journal.hpp +++ b/apps/openmw/mwbase/journal.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/topic.hpp" diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 694970e23d..52682342f7 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -6,7 +6,7 @@ #include "MyGUI_TextureUtility.h" #include "MyGUI_FactoryManager.h" -#include +#include #include #include #include diff --git a/apps/openmw/mwgui/bookpage.hpp b/apps/openmw/mwgui/bookpage.hpp index 28aa371cf3..458cf2a199 100644 --- a/apps/openmw/mwgui/bookpage.hpp +++ b/apps/openmw/mwgui/bookpage.hpp @@ -5,7 +5,7 @@ #include "MyGUI_Widget.h" #include -#include +#include #include #include diff --git a/apps/openmw/mwgui/journalviewmodel.hpp b/apps/openmw/mwgui/journalviewmodel.hpp index 21c3a11785..9efdeae546 100644 --- a/apps/openmw/mwgui/journalviewmodel.hpp +++ b/apps/openmw/mwgui/journalviewmodel.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 1e52ff26af..2494775513 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -1,7 +1,7 @@ #ifndef MWGUI_MAPWINDOW_H #define MWGUI_MAPWINDOW_H -#include +#include #include "windowpinnablebase.hpp" diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index a4a7662263..de63b99067 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include "locals.hpp" diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 7563ad0150..9a3dd73422 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include "openal_output.hpp" diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index 8f521c8a68..d8d2cefbf7 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index 6f3ab3bceb..017adf1e31 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -24,7 +24,7 @@ #ifndef BSA_BSA_FILE_H #define BSA_BSA_FILE_H -#include +#include #include #include #include diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index c1f1679924..57842796f5 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_DEFS_H #define OPENMW_ESM_DEFS_H -#include +#include namespace ESM { diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index 6f51c767ec..d3e6e7feae 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include namespace ESM diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 897c8fe731..b6c0ebc701 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_READER_H #define OPENMW_ESM_READER_H -#include +#include #include #include #include diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 32abb77993..028341ced5 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_ESM_LAND_H #define OPENMW_ESM_LAND_H -#include +#include #include "esmcommon.hpp" diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 321bcf7c8c..66f6fde97c 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace { diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 91ae93b40b..79e1cc2a52 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -42,7 +42,7 @@ #include #include -#include +#include #include "record.hpp" #include "niftypes.hpp" diff --git a/extern/shiny/Main/Preprocessor.hpp b/extern/shiny/Main/Preprocessor.hpp index 7ee30ae7fc..4eb5334993 100644 --- a/extern/shiny/Main/Preprocessor.hpp +++ b/extern/shiny/Main/Preprocessor.hpp @@ -1,6 +1,8 @@ #ifndef SH_PREPROCESSOR_H #define SH_PREPROCESSOR_H +#include + #include #include diff --git a/libs/platform/stdint.h b/libs/platform/stdint.h deleted file mode 100644 index 00af741b1e..0000000000 --- a/libs/platform/stdint.h +++ /dev/null @@ -1,21 +0,0 @@ -// Wrapper for MSVC -#ifndef _STDINT_WRAPPER_H -#define _STDINT_WRAPPER_H - -#if (_MSC_VER >= 1600) - -#include - -#else - -#include - -// Pull the boost names into the global namespace for convenience -using boost::int32_t; -using boost::uint32_t; -using boost::int64_t; -using boost::uint64_t; - -#endif - -#endif From c9e349f60f2823cd7c98fdc13c2d5f4a8ee67c46 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Feb 2014 18:48:35 +0100 Subject: [PATCH 052/240] Terrain: support alternate coordinate systems. Get rid of LoadingListener for now --- apps/openmw/mwrender/renderingmanager.cpp | 7 +-- apps/openmw/mwrender/terrainstorage.cpp | 2 +- apps/openmw/mwrender/terrainstorage.hpp | 2 +- components/terrain/chunk.cpp | 2 +- components/terrain/defs.hpp | 41 ++++++++++++++ components/terrain/material.cpp | 1 - components/terrain/quadtreenode.cpp | 46 ++++++++++++---- components/terrain/quadtreenode.hpp | 6 +-- components/terrain/storage.hpp | 6 +-- components/terrain/world.cpp | 66 +++++++++++++++-------- components/terrain/world.hpp | 25 +++++---- 11 files changed, 147 insertions(+), 57 deletions(-) create mode 100644 components/terrain/defs.hpp diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 928a341075..cf97afa7ac 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1043,15 +1043,12 @@ void RenderingManager::enableTerrain(bool enable) { if (!mTerrain) { - Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); - Loading::ScopedLoad load(listener); - mTerrain = new Terrain::World(listener, mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, + mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain, Settings::Manager::getBool("distant land", "Terrain"), - Settings::Manager::getBool("shader", "Terrain")); + Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64); mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"), Settings::Manager::getBool("split", "Shadows")); mTerrain->update(mRendering.getCamera()->getRealPosition()); - mTerrain->setLoadingListener(NULL); } mTerrain->setVisible(true); } diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 750441f6a7..2ccf3bd0a7 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -167,7 +167,7 @@ namespace MWRender } - void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 2ef014aaf1..f84da5c5dc 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -36,7 +36,7 @@ namespace MWRender /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer); diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index a5c6290884..a05066313c 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -52,7 +52,7 @@ namespace Terrain mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), + mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), mNode->getTerrain()->getAlign(), mVertexBuffer, mNormalBuffer, mColourBuffer); mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp new file mode 100644 index 0000000000..9f0b8c09b2 --- /dev/null +++ b/components/terrain/defs.hpp @@ -0,0 +1,41 @@ +#ifndef COMPONENTS_TERRAIN_DEFS_HPP +#define COMPONENTS_TERRAIN_DEFS_HPP + +namespace Terrain +{ + + /// The alignment of the terrain + enum Alignment + { + /// Terrain is in the X/Z plane + Align_XZ = 0, + /// Terrain is in the X/Y plane + Align_XY = 1, + /// Terrain is in the Y/Z plane. + /// UNTESTED - use at own risk. + /// Besides, X as up axis? What is wrong with you? ;) + Align_YZ = 2 + }; + + inline void convertPosition(Alignment align, float &x, float &y, float &z) + { + switch (align) + { + case Align_XY: + return; + case Align_XZ: + std::swap(y, z); + // This is since -Z should be going *into* the screen + // If not doing this, we'd get wrong vertex winding + z *= -1; + return; + case Align_YZ: + std::swap(x, y); + std::swap(y, z); + return; + } + } + +} + +#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 8e78d22166..31874af519 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -235,7 +235,6 @@ namespace Terrain sh::MaterialInstancePass* p = material->createPass (); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); if (layerOffset != 0) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 82ccc7c89a..cb7660fedc 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -169,7 +169,11 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const pos = mParent->getCenter(); pos = mCenter - pos; float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - mSceneNode->setPosition(Ogre::Vector3(pos.x*cellWorldSize, pos.y*cellWorldSize, 0)); + + Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0); + mTerrain->convertPosition(sceneNodePos.x, sceneNodePos.y, sceneNodePos.z); + + mSceneNode->setPosition(sceneNodePos); mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } @@ -212,11 +216,31 @@ void QuadTreeNode::initAabb() mChildren[i]->initAabb(); mBounds.merge(mChildren[i]->getBoundingBox()); } - mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, mBounds.getMinimum().z), - Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, mBounds.getMaximum().z)); + float minH, maxH; + switch (mTerrain->getAlign()) + { + case Terrain::Align_XY: + minH = mBounds.getMinimum().z; + maxH = mBounds.getMaximum().z; + break; + case Terrain::Align_XZ: + minH = mBounds.getMinimum().y; + maxH = mBounds.getMinimum().y; + break; + case Terrain::Align_YZ: + minH = mBounds.getMinimum().x; + maxH = mBounds.getMaximum().x; + break; + } + Ogre::Vector3 min(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, minH); + Ogre::Vector3 max(Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, maxH)); + mBounds = Ogre::AxisAlignedBox (min, max); + mTerrain->convertBounds(mBounds); } - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0), - mBounds.getMaximum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0)); + Ogre::Vector3 offset(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0); + mTerrain->convertPosition(offset); + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + offset, + mBounds.getMaximum() + offset); } void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) @@ -229,7 +253,12 @@ const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox() return mBounds; } -void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loadingListener) +const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox() +{ + return mWorldBounds; +} + +void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) { const Ogre::AxisAlignedBox& bounds = getBoundingBox(); if (bounds.isNull()) @@ -263,9 +292,6 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loa bool hadChunk = hasChunk(); - if (loadingListener) - loadingListener->indicateProgress(); - if (!distantLand && dist > 8192*2) { if (mIsActive) @@ -353,7 +379,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos, Loading::Listener* loa } assert(hasChildren() && "Leaf node's LOD needs to be 0"); for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos, loadingListener); + mChildren[i]->update(cameraPos); } } diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index ea299c096d..32c7dd56aa 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -5,8 +5,6 @@ #include #include -#include - namespace Ogre { class Rectangle2D; @@ -95,10 +93,12 @@ namespace Terrain /// Get bounding box in local coordinates const Ogre::AxisAlignedBox& getBoundingBox(); + const Ogre::AxisAlignedBox& getWorldBoundingBox(); + World* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - void update (const Ogre::Vector3& cameraPos, Loading::Listener* loadingListener); + void update (const Ogre::Vector3& cameraPos); /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. /// Call after QuadTreeNode::update! diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index d8cdab9ec1..d7fb784924 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -1,10 +1,10 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include - #include +#include "defs.hpp" + namespace Terrain { @@ -43,7 +43,7 @@ namespace Terrain /// @param vertexBuffer buffer to write vertices /// @param normalBuffer buffer to write vertex normals /// @param colourBuffer buffer to write vertex colours - virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, + virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, Ogre::HardwareVertexBufferSharedPtr vertexBuffer, Ogre::HardwareVertexBufferSharedPtr normalBuffer, Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 4273f227d6..49a561c0ef 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include "storage.hpp" #include "quadtreenode.hpp" @@ -51,27 +49,25 @@ namespace namespace Terrain { - World::World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool distantLand, bool shaders) + World::World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) : mStorage(storage) - , mMinBatchSize(1) - , mMaxBatchSize(64) + , mMinBatchSize(minBatchSize) + , mMaxBatchSize(maxBatchSize) , mSceneMgr(sceneMgr) , mVisibilityFlags(visibilityFlags) , mDistantLand(distantLand) , mShaders(shaders) , mVisible(true) - , mLoadingListener(loadingListener) + , mAlign(align) , mMaxX(0) , mMinX(0) , mMaxY(0) , mMinY(0) { - loadingListener->setLabel("Creating terrain"); - loadingListener->indicateProgress(); - mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + /// \todo make composite map size configurable Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, @@ -96,11 +92,11 @@ namespace Terrain mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); buildQuadTree(mRootNode); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); mRootNode->initAabb(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); mRootNode->initNeighbours(); - loadingListener->indicateProgress(); + //loadingListener->indicateProgress(); } World::~World() @@ -120,8 +116,12 @@ namespace Terrain Ogre::Vector2 center = node->getCenter(); float cellWorldSize = getStorage()->getCellWorldSize(); if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), - Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ))); + { + Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); + convertBounds(bounds); + node->setBoundingBox(bounds); + } else node->markAsDummy(); // no data available for this node, skip it return; @@ -161,7 +161,7 @@ namespace Terrain { if (!mVisible) return; - mRootNode->update(cameraPos, mLoadingListener); + mRootNode->update(cameraPos); mRootNode->updateIndexBuffers(); } @@ -173,11 +173,7 @@ namespace Terrain || center.y < mMinY) return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); - Ogre::AxisAlignedBox box = node->getBoundingBox(); - float cellWorldSize = getStorage()->getCellWorldSize(); - box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize, - box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize); - return box; + return node->getWorldBoundingBox(); } Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) @@ -414,5 +410,33 @@ namespace Terrain return mVisible; } + void World::convertPosition(float &x, float &y, float &z) + { + Terrain::convertPosition(mAlign, x, y, z); + } + + void World::convertPosition(Ogre::Vector3 &pos) + { + convertPosition(pos.x, pos.y, pos.z); + } + + void World::convertBounds(Ogre::AxisAlignedBox& bounds) + { + switch (mAlign) + { + case Align_XY: + return; + case Align_XZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + // Because we changed sign of Z + std::swap(bounds.getMinimum().z, bounds.getMaximum().z); + return; + case Align_YZ: + convertPosition(bounds.getMinimum()); + convertPosition(bounds.getMaximum()); + return; + } + } } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index bf733b889a..31bd51ab15 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -6,10 +6,7 @@ #include #include -namespace Loading -{ - class Listener; -} +#include "defs.hpp" namespace Ogre { @@ -33,7 +30,6 @@ namespace Terrain { public: /// @note takes ownership of \a storage - /// @param loadingListener Listener to update with progress /// @param sceneMgr scene manager to use /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param visbilityFlags visibility flags for the created meshes @@ -41,12 +37,13 @@ namespace Terrain /// This is a temporary option until it can be streamlined. /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually /// faster so this is just here for compatibility. - World(Loading::Listener* loadingListener, Ogre::SceneManager* sceneMgr, - Storage* storage, int visiblityFlags, bool distantLand, bool shaders); + /// @param align The align of the terrain, see Alignment enum + /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. + /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. + World(Ogre::SceneManager* sceneMgr, + Storage* storage, int visiblityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); ~World(); - void setLoadingListener(Loading::Listener* loadingListener) { mLoadingListener = loadingListener; } - bool getDistantLandEnabled() { return mDistantLand; } bool getShadersEnabled() { return mShaders; } bool getShadowsEnabled() { return mShadows; } @@ -86,14 +83,15 @@ namespace Terrain void enableSplattingShader(bool enabled); + Alignment getAlign() { return mAlign; } + private: bool mDistantLand; bool mShaders; bool mShadows; bool mSplitShadows; bool mVisible; - - Loading::Listener* mLoadingListener; + Alignment mAlign; QuadTreeNode* mRootNode; Ogre::SceneNode* mRootSceneNode; @@ -138,6 +136,11 @@ namespace Terrain void clearCompositeMapSceneManager(); void renderCompositeMap (Ogre::TexturePtr target); + // Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign + void convertPosition (float& x, float& y, float& z); + void convertPosition (Ogre::Vector3& pos); + void convertBounds (Ogre::AxisAlignedBox& bounds); + private: // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. From 64c99325974deec0fbe0839df488a71e40f4ceac Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Feb 2014 19:04:12 +0100 Subject: [PATCH 053/240] Terrain: remove hard dependency on shiny - can now be compiled without it (fixed function) --- components/CMakeLists.txt | 1 + components/terrain/material.cpp | 6 ++++++ components/terrain/world.cpp | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 831b140575..938ef2e4dc 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -73,6 +73,7 @@ add_component_dir (translation translation ) +add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain quadtreenode chunk world storage material ) diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 31874af519..78b17909a5 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -6,7 +6,9 @@ #include +#if TERRAIN_USE_SHADER #include +#endif namespace { @@ -64,7 +66,9 @@ namespace Terrain assert(!renderCompositeMap || !displayCompositeMap); if (!mat.isNull()) { +#if TERRAIN_USE_SHADER sh::Factory::getInstance().destroyMaterialInstance(mat->getName()); +#endif Ogre::MaterialManager::getSingleton().remove(mat->getName()); } @@ -144,6 +148,7 @@ namespace Terrain return mat; } +#if TERRAIN_USE_SHADER else { sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance (name.str()); @@ -343,6 +348,7 @@ namespace Terrain } } } +#endif return Ogre::MaterialManager::getSingleton().getByName(name.str()); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 49a561c0ef..e633c1a00a 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -65,6 +65,12 @@ namespace Terrain , mMaxY(0) , mMinY(0) { +#if TERRAIN_USE_SHADER == 0 + if (mShaders) + std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl; + mShaders = false; +#endif + mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); /// \todo make composite map size configurable From 8730b61362fdee33869a2ac1870fb0aa69a5534c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 17 Feb 2014 15:00:01 +0100 Subject: [PATCH 054/240] Render maps after *all* cells have finished loading Still not fixing Bug #772, but at least this will allow for background loading of terrain. --- apps/openmw/mwworld/scene.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a6493ecf84..40004807b4 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -162,8 +162,6 @@ namespace MWWorld mRendering.cellAdded (cell); mRendering.configureAmbient(*cell); - mRendering.requestMap(cell); - mRendering.configureAmbient(*cell); } // register local scripts @@ -198,6 +196,9 @@ namespace MWWorld mechMgr->watchActor(player); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + + for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) + mRendering.requestMap(*active); } void Scene::changeToVoid() From 6c863486e18f08ce40ee185817e2047d705c2cb2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 17 Feb 2014 22:09:07 +0100 Subject: [PATCH 055/240] Terrain: fix an embarrassing copy&paste mistake. --- components/terrain/quadtreenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index cb7660fedc..3969d4761e 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -225,7 +225,7 @@ void QuadTreeNode::initAabb() break; case Terrain::Align_XZ: minH = mBounds.getMinimum().y; - maxH = mBounds.getMinimum().y; + maxH = mBounds.getMaximum().y; break; case Terrain::Align_YZ: minH = mBounds.getMinimum().x; From 065b6d3331d7190746cba67d479d8f9e552d6472 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Feb 2014 12:03:35 +0100 Subject: [PATCH 056/240] Terrain: moved ESM::Land load to earlier in the startup procedure Required for background loading as we cannot load ESM::Land data from background threads. --- apps/openmw/mwrender/globalmap.cpp | 7 +++---- apps/openmw/mwrender/terrainstorage.cpp | 7 ------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 018dc082a3..76ad1890fa 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -75,10 +75,9 @@ namespace MWRender if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VHGT)) - { - land->loadData(ESM::Land::DATA_VHGT); - } + int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(mask)) + land->loadData(mask); } for (int cellY=0; cellYgetStore(); ESM::Land* land = esmStore.get().search(cellX, cellY); - // Load the data we are definitely going to need - int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; - if (land && !land->isDataLoaded(mask)) - land->loadData(mask); return land; } @@ -316,9 +312,6 @@ namespace MWRender ESM::Land* land = getLand(cellX, cellY); if (land) { - if (!land->isDataLoaded(ESM::Land::DATA_VTEX)) - land->loadData(ESM::Land::DATA_VTEX); - int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin From b3fed853ae3931655fb094cf0c5efd4ff12e41cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Feb 2014 12:27:22 +0100 Subject: [PATCH 057/240] Terrain: take cell world size into account for LOD selection --- components/terrain/quadtreenode.cpp | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 3969d4761e..44bf320e40 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -171,7 +171,7 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0); - mTerrain->convertPosition(sceneNodePos.x, sceneNodePos.y, sceneNodePos.z); + mTerrain->convertPosition(sceneNodePos); mSceneNode->setPosition(sceneNodePos); @@ -274,21 +274,23 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) mParent->getSceneNode()->addChild(mSceneNode); } - /// \todo implement error metrics or some other means of not using arbitrary values - /// (general quality needs to be user configurable as well) + // Simple LOD selection + /// \todo use error metrics? size_t wantedLod = 0; - if (dist > 8192*1) - wantedLod = 1; - if (dist > 8192*2) - wantedLod = 2; - if (dist > 8192*5) - wantedLod = 3; - if (dist > 8192*12) - wantedLod = 4; - if (dist > 8192*32) - wantedLod = 5; - if (dist > 8192*64) + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); + + if (dist > cellWorldSize*64) wantedLod = 6; + else if (dist > cellWorldSize*32) + wantedLod = 5; + else if (dist > cellWorldSize*12) + wantedLod = 4; + else if (dist > cellWorldSize*5) + wantedLod = 3; + else if (dist > cellWorldSize*2) + wantedLod = 2; + else if (dist > cellWorldSize) + wantedLod = 1; bool hadChunk = hasChunk(); From 195071efc7f69359358d7dde50837facc6763884 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Feb 2014 16:44:37 +0100 Subject: [PATCH 058/240] Terrain: geometry is now loaded in background threads. TODO: background load layer textures and blendmaps. "Distant land" setting has been removed for now (i.e. always enabled). --- apps/openmw/mwrender/renderingmanager.cpp | 5 + apps/openmw/mwrender/terrainstorage.cpp | 17 +-- apps/openmw/mwrender/terrainstorage.hpp | 18 ++- apps/openmw/mwworld/store.hpp | 4 + components/terrain/chunk.cpp | 11 +- components/terrain/chunk.hpp | 5 +- components/terrain/defs.hpp | 7 + components/terrain/quadtreenode.cpp | 171 +++++++++++----------- components/terrain/quadtreenode.hpp | 16 ++ components/terrain/storage.hpp | 27 ++-- components/terrain/world.cpp | 69 +++++++++ components/terrain/world.hpp | 42 +++++- 12 files changed, 266 insertions(+), 126 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cf97afa7ac..68bf957c8c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -651,6 +651,11 @@ void RenderingManager::setGlare(bool glare) void RenderingManager::requestMap(MWWorld::CellStore* cell) { + // FIXME: move to other method + // TODO: probably not needed when crossing a cell border. Could delay the map render until we are loaded. + if (mTerrain) + mTerrain->syncLoad(); + if (cell->getCell()->isExterior()) { assert(mTerrain); diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 8597c762c5..ec26b18720 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -164,9 +164,9 @@ namespace MWRender } void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) + std::vector& positions, + std::vector& normals, + std::vector& colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = 1 << lodLevel; @@ -180,11 +180,8 @@ namespace MWRender size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; - std::vector colors; - colors.resize(numVerts*numVerts*4); - std::vector positions; + colours.resize(numVerts*numVerts*4); positions.resize(numVerts*numVerts*3); - std::vector normals; normals.resize(numVerts*numVerts*3); Ogre::Vector3 normal; @@ -270,7 +267,7 @@ namespace MWRender color.a = 1; Ogre::uint32 rsColor; Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); - memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); + memcpy(&colours[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); ++vertX; } @@ -283,10 +280,6 @@ namespace MWRender assert(vertX_ == numVerts); // Ensure we covered whole area } assert(vertY_ == numVerts); // Ensure we covered whole area - - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true); } TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY, diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index f84da5c5dc..b1acdb7859 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -19,8 +19,8 @@ namespace MWRender /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); - /// Get the minimum and maximum heights of a terrain chunk. - /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Get the minimum and maximum heights of a terrain region. + /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units @@ -30,16 +30,18 @@ namespace MWRender virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); /// Fill vertex buffers for a terrain chunk. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units - /// @param vertexBuffer buffer to write vertices - /// @param normalBuffer buffer to write vertex normals - /// @param colourBuffer buffer to write vertex colours + /// @param positions buffer to write vertices + /// @param normals buffer to write vertex normals + /// @param colours buffer to write vertex colours virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer); + std::vector& positions, + std::vector& normals, + std::vector& colours); /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 7bd00d6bfd..6b99c0a0c4 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -388,6 +388,8 @@ namespace MWWorld typedef std::vector::const_iterator iterator; + // Must be threadsafe! Called from terrain background loading threads. + // Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased const ESM::LandTexture *search(size_t index, size_t plugin) const { assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; @@ -487,6 +489,8 @@ namespace MWWorld return iterator(mStatic.end()); } + // Must be threadsafe! Called from terrain background loading threads. + // Not a big deal here, since ESM::Land can never be modified or inserted/erased ESM::Land *search(int x, int y) const { ESM::Land land; land.mX = x, land.mY = y; diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index a05066313c..139694ee5c 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -10,9 +10,9 @@ namespace Terrain { - Chunk::Chunk(QuadTreeNode* node, short lodLevel) + Chunk::Chunk(QuadTreeNode* node, const LoadResponseData& data) : mNode(node) - , mVertexLod(lodLevel) + , mVertexLod(node->getNativeLodLevel()) , mAdditionalLod(0) { mVertexData = OGRE_NEW Ogre::VertexData; @@ -20,6 +20,8 @@ namespace Terrain unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); + size_t lodLevel = mNode->getNativeLodLevel(); + // Set the total number of vertices size_t numVertsOneSide = mNode->getSize() * (verts-1); numVertsOneSide /= 1 << lodLevel; @@ -52,8 +54,9 @@ namespace Terrain mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), mNode->getTerrain()->getAlign(), - mVertexBuffer, mNormalBuffer, mColourBuffer); + mVertexBuffer->writeData(0, mVertexBuffer->getSizeInBytes(), &data.mPositions[0], true); + mNormalBuffer->writeData(0, mNormalBuffer->getSizeInBytes(), &data.mNormals[0], true); + mColourBuffer->writeData(0, mColourBuffer->getSizeInBytes(), &data.mColours[0], true); mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index d74c65ba6d..a6477df284 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -8,6 +8,7 @@ namespace Terrain { class QuadTreeNode; + struct LoadResponseData; /** * @brief Renders a chunk of terrain, either using alpha splatting or a composite map. @@ -15,8 +16,8 @@ namespace Terrain class Chunk : public Ogre::Renderable, public Ogre::MovableObject { public: - /// @param lodLevel LOD level for the vertex buffer. - Chunk (QuadTreeNode* node, short lodLevel); + Chunk (QuadTreeNode* node, const LoadResponseData& data); + virtual ~Chunk(); void setMaterial (const Ogre::MaterialPtr& material); diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index 9f0b8c09b2..c14608e76d 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -36,6 +36,13 @@ namespace Terrain } } + struct LayerInfo + { + std::string mDiffuseMap; + std::string mNormalMap; + bool mParallax; // Height info in normal map alpha channel? + bool mSpecular; // Specular info in diffuse map alpha channel? + }; } #endif diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 44bf320e40..721771ff36 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -153,6 +153,7 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const , mParent(parent) , mTerrain(terrain) , mChunk(NULL) + , mLoadState(LS_Unloaded) { mBounds.setNull(); for (int i=0; i<4; ++i) @@ -266,8 +267,6 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) float dist = distance(mWorldBounds, cameraPos); - bool distantLand = mTerrain->getDistantLandEnabled(); - // Make sure our scene node is attached if (!mSceneNode->isInSceneGraph()) { @@ -292,100 +291,110 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) else if (dist > cellWorldSize) wantedLod = 1; - bool hadChunk = hasChunk(); - - if (!distantLand && dist > 8192*2) - { - if (mIsActive) - { - destroyChunks(true); - mIsActive = false; - } - return; - } - mIsActive = true; - if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod) + bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod; + + if (wantToDisplay) { // Wanted LOD is small enough to render this node in one chunk - if (!mChunk) + if (mLoadState == LS_Unloaded) { - mChunk = new Chunk(this, mLodLevel); - mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); - mChunk->setCastShadows(true); - mSceneNode->attachObject(mChunk); - - mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); - mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); - - if (mSize == 1) - { - ensureLayerInfo(); - mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); - } - else - { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); - } + mLoadState = LS_Loading; + mTerrain->queueLoad(this); } - // Additional (index buffer) LOD is currently disabled. - // This is due to a problem with the LOD selection when a node splits. - // After splitting, the distance is measured from the children's bounding boxes, which are possibly - // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD - // than the original node. - // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. - // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour - // node hasn't split yet, and has a higher LOD than our node's child: - // ----- ----- ------------ - // | LOD | LOD | | - // | 1 | 1 | | - // |-----|-----| LOD 0 | - // | LOD | LOD | | - // | 0 | 0 | | - // ----- ----- ------------ - // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're - // doing here. - // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. - //mChunk->setAdditionalLod(wantedLod - mLodLevel); - - mChunk->setVisible(true); - - if (!hadChunk && hasChildren()) + if (mLoadState == LS_Loaded) { - // Make sure child scene nodes are detached - mSceneNode->removeAllChildren(); + // Additional (index buffer) LOD is currently disabled. + // This is due to a problem with the LOD selection when a node splits. + // After splitting, the distance is measured from the children's bounding boxes, which are possibly + // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD + // than the original node. + // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. + // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour + // node hasn't split yet, and has a higher LOD than our node's child: + // ----- ----- ------------ + // | LOD | LOD | | + // | 1 | 1 | | + // |-----|-----| LOD 0 | + // | LOD | LOD | | + // | 0 | 0 | | + // ----- ----- ------------ + // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're + // doing here. + // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. + //mChunk->setAdditionalLod(wantedLod - mLodLevel); - // If distant land is enabled, keep the chunks around in case we need them again, - // otherwise, prefer low memory usage - if (!distantLand) - for (int i=0; i<4; ++i) - mChildren[i]->destroyChunks(true); + if (!mChunk->getVisible() && hasChildren()) + { + // Make sure child scene nodes are detached + mSceneNode->removeAllChildren(); + + // TODO: unload + //for (int i=0; i<4; ++i) + // mChildren[i]->unload(); + } + + mChunk->setVisible(true); } } - else + + if (wantToDisplay && mLoadState != LS_Loaded) { - // Wanted LOD is too detailed to be rendered in one chunk, - // so split it up by delegating to child nodes - if (hadChunk) + // We want to display, but aren't ready yet. Perhaps our child nodes are ready? + // TODO: this doesn't check child-child nodes... + if (hasChildren()) { - // If distant land is enabled, keep the chunks around in case we need them again, - // otherwise, prefer low memory usage - if (!distantLand) - destroyChunks(false); - else if (mChunk) - mChunk->setVisible(false); + for (int i=0; i<4; ++i) + if (mChildren[i]->hasChunk()) + mChildren[i]->update(cameraPos); + } + } + if (!wantToDisplay) + { + // We do not want to display this node - delegate to children + if (mChunk) + mChunk->setVisible(false); + if (hasChildren()) + { + for (int i=0; i<4; ++i) + mChildren[i]->update(cameraPos); } - assert(hasChildren() && "Leaf node's LOD needs to be 0"); - for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos); } } -void QuadTreeNode::destroyChunks(bool children) +void QuadTreeNode::load(const LoadResponseData &data) +{ + assert (!mChunk); + + std::cout << "loading " << std::endl; + mChunk = new Chunk(this, data); + mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); + mChunk->setCastShadows(true); + mSceneNode->attachObject(mChunk); + + mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); + mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); + + if (mSize == 1) + { + ensureLayerInfo(); + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } + + mChunk->setVisible(false); + + mLoadState = LS_Loaded; +} + +void QuadTreeNode::unload() { if (mChunk) { @@ -411,9 +420,7 @@ void QuadTreeNode::destroyChunks(bool children) mCompositeMap.setNull(); } } - else if (children && hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->destroyChunks(true); + mLoadState = LS_Unloaded; } void QuadTreeNode::updateIndexBuffers() diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 32c7dd56aa..0bba1024c0 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -15,6 +15,7 @@ namespace Terrain class World; class Chunk; class MaterialGenerator; + struct LoadResponseData; enum Direction { @@ -33,6 +34,13 @@ namespace Terrain Root }; + enum LoadState + { + LS_Unloaded, + LS_Loading, + LS_Loaded + }; + /** * @brief A node in the quad tree for our terrain. Depending on LOD, * a node can either choose to render itself in one batch (merging its children), @@ -124,10 +132,18 @@ namespace Terrain /// @param quads collect quads here so they can be deleted later void prepareForCompositeMap(Ogre::TRect area); + /// Create a chunk for this node from the given data. + void load (const LoadResponseData& data); + void unload(); + + LoadState getLoadState() { return mLoadState; } + private: // Stored here for convenience in case we need layer list again MaterialGenerator* mMaterialGenerator; + LoadState mLoadState; + /// Is this node (or any of its child nodes) currently configured to render itself? /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) bool mIsActive; diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index d7fb784924..1b1968f1ac 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -7,15 +7,6 @@ namespace Terrain { - - struct LayerInfo - { - std::string mDiffuseMap; - std::string mNormalMap; - bool mParallax; // Height info in normal map alpha channel? - bool mSpecular; // Specular info in diffuse map alpha channel? - }; - /// We keep storage of terrain data abstract here since we need different implementations for game and editor class Storage { @@ -26,8 +17,8 @@ namespace Terrain /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; - /// Get the minimum and maximum heights of a terrain chunk. - /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. + /// Get the minimum and maximum heights of a terrain region. + /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree. /// Larger chunks can simply merge AABB of children. /// @param size size of the chunk in cell units /// @param center center of the chunk in cell units @@ -37,16 +28,18 @@ namespace Terrain virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0; /// Fill vertex buffers for a terrain chunk. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @param lodLevel LOD level, 0 = most detailed /// @param size size of the terrain chunk in cell units /// @param center center of the chunk in cell units - /// @param vertexBuffer buffer to write vertices - /// @param normalBuffer buffer to write vertex normals - /// @param colourBuffer buffer to write vertex colours + /// @param positions buffer to write vertices + /// @param normals buffer to write vertex normals + /// @param colours buffer to write vertex colours virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, - Ogre::HardwareVertexBufferSharedPtr vertexBuffer, - Ogre::HardwareVertexBufferSharedPtr normalBuffer, - Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; + std::vector& positions, + std::vector& normals, + std::vector& colours) = 0; /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index e633c1a00a..87baeff841 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -64,6 +64,8 @@ namespace Terrain , mMinX(0) , mMaxY(0) , mMinY(0) + , mChunksLoading(0) + , mWorkQueueChannel(0) { #if TERRAIN_USE_SHADER == 0 if (mShaders) @@ -103,10 +105,19 @@ namespace Terrain //loadingListener->indicateProgress(); mRootNode->initNeighbours(); //loadingListener->indicateProgress(); + + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + mWorkQueueChannel = wq->getChannel("LargeTerrain"); + wq->addRequestHandler(mWorkQueueChannel, this); + wq->addResponseHandler(mWorkQueueChannel, this); } World::~World() { + Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); + wq->removeRequestHandler(mWorkQueueChannel, this); + wq->removeResponseHandler(mWorkQueueChannel, this); + delete mRootNode; delete mStorage; } @@ -445,4 +456,62 @@ namespace Terrain } } + void World::syncLoad() + { + while (mChunksLoading) + { + OGRE_THREAD_SLEEP(0); + Ogre::Root::getSingleton().getWorkQueue()->processResponses(); + } + } + + Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) + { + const LoadRequestData data = Ogre::any_cast(req->getData()); + + QuadTreeNode* node = data.mNode; + + LoadResponseData* responseData = new LoadResponseData(); + + Ogre::Timer timer; + getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), + responseData->mPositions, responseData->mNormals, responseData->mColours); + + std::cout << "THREAD" << std::endl; + + responseData->time = timer.getMicroseconds(); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + + void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) + { + static unsigned long time = 0; + if (res->succeeded()) + { + LoadResponseData* data = Ogre::any_cast(res->getData()); + + const LoadRequestData requestData = Ogre::any_cast(res->getRequest()->getData()); + + requestData.mNode->load(*data); + + time += data->time; + + delete data; + + std::cout << "RESPONSE, reqs took ms" << time/1000.f << std::endl; + } + --mChunksLoading; + } + + void World::queueLoad(QuadTreeNode *node) + { + LoadRequestData data; + data.mNode = node; + data.mPack = getShadersEnabled(); + + const Ogre::uint16 loadRequestId = 1; + Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, loadRequestId, Ogre::Any(data)); + ++mChunksLoading; + } } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 31bd51ab15..d552bccb00 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "defs.hpp" @@ -26,7 +27,7 @@ namespace Terrain * Cracks at LOD transitions are avoided using stitching. * @note Multiple cameras are not supported yet */ - class World + class World : public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler { public: /// @note takes ownership of \a storage @@ -85,7 +86,16 @@ namespace Terrain Alignment getAlign() { return mAlign; } + /// Wait until all background loading is complete. + void syncLoad(); + private: + // Called from a background worker thread + Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ); + // Called from the main thread + void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ); + Ogre::uint16 mWorkQueueChannel; + bool mDistantLand; bool mShaders; bool mShadows; @@ -99,6 +109,9 @@ namespace Terrain int mVisibilityFlags; + /// The number of chunks currently loading in a background thread. If 0, we have finished loading! + int mChunksLoading; + Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mCompositeMapSceneMgr; @@ -141,6 +154,9 @@ namespace Terrain void convertPosition (Ogre::Vector3& pos); void convertBounds (Ogre::AxisAlignedBox& bounds); + // Adds a WorkQueue request to load a chunk for this node in the background. + void queueLoad (QuadTreeNode* node); + private: // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. @@ -152,6 +168,30 @@ namespace Terrain Ogre::TexturePtr mCompositeMapRenderTexture; }; + struct LoadRequestData + { + QuadTreeNode* mNode; + bool mPack; + + friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) + { return o; } + }; + + struct LoadResponseData + { + std::vector mPositions; + std::vector mNormals; + std::vector mColours; + // Since we can't create a texture from a different thread, this only holds the raw texel data + std::vector< std::vector > mBlendmaps; + std::vector mLayerList; + + unsigned long time; + + friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) + { return o; } + }; + } #endif From edb5a540925f9efc2631b68113a3373aa4da9291 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Feb 2014 18:40:29 +0100 Subject: [PATCH 059/240] Include some more required Ogre headers explicitely. --- apps/opencs/view/render/scenewidget.cpp | 2 ++ apps/openmw/engine.cpp | 1 + apps/openmw/mwgui/enchantingdialog.cpp | 2 ++ apps/openmw/mwgui/loadingscreen.cpp | 6 ++++++ apps/openmw/mwgui/loadingscreen.hpp | 1 + apps/openmw/mwgui/mapwindow.cpp | 1 + apps/openmw/mwgui/repair.cpp | 2 ++ apps/openmw/mwgui/tooltips.cpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ apps/openmw/mwmechanics/npcstats.cpp | 4 +++- apps/openmw/mwrender/animation.cpp | 2 ++ apps/openmw/mwrender/characterpreview.cpp | 5 ++++- apps/openmw/mwrender/characterpreview.hpp | 1 + apps/openmw/mwrender/debugging.cpp | 2 ++ apps/openmw/mwrender/effectmanager.cpp | 2 ++ apps/openmw/mwrender/localmap.cpp | 2 ++ apps/openmw/mwrender/npcanimation.cpp | 5 +++++ apps/openmw/mwrender/occlusionquery.cpp | 2 ++ apps/openmw/mwrender/refraction.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 2 ++ apps/openmw/mwrender/ripplesimulation.cpp | 4 ++++ apps/openmw/mwrender/shadows.cpp | 2 ++ apps/openmw/mwrender/sky.cpp | 1 + apps/openmw/mwrender/videoplayer.cpp | 5 +++++ apps/openmw/mwrender/water.cpp | 7 ++++++- apps/openmw/mwworld/physicssystem.cpp | 1 + components/bsa/bsa_archive.cpp | 1 + components/nifogre/mesh.cpp | 2 ++ components/nifogre/ogrenifloader.cpp | 3 +++ components/ogreinit/ogreinit.cpp | 1 + components/terrain/chunk.cpp | 1 + components/terrain/quadtreenode.cpp | 3 +++ components/terrain/world.cpp | 4 ++++ extern/sdl4ogre/sdlcursormanager.cpp | 1 + extern/sdl4ogre/sdlwindowhelper.cpp | 1 + extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp | 2 ++ libs/openengine/ogre/fader.cpp | 1 + libs/openengine/ogre/imagerotate.cpp | 7 +++++++ libs/openengine/ogre/renderer.cpp | 1 + libs/openengine/ogre/selectionbuffer.cpp | 3 +++ libs/openengine/ogre/selectionbuffer.hpp | 1 + 41 files changed, 97 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6f07c1b0d5..3c389bec20 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "navigation.hpp" diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index af8d494261..4c3cadb3b6 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1,6 +1,7 @@ #include "engine.hpp" #include +#include #include #include diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 92205c3e99..ada004612a 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -1,5 +1,7 @@ #include "enchantingdialog.hpp" +#include + #include #include "../mwbase/environment.hpp" diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 868b582096..53a45db09b 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -1,6 +1,12 @@ #include "loadingscreen.hpp" #include +#include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2d1d7431f8..e91e5951db 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -2,6 +2,7 @@ #define MWGUI_LOADINGSCREEN_H #include +#include #include "windowbase.hpp" diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 51e160d26f..1cc9610df4 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/repair.cpp b/apps/openmw/mwgui/repair.cpp index d729ee7fa0..de96bcacd1 100644 --- a/apps/openmw/mwgui/repair.cpp +++ b/apps/openmw/mwgui/repair.cpp @@ -1,5 +1,7 @@ #include "repair.hpp" +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 8716c4dea8..f941c699b4 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -1,5 +1,7 @@ #include "tooltips.hpp" +#include + #include #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f440782569..1b71157a74 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "MyGUI_UString.h" #include "MyGUI_IPointer.h" #include "MyGUI_ResourceImageSetPointer.h" diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 8918bfbe70..74587a626f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include @@ -509,4 +511,4 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mTimeToStartDrowning = state.mTimeToStartDrowning; mLastDrowningHit = state.mLastDrowningHit; mLevelHealthBonus = state.mLevelHealthBonus; -} \ No newline at end of file +} diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 6ba3b1fcda..e62fee6e20 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 2808286520..013e3daf4f 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -1,10 +1,13 @@ #include "characterpreview.hpp" - #include #include #include #include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index cd30cdf466..16e6ab017d 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -3,6 +3,7 @@ #include #include +#include #include diff --git a/apps/openmw/mwrender/debugging.cpp b/apps/openmw/mwrender/debugging.cpp index 241f7e470c..ba39d10d5f 100644 --- a/apps/openmw/mwrender/debugging.cpp +++ b/apps/openmw/mwrender/debugging.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index 7d41525b74..1e6119daac 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "animation.hpp" #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 772813c73d..003f083009 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d07aad31d4..60760b7fb2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -4,6 +4,11 @@ #include #include #include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/occlusionquery.cpp b/apps/openmw/mwrender/occlusionquery.cpp index 2461034717..67bc75e02d 100644 --- a/apps/openmw/mwrender/occlusionquery.cpp +++ b/apps/openmw/mwrender/occlusionquery.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "renderconst.hpp" diff --git a/apps/openmw/mwrender/refraction.cpp b/apps/openmw/mwrender/refraction.cpp index d590dbf4c3..f22968e9d0 100644 --- a/apps/openmw/mwrender/refraction.cpp +++ b/apps/openmw/mwrender/refraction.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 68bf957c8c..b67875405b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -5,12 +5,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index e5db8346f7..f52deedcc6 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/shadows.cpp b/apps/openmw/mwrender/shadows.cpp index 9ebb0ab087..33e3376498 100644 --- a/apps/openmw/mwrender/shadows.cpp +++ b/apps/openmw/mwrender/shadows.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 39f7ccc854..90c08c2995 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index adf20dc633..f3c0971e76 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -9,6 +9,11 @@ #include #include #include +#include +#include +#include +#include +#include #include diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 9e3105168b..1fa5d88341 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -1,11 +1,16 @@ #include "water.hpp" -#include +#include #include #include +#include #include #include #include +#include +#include +#include +#include #include "sky.hpp" #include "renderingmanager.hpp" diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1a16870daa..8ef5797cac 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index eb741fb105..6574f096b5 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "bsa_file.hpp" #include "../files/constrainedfiledatastream.hpp" diff --git a/components/nifogre/mesh.cpp b/components/nifogre/mesh.cpp index 43622cb9ae..8bebe05893 100644 --- a/components/nifogre/mesh.cpp +++ b/components/nifogre/mesh.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 43e8934c82..acab419b03 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index 840cf4bb05..1b9a899a0b 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE #include diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index 139694ee5c..6f2b9ff73f 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "quadtreenode.hpp" #include "world.hpp" diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 721771ff36..1c85539672 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include "world.hpp" #include "chunk.hpp" diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 87baeff841..4d46165118 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -4,7 +4,11 @@ #include #include #include +#include +#include +#include #include +#include // TEMP #include "storage.hpp" #include "quadtreenode.hpp" diff --git a/extern/sdl4ogre/sdlcursormanager.cpp b/extern/sdl4ogre/sdlcursormanager.cpp index 65fb7f98bc..5ef274b7e7 100644 --- a/extern/sdl4ogre/sdlcursormanager.cpp +++ b/extern/sdl4ogre/sdlcursormanager.cpp @@ -1,6 +1,7 @@ #include "sdlcursormanager.hpp" #include +#include #include #include diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp index f819043cfd..2a14cc6b4d 100644 --- a/extern/sdl4ogre/sdlwindowhelper.cpp +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp index f215f4ab7d..ad8e6d2b03 100644 --- a/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp +++ b/extern/shiny/Platforms/Ogre/OgreTextureUnitState.cpp @@ -1,5 +1,7 @@ #include "OgreTextureUnitState.hpp" +#include + #include #include diff --git a/libs/openengine/ogre/fader.cpp b/libs/openengine/ogre/fader.cpp index 923b0b7e38..20b9296da1 100644 --- a/libs/openengine/ogre/fader.cpp +++ b/libs/openengine/ogre/fader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace Ogre; diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp index 9c32924f1f..cc5f572cf9 100644 --- a/libs/openengine/ogre/imagerotate.cpp +++ b/libs/openengine/ogre/imagerotate.cpp @@ -8,6 +8,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include using namespace Ogre; using namespace OEngine::Render; diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index c86697497f..c816f2060c 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp index 69375b74d2..5aeb35c287 100644 --- a/libs/openengine/ogre/selectionbuffer.cpp +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -5,6 +5,9 @@ #include #include #include +#include +#include + #include #include diff --git a/libs/openengine/ogre/selectionbuffer.hpp b/libs/openengine/ogre/selectionbuffer.hpp index c487b24b04..b9b4cd9d96 100644 --- a/libs/openengine/ogre/selectionbuffer.hpp +++ b/libs/openengine/ogre/selectionbuffer.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace OEngine { From 2a4e99c06949a4f67f96eea0c6bb2ec6fb6c0f74 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 21 Feb 2014 15:11:14 +0100 Subject: [PATCH 060/240] Set StaticGeometry origin Fixes an exception for coordinates far away from (0,0,0). --- apps/openmw/mwrender/objects.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index c97e5279a0..9c10ca84b0 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -109,6 +109,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) { uniqueID = uniqueID+1; sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); + sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition()); mStaticGeometrySmall[ptr.getCell()] = sg; sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance")); @@ -122,6 +123,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) { uniqueID = uniqueID+1; sg = mRenderer.getScene()->createStaticGeometry("sg" + Ogre::StringConverter::toString(uniqueID)); + sg->setOrigin(ptr.getRefData().getBaseNode()->getPosition()); mStaticGeometry[ptr.getCell()] = sg; } else From 97c3efb3ba8dbcdc7ac503ecbdba8163830bb4b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Feb 2014 19:23:42 +0100 Subject: [PATCH 061/240] Terrain: decoupled Chunk from QuadTreeNode. --- components/CMakeLists.txt | 2 +- components/terrain/buffercache.cpp | 200 ++++++++++++++++++++++++++++ components/terrain/buffercache.hpp | 36 +++++ components/terrain/chunk.cpp | 76 ++--------- components/terrain/chunk.hpp | 17 +-- components/terrain/defs.hpp | 8 ++ components/terrain/quadtreenode.cpp | 39 +++++- components/terrain/quadtreenode.hpp | 10 +- components/terrain/world.cpp | 197 +-------------------------- components/terrain/world.hpp | 28 +--- 10 files changed, 304 insertions(+), 309 deletions(-) create mode 100644 components/terrain/buffercache.cpp create mode 100644 components/terrain/buffercache.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 938ef2e4dc..0a99c9528a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -75,7 +75,7 @@ add_component_dir (translation add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world storage material + quadtreenode chunk world storage material buffercache compositemap defs ) add_component_dir (loadinglistener diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp new file mode 100644 index 0000000000..f693c0e402 --- /dev/null +++ b/components/terrain/buffercache.cpp @@ -0,0 +1,200 @@ +#include "buffercache.hpp" + +#include + +#include "defs.hpp" + +namespace Terrain +{ + + Ogre::HardwareVertexBufferSharedPtr BufferCache::getUVBuffer() + { + if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) + { + return mUvBufferMap[mNumVerts]; + } + + int vertexCount = mNumVerts * mNumVerts; + + std::vector uvs; + uvs.reserve(vertexCount*2); + + for (unsigned int col = 0; col < mNumVerts; ++col) + { + for (unsigned int row = 0; row < mNumVerts; ++row) + { + uvs.push_back(col / static_cast(mNumVerts-1)); // U + uvs.push_back(row / static_cast(mNumVerts-1)); // V + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( + Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), + vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + + buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); + + mUvBufferMap[mNumVerts] = buffer; + return buffer; + } + + Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(int flags) + { + unsigned int verts = mNumVerts; + + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) + { + return mIndexBufferMap[flags]; + } + + // LOD level n means every 2^n-th vertex is kept + size_t lodLevel = (flags >> (4*4)); + + size_t lodDeltas[4]; + for (int i=0; i<4; ++i) + lodDeltas[i] = (flags >> (4*i)) & (0xf); + + bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); + + size_t increment = 1 << lodLevel; + assert(increment < verts); + std::vector indices; + indices.reserve((verts-1)*(verts-1)*2*3 / increment); + + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; + // If any edge needs stitching we'll skip all edges at this point, + // mainly because stitching one edge would have an effect on corners and on the adjacent edges + if (anyDeltas) + { + colStart += increment; + colEnd -= increment; + rowEnd -= increment; + rowStart += increment; + } + for (size_t row = rowStart; row < rowEnd; row += increment) + { + for (size_t col = colStart; col < colEnd; col += increment) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); + + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + } + } + + size_t innerStep = increment; + if (anyDeltas) + { + // Now configure LOD transitions at the edges - this is pretty tedious, + // and some very long and boring code, but it works great + + // South + size_t row = 0; + size_t outerStep = 1 << (lodDeltas[South] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); + // Make sure not to touch the right edge + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); + else + indices.push_back(verts*(col+outerStep)+row+innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); + } + } + + // North + row = verts-1; + outerStep = 1 << (lodDeltas[North] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); + // Make sure not to touch the left edge + if (col == 0) + indices.push_back(verts*(col+innerStep)+row-innerStep); + else + indices.push_back(verts*col+row-innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); + } + } + + // West + size_t col = 0; + outerStep = 1 << (lodDeltas[West] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*col+row); + // Make sure not to touch the top edge + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); + else + indices.push_back(verts*(col+innerStep)+row+outerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); + } + } + + // East + col = verts-1; + outerStep = 1 << (lodDeltas[East] + lodLevel); + for (size_t row = 0; row < verts-1; row += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*col+row+outerStep); + // Make sure not to touch the bottom edge + if (row == 0) + indices.push_back(verts*(col-innerStep)+row+innerStep); + else + indices.push_back(verts*(col-innerStep)+row); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); + } + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, + indices.size(), Ogre::HardwareBuffer::HBU_STATIC); + buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); + mIndexBufferMap[flags] = buffer; + return buffer; + } + +} diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp new file mode 100644 index 0000000000..f0aea9bfd8 --- /dev/null +++ b/components/terrain/buffercache.hpp @@ -0,0 +1,36 @@ +#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H +#define COMPONENTS_TERRAIN_BUFFERCACHE_H + +#include +#include + +#include + +namespace Terrain +{ + + /// @brief Implements creation and caching of vertex buffers for terrain chunks. + class BufferCache + { + public: + BufferCache(unsigned int numVerts) : mNumVerts(numVerts) {} + + /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) + /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) + Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags); + + Ogre::HardwareVertexBufferSharedPtr getUVBuffer (); + + private: + // Index buffers are shared across terrain batches where possible. There is one index buffer for each + // combination of LOD deltas and index buffer LOD we may need. + std::map mIndexBufferMap; + + std::map mUvBufferMap; + + unsigned int mNumVerts; + }; + +} + +#endif diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index 6f2b9ff73f..cf6c99cf48 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -4,31 +4,17 @@ #include #include -#include "quadtreenode.hpp" -#include "world.hpp" -#include "storage.hpp" +#include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp namespace Terrain { - Chunk::Chunk(QuadTreeNode* node, const LoadResponseData& data) - : mNode(node) - , mVertexLod(node->getNativeLodLevel()) - , mAdditionalLod(0) + Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data) + : mBounds(bounds) { mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; - - unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); - - size_t lodLevel = mNode->getNativeLodLevel(); - - // Set the total number of vertices - size_t numVertsOneSide = mNode->getSize() * (verts-1); - numVertsOneSide /= 1 << lodLevel; - numVertsOneSide += 1; - assert(numVertsOneSide == verts); - mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; + mVertexData->vertexCount = data.mPositions.size()/3; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; @@ -40,15 +26,16 @@ namespace Terrain vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); mVertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + // Normals vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); mNormalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); + // UV texture coordinates vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0); - Ogre::HardwareVertexBufferSharedPtr uvBuf = mNode->getTerrain()->getVertexBuffer(numVertsOneSide); // Colours vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); @@ -61,54 +48,17 @@ namespace Terrain mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); - mVertexData->vertexBufferBinding->setBinding(2, uvBuf); + mVertexData->vertexBufferBinding->setBinding(2, uvBuffer); mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer); mIndexData = OGRE_NEW Ogre::IndexData(); mIndexData->indexStart = 0; } - - - void Chunk::updateIndexBuffer() + void Chunk::setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer) { - // Fetch a suitable index buffer (which may be shared) - size_t ourLod = mVertexLod + mAdditionalLod; - - int flags = 0; - - for (int i=0; i<4; ++i) - { - QuadTreeNode* neighbour = mNode->getNeighbour((Direction)i); - - // If the neighbour isn't currently rendering itself, - // go up until we find one. NOTE: We don't need to go down, - // because in that case neighbour's detail would be higher than - // our detail and the neighbour would handle stitching by itself. - while (neighbour && !neighbour->hasChunk()) - neighbour = neighbour->getParent(); - - size_t lod = 0; - if (neighbour) - lod = neighbour->getActualLodLevel(); - - if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - - lod = 0; // neighbours with more detail will do the stitching themselves - - // Use 4 bits for each LOD delta - if (lod > 0) - { - assert (lod - ourLod < (1 << 4)); - flags |= int(lod - ourLod) << (4*i); - } - } - - flags |= ((int)mAdditionalLod) << (4*4); - - size_t numIndices; - mIndexBuffer = mNode->getTerrain()->getIndexBuffer(flags, numIndices); - mIndexData->indexCount = numIndices; - mIndexData->indexBuffer = mIndexBuffer; + mIndexData->indexBuffer = buffer; + mIndexData->indexCount = buffer->getNumIndexes(); } Chunk::~Chunk() @@ -124,12 +74,12 @@ namespace Terrain const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const { - return mNode->getBoundingBox(); + return mBounds; } Ogre::Real Chunk::getBoundingRadius(void) const { - return mNode->getBoundingBox().getHalfSize().length(); + return mBounds.getHalfSize().length(); } void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue) @@ -150,7 +100,7 @@ namespace Terrain void Chunk::getRenderOperation(Ogre::RenderOperation& op) { - assert (!mIndexBuffer.isNull() && "Trying to render, but no index buffer set!"); + assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!"); op.useIndexes = true; op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; op.vertexData = mVertexData; diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index a6477df284..d198de264e 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -7,7 +7,7 @@ namespace Terrain { - class QuadTreeNode; + class BufferCache; struct LoadResponseData; /** @@ -16,18 +16,13 @@ namespace Terrain class Chunk : public Ogre::Renderable, public Ogre::MovableObject { public: - Chunk (QuadTreeNode* node, const LoadResponseData& data); + Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data); virtual ~Chunk(); void setMaterial (const Ogre::MaterialPtr& material); - /// Set additional LOD applied on top of vertex LOD. \n - /// This is achieved by changing the index buffer to omit vertices. - void setAdditionalLod (size_t lod) { mAdditionalLod = lod; } - size_t getAdditionalLod() { return mAdditionalLod; } - - void updateIndexBuffer(); + void setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer); // Inherited from MovableObject virtual const Ogre::String& getMovableType(void) const { static Ogre::String t = "MW_TERRAIN"; return t; } @@ -45,18 +40,14 @@ namespace Terrain virtual const Ogre::LightList& getLights(void) const; private: - QuadTreeNode* mNode; + Ogre::AxisAlignedBox mBounds; Ogre::MaterialPtr mMaterial; - size_t mVertexLod; - size_t mAdditionalLod; - Ogre::VertexData* mVertexData; Ogre::IndexData* mIndexData; Ogre::HardwareVertexBufferSharedPtr mVertexBuffer; Ogre::HardwareVertexBufferSharedPtr mNormalBuffer; Ogre::HardwareVertexBufferSharedPtr mColourBuffer; - Ogre::HardwareIndexBufferSharedPtr mIndexBuffer; }; } diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index c14608e76d..1a081c969d 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -36,6 +36,14 @@ namespace Terrain } } + enum Direction + { + North = 0, + East = 1, + South = 2, + West = 3 + }; + struct LayerInfo { std::string mDiffuseMap; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 1c85539672..9139706061 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -9,7 +9,7 @@ #include "world.hpp" #include "chunk.hpp" #include "storage.hpp" - +#include "buffercache.hpp" #include "material.hpp" using namespace Terrain; @@ -372,7 +372,7 @@ void QuadTreeNode::load(const LoadResponseData &data) assert (!mChunk); std::cout << "loading " << std::endl; - mChunk = new Chunk(this, data); + mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mChunk->setCastShadows(true); mSceneNode->attachObject(mChunk); @@ -429,7 +429,38 @@ void QuadTreeNode::unload() void QuadTreeNode::updateIndexBuffers() { if (hasChunk()) - mChunk->updateIndexBuffer(); + { + // Fetch a suitable index buffer (which may be shared) + size_t ourLod = getActualLodLevel(); + + int flags = 0; + + for (int i=0; i<4; ++i) + { + QuadTreeNode* neighbour = getNeighbour((Direction)i); + + // If the neighbour isn't currently rendering itself, + // go up until we find one. NOTE: We don't need to go down, + // because in that case neighbour's detail would be higher than + // our detail and the neighbour would handle stitching by itself. + while (neighbour && !neighbour->hasChunk()) + neighbour = neighbour->getParent(); + size_t lod = 0; + if (neighbour) + lod = neighbour->getActualLodLevel(); + if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - + lod = 0; // neighbours with more detail will do the stitching themselves + // Use 4 bits for each LOD delta + if (lod > 0) + { + assert (lod - ourLod < (1 << 4)); + flags |= int(lod - ourLod) << (4*i); + } + } + flags |= 0 /*((int)mAdditionalLod)*/ << (4*4); + + mChunk->setIndexBuffer(mTerrain->getBufferCache().getIndexBuffer(flags)); + } else if (hasChildren()) { for (int i=0; i<4; ++i) @@ -445,7 +476,7 @@ bool QuadTreeNode::hasChunk() size_t QuadTreeNode::getActualLodLevel() { assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk"); - return mLodLevel + mChunk->getAdditionalLod(); + return mLodLevel /* + mChunk->getAdditionalLod() */; } void QuadTreeNode::ensureLayerInfo() diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0bba1024c0..134bc2d347 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -5,6 +5,8 @@ #include #include +#include "defs.hpp" + namespace Ogre { class Rectangle2D; @@ -17,14 +19,6 @@ namespace Terrain class MaterialGenerator; struct LoadResponseData; - enum Direction - { - North = 0, - East = 1, - South = 2, - West = 3 - }; - enum ChildDirection { NW = 0, diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 4d46165118..69748a7215 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -70,6 +69,7 @@ namespace Terrain , mMinY(0) , mChunksLoading(0) , mWorkQueueChannel(0) + , mCache(storage->getCellVertices()) { #if TERRAIN_USE_SHADER == 0 if (mShaders) @@ -197,201 +197,6 @@ namespace Terrain return node->getWorldBoundingBox(); } - Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) - { - if (mUvBufferMap.find(numVertsOneSide) != mUvBufferMap.end()) - { - return mUvBufferMap[numVertsOneSide]; - } - - int vertexCount = numVertsOneSide * numVertsOneSide; - - std::vector uvs; - uvs.reserve(vertexCount*2); - - for (int col = 0; col < numVertsOneSide; ++col) - { - for (int row = 0; row < numVertsOneSide; ++row) - { - uvs.push_back(col / static_cast(numVertsOneSide-1)); // U - uvs.push_back(row / static_cast(numVertsOneSide-1)); // V - } - } - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); - - mUvBufferMap[numVertsOneSide] = buffer; - return buffer; - } - - Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) - { - unsigned int verts = mStorage->getCellVertices(); - - if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) - { - numIndices = mIndexBufferMap[flags]->getNumIndexes(); - return mIndexBufferMap[flags]; - } - - // LOD level n means every 2^n-th vertex is kept - size_t lodLevel = (flags >> (4*4)); - - size_t lodDeltas[4]; - for (int i=0; i<4; ++i) - lodDeltas[i] = (flags >> (4*i)) & (0xf); - - bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); - - size_t increment = 1 << lodLevel; - assert(increment < verts); - std::vector indices; - indices.reserve((verts-1)*(verts-1)*2*3 / increment); - - size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; - // If any edge needs stitching we'll skip all edges at this point, - // mainly because stitching one edge would have an effect on corners and on the adjacent edges - if (anyDeltas) - { - colStart += increment; - colEnd -= increment; - rowEnd -= increment; - rowStart += increment; - } - for (size_t row = rowStart; row < rowEnd; row += increment) - { - for (size_t col = colStart; col < colEnd; col += increment) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row+increment); - indices.push_back(verts*col+row+increment); - - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row); - indices.push_back(verts*(col+increment)+row+increment); - } - } - - size_t innerStep = increment; - if (anyDeltas) - { - // Now configure LOD transitions at the edges - this is pretty tedious, - // and some very long and boring code, but it works great - - // South - size_t row = 0; - size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+outerStep)+row); - // Make sure not to touch the right edge - if (col+outerStep == verts-1) - indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); - else - indices.push_back(verts*(col+outerStep)+row+innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col)+row); - indices.push_back(verts*(col+i+innerStep)+row+innerStep); - indices.push_back(verts*(col+i)+row+innerStep); - } - } - - // North - row = verts-1; - outerStep = 1 << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*(col+outerStep)+row); - indices.push_back(verts*col+row); - // Make sure not to touch the left edge - if (col == 0) - indices.push_back(verts*(col+innerStep)+row-innerStep); - else - indices.push_back(verts*col+row-innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col+i)+row-innerStep); - indices.push_back(verts*(col+i+innerStep)+row-innerStep); - indices.push_back(verts*(col+outerStep)+row); - } - } - - // West - size_t col = 0; - outerStep = 1 << (lodDeltas[West] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*col+row); - // Make sure not to touch the top edge - if (row+outerStep == verts-1) - indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); - else - indices.push_back(verts*(col+innerStep)+row+outerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row); - indices.push_back(verts*(col+innerStep)+row+i); - indices.push_back(verts*(col+innerStep)+row+i+innerStep); - } - } - - // East - col = verts-1; - outerStep = 1 << (lodDeltas[East] + lodLevel); - for (size_t row = 0; row < verts-1; row += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*col+row+outerStep); - // Make sure not to touch the bottom edge - if (row == 0) - indices.push_back(verts*(col-innerStep)+row+innerStep); - else - indices.push_back(verts*(col-innerStep)+row); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*(col-innerStep)+row+i+innerStep); - indices.push_back(verts*(col-innerStep)+row+i); - } - } - } - - - - numIndices = indices.size(); - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, - numIndices, Ogre::HardwareBuffer::HBU_STATIC); - buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); - mIndexBufferMap[flags] = buffer; - return buffer; - } - void World::renderCompositeMap(Ogre::TexturePtr target) { mCompositeMapRenderTarget->update(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d552bccb00..3547387444 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -1,13 +1,12 @@ #ifndef COMPONENTS_TERRAIN_H #define COMPONENTS_TERRAIN_H -#include -#include #include #include #include #include "defs.hpp" +#include "buffercache.hpp" namespace Ogre { @@ -125,25 +124,12 @@ namespace Terrain void buildQuadTree(QuadTreeNode* node); + BufferCache mCache; + public: // ----INTERNAL---- - - enum IndexBufferFlags - { - IBF_North = 1 << 0, - IBF_East = 1 << 1, - IBF_South = 1 << 2, - IBF_West = 1 << 3 - }; - - /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) - /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) - /// @param numIndices number of indices that were used will be written here - Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags, size_t& numIndices); - - Ogre::HardwareVertexBufferSharedPtr getVertexBuffer (int numVertsOneSide); - Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } + BufferCache& getBufferCache() { return mCache; } // Delete all quads void clearCompositeMapSceneManager(); @@ -158,12 +144,6 @@ namespace Terrain void queueLoad (QuadTreeNode* node); private: - // Index buffers are shared across terrain batches where possible. There is one index buffer for each - // combination of LOD deltas and index buffer LOD we may need. - std::map mIndexBufferMap; - - std::map mUvBufferMap; - Ogre::RenderTarget* mCompositeMapRenderTarget; Ogre::TexturePtr mCompositeMapRenderTexture; }; From 6a002d19b3241dddcbfa163ca4ee9674dee4e54e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Feb 2014 03:15:04 +0100 Subject: [PATCH 062/240] Terrain: destroy no longer used materials --- components/terrain/chunk.cpp | 9 +++++++++ components/terrain/chunk.hpp | 1 + 2 files changed, 10 insertions(+) diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index cf6c99cf48..6248092292 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -3,6 +3,10 @@ #include #include #include +#include + +#include + #include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp @@ -63,6 +67,11 @@ namespace Terrain Chunk::~Chunk() { +#if TERRAIN_USE_SHADER + sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); +#endif + Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); + OGRE_DELETE mVertexData; OGRE_DELETE mIndexData; } diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index d198de264e..9a7d20fb85 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -20,6 +20,7 @@ namespace Terrain virtual ~Chunk(); + /// @note Takes ownership of \a material void setMaterial (const Ogre::MaterialPtr& material); void setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer); From 4328e0816229b246bff4da040d7a36f710ac67a8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Feb 2014 03:18:19 +0100 Subject: [PATCH 063/240] Terrain: get rid of unneeded members --- components/terrain/chunk.cpp | 18 +++++++++--------- components/terrain/chunk.hpp | 3 --- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index 6248092292..ed640be5f2 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -28,12 +28,12 @@ namespace Terrain // Positions vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - mVertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + Ogre::HardwareVertexBufferSharedPtr vertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); // Normals vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - mNormalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), + Ogre::HardwareVertexBufferSharedPtr normalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); @@ -43,17 +43,17 @@ namespace Terrain // Colours vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), + Ogre::HardwareVertexBufferSharedPtr colourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - mVertexBuffer->writeData(0, mVertexBuffer->getSizeInBytes(), &data.mPositions[0], true); - mNormalBuffer->writeData(0, mNormalBuffer->getSizeInBytes(), &data.mNormals[0], true); - mColourBuffer->writeData(0, mColourBuffer->getSizeInBytes(), &data.mColours[0], true); + vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &data.mPositions[0], true); + normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &data.mNormals[0], true); + colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &data.mColours[0], true); - mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); - mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); + mVertexData->vertexBufferBinding->setBinding(0, vertexBuffer); + mVertexData->vertexBufferBinding->setBinding(1, normalBuffer); mVertexData->vertexBufferBinding->setBinding(2, uvBuffer); - mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer); + mVertexData->vertexBufferBinding->setBinding(3, colourBuffer); mIndexData = OGRE_NEW Ogre::IndexData(); mIndexData->indexStart = 0; diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index 9a7d20fb85..d037661aee 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -46,9 +46,6 @@ namespace Terrain Ogre::VertexData* mVertexData; Ogre::IndexData* mIndexData; - Ogre::HardwareVertexBufferSharedPtr mVertexBuffer; - Ogre::HardwareVertexBufferSharedPtr mNormalBuffer; - Ogre::HardwareVertexBufferSharedPtr mColourBuffer; }; } From 1d926816b587c75e43cf6824e29f06efb42e18fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Mar 2014 21:45:43 +0100 Subject: [PATCH 064/240] Terrain: background load blendmaps & layer textures. Refactor QuadTree update. --- apps/openmw/mwrender/renderingmanager.cpp | 23 +-- apps/openmw/mwrender/renderingmanager.hpp | 4 + apps/openmw/mwrender/terrainstorage.cpp | 47 ++++-- apps/openmw/mwrender/terrainstorage.hpp | 20 ++- apps/openmw/mwworld/scene.cpp | 4 +- components/terrain/chunk.cpp | 1 + components/terrain/defs.hpp | 9 ++ components/terrain/material.cpp | 4 + components/terrain/quadtreenode.cpp | 185 +++++++++++++--------- components/terrain/quadtreenode.hpp | 17 +- components/terrain/storage.hpp | 15 +- components/terrain/world.cpp | 80 +++++++--- components/terrain/world.hpp | 30 +++- 13 files changed, 296 insertions(+), 143 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b67875405b..7c70b17f07 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -651,13 +651,20 @@ void RenderingManager::setGlare(bool glare) mSkyManager->setGlare(glare); } +void RenderingManager::updateTerrain() +{ + if (mTerrain) + { + // Avoid updating with dims.getCenter for each cell. Player position should be good enough + mTerrain->update(mRendering.getCamera()->getRealPosition()); + mTerrain->syncLoad(); + // need to update again so the chunks that were just loaded can be made visible + mTerrain->update(mRendering.getCamera()->getRealPosition()); + } +} + void RenderingManager::requestMap(MWWorld::CellStore* cell) { - // FIXME: move to other method - // TODO: probably not needed when crossing a cell border. Could delay the map render until we are loaded. - if (mTerrain) - mTerrain->syncLoad(); - if (cell->getCell()->isExterior()) { assert(mTerrain); @@ -666,9 +673,6 @@ void RenderingManager::requestMap(MWWorld::CellStore* cell) Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5); dims.merge(mTerrain->getWorldBoundingBox(center)); - if (dims.isFinite()) - mTerrain->update(dims.getCenter()); - mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z); } else @@ -1059,8 +1063,7 @@ void RenderingManager::enableTerrain(bool enable) } mTerrain->setVisible(true); } - else - if (mTerrain) + else if (mTerrain) mTerrain->setVisible(false); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 64ec029ce8..115a94786e 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -180,6 +180,10 @@ public: void removeWaterRippleEmitter (const MWWorld::Ptr& ptr); void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr); + void updateTerrain (); + ///< update the terrain according to the player position. Usually done automatically, but should be done manually + /// before calling requestMap + void requestMap (MWWorld::CellStore* cell); ///< request the local map for a cell diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index ec26b18720..2558c95c59 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,8 @@ #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" +#include + namespace MWRender { @@ -329,8 +332,24 @@ namespace MWRender return texture; } + void TerrainStorage::getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) + { + for (std::vector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) + { + out.push_back(Terrain::LayerCollection()); + out.back().mTarget = *it; + getBlendmapsImpl((*it)->getSize(), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers); + } + } + void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) + bool pack, std::vector &blendmaps, std::vector &layerList) + { + getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList); + } + + void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter, + bool pack, std::vector &blendmaps, std::vector &layerList) { // TODO - blending isn't completely right yet; the blending radius appears to be // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap @@ -375,16 +394,14 @@ namespace MWRender // Second iteration - create and fill in the blend maps const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1; - std::vector data; - data.resize(blendmapSize * blendmapSize * channels, 0); for (int i=0; iloadRawData(stream, blendmapSize, blendmapSize, format); - blendmaps.push_back(map); + blendmaps.push_back(Ogre::PixelBox(blendmapSize, blendmapSize, 1, format, pData)); } } @@ -527,6 +540,12 @@ namespace MWRender info.mSpecular = true; } + // This wasn't cached, so the textures are probably not loaded either. + // Background load them so they are hopefully already loaded once we need them! + Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mDiffuseMap, "General"); + if (!info.mNormalMap.empty()) + Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mNormalMap, "General"); + mLayerInfoMap[texture] = info; return info; diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index b1acdb7859..b5e6012f3a 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -46,6 +46,7 @@ namespace MWRender /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from *one* background thread. /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - @@ -54,9 +55,21 @@ namespace MWRender /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + std::vector& blendmaps, std::vector& layerList); + /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. + /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. + /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from *one* background thread. + /// @param nodes A collection of nodes for which to retrieve the aforementioned data + /// @param out Output vector + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack); + virtual float getHeightAt (const Ogre::Vector3& worldPos); virtual Terrain::LayerInfo getDefaultLayer(); @@ -86,6 +99,11 @@ namespace MWRender std::map mLayerInfoMap; Terrain::LayerInfo getLayerInfo(const std::string& texture); + + // Non-virtual + void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, + std::vector& blendmaps, + std::vector& layerList); }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 40004807b4..caae72c137 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -195,10 +195,12 @@ namespace MWWorld mechMgr->updateCell(old, player); mechMgr->watchActor(player); - MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); + mRendering.updateTerrain(); for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) mRendering.requestMap(*active); + + MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); } void Scene::changeToVoid() diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index ed640be5f2..0820dcc732 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -110,6 +110,7 @@ namespace Terrain void Chunk::getRenderOperation(Ogre::RenderOperation& op) { assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!"); + assert(!mMaterial.isNull() && "Trying to render, but no material set!"); op.useIndexes = true; op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; op.vertexData = mVertexData; diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index 1a081c969d..6859376532 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -3,6 +3,7 @@ namespace Terrain { + class QuadTreeNode; /// The alignment of the terrain enum Alignment @@ -51,6 +52,14 @@ namespace Terrain bool mParallax; // Height info in normal map alpha channel? bool mSpecular; // Specular info in diffuse map alpha channel? }; + + struct LayerCollection + { + QuadTreeNode* mTarget; + // Since we can't create a texture from a different thread, this only holds the raw texel data + std::vector mBlendmaps; + std::vector mLayers; + }; } #endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 78b17909a5..faa73a9864 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -48,11 +48,15 @@ namespace Terrain Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat) { + assert(!mLayerList.empty() && "Can't create material with no layers"); + return create(mat, false, false); } Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT(Ogre::MaterialPtr mat) { + assert(!mLayerList.empty() && "Can't create material with no layers"); + return create(mat, true, false); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 9139706061..56cfc2a745 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -144,7 +144,6 @@ namespace QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) : mMaterialGenerator(NULL) - , mIsActive(false) , mIsDummy(false) , mSize(size) , mLodLevel(Log2(mSize)) @@ -262,11 +261,13 @@ const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox() return mWorldBounds; } -void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) +bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) { - const Ogre::AxisAlignedBox& bounds = getBoundingBox(); - if (bounds.isNull()) - return; + if (isDummy()) + return true; + + if (mBounds.isNull()) + return true; float dist = distance(mWorldBounds, cameraPos); @@ -291,11 +292,9 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) wantedLod = 3; else if (dist > cellWorldSize*2) wantedLod = 2; - else if (dist > cellWorldSize) + else if (dist > cellWorldSize * 1.42) // < sqrt2 so the 3x3 grid around player is always highest lod wantedLod = 1; - mIsActive = true; - bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod; if (wantToDisplay) @@ -305,6 +304,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) { mLoadState = LS_Loading; mTerrain->queueLoad(this); + return false; } if (mLoadState == LS_Loaded) @@ -331,47 +331,60 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos) if (!mChunk->getVisible() && hasChildren()) { - // Make sure child scene nodes are detached - mSceneNode->removeAllChildren(); - - // TODO: unload - //for (int i=0; i<4; ++i) - // mChildren[i]->unload(); + for (int i=0; i<4; ++i) + mChildren[i]->unload(true); } - mChunk->setVisible(true); + + return true; } + return false; // LS_Loading } - if (wantToDisplay && mLoadState != LS_Loaded) + // We do not want to display this node - delegate to children if they are already loaded + if (!wantToDisplay && hasChildren()) { - // We want to display, but aren't ready yet. Perhaps our child nodes are ready? - // TODO: this doesn't check child-child nodes... - if (hasChildren()) - { - for (int i=0; i<4; ++i) - if (mChildren[i]->hasChunk()) - mChildren[i]->update(cameraPos); - } - } - if (!wantToDisplay) - { - // We do not want to display this node - delegate to children if (mChunk) - mChunk->setVisible(false); - if (hasChildren()) { + // Are children already loaded? + bool childrenLoaded = true; for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos); + if (!mChildren[i]->update(cameraPos)) + childrenLoaded = false; + + if (!childrenLoaded) + { + // Make sure child scene nodes are detached until all children are loaded + mSceneNode->removeAllChildren(); + } + else + { + // Delegation went well, we can unload now + unload(); + + for (int i=0; i<4; ++i) + { + if (!mChildren[i]->getSceneNode()->isInSceneGraph()) + mSceneNode->addChild(mChildren[i]->getSceneNode()); + } + } + return true; + } + else + { + bool success = true; + for (int i=0; i<4; ++i) + success = mChildren[i]->update(cameraPos) & success; + return success; } } + return false; } void QuadTreeNode::load(const LoadResponseData &data) { assert (!mChunk); - std::cout << "loading " << std::endl; mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mChunk->setCastShadows(true); @@ -380,50 +393,49 @@ void QuadTreeNode::load(const LoadResponseData &data) mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); - if (mSize == 1) + if (mTerrain->areLayersLoaded()) { - ensureLayerInfo(); - mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + if (mSize == 1) + { + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } } - else - { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); - } - + // else: will be loaded in loadMaterials() after background thread has finished loading layers mChunk->setVisible(false); mLoadState = LS_Loaded; } -void QuadTreeNode::unload() +void QuadTreeNode::unload(bool recursive) { if (mChunk) { - Ogre::MaterialManager::getSingleton().remove(mChunk->getMaterial()->getName()); mSceneNode->detachObject(mChunk); delete mChunk; mChunk = NULL; - // destroy blendmaps - if (mMaterialGenerator) - { - const std::vector& list = mMaterialGenerator->getBlendmapList(); - for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) - Ogre::TextureManager::getSingleton().remove((*it)->getName()); - mMaterialGenerator->setBlendmapList(std::vector()); - mMaterialGenerator->setLayerList(std::vector()); - mMaterialGenerator->setCompositeMap(""); - } if (!mCompositeMap.isNull()) { Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName()); mCompositeMap.setNull(); } + + // Do *not* set this when we are still loading! + mLoadState = LS_Unloaded; + } + + if (recursive && hasChildren()) + { + for (int i=0; i<4; ++i) + mChildren[i]->unload(); } - mLoadState = LS_Unloaded; } void QuadTreeNode::updateIndexBuffers() @@ -479,17 +491,53 @@ size_t QuadTreeNode::getActualLodLevel() return mLodLevel /* + mChunk->getAdditionalLod() */; } -void QuadTreeNode::ensureLayerInfo() +void QuadTreeNode::loadLayers(const LayerCollection& collection) { - if (mMaterialGenerator->hasLayers()) + assert (!mMaterialGenerator->hasLayers()); + + std::vector blendTextures; + for (std::vector::const_iterator it = collection.mBlendmaps.begin(); it != collection.mBlendmaps.end(); ++it) + { + // TODO: clean up blend textures on destruction + static int count=0; + Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/" + + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format); + + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true)); + map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format); + blendTextures.push_back(map); + } + + mMaterialGenerator->setLayerList(collection.mLayers); + mMaterialGenerator->setBlendmapList(blendTextures); +} + +void QuadTreeNode::loadMaterials() +{ + if (isDummy()) return; - std::vector blendmaps; - std::vector layerList; - mTerrain->getStorage()->getBlendmaps(mSize, mCenter, mTerrain->getShadersEnabled(), blendmaps, layerList); + // Load children first since we depend on them when creating a composite map + if (hasChildren()) + { + for (int i=0; i<4; ++i) + mChildren[i]->loadMaterials(); + } - mMaterialGenerator->setLayerList(layerList); - mMaterialGenerator->setBlendmapList(blendmaps); + if (mChunk) + { + if (mSize == 1) + { + mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); + } + else + { + ensureCompositeMap(); + mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); + mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + } + } } void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) @@ -525,8 +573,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) } else { - ensureLayerInfo(); - + // TODO: when to destroy? Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(Ogre::MaterialPtr()); makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material); } @@ -570,13 +617,3 @@ void QuadTreeNode::applyMaterials() for (int i=0; i<4; ++i) mChildren[i]->applyMaterials(); } - -void QuadTreeNode::setVisible(bool visible) -{ - if (!visible && mChunk) - mChunk->setVisible(false); - - if (hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->setVisible(visible); -} diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 134bc2d347..c575894879 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -51,8 +51,6 @@ namespace Terrain QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); ~QuadTreeNode(); - void setVisible(bool visible); - /// Rebuild all materials void applyMaterials(); @@ -100,7 +98,9 @@ namespace Terrain World* getTerrain() { return mTerrain; } /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - void update (const Ogre::Vector3& cameraPos); + /// @param force Always choose to render this node, even if not the perfect LOD. + /// @return Did we (or all of our children) choose to render? + bool update (const Ogre::Vector3& cameraPos); /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. /// Call after QuadTreeNode::update! @@ -122,13 +122,17 @@ namespace Terrain /// Add a textured quad to a specific 2d area in the composite map scenemanager. /// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply /// call this method on their children. + /// @note Do not call this before World::areLayersLoaded() == true /// @param area area in image space to put the quad /// @param quads collect quads here so they can be deleted later void prepareForCompositeMap(Ogre::TRect area); /// Create a chunk for this node from the given data. void load (const LoadResponseData& data); - void unload(); + void unload(bool recursive=false); + void loadLayers (const LayerCollection& collection); + /// This is recursive! Call it once on the root node after all leafs have loaded layers. + void loadMaterials(); LoadState getLoadState() { return mLoadState; } @@ -138,10 +142,6 @@ namespace Terrain LoadState mLoadState; - /// Is this node (or any of its child nodes) currently configured to render itself? - /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) - bool mIsActive; - bool mIsDummy; float mSize; size_t mLodLevel; // LOD if we were to render this node in one chunk @@ -162,7 +162,6 @@ namespace Terrain Ogre::TexturePtr mCompositeMap; - void ensureLayerInfo(); void ensureCompositeMap(); }; diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 1b1968f1ac..d3770ea57a 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -44,6 +44,7 @@ namespace Terrain /// Create textures holding layer blend values for a terrain chunk. /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! /// @param chunkSize size of the terrain chunk in cell units /// @param chunkCenter center of the chunk in cell units /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - @@ -52,9 +53,21 @@ namespace Terrain /// @param blendmaps created blendmaps will be written here /// @param layerList names of the layer textures used will be written here virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + std::vector& blendmaps, std::vector& layerList) = 0; + /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. + /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. + /// @note The terrain chunks shouldn't be larger than one cell since otherwise we might + /// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used. + /// @note May be called from background threads. Make sure to only call thread-safe functions from here! + /// @param nodes A collection of nodes for which to retrieve the aforementioned data + /// @param out Output vector + /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - + /// otherwise, each texture contains blend values for one layer only. Shader-based rendering + /// can utilize packing, FFP can't. + virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) = 0; + virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0; virtual LayerInfo getDefaultLayer() = 0; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 69748a7215..844144d7c4 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -7,7 +7,6 @@ #include #include #include -#include // TEMP #include "storage.hpp" #include "quadtreenode.hpp" @@ -52,6 +51,9 @@ namespace namespace Terrain { + const Ogre::uint REQ_ID_CHUNK = 1; + const Ogre::uint REQ_ID_LAYERS = 2; + World::World(Ogre::SceneManager* sceneMgr, Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) : mStorage(storage) @@ -70,6 +72,7 @@ namespace Terrain , mChunksLoading(0) , mWorkQueueChannel(0) , mCache(storage->getCellVertices()) + , mLayerLoadPending(true) { #if TERRAIN_USE_SHADER == 0 if (mShaders) @@ -102,8 +105,12 @@ namespace Terrain mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); + // While building the quadtree, remember leaf nodes since we need to load their layers + LayersRequestData data; + data.mPack = getShadersEnabled(); + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); - buildQuadTree(mRootNode); + buildQuadTree(mRootNode, data.mNodes); //loadingListener->indicateProgress(); mRootNode->initAabb(); //loadingListener->indicateProgress(); @@ -114,6 +121,9 @@ namespace Terrain mWorkQueueChannel = wq->getChannel("LargeTerrain"); wq->addRequestHandler(mWorkQueueChannel, this); wq->addResponseHandler(mWorkQueueChannel, this); + + // Start loading layers in the background (for leaf nodes) + wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data)); } World::~World() @@ -126,7 +136,7 @@ namespace Terrain delete mStorage; } - void World::buildQuadTree(QuadTreeNode *node) + void World::buildQuadTree(QuadTreeNode *node, std::vector& leafs) { float halfSize = node->getSize()/2.f; @@ -142,6 +152,7 @@ namespace Terrain Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); convertBounds(bounds); node->setBoundingBox(bounds); + leafs.push_back(node); } else node->markAsDummy(); // no data available for this node, skip it @@ -164,10 +175,10 @@ namespace Terrain node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); - buildQuadTree(node->getChild(SW)); - buildQuadTree(node->getChild(SE)); - buildQuadTree(node->getChild(NW)); - buildQuadTree(node->getChild(NE)); + buildQuadTree(node->getChild(SW), leafs); + buildQuadTree(node->getChild(SE), leafs); + buildQuadTree(node->getChild(NW), leafs); + buildQuadTree(node->getChild(NE), leafs); // if all children are dummy, we are also dummy for (int i=0; i<4; ++i) @@ -267,7 +278,7 @@ namespace Terrain void World::syncLoad() { - while (mChunksLoading) + while (mChunksLoading || mLayerLoadPending) { OGRE_THREAD_SLEEP(0); Ogre::Root::getSingleton().getWorkQueue()->processResponses(); @@ -276,27 +287,36 @@ namespace Terrain Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) { - const LoadRequestData data = Ogre::any_cast(req->getData()); + if (req->getType() == REQ_ID_CHUNK) + { + const LoadRequestData data = Ogre::any_cast(req->getData()); - QuadTreeNode* node = data.mNode; + QuadTreeNode* node = data.mNode; - LoadResponseData* responseData = new LoadResponseData(); + LoadResponseData* responseData = new LoadResponseData(); - Ogre::Timer timer; - getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), - responseData->mPositions, responseData->mNormals, responseData->mColours); + getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(), + responseData->mPositions, responseData->mNormals, responseData->mColours); - std::cout << "THREAD" << std::endl; + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } + else // REQ_ID_LAYERS + { + const LayersRequestData data = Ogre::any_cast(req->getData()); - responseData->time = timer.getMicroseconds(); + LayersResponseData* responseData = new LayersResponseData(); - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); + + return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + } } void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) { - static unsigned long time = 0; - if (res->succeeded()) + assert(res->succeeded() && "Response failure not handled"); + + if (res->getRequest()->getType() == REQ_ID_CHUNK) { LoadResponseData* data = Ogre::any_cast(res->getData()); @@ -304,23 +324,31 @@ namespace Terrain requestData.mNode->load(*data); - time += data->time; - delete data; - std::cout << "RESPONSE, reqs took ms" << time/1000.f << std::endl; + --mChunksLoading; + } + else // REQ_ID_LAYERS + { + LayersResponseData* data = Ogre::any_cast(res->getData()); + + for (std::vector::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it) + { + it->mTarget->loadLayers(*it); + } + + mRootNode->loadMaterials(); + + mLayerLoadPending = false; } - --mChunksLoading; } void World::queueLoad(QuadTreeNode *node) { LoadRequestData data; data.mNode = node; - data.mPack = getShadersEnabled(); - const Ogre::uint16 loadRequestId = 1; - Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, loadRequestId, Ogre::Any(data)); + Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data)); ++mChunksLoading; } } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 3547387444..26a6d034dc 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -122,15 +122,20 @@ namespace Terrain /// Maximum size of a terrain batch along one side (in cell units) float mMaxBatchSize; - void buildQuadTree(QuadTreeNode* node); + void buildQuadTree(QuadTreeNode* node, std::vector& leafs); BufferCache mCache; + // Are layers for leaf nodes loaded? This is done once at startup (but in a background thread) + bool mLayerLoadPending; + public: // ----INTERNAL---- Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } BufferCache& getBufferCache() { return mCache; } + bool areLayersLoaded() { return !mLayerLoadPending; } + // Delete all quads void clearCompositeMapSceneManager(); void renderCompositeMap (Ogre::TexturePtr target); @@ -151,7 +156,6 @@ namespace Terrain struct LoadRequestData { QuadTreeNode* mNode; - bool mPack; friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) { return o; } @@ -162,16 +166,28 @@ namespace Terrain std::vector mPositions; std::vector mNormals; std::vector mColours; - // Since we can't create a texture from a different thread, this only holds the raw texel data - std::vector< std::vector > mBlendmaps; - std::vector mLayerList; - - unsigned long time; friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) { return o; } }; + struct LayersRequestData + { + std::vector mNodes; + bool mPack; + + friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r) + { return o; } + }; + + struct LayersResponseData + { + std::vector mLayerCollections; + + friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r) + { return o; } + }; + } #endif From 83b6fcf22e1d6367eec2182958b3821907b5eb52 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 5 Mar 2014 22:24:05 +0100 Subject: [PATCH 065/240] Bug #416: Workaround for page flipping problem --- apps/openmw/mwgui/loadingscreen.cpp | 13 +++++++++++++ apps/openmw/mwworld/scene.cpp | 7 ++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 53a45db09b..46c6aebdc8 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -72,6 +72,10 @@ namespace MWGui void LoadingScreen::loadingOn() { + // Early-out if already on + if (mRectangle->getVisible()) + return; + // Temporarily turn off VSync, we want to do actual loading rather than waiting for the screen to sync. // Threaded loading would be even better, of course - especially because some drivers force VSync to on and we can't change it. // In Ogre 1.8, the swapBuffers argument is useless and setVSyncEnabled is bugged with GLX, nothing we can do :/ @@ -80,6 +84,15 @@ namespace MWGui mWindow->setVSyncEnabled(false); #endif + if (!mFirstLoad) + { + // When the loading screen is updated, we will disable render target clearing and only draw the loading bar itself. + // But if using page flipping, this can cause jitteriness as it will alternate between the last two rendered frames. + // Even though we attempt to disable vsync above, this may not work if it's forced on the driver level. + // Therefore render the same frame twice before activating the loading bar. + mWindow->update(); + } + setVisible(true); if (mFirstLoad) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index caae72c137..3d4413a357 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -214,12 +214,13 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { - mRendering.enableTerrain(true); Nif::NIFFile::CacheLock cachelock; Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); + mRendering.enableTerrain(true); + std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); @@ -364,11 +365,11 @@ namespace MWWorld Nif::NIFFile::CacheLock lock; MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.5); - mRendering.enableTerrain(false); - Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); + mRendering.enableTerrain(false); + std::string loadingInteriorText = "#{sLoadingMessage2}"; loadingListener->setLabel(loadingInteriorText); From d0f98103e4fb57a609fbe84d8b9865f5dd199a47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Mar 2014 02:58:46 +0100 Subject: [PATCH 066/240] Terrain: re-added "distant land=off" path Still a hack, but the overhead of building and traversing the quad tree appears negligible. --- components/CMakeLists.txt | 2 +- components/terrain/quadtreenode.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0a99c9528a..db4ecad0b1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -75,7 +75,7 @@ add_component_dir (translation add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world storage material buffercache compositemap defs + quadtreenode chunk world storage material buffercache defs ) add_component_dir (loadinglistener diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 56cfc2a745..40a8baaf02 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -282,6 +282,9 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) size_t wantedLod = 0; float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); + if (!mTerrain->getDistantLandEnabled() && dist > cellWorldSize) + return true; + if (dist > cellWorldSize*64) wantedLod = 6; else if (dist > cellWorldSize*32) From e08f6c9ce3866204fcd6fd5bf3cd83c3c36a2e44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 6 Mar 2014 04:01:25 +0100 Subject: [PATCH 067/240] Bug #416: Copy framebuffer to a texture instead of not clearing Potentially faster than the previous workaround, and should work for triple buffering too. --- apps/openmw/mwgui/loadingscreen.cpp | 35 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 46c6aebdc8..37e29591b8 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -86,11 +87,26 @@ namespace MWGui if (!mFirstLoad) { - // When the loading screen is updated, we will disable render target clearing and only draw the loading bar itself. - // But if using page flipping, this can cause jitteriness as it will alternate between the last two rendered frames. - // Even though we attempt to disable vsync above, this may not work if it's forced on the driver level. - // Therefore render the same frame twice before activating the loading bar. - mWindow->update(); + mBackgroundImage->setImageTexture(""); + int width = mWindow->getWidth(); + int height = mWindow->getHeight(); + const std::string textureName = "@loading_background"; + Ogre::TexturePtr texture; + texture = Ogre::TextureManager::getSingleton().getByName(textureName); + if (texture.isNull()) + { + texture = Ogre::TextureManager::getSingleton().createManual(textureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + width, height, 0, mWindow->suggestPixelFormat(), Ogre::TU_DYNAMIC_WRITE_ONLY); + } + texture->unload(); + texture->setWidth(width); + texture->setHeight(height); + texture->createInternalResources(); + mWindow->copyContentsToMemory(texture->getBuffer()->lock(Ogre::Image::Box(0,0,width,height), Ogre::HardwareBuffer::HBL_DISCARD)); + texture->getBuffer()->unlock(); + mBackgroundImage->setImageTexture(texture->getName()); } setVisible(true); @@ -99,10 +115,6 @@ namespace MWGui { changeWallpaper(); } - else - { - mBackgroundImage->setImageTexture(""); - } MWBase::Environment::get().getWindowManager()->pushGuiMode(mFirstLoad ? GM_LoadingWallpaper : GM_Loading); } @@ -216,8 +228,6 @@ namespace MWGui MWBase::Environment::get().getInputManager()->update(0, true); - mWindow->getViewport(0)->setClearEveryFrame(false); - // First, swap buffers from last draw, then, queue an update of the // window contents, but don't swap buffers (which would have // caused a sync / flush and would be expensive). @@ -227,9 +237,6 @@ namespace MWGui mWindow->update(false); - mWindow->getViewport(0)->setClearEveryFrame(true); - - mRectangle->setVisible(false); // resume 3d rendering From 7386d3eb0b00a20e48d8c513a3214e56eedba730 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 10:01:23 +0100 Subject: [PATCH 068/240] moved navigation into the WorldspaceWidget --- apps/opencs/view/render/scenewidget.hpp | 2 ++ apps/opencs/view/render/worldspacewidget.cpp | 34 +++++++++++++++++++- apps/opencs/view/render/worldspacewidget.hpp | 24 ++++++++++++++ apps/opencs/view/world/scenesubview.cpp | 24 +++----------- apps/opencs/view/world/scenesubview.hpp | 15 ++------- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 4a18c42443..c0537aa734 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -25,6 +25,8 @@ namespace CSVRender QPaintEngine* paintEngine() const; + protected: + void setNavigation (Navigation *navigation); ///< \attention The ownership of \a navigation is not transferred to *this. diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index c88821a629..dcd152bb33 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -1,6 +1,38 @@ #include "worldspacewidget.hpp" +#include "../world/scenetoolmode.hpp" + CSVRender::WorldspaceWidget::WorldspaceWidget (QWidget *parent) : SceneWidget (parent) -{} \ No newline at end of file +{} + +void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) +{ + if (mode=="1st") + setNavigation (&m1st); + else if (mode=="free") + setNavigation (&mFree); + else if (mode=="orbit") + setNavigation (&mOrbit); +} + +void CSVRender::WorldspaceWidget::selectDefaultNavigationMode() +{ + setNavigation (&m1st); +} + +CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector ( + CSVWorld::SceneToolbar *parent) +{ + CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); + + tool->addButton (":door.png", "1st"); /// \todo replace icons + tool->addButton (":GMST.png", "free"); + tool->addButton (":Info.png", "orbit"); + + connect (tool, SIGNAL (modeChanged (const std::string&)), + this, SLOT (selectNavigationMode (const std::string&))); + + return tool; +} \ No newline at end of file diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 1c122c9353..9d9452c173 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -3,15 +3,39 @@ #include "scenewidget.hpp" +#include "navigation1st.hpp" +#include "navigationfree.hpp" +#include "navigationorbit.hpp" + +namespace CSVWorld +{ + class SceneToolMode; + class SceneToolbar; +} + namespace CSVRender { class WorldspaceWidget : public SceneWidget { Q_OBJECT + CSVRender::Navigation1st m1st; + CSVRender::NavigationFree mFree; + CSVRender::NavigationOrbit mOrbit; + public: WorldspaceWidget (QWidget *parent = 0); + + CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); + ///< \important The created tool is not added to the toolbar (via addTool). Doing that + /// is the responsibility of the calling function. + + void selectDefaultNavigationMode(); + + private slots: + + void selectNavigationMode (const std::string& mode); }; } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 516a5db802..88b84ace54 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -33,19 +33,13 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48, this); - // navigation mode - SceneToolMode *tool = new SceneToolMode (toolbar); - tool->addButton (":door.png", "1st"); /// \todo replace icons - tool->addButton (":GMST.png", "free"); - tool->addButton (":Info.png", "orbit"); + mScene = new CSVRender::WorldspaceWidget (this); + + SceneToolMode *tool = mScene->makeNavigationSelector (toolbar); toolbar->addTool (tool); - connect (tool, SIGNAL (modeChanged (const std::string&)), - this, SLOT (selectNavigationMode (const std::string&))); layout2->addWidget (toolbar, 0); - mScene = new CSVRender::WorldspaceWidget (this); - layout2->addWidget (mScene, 1); layout->insertLayout (0, layout2, 1); @@ -60,7 +54,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D setWidget (widget); - mScene->setNavigation (&m1st); + mScene->selectDefaultNavigationMode(); } void CSVWorld::SceneSubView::setEditLock (bool locked) @@ -79,13 +73,3 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } - -void CSVWorld::SceneSubView::selectNavigationMode (const std::string& mode) -{ - if (mode=="1st") - mScene->setNavigation (&m1st); - else if (mode=="free") - mScene->setNavigation (&mFree); - else if (mode=="orbit") - mScene->setNavigation (&mOrbit); -} diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index f944e17c3e..21b3dc204b 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -3,10 +3,6 @@ #include "../doc/subview.hpp" -#include "../render/navigation1st.hpp" -#include "../render/navigationfree.hpp" -#include "../render/navigationorbit.hpp" - class QModelIndex; namespace CSMDoc @@ -16,7 +12,7 @@ namespace CSMDoc namespace CSVRender { - class SceneWidget; + class WorldspaceWidget; } namespace CSVWorld @@ -30,10 +26,7 @@ namespace CSVWorld Q_OBJECT TableBottomBox *mBottom; - CSVRender::SceneWidget *mScene; - CSVRender::Navigation1st m1st; - CSVRender::NavigationFree mFree; - CSVRender::NavigationOrbit mOrbit; + CSVRender::WorldspaceWidget *mScene; public: @@ -44,10 +37,6 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); - - private slots: - - void selectNavigationMode (const std::string& mode); }; } From 397921e45766f7f8c99591264fd2310a2a99c9e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 10:13:39 +0100 Subject: [PATCH 069/240] specialising WorldspaceWdiget into paged and unpaged subclasses --- apps/opencs/CMakeLists.txt | 2 +- .../view/render/pagedworldspacewidget.cpp | 6 ++++++ .../view/render/pagedworldspacewidget.hpp | 18 ++++++++++++++++++ .../view/render/unpagedworldspacewidget.cpp | 6 ++++++ .../view/render/unpagedworldspacewidget.hpp | 18 ++++++++++++++++++ apps/opencs/view/world/scenesubview.cpp | 8 ++++++-- 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/view/render/pagedworldspacewidget.cpp create mode 100644 apps/opencs/view/render/pagedworldspacewidget.hpp create mode 100644 apps/opencs/view/render/unpagedworldspacewidget.cpp create mode 100644 apps/opencs/view/render/unpagedworldspacewidget.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 72757556db..621b9bf869 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -64,7 +64,7 @@ opencs_units (view/world ) opencs_units (view/render - scenewidget worldspacewidget + scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget ) opencs_units_noqt (view/render diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp new file mode 100644 index 0000000000..fa32e39596 --- /dev/null +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -0,0 +1,6 @@ + +#include "pagedworldspacewidget.hpp" + +CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent) +: WorldspaceWidget (parent) +{} \ No newline at end of file diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp new file mode 100644 index 0000000000..172e2477a1 --- /dev/null +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -0,0 +1,18 @@ +#ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H +#define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H + +#include "worldspacewidget.hpp" + +namespace CSVRender +{ + class PagedWorldspaceWidget : public WorldspaceWidget + { + Q_OBJECT + + public: + + PagedWorldspaceWidget (QWidget *parent); + }; +} + +#endif diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp new file mode 100644 index 0000000000..6ccb1b99b8 --- /dev/null +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -0,0 +1,6 @@ + +#include "unpagedworldspacewidget.hpp" + +CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (QWidget *parent) +: WorldspaceWidget (parent) +{} \ No newline at end of file diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp new file mode 100644 index 0000000000..7f9c895bce --- /dev/null +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -0,0 +1,18 @@ +#ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H +#define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H + +#include "worldspacewidget.hpp" + +namespace CSVRender +{ + class UnpagedWorldspaceWidget : public WorldspaceWidget + { + Q_OBJECT + + public: + + UnpagedWorldspaceWidget (QWidget *parent); + }; +} + +#endif diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 88b84ace54..091e78ec48 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,7 +9,8 @@ #include "../filter/filterbox.hpp" -#include "../render/worldspacewidget.hpp" +#include "../render/pagedworldspacewidget.hpp" +#include "../render/unpagedworldspacewidget.hpp" #include "tablebottombox.hpp" #include "creator.hpp" @@ -33,7 +34,10 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48, this); - mScene = new CSVRender::WorldspaceWidget (this); + if (id.getId()[0]=='#') + mScene = new CSVRender::PagedWorldspaceWidget (this); + else + mScene = new CSVRender::UnpagedWorldspaceWidget (this); SceneToolMode *tool = mScene->makeNavigationSelector (toolbar); toolbar->addTool (tool); From 7e0f0c8402b16637674c862c00093b09743734d9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 12:42:09 +0100 Subject: [PATCH 070/240] fixed SceneWidget destructor --- apps/opencs/view/render/scenewidget.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6f07c1b0d5..f71329c611 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -94,7 +94,11 @@ namespace CSVRender SceneWidget::~SceneWidget() { - Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + if (mWindow) + Ogre::Root::getSingleton().destroyRenderTarget (mWindow); + + if (mSceneMgr) + Ogre::Root::getSingleton().destroySceneManager (mSceneMgr); } void SceneWidget::setNavigation (Navigation *navigation) From 4a119c8f465264cd9894c772f8d6e843699e3570 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 13:02:21 +0100 Subject: [PATCH 071/240] ambient lighting in interior cells --- apps/opencs/view/render/scenewidget.cpp | 5 ++ apps/opencs/view/render/scenewidget.hpp | 4 ++ .../view/render/unpagedworldspacewidget.cpp | 60 ++++++++++++++++++- .../view/render/unpagedworldspacewidget.hpp | 28 ++++++++- apps/opencs/view/world/scenesubview.cpp | 2 +- 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index f71329c611..2c98b60cf7 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -59,6 +59,11 @@ namespace CSVRender timer->start (20); /// \todo make this configurable } + void SceneWidget::setAmbient (const Ogre::ColourValue& colour) + { + mSceneMgr->setAmbientLight (colour); + } + void SceneWidget::updateOgreWindow() { if (mWindow) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index c0537aa734..ad68897ac0 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -8,6 +8,7 @@ namespace Ogre class Camera; class SceneManager; class RenderWindow; + class ColourValue; } namespace CSVRender @@ -25,6 +26,9 @@ namespace CSVRender QPaintEngine* paintEngine() const; + void setAmbient (const Ogre::ColourValue& colour); + ///< \note The actual ambient colour may differ based on lighting settings. + protected: void setNavigation (Navigation *navigation); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 6ccb1b99b8..812a3b165e 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -1,6 +1,60 @@ #include "unpagedworldspacewidget.hpp" -CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (QWidget *parent) -: WorldspaceWidget (parent) -{} \ No newline at end of file +#include + +#include "../../model/doc/document.hpp" + +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" + +void CSVRender::UnpagedWorldspaceWidget::update() +{ + const CSMWorld::Record& record = + dynamic_cast&> (mCellsModel->getRecord (mCellId)); + + Ogre::ColourValue colour; + colour.setAsABGR (record.get().mAmbi.mAmbient); + setAmbient (colour); + + /// \todo deal with mSunlight and mFog/mForDensity +} + +CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, + CSMDoc::Document& document, QWidget *parent) +: WorldspaceWidget (parent), mCellId (cellId) +{ + mCellsModel = &dynamic_cast ( + *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); + connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); + + update(); +} + +void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0); + + if (cellIndex.row()>=topLeft.row() && cellIndex.row()getModelIndex (mCellId, 0); + + if (cellIndex.row()>=start && cellIndex.row()<=end) + { + + } +} \ No newline at end of file diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index 7f9c895bce..17dc46918d 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -1,17 +1,43 @@ #ifndef OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H +#include + #include "worldspacewidget.hpp" +class QModelIndex; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class IdTable; +} + namespace CSVRender { class UnpagedWorldspaceWidget : public WorldspaceWidget { Q_OBJECT + std::string mCellId; + CSMWorld::IdTable *mCellsModel; + + void update(); + public: - UnpagedWorldspaceWidget (QWidget *parent); + UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, + QWidget *parent); + + private slots: + + void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); }; } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 091e78ec48..d04cc4c15b 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -37,7 +37,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D if (id.getId()[0]=='#') mScene = new CSVRender::PagedWorldspaceWidget (this); else - mScene = new CSVRender::UnpagedWorldspaceWidget (this); + mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); SceneToolMode *tool = mScene->makeNavigationSelector (toolbar); toolbar->addTool (tool); From f04348fb8e3208b32e286acc4f0031c67e1f39c2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 13:43:06 +0100 Subject: [PATCH 072/240] do not offer view action for cells flagged as deleted --- apps/opencs/view/world/table.cpp | 13 +++++++++++-- apps/opencs/view/world/table.hpp | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 95433dbfc0..4d469fd209 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -40,7 +40,16 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction(mCloneAction); if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) - menu.addAction (mViewAction); + { + int row = selectedRows.begin()->row(); + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + + CSMWorld::UniversalId id = mModel->view (row).first; + + if (!mData.getCells().getRecord (id.getId()).isDeleted()) + menu.addAction (mViewAction); + } } if (mCreateAction) @@ -168,7 +177,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document), mData (data) { mModel = &dynamic_cast (*data.getTableModel (id)); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 3bf673dac3..3791bf4ac2 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -48,6 +48,7 @@ namespace CSVWorld CSMWorld::IdTable *mModel; bool mEditLock; int mRecordStatusDisplay; + CSMWorld::Data& mData; /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you /// should NOT use it for anything else. From 0254a209f6cd1f8fe604b4e104f45d9719f87024 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 13:51:21 +0100 Subject: [PATCH 073/240] some general cleanup --- apps/opencs/view/world/table.cpp | 34 +++++++++++++------------ apps/opencs/view/world/table.hpp | 21 ++++++--------- apps/opencs/view/world/tablesubview.cpp | 2 +- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4d469fd209..4bb9955e6e 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -2,7 +2,6 @@ #include "table.hpp" #include - #include #include #include @@ -10,6 +9,8 @@ #include #include +#include "../../model/doc/document.hpp" + #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtableproxymodel.hpp" @@ -47,7 +48,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) CSMWorld::UniversalId id = mModel->view (row).first; - if (!mData.getCells().getRecord (id.getId()).isDeleted()) + if (!mDocument.getData().getCells().getRecord (id.getId()).isDeleted()) menu.addAction (mViewAction); } } @@ -175,11 +176,12 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const return deletableIds; } -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting, const CSMDoc::Document& document) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document), mData (data) +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, + bool createAndDelete, bool sorting, CSMDoc::Document& document) +: mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), + mDocument (document) { - mModel = &dynamic_cast (*data.getTableModel (id)); + mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel->setSourceModel (mModel); @@ -203,7 +205,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - undoStack, this); + mDocument.getUndoStack(), this); mDelegates.push_back (delegate); setItemDelegateForColumn (i, delegate); @@ -285,13 +287,13 @@ void CSVWorld::Table::revertRecord() if (revertableIds.size()>0) { if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); + mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + mDocument.getUndoStack().push (new CSMWorld::RevertCommand (*mModel, *iter)); if (revertableIds.size()>1) - mUndoStack.endMacro(); + mDocument.getUndoStack().endMacro(); } } } @@ -305,13 +307,13 @@ void CSVWorld::Table::deleteRecord() if (deletableIds.size()>0) { if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); + mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (*mModel, *iter)); if (deletableIds.size()>1) - mUndoStack.endMacro(); + mDocument.getUndoStack().endMacro(); } } } @@ -364,7 +366,7 @@ void CSVWorld::Table::moveUpRecord() for (int i=1; i command (new CSMWorld::ModifyCommand (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); - mUndoStack.push (command.release()); + mDocument.getUndoStack().push (command.release()); } } //TODO handle drops from different document } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 3791bf4ac2..e8d5648d16 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -10,13 +10,14 @@ #include "../../model/filter/node.hpp" #include "../../model/world/columnbase.hpp" -namespace CSMDoc { - class Document; -} - class QUndoStack; class QAction; +namespace CSMDoc +{ + class Document; +} + namespace CSMWorld { class Data; @@ -35,7 +36,6 @@ namespace CSVWorld Q_OBJECT std::vector mDelegates; - QUndoStack& mUndoStack; QAction *mEditAction; QAction *mCreateAction; QAction *mCloneAction; @@ -48,11 +48,7 @@ namespace CSVWorld CSMWorld::IdTable *mModel; bool mEditLock; int mRecordStatusDisplay; - CSMWorld::Data& mData; - - /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you - /// should NOT use it for anything else. - const CSMDoc::Document& mDocument; + CSMDoc::Document& mDocument; private: @@ -72,9 +68,8 @@ namespace CSVWorld public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, - bool sorting, const CSMDoc::Document& document); - + Table (const CSMWorld::UniversalId& id, bool createAndDelete, + bool sorting, CSMDoc::Document& document); ///< \param createAndDelete Allow creation and deletion of records. /// \param sorting Allow changing order of rows in the view via column headers. diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 30a60536ae..2d08d186e5 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -24,7 +24,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); + new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); From 516a3b9abd27c126bc8864f02bcb2e50303a8939 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Mar 2014 16:40:08 +0100 Subject: [PATCH 074/240] close interior cell view when cell is deleted --- .../view/render/unpagedworldspacewidget.cpp | 22 ++++++++++++------- apps/opencs/view/render/worldspacewidget.hpp | 4 ++++ apps/opencs/view/world/scenesubview.cpp | 7 ++++++ apps/opencs/view/world/scenesubview.hpp | 4 ++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 812a3b165e..c7edbe79b1 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -38,13 +38,21 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0); + int index = mCellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification); + QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, index); - if (cellIndex.row()>=topLeft.row() && cellIndex.row()=topLeft.row() && cellIndex.row()<=bottomRight.row()) { - /// \todo possible optimisation: check columns and update only if relevant columns have - /// changed - update(); + if (mCellsModel->data (cellIndex).toInt()==CSMWorld::RecordBase::State_Deleted) + { + emit closeRequest(); + } + else + { + /// \todo possible optimisation: check columns and update only if relevant columns have + /// changed + update(); + } } } @@ -54,7 +62,5 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI QModelIndex cellIndex = mCellsModel->getModelIndex (mCellId, 0); if (cellIndex.row()>=start && cellIndex.row()<=end) - { - - } + emit closeRequest(); } \ No newline at end of file diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 9d9452c173..2eccca3bf4 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -36,6 +36,10 @@ namespace CSVRender private slots: void selectNavigationMode (const std::string& mode); + + signals: + + void closeRequest(); }; } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index d04cc4c15b..3601ae0942 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -59,6 +59,8 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D setWidget (widget); mScene->selectDefaultNavigationMode(); + + connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); } void CSVWorld::SceneSubView::setEditLock (bool locked) @@ -77,3 +79,8 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); } + +void CSVWorld::SceneSubView::closeRequest() +{ + deleteLater(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index 21b3dc204b..ecf3fe4e49 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -37,6 +37,10 @@ namespace CSVWorld virtual void updateEditorSetting (const QString& key, const QString& value); virtual void setStatusBar (bool show); + + private slots: + + void closeRequest(); }; } From b0ae78e889f2b55f99988304224b14519e8fd80a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 6 Mar 2014 20:10:13 +0100 Subject: [PATCH 075/240] basicly enabled code --- apps/opencs/view/world/dialoguesubview.cpp | 3 +++ apps/opencs/view/world/dialoguesubview.hpp | 2 +- apps/opencs/view/world/subviews.cpp | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index cedb20de92..a3d723de3f 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -12,6 +12,9 @@ #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" +#include "recordstatusdelegate.hpp" +#include "util.hpp" + CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : SubView (id) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 64715f5b74..496730db58 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -18,7 +18,7 @@ namespace CSVWorld public: - DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); virtual void setEditLock (bool locked); }; diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 74ce03cce6..93ef7d2103 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -78,4 +78,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CreatorFactory >); manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); + + manager.add(CSMWorld::UniversalId::Type_Region, new CSVDoc::SubViewFactory); } \ No newline at end of file From f36bea03ab40acdd265f4bab73eb3e89d4c095b4 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 7 Mar 2014 19:33:46 +1100 Subject: [PATCH 076/240] Bug #1189 fix by scrawl --- apps/openmw/mwworld/worldimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b51e224f84..f154f3b248 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1873,6 +1873,8 @@ namespace MWWorld bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { + if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) + return false; // cannot get LOS unless both NPC's are enabled Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents(); float* pos1 = npc.getRefData().getPosition().pos; Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents(); From f0a45fa15feb05d5331924aef6e5a6fdf881653b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 7 Mar 2014 17:15:43 +0100 Subject: [PATCH 077/240] just saved progress --- apps/opencs/CMakeLists.txt | 5 +- apps/opencs/view/world/dialoguesubview.cpp | 135 +++++++++++++++------ apps/opencs/view/world/dialoguesubview.hpp | 50 ++++++++ 3 files changed, 147 insertions(+), 43 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6bcad1d08f..a7b39feb8c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator scriptedit + scenetoolmode infocreator scriptedit dialoguecreator ) opencs_units (view/render @@ -68,8 +68,7 @@ opencs_units (view/render ) opencs_units_noqt (view/world - dialoguesubview subviews - enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate + subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate scripthighlighter idvalidator dialoguecreator ) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index a3d723de3f..a411ca7f47 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -1,12 +1,17 @@ #include "dialoguesubview.hpp" +#include +#include + #include #include +#include #include #include #include #include +#include #include #include "../../model/world/columnbase.hpp" @@ -15,9 +20,88 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack) : +mParent(parent), +mTable(table), +mUndoStack(undoStack) +{} + +CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) +{ + CommandDelegate *delegate = NULL; + std::map::const_iterator delegateIt(mDelegates.find(display)); + if (delegateIt == mDelegates.end()) + { + delegate = CommandDelegateFactoryCollection::get().makeDelegate ( + display, mUndoStack, mParent); + mDelegates.insert(std::make_pair(display, delegate)); + } else + { + delegate = delegateIt->second; + } + connect(this, SIGNAL(closeEditor(QWidget *)), this, SLOT(editorDataCommited(QWidget*))); + return delegate; +} + +void CSVWorld::DialogueDelegateDispatcher::editorDataCommited( QWidget * editor ) +{ + std::cout<<"triggered"< + (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + std::map::const_iterator delegateIt(mDelegates.find(display)); + if (delegateIt != mDelegates.end()) + { + delegateIt->second->setEditorData(editor, index); + } +} + +void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + CSMWorld::ColumnBase::Display display = static_cast + (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + std::cout<<"setting data\n"; + std::map::const_iterator delegateIt(mDelegates.find(display)); + if (delegateIt != mDelegates.end()) + { + delegateIt->second->setModelData(editor, model, index); + } else { + std::cout<<"oooops\n"; + } +} + +void CSVWorld::DialogueDelegateDispatcher::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + //Does nothing +} + +QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(); //silencing warning, otherwise does nothing +} + +QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) +{ + QWidget* editor = NULL; + std::map::iterator delegateIt(mDelegates.find(display)); + if (delegateIt != mDelegates.end()) + { + editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index); + } + return editor; +} + CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) -: SubView (id) + bool createAndDelete) : + + SubView (id), + mDispatcher(new DialogueDelegateDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack())) + { QWidget *widget = new QWidget (this); @@ -33,6 +117,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel (model); + mWidgetMapper->setItemDelegate(mDispatcher.get()); for (int i=0; i (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - QWidget *widget = 0; + mDispatcher->makeDelegate(display); + QWidget *widget = mDispatcher->makeEditor(display, (model->index (0, i))); + + if (widget) + { + layout->addWidget (widget, i, 1); + mWidgetMapper->addMapping (widget, i); + } if (model->flags (model->index (0, i)) & Qt::ItemIsEditable) { - switch (display) - { - case CSMWorld::ColumnBase::Display_String: - layout->addWidget (widget = new QLineEdit, i, 1); - break; - - case CSMWorld::ColumnBase::Display_Integer: - - /// \todo configure widget properly (range) - layout->addWidget (widget = new QSpinBox, i, 1); - break; - - case CSMWorld::ColumnBase::Display_Float: - - /// \todo configure widget properly (range, format?) - layout->addWidget (widget = new QDoubleSpinBox, i, 1); - break; - - default: break; // silence warnings for other times for now - } } - else - { - switch (display) - { - case CSMWorld::ColumnBase::Display_String: - case CSMWorld::ColumnBase::Display_Integer: - case CSMWorld::ColumnBase::Display_Float: - - layout->addWidget (widget = new QLabel, i, 1); - break; - - default: break; // silence warnings for other times for now - } - } - - if (widget) - mWidgetMapper->addMapping (widget, i); } } diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 496730db58..142d942eb5 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -1,9 +1,22 @@ #ifndef CSV_WORLD_DIALOGUESUBVIEW_H #define CSV_WORLD_DIALOGUESUBVIEW_H +#include +#include + +#include + #include "../doc/subview.hpp" +#include "../../model/world/columnbase.hpp" class QDataWidgetMapper; +class QSize; +class QEvent; + +namespace CSMWorld +{ + class IdTable; +} namespace CSMDoc { @@ -12,9 +25,46 @@ namespace CSMDoc namespace CSVWorld { + class CommandDelegate; + + class DialogueDelegateDispatcher : public QAbstractItemDelegate + { + Q_OBJECT + std::map mDelegates; + + QObject* mParent; + + const CSMWorld::IdTable* mTable; //nor sure if it is needed TODO + + QUndoStack& mUndoStack; + + public: + DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack); + + CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display); + + QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index); + ///< will return null if delegate is not present, parent of the widget is same as for dispatcher itself + + virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; + + virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + + virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + ///< does nothing + + virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; + ///< does nothing + + private slots: + void editorDataCommited( QWidget * editor ); + + }; + class DialogueSubView : public CSVDoc::SubView { QDataWidgetMapper *mWidgetMapper; + std::auto_ptr mDispatcher; public: From 2e6b45dafbed4ac1c1e4f80154e62e035fdae210 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 7 Mar 2014 22:17:40 +0100 Subject: [PATCH 078/240] edit view, savind works --- CMakeLists.txt | 4 +- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 82 ++++++++++++++++++---- apps/opencs/view/world/dialoguesubview.hpp | 44 ++++++++++-- apps/opencs/view/world/subviews.cpp | 1 + 5 files changed, 110 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 954e161a1d..0f76e36880 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,8 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") - if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) - message(FATAL_ERROR "Silly Zini forgot to update the version again...") +# if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) +# message(FATAL_ERROR "Silly Zini forgot to update the version again...") else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5034d97538..b635f746ef 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator scriptedit dialoguecreator + scenetoolmode infocreator scriptedit dialoguesubview ) opencs_units (view/render diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index a411ca7f47..37a2ab130e 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -20,11 +20,45 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +/* +==============================DialogueDelegateDispatcherProxy========================================== +*/ +CSVWorld::refWrapper::refWrapper(const QModelIndex& index) : +mIndex(index) +{} + +CSVWorld::DialogueDelegateDispatcherProxy::DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display) : +mEditor(editor), +mDisplay(display), +mIndexWrapper(NULL) +{ +} + +void CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited() +{ + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); +} + +void CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index) +{ + mIndexWrapper.reset(new refWrapper(index)); +} + +QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const +{ + return mEditor; +} + +/* +==============================DialogueDelegateDispatcher========================================== +*/ + CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack) : mParent(parent), mTable(table), mUndoStack(undoStack) -{} +{ +} CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CSMWorld::ColumnBase::Display display) { @@ -39,13 +73,13 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS { delegate = delegateIt->second; } - connect(this, SIGNAL(closeEditor(QWidget *)), this, SLOT(editorDataCommited(QWidget*))); return delegate; } -void CSVWorld::DialogueDelegateDispatcher::editorDataCommited( QWidget * editor ) +void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display) { - std::cout<<"triggered"<second->setEditorData(editor, index); } + + for (unsigned i = 0; i < mProxys.size(); ++i) + { + if (mProxys[i]->getEditor() == editor) + { + mProxys[i]->setIndex(index); + } + } } -void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +void CSVWorld::DialogueDelegateDispatcher::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { - CSMWorld::ColumnBase::Display display = static_cast - (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - - std::cout<<"setting data\n"; std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { delegateIt->second->setModelData(editor, model, index); - } else { - std::cout<<"oooops\n"; } } @@ -92,15 +128,31 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: if (delegateIt != mDelegates.end()) { editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index); + DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); + connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); + mProxys.push_back(proxy); //deleted in the destructor } return editor; } +CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() +{ + for (unsigned i = 0; i < mProxys.size(); ++i) + { + delete mProxys[i]; //unique_ptr could be handy + } +} + +/* +==============================DialogueSubView========================================== +*/ + CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : SubView (id), - mDispatcher(new DialogueDelegateDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack())) + mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()) { QWidget *widget = new QWidget (this); @@ -117,7 +169,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel (model); - mWidgetMapper->setItemDelegate(mDispatcher.get()); + mWidgetMapper->setItemDelegate(&mDispatcher); for (int i=0; i (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); - mDispatcher->makeDelegate(display); - QWidget *widget = mDispatcher->makeEditor(display, (model->index (0, i))); + mDispatcher.makeDelegate(display); + QWidget *widget = mDispatcher.makeEditor(display, (model->index (0, i))); if (widget) { diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 142d942eb5..11cc918b19 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -27,6 +27,36 @@ namespace CSVWorld { class CommandDelegate; + class refWrapper + { + public: + refWrapper(const QModelIndex& index); + + const QModelIndex& mIndex; + }; + + class DialogueDelegateDispatcherProxy : public QObject + { + Q_OBJECT + QWidget* mEditor; + + CSMWorld::ColumnBase::Display mDisplay; + + + + std::auto_ptr mIndexWrapper; + public: + DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); + QWidget* getEditor() const; + + public slots: + void editorDataCommited(); + void setIndex(const QModelIndex& index); + + signals: + void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); + }; + class DialogueDelegateDispatcher : public QAbstractItemDelegate { Q_OBJECT @@ -34,13 +64,17 @@ namespace CSVWorld QObject* mParent; - const CSMWorld::IdTable* mTable; //nor sure if it is needed TODO + CSMWorld::IdTable* mTable; //nor sure if it is needed TODO QUndoStack& mUndoStack; + std::vector mProxys; //once we move to the C++11 we should use unique_ptr + public: DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack); + ~DialogueDelegateDispatcher(); + CSVWorld::CommandDelegate* makeDelegate(CSMWorld::ColumnBase::Display display); QWidget* makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index); @@ -48,7 +82,7 @@ namespace CSVWorld virtual void setEditorData (QWidget* editor, const QModelIndex& index) const; - virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const; virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing @@ -56,15 +90,15 @@ namespace CSVWorld virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; ///< does nothing - private slots: - void editorDataCommited( QWidget * editor ); + private slots: + void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); }; class DialogueSubView : public CSVDoc::SubView { QDataWidgetMapper *mWidgetMapper; - std::auto_ptr mDispatcher; + DialogueDelegateDispatcher mDispatcher; public: diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 93ef7d2103..4a94cbfe67 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -80,4 +80,5 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Region, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Spell, new CSVDoc::SubViewFactory); } \ No newline at end of file From 12de0afb03a382332f2da3d31b073fe9d49bb7ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 7 Mar 2014 06:11:00 +0100 Subject: [PATCH 079/240] Feature #50: Spawn projectiles Fix a bug in copyObjectToCell. Make actor rotations more consistent. --- apps/openmw/mwbase/world.hpp | 8 +- apps/openmw/mwclass/creaturelevlist.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/character.cpp | 12 +- apps/openmw/mwmechanics/spellcasting.cpp | 4 +- apps/openmw/mwrender/camera.cpp | 2 +- apps/openmw/mwrender/npcanimation.cpp | 48 +++- apps/openmw/mwrender/renderingmanager.cpp | 9 +- .../mwscript/transformationextensions.cpp | 34 +-- apps/openmw/mwstate/statemanagerimp.cpp | 4 +- apps/openmw/mwworld/actionteleport.cpp | 4 +- apps/openmw/mwworld/physicssystem.cpp | 16 +- apps/openmw/mwworld/worldimp.cpp | 245 +++++++++++++----- apps/openmw/mwworld/worldimp.hpp | 24 +- libs/openengine/bullet/physic.cpp | 2 + 16 files changed, 295 insertions(+), 127 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fd39789430..5c2912a8f0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -274,7 +274,7 @@ namespace MWBase virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; virtual void - moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0; + moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0; virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0; @@ -282,7 +282,7 @@ namespace MWBase virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; - virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0; + virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0; ///< place an object in a "safe" location (ie not in the void, etc). virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) @@ -464,8 +464,10 @@ namespace MWBase virtual void castSpell (const MWWorld::Ptr& actor) = 0; - virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) = 0; + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; virtual const std::vector& getContentFiles() const = 0; diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index ea586e5b63..caef521afb 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -57,7 +57,7 @@ namespace MWClass MWWorld::ManualRef ref(store, id); ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; // TODO: hold on to this for respawn purposes later - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); } ptr.getRefData().setCustomData(data.release()); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b5d83427b0..30cefe2df3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -578,13 +578,13 @@ namespace MWInput float rot[3]; rot[0] = -y; rot[1] = 0.0f; - rot[2] = x; + rot[2] = -x; // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { mPlayer->yaw(x); - mPlayer->pitch(-y); + mPlayer->pitch(y); } if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1fb22ce639..92be89f2f7 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -210,7 +210,7 @@ namespace MWMechanics && LOS ) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer())); + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); creatureStats.setHostile(true); } } @@ -541,7 +541,7 @@ namespace MWMechanics // TODO: Add AI to follow player and fight for him // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle())); } } else diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d19f32507d..04d909b497 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -313,6 +313,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentMovement); mCurrentMovement = movement; + mMovementAnimVelocity = 0.0f; if(!mCurrentMovement.empty()) { float vel, speedmult = 1.0f; @@ -320,7 +321,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run); if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + { + mMovementAnimVelocity = vel; speedmult = mMovementSpeed / vel; + } else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight) speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed else if (mMovementSpeed > 0.0f) @@ -330,10 +334,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f); mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul); - - mMovementAnimVelocity = vel; } - else mMovementAnimVelocity = 0.0f; } } @@ -1194,7 +1195,10 @@ void CharacterController::update(float duration) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if(rot.z != 0.0f && !inwater && !sneak) + // Don't play turning animations during attack. It would break positioning of the arrow bone when releasing a shot. + // Actually, in vanilla the turning animation is not even played when merely having equipped the weapon, + // but I don't think we need to go as far as that. + else if(rot.z != 0.0f && !inwater && !sneak && mUpperBodyState < UpperCharState_StartToMinAttack) { if(rot.z > 0.0f) movestate = CharState_TurnRight; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9745cdebe8..7f5a7fac55 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -587,7 +587,7 @@ namespace MWMechanics inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch); } - MWBase::Environment::get().getWorld()->launchProjectile(mId, false, enchantment->mEffects, mCaster, mSourceName); + MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName); return true; } @@ -666,7 +666,7 @@ namespace MWMechanics } } - MWBase::Environment::get().getWorld()->launchProjectile(mId, false, spell->mEffects, mCaster, mSourceName); + MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName); return true; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 9ae9c58788..2942649512 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -70,7 +70,7 @@ namespace MWRender if (!mVanity.enabled && !mPreviewMode) { mCamera->getParentNode()->setOrientation(xr); } else { - Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z); mCamera->getParentNode()->setOrientation(zr * xr); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 60760b7fb2..6169b6ddb2 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -532,7 +532,7 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) { float pitch = mPtr.getRefData().getPosition().rot[0]; Ogre::Node *node = baseinst->getBone("Bip01 Neck"); - node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch), Ogre::Node::TS_WORLD); // This has to be done before this function ends; // updateSkeletonInstance, below, touches the hands. @@ -543,9 +543,9 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) // In third person mode we may still need pitch for ranged weapon targeting float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; Ogre::Node *node = baseinst->getBone("Bip01 Spine2"); - node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); node = baseinst->getBone("Bip01 Spine1"); - node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. @@ -770,12 +770,35 @@ void NpcAnimation::attachArrow() void NpcAnimation::releaseArrow() { - // Thrown weapons get detached now MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weapon != inv.end() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + if (weapon == inv.end()) + return; + + // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) { + // Thrown weapons get detached now + NifOgre::ObjectScenePtr objects = mObjectParts[ESM::PRT_Weapon]; + + Ogre::Vector3 launchPos(0,0,0); + if (objects->mSkelBase) + { + launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (objects->mEntities.size()) + { + objects->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, 400); + showWeapons(false); + inv.remove(*weapon, 1, mPtr); } else @@ -784,6 +807,21 @@ void NpcAnimation::releaseArrow() MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); if (ammo == inv.end()) return; + + Ogre::Vector3 launchPos(0,0,0); + if (mAmmunition->mSkelBase) + { + launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (mAmmunition->mEntities.size()) + { + mAmmunition->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + /// \todo speed + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, 400); + inv.remove(*ammo, 1, mPtr); mAmmunition.setNull(); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 7c70b17f07..a0d06f0682 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -280,13 +280,12 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr) if(ptr.getRefData().getHandle() == mCamera->getHandle() && !mCamera->isVanityOrPreviewModeEnabled()) - mCamera->rotateCamera(rot, false); + mCamera->rotateCamera(-rot, false); - Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); + Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(rot.z), Ogre::Vector3::NEGATIVE_UNIT_Z); if(!MWWorld::Class::get(ptr).isActor()) - newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) * - Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo; - + newo = Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::NEGATIVE_UNIT_X) * + Ogre::Quaternion(Ogre::Radian(rot.y), Ogre::Vector3::NEGATIVE_UNIT_Y) * newo; ptr.getRefData().getBaseNode()->setOrientation(newo); } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 43a0fedce9..9a9175f191 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -82,27 +82,21 @@ namespace MWScript Interpreter::Type_Float angle = runtime[0].mFloat; runtime.pop(); - float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees(); - float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees(); - float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees(); - - float *objRot = ptr.getRefData().getPosition().rot; - - float lx = Ogre::Radian(objRot[0]).valueDegrees(); - float ly = Ogre::Radian(objRot[1]).valueDegrees(); - float lz = Ogre::Radian(objRot[2]).valueDegrees(); + float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); + float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); + float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees(); if (axis == "x") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az); } else if (axis == "y") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az); } else if (axis == "z") { - MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz); + MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -152,15 +146,15 @@ namespace MWScript if (axis=="x") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees()); } else if (axis=="y") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees()); } else if (axis=="z") { - runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees()); + runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } else throw std::runtime_error ("invalid ration axis: " + axis); @@ -313,7 +307,7 @@ namespace MWScript } if(store) { - MWBase::Environment::get().getWorld()->moveObject(ptr,*store,x,y,z); + MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity @@ -361,7 +355,7 @@ namespace MWScript int cx,cy; MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy); MWBase::Environment::get().getWorld()->moveObject(ptr, - *MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); + MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z); float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees(); float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees(); if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity @@ -421,7 +415,7 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = pos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); } else { @@ -462,7 +456,7 @@ namespace MWScript pos.rot[2] = zRot; MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = pos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos); } else { @@ -530,7 +524,7 @@ namespace MWScript MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, count); ref.getPtr().getCellRef().mPos = ipos; - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 3807a67b67..8b01bacdf6 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -148,7 +148,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot MWBase::World& world = *MWBase::Environment::get().getWorld(); - MWWorld::Ptr player = world.getPlayer().getPlayer(); + MWWorld::Ptr player = world.getPlayerPtr(); profile.mContentFiles = world.getContentFiles(); @@ -300,7 +300,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getWindowManager()->updatePlayer(); MWBase::Environment::get().getMechanicsManager()->playerLoaded(); - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index b4c572ba94..150f0bed24 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -41,11 +41,11 @@ namespace MWWorld int cellX; int cellY; world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY); - world->moveObject(actor,*world->getExterior(cellX,cellY), + world->moveObject(actor,world->getExterior(cellX,cellY), mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } else - world->moveObject(actor,*world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); + world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]); } } } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 8ef5797cac..1657cf2670 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -120,11 +120,9 @@ namespace MWWorld OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); if(!physicActor || !physicActor->getCollisionMode()) { - // FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why? - return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * - movement * time; + return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) + * movement * time; } btCollisionObject *colobj = physicActor->getCollisionBody(); @@ -140,14 +138,12 @@ namespace MWWorld Ogre::Vector3 velocity; if(position.z < waterlevel || isFlying) { - velocity = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)* - Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) * - movement; + velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* + Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; } else { - velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) * movement; + velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; if(!physicActor->getOnGround()) { // If falling, add part of the incoming velocity with the current inertia diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f154f3b248..0ef50c5828 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -228,6 +228,7 @@ namespace MWWorld mCells.clear(); + mMagicBolts.clear(); mProjectiles.clear(); mDoorStates.clear(); @@ -821,7 +822,7 @@ namespace MWWorld const ESM::Position &posdata = ptr.getRefData().getPosition(); Ogre::Vector3 pos(posdata.pos); Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X); + Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); MWRender::Animation *anim = mRendering->getAnimation(ptr); if(anim != NULL) @@ -858,7 +859,7 @@ namespace MWWorld } } - void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) + void World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); @@ -872,27 +873,27 @@ namespace MWWorld bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell); - if (*currCell != newCell) + if (currCell != newCell) { removeContainerScripts(ptr); if (isPlayer) { - if (!newCell.isExterior()) - changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.getCell()->mName), pos); + if (!newCell->isExterior()) + changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos); else { - int cellX = newCell.getCell()->getGridX(); - int cellY = newCell.getCell()->getGridY(); + int cellX = newCell->getCell()->getGridX(); + int cellY = newCell->getCell()->getGridY(); mWorldScene->changeCell(cellX, cellY, pos, false); } - addContainerScripts (getPlayerPtr(), &newCell); + addContainerScripts (getPlayerPtr(), newCell); } else { if (!mWorldScene->isCellActive(*currCell)) - copyObjectToCell(ptr, newCell, pos); - else if (!mWorldScene->isCellActive(newCell)) + ptr.getClass().copyToCell(ptr, *newCell, pos); + else if (!mWorldScene->isCellActive(*newCell)) { mWorldScene->removeObjectFromScene(ptr); mLocalScripts.remove(ptr); @@ -900,7 +901,7 @@ namespace MWWorld haveToMove = false; MWWorld::Ptr newPtr = MWWorld::Class::get(ptr) - .copyToCell(ptr, newCell); + .copyToCell(ptr, *newCell); newPtr.getRefData().setBaseNode(0); objectLeftActiveCell(ptr, newPtr); @@ -908,7 +909,7 @@ namespace MWWorld else { MWWorld::Ptr copy = - MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos); + MWWorld::Class::get(ptr).copyToCell(ptr, *newCell, pos); mRendering->updateObjectCell(ptr, copy); MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy); @@ -923,7 +924,7 @@ namespace MWWorld mLocalScripts.remove(ptr); removeContainerScripts (ptr); mLocalScripts.add(script, copy); - addContainerScripts (copy, &newCell); + addContainerScripts (copy, newCell); } } ptr.getRefData().setCount(0); @@ -947,7 +948,7 @@ namespace MWWorld cell = getExterior(cellX, cellY); } - moveObject(ptr, *cell, x, y, z); + moveObject(ptr, cell, x, y, z); return cell != ptr.getCell(); } @@ -1041,15 +1042,13 @@ namespace MWWorld while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; - float *worldRot = ptr.getRefData().getPosition().rot; + Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)); - Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)* - Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z)); - - Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)* - Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z)); + Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)* + Ogre::Quaternion(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z)); ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); mPhysics->rotateObject(ptr); @@ -1080,7 +1079,7 @@ namespace MWWorld pos.z = traced.z; } - moveObject(ptr, *ptr.getCell(), pos.x, pos.y, pos.z); + moveObject(ptr, ptr.getCell(), pos.x, pos.y, pos.z); } void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust) @@ -1091,9 +1090,9 @@ namespace MWWorld adjust); } - MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) + MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) { - return copyObjectToCell(ptr,Cell,pos,false); + return copyObjectToCell(ptr,cell,pos,false); } void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const @@ -1127,6 +1126,7 @@ namespace MWWorld { processDoors(duration); + moveMagicBolts(duration); moveProjectiles(duration); const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration); @@ -1506,15 +1506,7 @@ namespace MWWorld if (!result.first) return false; - CellStore* cell; - if (isCellExterior()) - { - int cellX, cellY; - positionToIndex(result.second[0], result.second[1], cellX, cellY); - cell = mCells.getExterior(cellX, cellY); - } - else - cell = getPlayerPtr().getCell(); + CellStore* cell = getPlayerPtr().getCell(); ESM::Position pos = getPlayerPtr().getRefData().getPosition(); pos.pos[0] = result.second[0]; @@ -1527,7 +1519,7 @@ namespace MWWorld // copy the object and set its count int origCount = object.getRefData().getCount(); object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, *cell, pos, true); + Ptr dropped = copyObjectToCell(object, cell, pos, true); object.getRefData().setCount(origCount); // only the player place items in the world, so no need to check actor @@ -1548,7 +1540,7 @@ namespace MWWorld } - Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos) + Ptr World::copyObjectToCell(const Ptr &object, CellStore* cell, ESM::Position pos, bool adjustPos) { if (object.getClass().isActor() || adjustPos) { @@ -1560,17 +1552,17 @@ namespace MWWorld } } - if (cell.isExterior()) + if (cell->isExterior()) { int cellX, cellY; positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY); - cell = *mCells.getExterior(cellX, cellY); + cell = mCells.getExterior(cellX, cellY); } MWWorld::Ptr dropped = - MWWorld::Class::get(object).copyToCell(object, cell, pos); + MWWorld::Class::get(object).copyToCell(object, *cell, pos); - if (mWorldScene->isCellActive(cell)) { + if (mWorldScene->isCellActive(*cell)) { if (dropped.getRefData().isEnabled()) { mWorldScene->addObjectToScene(dropped); } @@ -1578,7 +1570,7 @@ namespace MWWorld if (!script.empty()) { mLocalScripts.add(script, dropped); } - addContainerScripts(dropped, &cell); + addContainerScripts(dropped, cell); } return dropped; @@ -1608,7 +1600,7 @@ namespace MWWorld // copy the object and set its count int origCount = object.getRefData().getCount(); object.getRefData().setCount(amount); - Ptr dropped = copyObjectToCell(object, *cell, pos); + Ptr dropped = copyObjectToCell(object, cell, pos); object.getRefData().setCount(origCount); if(actor == mPlayer->getPlayer()) // Only call if dropped by player @@ -2143,7 +2135,37 @@ namespace MWWorld } } - void World::launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) + { + ProjectileState state; + state.mActorHandle = actor.getRefData().getHandle(); + state.mBow = bow; + state.mSpeed = speed; + + MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID); + + ESM::Position pos; + pos.pos[0] = worldPos.x; + pos.pos[1] = worldPos.y; + pos.pos[2] = worldPos.z; + + // Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction. + Ogre::Matrix3 mat; + orient.ToRotationMatrix(mat); + Ogre::Radian xr,yr,zr; + mat.ToEulerAnglesXYZ(xr, yr, zr); + pos.rot[0] = -xr.valueRadians(); + pos.rot[1] = -yr.valueRadians(); + pos.rot[2] = -zr.valueRadians(); + + MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false); + ptr.getRefData().setCount(1); + + mProjectiles[ptr] = state; + } + + void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName) { std::string projectileModel; @@ -2185,13 +2207,23 @@ namespace MWWorld pos.pos[0] = actor.getRefData().getPosition().pos[0]; pos.pos[1] = actor.getRefData().getPosition().pos[1]; pos.pos[2] = actor.getRefData().getPosition().pos[2] + height; - pos.rot[0] = actor.getRefData().getPosition().rot[0]; - pos.rot[1] = actor.getRefData().getPosition().rot[1]; - pos.rot[2] = actor.getRefData().getPosition().rot[2]; - ref.getPtr().getCellRef().mPos = pos; - MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), *actor.getCell(), pos); - ProjectileState state; + // Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction. + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + Ogre::Matrix3 mat; + orient.ToRotationMatrix(mat); + Ogre::Radian xr,yr,zr; + mat.ToEulerAnglesXYZ(xr, yr, zr); + pos.rot[0] = -xr.valueRadians(); + pos.rot[1] = -yr.valueRadians(); + pos.rot[2] = -zr.valueRadians(); + + ref.getPtr().getCellRef().mPos = pos; + CellStore* cell = actor.getCell(); + MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos); + + MagicBoltState state; state.mSourceName = sourceName; state.mId = id; state.mActorHandle = actor.getRefData().getHandle(); @@ -2209,7 +2241,7 @@ namespace MWWorld MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); - mProjectiles[ptr] = state; + mMagicBolts[ptr] = state; } void World::moveProjectiles(float duration) @@ -2226,13 +2258,100 @@ namespace MWWorld MWWorld::Ptr ptr = it->first; + Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); + + float speed = it->second.mSpeed; + + Ogre::Vector3 direction = orient.yAxis(); + direction.normalise(); + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + Ogre::Vector3 newPos = pos + direction * duration * speed; + + // Check for impact + btVector3 from(pos.x, pos.y, pos.z); + btVector3 to(newPos.x, newPos.y, newPos.z); + std::vector > collisions = mPhysEngine->rayTest2(from, to); + bool hit=false; + + // HACK: query against the shape as well, since the ray does not take the volume into account + // really, this should be a convex cast, but the whole physics system needs a rewrite + std::vector col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle()); + for (std::vector::iterator ci = col2.begin(); ci != col2.end(); ++ci) + collisions.push_back(std::make_pair(0.f,*ci)); + + for (std::vector >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt) + { + MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second); + if (obstacle == ptr) + continue; + + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + + // Arrow intersects with player immediately after shooting :/ + if (obstacle == caster) + continue; + + if (caster.isEmpty()) + caster = obstacle; + + if (obstacle.isEmpty()) + { + // Terrain + } + else if (obstacle.getClass().isActor()) + { + // Fargoth + obstacle.getClass().getCreatureStats(obstacle).setHealth(0); + } + hit = true; + } + if (hit) + { + mProjectiles.erase(it++); + continue; + } + + std::string handle = ptr.getRefData().getHandle(); + + moveObject(ptr, newPos.x, newPos.y, newPos.z); + + // HACK: Re-fetch Ptrs if necessary, since the cell might have changed + if (!ptr.getRefData().getCount()) + { + moved[handle] = it->second; + mProjectiles.erase(it++); + } + else + ++it; + } + + // HACK: Re-fetch Ptrs if necessary, since the cell might have changed + for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) + { + MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); + if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted + continue; + mProjectiles[getPtrViaHandle(it->first)] = it->second; + } + } + + void World::moveMagicBolts(float duration) + { + std::map moved; + for (std::map::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) + { + if (!mWorldScene->isCellActive(*it->first.getCell())) + { + deleteObject(it->first); + mMagicBolts.erase(it++); + continue; + } + + MWWorld::Ptr ptr = it->first; + Ogre::Vector3 rot(ptr.getRefData().getPosition().rot); - // TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move) - Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z); - orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X); - - + Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); static float fTargetSpellMaxSpeed = getStore().get().find("fTargetSpellMaxSpeed")->getFloat(); float speed = fTargetSpellMaxSpeed * it->second.mSpeed; @@ -2279,7 +2398,7 @@ namespace MWWorld explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName); deleteObject(ptr); - mProjectiles.erase(it++); + mMagicBolts.erase(it++); continue; } @@ -2291,29 +2410,29 @@ namespace MWWorld if (!ptr.getRefData().getCount()) { moved[handle] = it->second; - mProjectiles.erase(it++); + mMagicBolts.erase(it++); } else ++it; } // HACK: Re-fetch Ptrs if necessary, since the cell might have changed - for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) + for (std::map::iterator it = moved.begin(); it != moved.end(); ++it) { MWWorld::Ptr newPtr = searchPtrViaHandle(it->first); if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted continue; - mProjectiles[getPtrViaHandle(it->first)] = it->second; + mMagicBolts[getPtrViaHandle(it->first)] = it->second; } } void World::objectLeftActiveCell(Ptr object, Ptr movedPtr) { // For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information - if (mProjectiles.find(object) != mProjectiles.end()) - { + if (mMagicBolts.find(object) != mMagicBolts.end()) + deleteObject(movedPtr); + if (mProjectiles.find(object) != mProjectiles.end()) deleteObject(movedPtr); - } } const std::vector& World::getContentFiles() const @@ -2650,7 +2769,7 @@ namespace MWWorld MWWorld::ManualRef ref(getStore(), selectedCreature, 1); ref.getPtr().getCellRef().mPos = ipos; - safePlaceObject(ref.getPtr(),*cell,ipos); + safePlaceObject(ref.getPtr(), cell, ipos); } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 036cafe2db..40fe4c96a0 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,7 +90,7 @@ namespace MWWorld std::map mDoorStates; ///< only holds doors that are currently moving. 0 means closing, 1 opening - struct ProjectileState + struct MagicBoltState { // Id of spell or enchantment to apply when it hits std::string mId; @@ -108,6 +108,17 @@ namespace MWWorld bool mStack; }; + struct ProjectileState + { + // Actor who shot this projectile + std::string mActorHandle; + + MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from + + float mSpeed; + }; + + std::map mMagicBolts; std::map mProjectiles; void updateWeather(float duration); int getDaysPerMonth (int month) const; @@ -117,7 +128,7 @@ namespace MWWorld bool moveObjectImp (const Ptr& ptr, float x, float y, float z); ///< @return true if the active cell (cell player is in) changed - Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true); + Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true); void updateWindowManager (); void performUpdateSceneQueries (); @@ -134,6 +145,7 @@ namespace MWWorld void processDoors(float duration); ///< Run physics simulation and modify \a world accordingly. + void moveMagicBolts(float duration); void moveProjectiles(float duration); void doPhysics(float duration); @@ -341,7 +353,7 @@ namespace MWWorld virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); - virtual void moveObject (const Ptr& ptr, CellStore &newCell, float x, float y, float z); + virtual void moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z); virtual void scaleObject (const Ptr& ptr, float scale); @@ -351,7 +363,7 @@ namespace MWWorld virtual void localRotateObject (const Ptr& ptr, float x, float y, float z); - virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); + virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) @@ -552,8 +564,10 @@ namespace MWWorld */ virtual void castSpell (const MWWorld::Ptr& actor); - virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, + virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects, const MWWorld::Ptr& actor, const std::string& sourceName); + virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, + const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed); virtual const std::vector& getContentFiles() const; diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index f124abb990..4484d98625 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -597,6 +597,8 @@ namespace Physic std::vector PhysicEngine::getCollisions(const std::string& name) { RigidBody* body = getRigidBody(name); + if (!body) // fall back to raycasting body if there is no collision body + body = getRigidBody(name, true); ContactTestResultCallback callback; dynamicsWorld->contactTest(body, callback); return callback.mResult; From 072dc6d4383638421fbb271fa5b8fafffd36fc13 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 8 Mar 2014 05:51:47 +0100 Subject: [PATCH 080/240] Feature #50: Implement marksman mechanics. --- apps/openmw/mwclass/creature.cpp | 13 +-- apps/openmw/mwclass/npc.cpp | 16 +--- apps/openmw/mwmechanics/combat.cpp | 113 +++++++++++++++++++++++++- apps/openmw/mwmechanics/combat.hpp | 8 ++ apps/openmw/mwrender/npcanimation.cpp | 31 ++++++- apps/openmw/mwworld/worldimp.cpp | 25 +++--- apps/openmw/mwworld/worldimp.hpp | 2 +- 7 files changed, 171 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2939a5431e..33c4390a0a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -7,6 +7,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/movement.hpp" +#include "../mwmechanics/disease.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwbase/environment.hpp" @@ -243,15 +244,7 @@ namespace MWClass Ogre::Vector3 hitPosition = result.second; - MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); - const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - float hitchance = ref->mBase->mData.mCombat + - (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + - (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); - hitchance *= stats.getFatigueTerm(); - hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - - mageffects.get(ESM::MagicEffect::Blind).mMagnitude; - hitchance -= otherstats.getEvasion(); + float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat); if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { @@ -334,6 +327,8 @@ namespace MWClass if (damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + MWMechanics::diseaseContact(victim, ptr); + victim.getClass().onHit(victim, damage, true, weapon, ptr, true); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 20c7d9a0e0..50e002113a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -498,15 +498,7 @@ namespace MWClass if(!weapon.isEmpty()) weapskill = get(weapon).getEquipmentSkill(weapon); - MWMechanics::NpcStats &stats = getNpcStats(ptr); - const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - float hitchance = stats.getSkill(weapskill).getModified() + - (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + - (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); - hitchance *= stats.getFatigueTerm(); - hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - - mageffects.get(ESM::MagicEffect::Blind).mMagnitude; - hitchance -= otherstats.getEvasion(); + float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.getClass().getSkill(ptr, weapskill)); if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { @@ -516,6 +508,7 @@ namespace MWClass bool healthdmg; float damage = 0.0f; + MWMechanics::NpcStats &stats = getNpcStats(ptr); if(!weapon.isEmpty()) { const bool weaphashealth = get(weapon).hasItemHealth(weapon); @@ -615,6 +608,8 @@ namespace MWClass if (healthdmg && damage > 0) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + MWMechanics::diseaseContact(victim, ptr); + othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } @@ -648,9 +643,6 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - if (!attacker.isEmpty()) - MWMechanics::diseaseContact(ptr, attacker); - if (damage > 0.0f && !object.isEmpty()) MWMechanics::resistNormalWeapon(ptr, attacker, object, damage); diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 6aeb90dad3..cdc12e210e 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -4,9 +4,12 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/movement.hpp" +#include "../mwmechanics/spellcasting.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" @@ -17,14 +20,30 @@ namespace { -Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 n) +Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 normal) { return Ogre::Math::ATan2( - n.dotProduct( v1.crossProduct(v2) ), + normal.dotProduct( v1.crossProduct(v2) ), v1.dotProduct(v2) ); } +void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition) +{ + std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : ""; + if (!enchantmentName.empty()) + { + const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find( + enchantmentName); + if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) + { + MWMechanics::CastSpell cast(attacker, victim); + cast.mHitPosition = hitPosition; + cast.cast(object); + } + } +} + } namespace MWMechanics @@ -135,4 +154,94 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}"); } + void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile, + const Ogre::Vector3& hitPosition) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + + MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); + + const MWWorld::Class &othercls = victim.getClass(); + if(!othercls.isActor()) // Can't hit non-actors + return; + MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); + if(otherstats.isDead()) // Can't hit dead actors + return; + + if(attacker.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->setEnemy(victim); + + int weapskill = ESM::Skill::Marksman; + if(!weapon.isEmpty()) + weapskill = weapon.getClass().getEquipmentSkill(weapon); + + float skillValue = attacker.getClass().getSkill(attacker, + weapon.getClass().getEquipmentSkill(weapon)); + + if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f) + { + victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false); + return; + } + + float damage = 0.0f; + + float fDamageStrengthBase = gmst.find("fDamageStrengthBase")->getFloat(); + float fDamageStrengthMult = gmst.find("fDamageStrengthMult")->getFloat(); + + const unsigned char* attack = weapon.get()->mBase->mData.mChop; + damage = attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); // Bow/crossbow damage + if (weapon != projectile) + { + // Arrow/bolt damage + attack = projectile.get()->mBase->mData.mChop; + damage += attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); + } + + damage *= fDamageStrengthBase + + (attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1); + + if(attacker.getRefData().getHandle() == "player") + attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0); + + bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim); + if(!detected) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + if (victim.getClass().getCreatureStats(victim).getKnockedDown()) + damage *= gmst.find("fCombatKODamageMult")->getFloat(); + + // Apply "On hit" effect of the weapon + applyEnchantment(attacker, victim, weapon, hitPosition); + if (weapon != projectile) + applyEnchantment(attacker, victim, projectile, hitPosition); + + if (damage > 0) + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + + float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat(); + if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f) + victim.getClass().getContainerStore(victim).add(projectile, 1, victim); + + victim.getClass().onHit(victim, damage, true, projectile, attacker, true); + } + + float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue) + { + MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker); + const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); + float hitchance = skillValue + + (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + hitchance *= stats.getFatigueTerm(); + hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - + mageffects.get(ESM::MagicEffect::Blind).mMagnitude; + hitchance -= victim.getClass().getCreatureStats(victim).getEvasion(); + return hitchance; + } + } diff --git a/apps/openmw/mwmechanics/combat.hpp b/apps/openmw/mwmechanics/combat.hpp index 7f24156972..bc58227bf6 100644 --- a/apps/openmw/mwmechanics/combat.hpp +++ b/apps/openmw/mwmechanics/combat.hpp @@ -2,6 +2,7 @@ #define OPENMW_MECHANICS_COMBAT_H #include "../mwworld/ptr.hpp" +#include namespace MWMechanics { @@ -11,6 +12,13 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage); +/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt +void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile, + const Ogre::Vector3& hitPosition); + +/// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value +float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue); + } #endif diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6169b6ddb2..8f8e65cd02 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -779,6 +779,23 @@ void NpcAnimation::releaseArrow() Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + // Reduce fatigue + // somewhat of a guess, but using the weapon weight makes sense + const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + MWMechanics::CreatureStats& attackerStats = mPtr.getClass().getCreatureStats(mPtr); + MWMechanics::DynamicStat fatigue = attackerStats.getFatigue(); + const float normalizedEncumbrance = mPtr.getClass().getEncumbrance(mPtr) / mPtr.getClass().getCapacity(mPtr); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon->isEmpty()) + fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + attackerStats.setFatigue(fatigue); + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) { // Thrown weapons get detached now @@ -795,7 +812,12 @@ void NpcAnimation::releaseArrow() launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); } - MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, 400); + float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); + float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * + mPtr.getClass().getCreatureStats(mPtr).getAttackStrength(); + + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, speed); showWeapons(false); @@ -819,8 +841,11 @@ void NpcAnimation::releaseArrow() launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); } - /// \todo speed - MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, 400); + float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); + float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * mPtr.getClass().getCreatureStats(mPtr).getAttackStrength(); + + MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, speed); inv.remove(*ammo, 1, mPtr); mAmmunition.setNull(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0ef50c5828..62ef09ae45 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -28,7 +28,7 @@ #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/levelledlist.hpp" - +#include "../mwmechanics/combat.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/animation.hpp" @@ -2141,7 +2141,7 @@ namespace MWWorld ProjectileState state; state.mActorHandle = actor.getRefData().getHandle(); state.mBow = bow; - state.mSpeed = speed; + state.mVelocity = orient.yAxis() * speed; MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID); @@ -2258,14 +2258,19 @@ namespace MWWorld MWWorld::Ptr ptr = it->first; - Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); + // gravity constant - must be way lower than the gravity affecting actors, since we're not + // simulating aerodynamics at all + it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration; - float speed = it->second.mSpeed; - - Ogre::Vector3 direction = orient.yAxis(); - direction.normalise(); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); - Ogre::Vector3 newPos = pos + direction * duration * speed; + Ogre::Vector3 newPos = pos + it->second.mVelocity * duration; + + Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity); + Ogre::Matrix3 mat; + orient.ToRotationMatrix(mat); + Ogre::Radian xr,yr,zr; + mat.ToEulerAnglesXYZ(xr, yr, zr); + rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees()); // Check for impact btVector3 from(pos.x, pos.y, pos.z); @@ -2300,13 +2305,13 @@ namespace MWWorld } else if (obstacle.getClass().isActor()) { - // Fargoth - obstacle.getClass().getCreatureStats(obstacle).setHealth(0); + MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first); } hit = true; } if (hit) { + deleteObject(ptr); mProjectiles.erase(it++); continue; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 40fe4c96a0..b694e00f75 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -115,7 +115,7 @@ namespace MWWorld MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from - float mSpeed; + Ogre::Vector3 mVelocity; }; std::map mMagicBolts; From 3c60345d6b08081b5246299e652cfec7f2e6f23f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 15:10:55 +0100 Subject: [PATCH 081/240] nest wrapper into the proxy class --- apps/opencs/view/world/dialoguesubview.cpp | 2 +- apps/opencs/view/world/dialoguesubview.hpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 37a2ab130e..65a3f437dd 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -23,7 +23,7 @@ /* ==============================DialogueDelegateDispatcherProxy========================================== */ -CSVWorld::refWrapper::refWrapper(const QModelIndex& index) : +CSVWorld::DialogueDelegateDispatcherProxy::refWrapper::refWrapper(const QModelIndex& index) : mIndex(index) {} diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 11cc918b19..06c849e390 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -27,23 +27,23 @@ namespace CSVWorld { class CommandDelegate; - class refWrapper - { - public: - refWrapper(const QModelIndex& index); - - const QModelIndex& mIndex; - }; + //this can't be nested into the DialogueDelegateDispatcher, because it needs to emit signals class DialogueDelegateDispatcherProxy : public QObject { Q_OBJECT + class refWrapper + { + public: + refWrapper(const QModelIndex& index); + + const QModelIndex& mIndex; + }; + QWidget* mEditor; CSMWorld::ColumnBase::Display mDisplay; - - std::auto_ptr mIndexWrapper; public: DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); From a8b11829d4001c35d5d7c8345051d85e06eb3ea2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 15:18:40 +0100 Subject: [PATCH 082/240] disable not editable widgets --- apps/opencs/view/world/dialoguesubview.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 65a3f437dd..3eaa9bd610 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -189,11 +189,10 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM { layout->addWidget (widget, i, 1); mWidgetMapper->addMapping (widget, i); - } - - if (model->flags (model->index (0, i)) & Qt::ItemIsEditable) - { - + if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) + { + widget->setDisabled(true); + } } } } From 08ccae6b49ec22615b4cc6b7a047f4b2bbcd69d7 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 15:27:43 +0100 Subject: [PATCH 083/240] handle comboboxes --- apps/opencs/model/world/columnbase.cpp | 5 +++++ apps/opencs/model/world/columnbase.hpp | 2 ++ apps/opencs/model/world/idtable.cpp | 5 +++++ apps/opencs/model/world/idtable.hpp | 2 ++ apps/opencs/view/world/dialoguesubview.cpp | 10 +++++++++- 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 34bad20cc5..f6363fe2eb 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -17,4 +17,9 @@ bool CSMWorld::ColumnBase::isUserEditable() const std::string CSMWorld::ColumnBase::getTitle() const { return Columns::getName (static_cast (mColumnId)); +} + +int CSMWorld::ColumnBase::getId() const +{ + return mColumnId; } \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index e043336080..d990232f70 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -105,6 +105,8 @@ namespace CSMWorld ///< Can this column be edited directly by the user? virtual std::string getTitle() const; + + virtual int getId() const; }; template diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 453a7da6a3..97837e3687 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -230,4 +230,9 @@ std::pair CSMWorld::IdTable::view (int row) id = "sys::default"; return std::make_pair (UniversalId (UniversalId::Type_Scene, id), hint); +} + +int CSMWorld::IdTable::getColumnId(int column) const +{ + return mIdCollection->getColumn(column).getId(); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 5a271de443..0137413796 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -103,6 +103,8 @@ namespace CSMWorld std::pair view (int row) const; ///< Return the UniversalId and the hint for viewing \a row. If viewing is not /// supported by this table, return (UniversalId::Type_None, ""). + + int getColumnId(int column) const; }; } diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 3eaa9bd610..a946a6f6fb 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -16,6 +16,7 @@ #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/columns.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -123,13 +124,20 @@ QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) { + bool hasEnums = CSMWorld::Columns::hasEnums(static_cast(mTable->getColumnId(index.column() ) ) ); QWidget* editor = NULL; std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); - connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + if (hasEnums) //combox is used for all enums + { + connect(editor, SIGNAL(currentIndexChanged ( int)), proxy, SLOT(editorDataCommited())); + } else + { + connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + } connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor } From 9612c6a6fd95d98c6ecc6ec5aea53df1ee593cab Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 16:50:42 +0100 Subject: [PATCH 084/240] handle also bool boxes --- apps/opencs/view/world/dialoguesubview.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index a946a6f6fb..e8b53f2cdd 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -37,7 +37,10 @@ mIndexWrapper(NULL) void CSVWorld::DialogueDelegateDispatcherProxy::editorDataCommited() { - emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + if (mIndexWrapper.get()) + { + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + } } void CSVWorld::DialogueDelegateDispatcherProxy::setIndex(const QModelIndex& index) @@ -79,7 +82,6 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS void CSVWorld::DialogueDelegateDispatcher::editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display) { - std::cout<<"triggered"<(mTable->getColumnId(index.column() ) ) ); + if (display == CSMWorld::ColumnBase::Display_Boolean) + { + hasEnums = true; + } + QWidget* editor = NULL; std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) @@ -133,7 +140,7 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); if (hasEnums) //combox is used for all enums { - connect(editor, SIGNAL(currentIndexChanged ( int)), proxy, SLOT(editorDataCommited())); + connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); } else { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); From 1f1774b5e12f17eade42d916608763252c809ea3 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 16:54:05 +0100 Subject: [PATCH 085/240] changing the enumdelegate seteditordata --- apps/opencs/view/world/enumdelegate.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index fc9b7ee3b3..b882e20e69 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -60,7 +60,14 @@ void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& { if (QComboBox *comboBox = dynamic_cast (editor)) { - int value = index.data (Qt::EditRole).toInt(); + QVariant data = index.data (Qt::EditRole); + + if (!data.isValid()) + { + data = index.data (Qt::DisplayRole); + } + + int value = data.toInt(); std::size_t size = mValues.size(); From 0447be7e7a2d6b2960d6ffaa1fca5cac47049b73 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 8 Mar 2014 17:24:00 +0100 Subject: [PATCH 086/240] register extra views. do not display id --- apps/opencs/view/world/dialoguesubview.cpp | 7 +++++-- apps/opencs/view/world/subviews.cpp | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e8b53f2cdd..76c66160af 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -192,7 +192,9 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { - layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); + if(i) //the first run adds the pointless id field + { + layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i-1, 0); CSMWorld::ColumnBase::Display display = static_cast (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); @@ -202,13 +204,14 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (widget) { - layout->addWidget (widget, i, 1); + layout->addWidget (widget, i-1, 1); mWidgetMapper->addMapping (widget, i); if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) { widget->setDisabled(true); } } + } } } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 4a94cbfe67..2109f08a09 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -81,4 +81,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add(CSMWorld::UniversalId::Type_Region, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Spell, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Referenceable, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Birthsign, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Global, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Gmst, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Race, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Class, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactory); } \ No newline at end of file From 72a3c50eb87914bee5794f30fb58a41060401c34 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sat, 8 Mar 2014 13:45:54 -0500 Subject: [PATCH 087/240] (#1191) Disallow picking up if inventory disabled Check if window manager has allowed the inventory window if not, then items should not be possible to pick up --- apps/openmw/mwgui/inventorywindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 2ea09db61e..5fa1d3bb16 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -514,6 +514,9 @@ namespace MWGui void InventoryWindow::pickUpObject (MWWorld::Ptr object) { + // If the inventory is not yet enabled, don't pick anything up + if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory)) + return; // make sure the object is of a type that can be picked up std::string type = object.getTypeName(); if ( (type != typeid(ESM::Apparatus).name()) From b8ca067730907cb31e69cee23c7546dcac84ed64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Mar 2014 03:21:34 +0100 Subject: [PATCH 088/240] Small fix for terrain --- components/terrain/quadtreenode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 40a8baaf02..4461e2b024 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -357,6 +357,7 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) if (!childrenLoaded) { + mChunk->setVisible(true); // Make sure child scene nodes are detached until all children are loaded mSceneNode->removeAllChildren(); } From 6eaa7553f8367233526e41be709e62070e0f1c85 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Mar 2014 03:34:49 +0100 Subject: [PATCH 089/240] Fixes #1181: Enable controls when loading a savegame --- apps/openmw/mwbase/inputmanager.hpp | 3 +++ apps/openmw/mwinput/inputmanagerimp.cpp | 7 +++++++ apps/openmw/mwinput/inputmanagerimp.hpp | 3 +++ apps/openmw/mwstate/statemanagerimp.cpp | 2 ++ 4 files changed, 15 insertions(+) diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 8293cbfa7e..42922a5b3d 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -20,6 +20,9 @@ namespace MWBase InputManager() {} + /// Clear all savegame-specific data + virtual void clear() = 0; + virtual ~InputManager() {} virtual void update(float dt, bool loading) = 0; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 30cefe2df3..4bfd3f4651 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -143,6 +143,13 @@ namespace MWInput mControlSwitch["vanitymode"] = true; } + void InputManager::clear() + { + // Enable all controls + for (std::map::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it) + it->second = true; + } + InputManager::~InputManager() { mInputBinder->save (mUserFile); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index bd3f4954b1..2eab03a34e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -65,6 +65,9 @@ namespace MWInput virtual ~InputManager(); + /// Clear all savegame-specific data + virtual void clear(); + virtual void update(float dt, bool loading); void setPlayer (MWWorld::Player* player) { mPlayer = player; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 8b01bacdf6..05e9289377 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -20,6 +20,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -39,6 +40,7 @@ void MWState::StateManager::cleanup (bool force) MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear(); MWBase::Environment::get().getWorld()->clear(); MWBase::Environment::get().getWindowManager()->clear(); + MWBase::Environment::get().getInputManager()->clear(); mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); From 7eb10756d4ec48240acf72d23b447794c2f63605 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 9 Mar 2014 08:58:24 +0100 Subject: [PATCH 090/240] some extra views enabled --- apps/opencs/view/world/dialoguesubview.cpp | 7 ++----- apps/opencs/view/world/subviews.cpp | 1 + 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 76c66160af..e8b53f2cdd 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -192,9 +192,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { - if(i) //the first run adds the pointless id field - { - layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i-1, 0); + layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); CSMWorld::ColumnBase::Display display = static_cast (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); @@ -204,14 +202,13 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (widget) { - layout->addWidget (widget, i-1, 1); + layout->addWidget (widget, i, 1); mWidgetMapper->addMapping (widget, i); if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) { widget->setDisabled(true); } } - } } } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 2109f08a09..2fa2fc06be 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -89,4 +89,5 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add(CSMWorld::UniversalId::Type_Class, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactory); } \ No newline at end of file From 2de862126a42edad6e6ed0adbb61c34a3b4ae23f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Mar 2014 11:42:39 +0100 Subject: [PATCH 091/240] moved resources group creation from Engine to bsa component --- apps/openmw/engine.cpp | 49 ++------------------------------- apps/openmw/engine.hpp | 3 -- components/CMakeLists.txt | 2 +- components/bsa/resources.cpp | 53 ++++++++++++++++++++++++++++++++++++ components/bsa/resources.hpp | 16 +++++++++++ 5 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 components/bsa/resources.cpp create mode 100644 components/bsa/resources.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4c3cadb3b6..37821b990f 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -12,7 +12,7 @@ #include -#include +#include #include #include #include @@ -192,50 +192,6 @@ OMW::Engine::~Engine() SDL_Quit(); } -// Load BSA files - -void OMW::Engine::loadBSA() -{ - // We use separate resource groups to handle location priority. - const Files::PathContainer& dataDirs = mFileCollections.getPaths(); - - int i=0; - for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) - { - // Last data dir has the highest priority - std::string groupName = "Data" + Ogre::StringConverter::toString(dataDirs.size()-i, 8, '0'); - Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); - - std::string dataDirectory = iter->string(); - std::cout << "Data dir " << dataDirectory << std::endl; - Bsa::addDir(dataDirectory, mFSStrict, groupName); - ++i; - } - - i=0; - for (std::vector::const_iterator archive = mArchives.begin(); archive != mArchives.end(); ++archive) - { - if (mFileCollections.doesExist(*archive)) - { - // Last BSA has the highest priority - std::string groupName = "DataBSA" + Ogre::StringConverter::toString(mArchives.size()-i, 8, '0'); - - Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); - - const std::string archivePath = mFileCollections.getPath(*archive).string(); - std::cout << "Adding BSA archive " << archivePath << std::endl; - Bsa::addBSA(archivePath, groupName); - ++i; - } - else - { - std::stringstream message; - message << "Archive '" << *archive << "' not found"; - throw std::runtime_error(message.str()); - } - } -} - // add resources directory // \note This function works recursively. @@ -385,8 +341,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mOgre->createWindow("OpenMW", windowSettings); - loadBSA(); - + Bsa::registerResources (mFileCollections, mArchives, true, mFSStrict); // Create input and UI first to set up a bootstrapping environment for // showing a loading screen and keeping the window responsive while doing so diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 5c15ddf6fc..e0f51d0dcb 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -101,9 +101,6 @@ namespace OMW /// add a .zip resource void addZipResource (const boost::filesystem::path& path); - /// Load BSA files - void loadBSA(); - void executeLocalScripts(); virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index db4ecad0b1..f8a2969f9d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -15,7 +15,7 @@ add_component_dir (nifoverrides ) add_component_dir (bsa - bsa_archive bsa_file + bsa_archive bsa_file resources ) add_component_dir (nif diff --git a/components/bsa/resources.cpp b/components/bsa/resources.cpp new file mode 100644 index 0000000000..d06b3b4852 --- /dev/null +++ b/components/bsa/resources.cpp @@ -0,0 +1,53 @@ + +#include "resources.hpp" + +#include + +#include +#include + +#include "bsa_archive.hpp" + +void Bsa::registerResources (const Files::Collections& collections, + const std::vector& archives, bool useLooseFiles, bool fsStrict) +{ + const Files::PathContainer& dataDirs = collections.getPaths(); + + int i=0; + + if (useLooseFiles) + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) + { + // Last data dir has the highest priority + std::string groupName = "Data" + Ogre::StringConverter::toString(dataDirs.size()-i, 8, '0'); + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + + std::string dataDirectory = iter->string(); + std::cout << "Data dir " << dataDirectory << std::endl; + Bsa::addDir(dataDirectory, fsStrict, groupName); + ++i; + } + + i=0; + for (std::vector::const_iterator archive = archives.begin(); archive != archives.end(); ++archive) + { + if (collections.doesExist(*archive)) + { + // Last BSA has the highest priority + std::string groupName = "DataBSA" + Ogre::StringConverter::toString(archives.size()-i, 8, '0'); + + Ogre::ResourceGroupManager::getSingleton ().createResourceGroup (groupName); + + const std::string archivePath = collections.getPath(*archive).string(); + std::cout << "Adding BSA archive " << archivePath << std::endl; + Bsa::addBSA(archivePath, groupName); + ++i; + } + else + { + std::stringstream message; + message << "Archive '" << *archive << "' not found"; + throw std::runtime_error(message.str()); + } + } +} \ No newline at end of file diff --git a/components/bsa/resources.hpp b/components/bsa/resources.hpp new file mode 100644 index 0000000000..8c3fb7bef8 --- /dev/null +++ b/components/bsa/resources.hpp @@ -0,0 +1,16 @@ +#ifndef BSA_BSA_RESOURCES_H +#define BSA_BSA_RESOURCES_H + +#include +#include + +#include "../files/collections.hpp" + +namespace Bsa +{ + void registerResources (const Files::Collections& collections, + const std::vector& archives, bool useLooseFiles, bool fsStrict); + ///< Register resources directories and archives as OGRE resources groups +} + +#endif From a9dcc9097060da54a140616dc673763b46971059 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 9 Mar 2014 11:59:23 +0100 Subject: [PATCH 092/240] Another terrain fix --- components/terrain/quadtreenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 4461e2b024..21c1becb0a 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -438,7 +438,7 @@ void QuadTreeNode::unload(bool recursive) if (recursive && hasChildren()) { for (int i=0; i<4; ++i) - mChildren[i]->unload(); + mChildren[i]->unload(true); } } From 2b17f5dde95044c01e07c494417c93f75e87c0cc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Mar 2014 12:32:21 +0100 Subject: [PATCH 093/240] register resources locations on editor startup --- apps/opencs/editor.cpp | 19 ++++++++++++++----- apps/opencs/editor.hpp | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 2b2f41754b..9eb95fafac 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -11,6 +11,8 @@ #include +#include + #include "model/doc/document.hpp" #include "model/world/data.hpp" @@ -18,14 +20,17 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit) : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager), mIpcServerName ("org.openmw.OpenCS") { - Files::PathContainer dataDirs = readConfig(); + std::pair > config = readConfig(); - setupDataFiles (dataDirs); + setupDataFiles (config.first); CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg"); ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); + Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true, + mFsStrict); + mNewGame.setLocalData (mLocal); mFileDialog.setLocalData (mLocal); @@ -58,7 +63,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) } } -Files::PathContainer CS::Editor::readConfig() +std::pair > CS::Editor::readConfig() { boost::program_options::variables_map variables; boost::program_options::options_description desc("Syntax: opencs \nAllowed options"); @@ -68,7 +73,9 @@ Files::PathContainer CS::Editor::readConfig() ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")) - ("resources", boost::program_options::value()->default_value("resources")); + ("resources", boost::program_options::value()->default_value("resources")) + ("fallback-archive", boost::program_options::value >()-> + default_value(std::vector(), "fallback-archive")->multitoken()); boost::program_options::notify(variables); @@ -76,6 +83,8 @@ Files::PathContainer CS::Editor::readConfig() mDocumentManager.setResourceDir (variables["resources"].as()); + mFsStrict = variables["fs-strict"].as(); + Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); @@ -105,7 +114,7 @@ Files::PathContainer CS::Editor::readConfig() dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - return dataDirs; + return std::make_pair (dataDirs, variables["fallback-archive"].as >()); } void CS::Editor::createGame() diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 0f1c7a682d..ec417ba8e0 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -41,12 +41,12 @@ namespace CS CSVDoc::NewGameDialogue mNewGame; CSVSettings::UserSettingsDialog mSettings; CSVDoc::FileDialog mFileDialog; - boost::filesystem::path mLocal; + bool mFsStrict; void setupDataFiles (const Files::PathContainer& dataDirs); - Files::PathContainer readConfig(); + std::pair > readConfig(); ///< \return data paths // not implemented From b2fdaa74b0bdc9fb45de67fd45608249a09aafe2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 9 Mar 2014 18:44:04 +0100 Subject: [PATCH 094/240] trying to set the size policy --- apps/opencs/view/world/dialoguesubview.cpp | 42 +++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e8b53f2cdd..6307afabb2 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -174,9 +174,21 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM setWidget (widget); - QGridLayout *layout = new QGridLayout; + QFrame* line = new QFrame(this); + line->setObjectName(QString::fromUtf8("line")); + line->setGeometry(QRect(320, 150, 118, 3)); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); - widget->setLayout (layout); + QVBoxLayout *mainLayout = new QVBoxLayout; + QGridLayout *unlockedLayout = new QGridLayout; + QGridLayout *lockedLayout = new QGridLayout; + mainLayout->addLayout(lockedLayout, 0); + mainLayout->addWidget(line, 1); + mainLayout->addLayout(unlockedLayout, 2); + mainLayout->addStretch(1); + + widget->setLayout (mainLayout); QAbstractItemModel *model = document.getData().getTableModel (id); @@ -186,27 +198,39 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mWidgetMapper->setModel (model); mWidgetMapper->setItemDelegate(&mDispatcher); + int unlocked = 0; + int locked = 0; + std::vector editors; for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { - layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); - CSMWorld::ColumnBase::Display display = static_cast (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mDispatcher.makeDelegate(display); - QWidget *widget = mDispatcher.makeEditor(display, (model->index (0, i))); + QWidget *editor = mDispatcher.makeEditor(display, (model->index (0, i))); - if (widget) + if (editor) { - layout->addWidget (widget, i, 1); - mWidgetMapper->addMapping (widget, i); + editors.push_back(editor); + mWidgetMapper->addMapping (editor, i); + QLabel* label = new QLabel(model->headerData (i, Qt::Horizontal).toString()); + label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) { - widget->setDisabled(true); + editor->setDisabled(true); + lockedLayout->addWidget (label, locked, 0); + lockedLayout->addWidget (editor, locked, 1); + ++locked; + } else + { + unlockedLayout->addWidget (label, unlocked, 0); + unlockedLayout->addWidget (editor, unlocked, 1); + ++unlocked; } } } From 9ab920bd802e2d248f8977e9d8a7879579214fa5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 09:37:53 +0100 Subject: [PATCH 095/240] display id and other not editable data --- apps/opencs/view/world/enumdelegate.cpp | 4 ++++ apps/opencs/view/world/util.cpp | 32 ++++++++++++++++++++++++- apps/opencs/view/world/util.hpp | 3 +++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index b882e20e69..7f40b966dc 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -65,6 +65,10 @@ void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& if (!data.isValid()) { data = index.data (Qt::DisplayRole); + if (!data.isValid()) + { + return; + } } int value = data.toInt(); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 97af3b99c5..fd35ef3ef9 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "../../model/world/commands.hpp" @@ -119,7 +121,7 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if (!index.data().isValid()) + if (!(index.data(Qt::EditRole).isValid() or index.data(Qt::DisplayRole).isValid())) return 0; return QStyledItemDelegate::createEditor (parent, option, index); @@ -140,4 +142,32 @@ bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) { return false; +} + +void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const +{ + QVariant v = index.data(Qt::EditRole); + if (!v.isValid()) + { + v = index.data(Qt::DisplayRole); + if (!v.isValid()) + { + return; + } + } + + QByteArray n = editor->metaObject()->userProperty().name(); + + if (n == "dateTime") { + if (editor->inherits("QTimeEdit")) + n = "time"; + else if (editor->inherits("QDateEdit")) + n = "date"; + } + + if (!n.isEmpty()) { + if (!v.isValid()) + v = QVariant(editor->property(n).userType(), (const void *)0); + editor->setProperty(n, v); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 87f118cd74..0d04dda4ee 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -111,6 +111,9 @@ namespace CSVWorld virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); ///< \return Does column require update? + virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; + + private slots: virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {} From 361bc559730f292985d57b0420045054700df44f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 09:47:41 +0100 Subject: [PATCH 096/240] dont force trying display --- apps/opencs/view/world/dialoguesubview.cpp | 2 +- apps/opencs/view/world/enumdelegate.cpp | 4 ++-- apps/opencs/view/world/enumdelegate.hpp | 2 +- apps/opencs/view/world/util.cpp | 12 +++++++----- apps/opencs/view/world/util.hpp | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6307afabb2..6145c9dfa7 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -93,7 +93,7 @@ void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { - delegateIt->second->setEditorData(editor, index); + delegateIt->second->setEditorData(editor, index, true); } for (unsigned i = 0; i < mProxys.size(); ++i) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 7f40b966dc..a6089a1b36 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -56,13 +56,13 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio return comboBox; } -void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const +void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { if (QComboBox *comboBox = dynamic_cast (editor)) { QVariant data = index.data (Qt::EditRole); - if (!data.isValid()) + if (tryDisplay && !data.isValid()) { data = index.data (Qt::DisplayRole); if (!data.isValid()) diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index 606f9278a1..ac07f3d1ed 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -34,7 +34,7 @@ namespace CSVWorld virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; - virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; + virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index fd35ef3ef9..74aaeb6f0e 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -144,18 +144,20 @@ bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, return false; } -void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index) const +void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { QVariant v = index.data(Qt::EditRole); - if (!v.isValid()) + if (tryDisplay) { - v = index.data(Qt::DisplayRole); if (!v.isValid()) { - return; + v = index.data(Qt::DisplayRole); + if (!v.isValid()) + { + return; + } } } - QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") { diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 0d04dda4ee..23b9fcfac1 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -111,7 +111,7 @@ namespace CSVWorld virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); ///< \return Does column require update? - virtual void setEditorData (QWidget *editor, const QModelIndex& index) const; + virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; private slots: From 73cb3ac0ed3f469877f04bebf26861b8bce43dbe Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 12:04:03 +0100 Subject: [PATCH 097/240] embelishing --- apps/opencs/view/world/dialoguesubview.cpp | 14 ++++++++--- apps/opencs/view/world/enumdelegate.cpp | 4 +-- apps/opencs/view/world/enumdelegate.hpp | 6 +++-- apps/opencs/view/world/util.cpp | 29 +++++++++++++++++++++- apps/opencs/view/world/util.hpp | 6 +++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6145c9dfa7..c4e9b805c7 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -136,14 +136,20 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { - editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index); + editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); - if (hasEnums) //combox is used for all enums + if (display == CSMWorld::ColumnBase::Display_Boolean) { - connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); + connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited())); } else { - connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + if (hasEnums) //combox is used for all enums + { + connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); + } else + { + connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + } } connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index a6089a1b36..858458b37b 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -42,9 +42,9 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector } QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const + const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { - if (!index.data().isValid()) + if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) return 0; QComboBox *comboBox = new QComboBox (parent); diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index ac07f3d1ed..8bd96a6f20 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -31,8 +31,10 @@ namespace CSVWorld EnumDelegate (const std::vector >& values, QUndoStack& undoStack, QObject *parent); - virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const; + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index, + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 74aaeb6f0e..7e8cddd1f4 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -6,6 +6,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "../../model/world/commands.hpp" @@ -119,10 +124,32 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode } QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const + const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { if (!(index.data(Qt::EditRole).isValid() or index.data(Qt::DisplayRole).isValid())) + { return 0; + } + + if (display != CSMWorld::ColumnBase::Display_None) + { + if (display == CSMWorld::ColumnBase::Display_Integer) + { + return new QSpinBox(parent); + } + if (display == CSMWorld::ColumnBase::Display_Integer) + { + return new QDoubleSpinBox(parent); + } + if (display == CSMWorld::ColumnBase::Display_String) + { + return new QLineEdit(parent); + } + if (display == CSMWorld::ColumnBase::Display_Boolean) + { + return new QCheckBox(parent); + } + } return QStyledItemDelegate::createEditor (parent, option, index); } diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 23b9fcfac1..814f09d3a2 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -101,8 +101,10 @@ namespace CSVWorld virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; - virtual QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const; + virtual QWidget *createEditor (QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index, + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; void setEditLock (bool locked); From 7812427836200bfcb01ebd48477d319822125381 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Mar 2014 12:44:34 +0100 Subject: [PATCH 098/240] added Preview submenu item; fixed View issues regarding exterior cells --- apps/opencs/model/world/data.cpp | 6 +-- apps/opencs/model/world/idtable.cpp | 9 ++++- apps/opencs/model/world/idtable.hpp | 5 ++- apps/opencs/model/world/universalid.cpp | 2 + apps/opencs/model/world/universalid.hpp | 3 +- apps/opencs/view/world/scenesubview.cpp | 2 +- apps/opencs/view/world/table.cpp | 51 ++++++++++++++++++------- apps/opencs/view/world/table.hpp | 3 ++ 8 files changed, 60 insertions(+), 21 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d68b79ff0a..d60dcae117 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -250,9 +250,9 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell); - addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, - UniversalId::Type_Referenceable); - addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell), UniversalId::Type_References, UniversalId::Type_Reference, false); + addModel (new IdTable (&mReferenceables, IdTable::Reordering_None, IdTable::Viewing_None, true), + UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); + addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell, true), UniversalId::Type_References, UniversalId::Type_Reference, false); addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 453a7da6a3..56b16f5a15 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -5,8 +5,8 @@ #include "columnbase.hpp" CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering, - Viewing viewing) -: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing) + Viewing viewing, bool preview) +: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing), mPreview (preview) {} CSMWorld::IdTable::~IdTable() @@ -196,6 +196,11 @@ CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const return mViewing; } +bool CSMWorld::IdTable::hasPreview() const +{ + return mPreview; +} + std::pair CSMWorld::IdTable::view (int row) const { std::string id; diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 5a271de443..7d812b083f 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -39,6 +39,7 @@ namespace CSMWorld CollectionBase *mIdCollection; Reordering mReordering; Viewing mViewing; + bool mPreview; // not implemented IdTable (const IdTable&); @@ -47,7 +48,7 @@ namespace CSMWorld public: IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_None, - Viewing viewing = Viewing_None); + Viewing viewing = Viewing_None, bool preview = false); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -100,6 +101,8 @@ namespace CSMWorld Viewing getViewing() const; + bool hasPreview() const; + std::pair view (int row) const; ///< Return the UniversalId and the hint for viewing \a row. If viewing is not /// supported by this table, return (UniversalId::Type_None, ""). diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 1d1b3c960f..a62acc02bb 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -92,6 +92,8 @@ namespace { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 0c17da03be..34167cd854 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -95,7 +95,8 @@ namespace CSMWorld Type_TopicInfo, Type_JournalInfos, Type_JournalInfo, - Type_Scene + Type_Scene, + Type_Preview }; enum { NumberOfTypes = Type_Scene+1 }; diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 3601ae0942..66e0266046 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -34,7 +34,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48, this); - if (id.getId()[0]=='#') + if (id.getId()=="sys::default") mScene = new CSVRender::PagedWorldspaceWidget (this); else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4bb9955e6e..29bca2f6b3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -39,18 +39,6 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (mCreateAction) menu.addAction(mCloneAction); - - if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) - { - int row = selectedRows.begin()->row(); - - row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); - - CSMWorld::UniversalId id = mModel->view (row).first; - - if (!mDocument.getData().getCells().getRecord (id.getId()).isDeleted()) - menu.addAction (mViewAction); - } } if (mCreateAction) @@ -95,6 +83,28 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) } } + if (selectedRows.size()==1) + { + if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) + { + int row = selectedRows.begin()->row(); + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + + CSMWorld::UniversalId id = mModel->view (row).first; + + int index = mDocument.getData().getCells().searchId (id.getId()); + // index==-1: the ID references a worldspace instead of a cell (ignore for now and go + // ahead) + + if (index==-1 || !mDocument.getData().getCells().getRecord (index).isDeleted()) + menu.addAction (mViewAction); + } + + if (mModel->hasPreview()) + menu.addAction (mPreviewAction); + } + menu.exec (event->globalPos()); } @@ -249,6 +259,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, connect (mViewAction, SIGNAL (triggered()), this, SLOT (viewRecord())); addAction (mViewAction); + mPreviewAction = new QAction (tr ("Preview"), this); + connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); + addAction (mPreviewAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); @@ -406,7 +420,7 @@ void CSVWorld::Table::viewRecord() if (selectedRows.size()==1) { - int row =selectedRows.begin()->row(); + int row = selectedRows.begin()->row(); row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); @@ -417,6 +431,17 @@ void CSVWorld::Table::viewRecord() } } +void CSVWorld::Table::previewRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + std::string id = getUniversalId (selectedRows.begin()->row()).getId(); + + emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , ""); + } +} void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) { int columns = mModel->columnCount(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index e8d5648d16..4231a4a432 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -44,6 +44,7 @@ namespace CSVWorld QAction *mMoveUpAction; QAction *mMoveDownAction; QAction *mViewAction; + QAction *mPreviewAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; @@ -111,6 +112,8 @@ namespace CSVWorld void viewRecord(); + void previewRecord(); + public slots: void tableSizeUpdate(); From 874847c959a7e4e93902b1dd891e06d823182f36 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 13:25:01 +0100 Subject: [PATCH 099/240] handle color --- apps/opencs/view/world/dialoguesubview.cpp | 15 +++++++-------- apps/opencs/view/world/util.cpp | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index c4e9b805c7..6a37ce59c9 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -127,9 +127,14 @@ QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) { bool hasEnums = CSMWorld::Columns::hasEnums(static_cast(mTable->getColumnId(index.column() ) ) ); - if (display == CSMWorld::ColumnBase::Display_Boolean) + QVariant variant = index.data(); + if (!variant.isValid()) { - hasEnums = true; + variant = index.data(Qt::DisplayRole); + if (!variant.isValid()) + { + return 0; + } } QWidget* editor = NULL; @@ -138,11 +143,6 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: { editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); - if (display == CSMWorld::ColumnBase::Display_Boolean) - { - connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited())); - } else - { if (hasEnums) //combox is used for all enums { connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); @@ -150,7 +150,6 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); } - } connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 7e8cddd1f4..5da616ebb8 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -13,6 +13,7 @@ #include #include "../../model/world/commands.hpp" +#include CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) : mModel (model) @@ -126,18 +127,27 @@ void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemMode QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { - if (!(index.data(Qt::EditRole).isValid() or index.data(Qt::DisplayRole).isValid())) + QVariant variant = index.data(); + if (!variant.isValid()) { - return 0; + variant = index.data(Qt::DisplayRole); + if (!variant.isValid()) + { + return 0; + } } if (display != CSMWorld::ColumnBase::Display_None) { + if (variant.type() == QVariant::Color) + { + return new QLineEdit(parent); + } if (display == CSMWorld::ColumnBase::Display_Integer) { return new QSpinBox(parent); } - if (display == CSMWorld::ColumnBase::Display_Integer) + if (display == CSMWorld::ColumnBase::Display_Float) { return new QDoubleSpinBox(parent); } From cc96a38921c63645ca1f9e042c5d74d60f3b0635 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 13:27:52 +0100 Subject: [PATCH 100/240] use qlinedit for the var --- apps/opencs/view/world/util.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 5da616ebb8..14d5c89a0e 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -147,6 +147,10 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return new QSpinBox(parent); } + if (display == CSMWorld::ColumnBase::Display_Var) + { + return new QLineEdit(parent); + } if (display == CSMWorld::ColumnBase::Display_Float) { return new QDoubleSpinBox(parent); From 089732419a4ac40c688bc5b62c722843d8c17352 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 14:11:49 +0100 Subject: [PATCH 101/240] introduce longString --- apps/opencs/model/world/columnbase.hpp | 1 + apps/opencs/model/world/columnimp.hpp | 4 +-- apps/opencs/view/world/dialoguesubview.cpp | 38 +++++++++++++++++----- apps/opencs/view/world/enumdelegate.cpp | 1 + apps/opencs/view/world/util.cpp | 19 +++++++++-- apps/opencs/view/world/util.hpp | 2 +- 6 files changed, 51 insertions(+), 14 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index d990232f70..fe310d0aa4 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -28,6 +28,7 @@ namespace CSMWorld { Display_None, //Do not use Display_String, + Display_LongString, //CONCRETE TYPES STARTS HERE Display_Skill, diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index def225018f..6311562a6a 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -202,7 +202,7 @@ namespace CSMWorld struct DescriptionColumn : public Column { DescriptionColumn() - : Column (Columns::ColumnId_Description, ColumnBase::Display_String) + : Column (Columns::ColumnId_Description, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const @@ -1380,7 +1380,7 @@ namespace CSMWorld template struct QuestDescriptionColumn : public Column { - QuestDescriptionColumn() : Column (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {} + QuestDescriptionColumn() : Column (Columns::ColumnId_QuestDescription, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6a37ce59c9..1f6c411489 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" @@ -126,30 +130,46 @@ QSize CSVWorld::DialogueDelegateDispatcher::sizeHint (const QStyleOptionViewItem QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase::Display display, const QModelIndex& index) { - bool hasEnums = CSMWorld::Columns::hasEnums(static_cast(mTable->getColumnId(index.column() ) ) ); QVariant variant = index.data(); if (!variant.isValid()) { variant = index.data(Qt::DisplayRole); if (!variant.isValid()) { - return 0; + return NULL; } } + QWidget* editor = NULL; std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); - if (hasEnums) //combox is used for all enums - { - connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); - } else - { - connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); - } + + bool skip = false; + if (qobject_cast(editor)) + { + connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + skip = true; + } + if(!skip && qobject_cast(editor)) + { + connect(editor, SIGNAL(stateChanged(int)), proxy, SLOT(editorDataCommited())); + skip = true; + } + if(!skip && qobject_cast(editor)) + { + connect(editor, SIGNAL(textChanged()), proxy, SLOT(editorDataCommited())); + skip = true; + } + if(!skip && qobject_cast(editor)) + { + connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); + skip = true; + } + connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor } diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 858458b37b..e5a766731e 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -9,6 +9,7 @@ #include #include "../../model/world/commands.hpp" +#include void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 14d5c89a0e..f93edab3e2 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../../model/world/commands.hpp" #include @@ -155,7 +156,11 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return new QDoubleSpinBox(parent); } - if (display == CSMWorld::ColumnBase::Display_String) + if (display == CSMWorld::ColumnBase::Display_LongString) + { + return new QTextEdit(parent); + } + if (display == CSMWorld::ColumnBase::Display_String || display == CSMWorld::ColumnBase::Display_Skill) { return new QLineEdit(parent); } @@ -185,7 +190,7 @@ bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, return false; } -void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const +void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) { QVariant v = index.data(Qt::EditRole); if (tryDisplay) @@ -198,7 +203,16 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde return; } } + QPlainTextEdit* plainTextEdit = qobject_cast(editor); + if(plainTextEdit) + { + if(plainTextEdit->toPlainText() == v.toString()) + { + return; + } + } } + QByteArray n = editor->metaObject()->userProperty().name(); if (n == "dateTime") { @@ -213,4 +227,5 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde v = QVariant(editor->property(n).userType(), (const void *)0); editor->setProperty(n, v); } + } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 814f09d3a2..9b9d89535f 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -113,7 +113,7 @@ namespace CSVWorld virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); ///< \return Does column require update? - virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; + virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false); private slots: From f4614c2c63af7e33a662f2266cf1f9ad0606ce40 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 17:52:45 +0100 Subject: [PATCH 102/240] use qlabels for not editable fields --- apps/opencs/view/world/dialoguesubview.cpp | 35 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 1f6c411489..251c2755ea 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -94,6 +94,31 @@ void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const CSMWorld::ColumnBase::Display display = static_cast (mTable->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + QLabel* label = qobject_cast(editor); + if(label) + { + QVariant v = index.data(Qt::EditRole); + if (!v.isValid()) + { + v = index.data(Qt::DisplayRole); + if (!v.isValid()) + { + return; + } + } + if (CSMWorld::Columns::hasEnums(static_cast(mTable->getColumnId(index.column())))) + { + int data = v.toInt(); + std::vector enumNames (CSMWorld::Columns::getEnums (static_cast (mTable->getColumnId (index.column())))); + label->setText(QString::fromUtf8(enumNames.at(data).c_str())); + } else + { + label->clear(); + label->setText(v.toString()); + } + return; + } + std::map::const_iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { @@ -140,12 +165,17 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: } } - QWidget* editor = NULL; + if (! (mTable->flags (index) & Qt::ItemIsEditable)) + { + editor = new QLabel(qobject_cast(mParent)); + return editor; + } + std::map::iterator delegateIt(mDelegates.find(display)); if (delegateIt != mDelegates.end()) { - editor = delegateIt->second->createEditor(dynamic_cast(mParent), QStyleOptionViewItem(), index, display); + editor = delegateIt->second->createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index, display); DialogueDelegateDispatcherProxy* proxy = new DialogueDelegateDispatcherProxy(editor, display); bool skip = false; @@ -247,7 +277,6 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) { - editor->setDisabled(true); lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); ++locked; From 17b521cec8155264c10e293e3aada7f56075b853 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 10 Mar 2014 17:57:40 +0100 Subject: [PATCH 103/240] additional check --- apps/opencs/view/world/dialoguesubview.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 251c2755ea..5c083f8810 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -113,8 +113,10 @@ void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const label->setText(QString::fromUtf8(enumNames.at(data).c_str())); } else { - label->clear(); - label->setText(v.toString()); + if (QVariant::String == v.type()) + { + label->setText(v.toString()); + } } return; } From ef1364878fc98876726759911a18b97ee5b6965e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Mar 2014 18:35:49 +0100 Subject: [PATCH 104/240] moved test cube from SceneWidget to WorldspaceWidget --- apps/opencs/view/render/scenewidget.cpp | 10 +++++----- apps/opencs/view/render/scenewidget.hpp | 2 ++ apps/opencs/view/render/worldspacewidget.cpp | 11 ++++++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 31d5d03187..7b790fd581 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -45,11 +45,6 @@ namespace CSVRender mCamera = mSceneMgr->createCamera("foo"); - Ogre::Entity* ent = mSceneMgr->createEntity("cube", Ogre::SceneManager::PT_CUBE); - ent->setMaterialName("BaseWhite"); - - mSceneMgr->getRootSceneNode()->attachObject(ent); - mCamera->setPosition(300,300,300); mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); @@ -118,6 +113,11 @@ namespace CSVRender } } + Ogre::SceneManager *SceneWidget::getSceneManager() + { + return mSceneMgr; + } + void SceneWidget::paintEvent(QPaintEvent* e) { if (!mWindow) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index ad68897ac0..05b06b2873 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -34,6 +34,8 @@ namespace CSVRender void setNavigation (Navigation *navigation); ///< \attention The ownership of \a navigation is not transferred to *this. + Ogre::SceneManager *getSceneManager(); + private: void paintEvent(QPaintEvent* e); void resizeEvent(QResizeEvent* e); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index dcd152bb33..9959c5a673 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -1,11 +1,20 @@ #include "worldspacewidget.hpp" +#include +#include +#include + #include "../world/scenetoolmode.hpp" CSVRender::WorldspaceWidget::WorldspaceWidget (QWidget *parent) : SceneWidget (parent) -{} +{ + Ogre::Entity* ent = getSceneManager()->createEntity("cube", Ogre::SceneManager::PT_CUBE); + ent->setMaterialName("BaseWhite"); + + getSceneManager()->getRootSceneNode()->attachObject(ent); +} void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) { From a25271f0b61055c66d9f17ab05a88a0cd5868afd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 09:14:13 +0100 Subject: [PATCH 105/240] moving bloat away from the dispatcher --- apps/opencs/view/world/dialoguesubview.cpp | 81 +++++++++++++++------- apps/opencs/view/world/dialoguesubview.hpp | 24 +++++++ apps/opencs/view/world/util.cpp | 4 +- apps/opencs/view/world/util.hpp | 2 +- 4 files changed, 84 insertions(+), 27 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 5c083f8810..65db33cbfc 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -24,6 +24,59 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +/* +==============================NotEditableSubDelegate========================================== +*/ +CSVWorld::NotEditableSubDelegate::NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent) : +QAbstractItemDelegate(parent), +mTable(table) +{} + +void CSVWorld::NotEditableSubDelegate::setEditorData (QLabel* editor, const QModelIndex& index) const +{ + QVariant v = index.data(Qt::EditRole); + if (!v.isValid()) + { + v = index.data(Qt::DisplayRole); + if (!v.isValid()) + { + return; + } + } + + if (QVariant::String == v.type()) + { + editor->setText(v.toString()); + } else //else we are facing enums + { + int data = v.toInt(); + std::vector enumNames (CSMWorld::Columns::getEnums (static_cast (mTable->getColumnId (index.column())))); + editor->setText(QString::fromUtf8(enumNames.at(data).c_str())); + } +} + +void CSVWorld::NotEditableSubDelegate::setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const +{ + //not editable widgets will not save model data +} + +void CSVWorld::NotEditableSubDelegate::paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + //does nothing +} + +QSize CSVWorld::NotEditableSubDelegate::sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + return QSize(); +} + +QWidget* CSVWorld::NotEditableSubDelegate::createEditor (QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index, + CSMWorld::ColumnBase::Display display) const +{ + return new QLabel(parent); +} /* ==============================DialogueDelegateDispatcherProxy========================================== @@ -64,7 +117,8 @@ QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, QUndoStack& undoStack) : mParent(parent), mTable(table), -mUndoStack(undoStack) +mUndoStack(undoStack), +mNotEditableDelegate(table, parent) { } @@ -97,27 +151,7 @@ void CSVWorld::DialogueDelegateDispatcher::setEditorData (QWidget* editor, const QLabel* label = qobject_cast(editor); if(label) { - QVariant v = index.data(Qt::EditRole); - if (!v.isValid()) - { - v = index.data(Qt::DisplayRole); - if (!v.isValid()) - { - return; - } - } - if (CSMWorld::Columns::hasEnums(static_cast(mTable->getColumnId(index.column())))) - { - int data = v.toInt(); - std::vector enumNames (CSMWorld::Columns::getEnums (static_cast (mTable->getColumnId (index.column())))); - label->setText(QString::fromUtf8(enumNames.at(data).c_str())); - } else - { - if (QVariant::String == v.type()) - { - label->setText(v.toString()); - } - } + mNotEditableDelegate.setEditorData(label, index); return; } @@ -170,8 +204,7 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: QWidget* editor = NULL; if (! (mTable->flags (index) & Qt::ItemIsEditable)) { - editor = new QLabel(qobject_cast(mParent)); - return editor; + return mNotEditableDelegate.createEditor(qobject_cast(mParent), QStyleOptionViewItem(), index, display); } std::map::iterator delegateIt(mDelegates.find(display)); diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 06c849e390..33514d205a 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -12,6 +12,7 @@ class QDataWidgetMapper; class QSize; class QEvent; +class QLabel; namespace CSMWorld { @@ -27,6 +28,27 @@ namespace CSVWorld { class CommandDelegate; + class NotEditableSubDelegate : public QAbstractItemDelegate + { + const CSMWorld::IdTable* mTable; + public: + NotEditableSubDelegate(const CSMWorld::IdTable* table, QObject * parent = 0); + + virtual void setEditorData (QLabel* editor, const QModelIndex& index) const; + + virtual void setModelData (QWidget* editor, QAbstractItemModel* model, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const; + + virtual void paint (QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + ///< does nothing + + virtual QSize sizeHint (const QStyleOptionViewItem& option, const QModelIndex& index) const; + ///< does nothing + + virtual QWidget *createEditor (QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index, + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; + }; //this can't be nested into the DialogueDelegateDispatcher, because it needs to emit signals class DialogueDelegateDispatcherProxy : public QObject @@ -68,6 +90,8 @@ namespace CSVWorld QUndoStack& mUndoStack; + NotEditableSubDelegate mNotEditableDelegate; + std::vector mProxys; //once we move to the C++11 we should use unique_ptr public: diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index f93edab3e2..3635ee1d40 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -190,7 +190,7 @@ bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, return false; } -void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) +void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { QVariant v = index.data(Qt::EditRole); if (tryDisplay) @@ -204,7 +204,7 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde } } QPlainTextEdit* plainTextEdit = qobject_cast(editor); - if(plainTextEdit) + if(plainTextEdit) //for some reason it is easier to brake the loop here { if(plainTextEdit->toPlainText() == v.toString()) { diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 9b9d89535f..814f09d3a2 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -113,7 +113,7 @@ namespace CSVWorld virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); ///< \return Does column require update? - virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false); + virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; private slots: From 87286232d71c70f01e4d045f645c546daf78739e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 09:38:53 +0100 Subject: [PATCH 106/240] fixed bug, god bless overloading --- apps/opencs/view/world/enumdelegate.cpp | 19 +++++++++++++++++-- apps/opencs/view/world/enumdelegate.hpp | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index e5a766731e..40f447f9ea 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -14,7 +14,7 @@ void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { - if (QComboBox *comboBox = dynamic_cast (editor)) + if (QComboBox *comboBox = qobject_cast (editor)) { QString value = comboBox->currentText(); @@ -57,9 +57,24 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio return comboBox; } +QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (!index.data(Qt::EditRole).isValid()) + return 0; + + QComboBox *comboBox = new QComboBox (parent); + + for (std::vector >::const_iterator iter (mValues.begin()); + iter!=mValues.end(); ++iter) + comboBox->addItem (iter->second); + + return comboBox; +} + void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { - if (QComboBox *comboBox = dynamic_cast (editor)) + if (QComboBox *comboBox = qobject_cast (editor)) //qobject_cast is faster than dynamic_cast { QVariant data = index.data (Qt::EditRole); diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index 8bd96a6f20..e3ecc051db 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -36,6 +36,10 @@ namespace CSVWorld const QModelIndex& index, CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const; + virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; virtual void paint (QPainter *painter, const QStyleOptionViewItem& option, From 1edf5a4414863289f6cf2c415f698647057dfd1e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 10:47:30 +0100 Subject: [PATCH 107/240] registered additional subviews. --- apps/opencs/view/world/subviews.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 2fa2fc06be..d4d9565a4a 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -87,7 +87,14 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add(CSMWorld::UniversalId::Type_Gmst, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Race, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Class, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactory); manager.add(CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Filter, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Sound, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Faction, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Skill, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_JournalInfo, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_TopicInfo, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactory); + manager.add(CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactory); } \ No newline at end of file From 5ad572346b8e7f2f5f1f9fb42c4d2906526c7375 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 13:02:25 +0100 Subject: [PATCH 108/240] referencables also work now --- apps/opencs/view/doc/view.cpp | 12 ++++++++++-- apps/opencs/view/world/util.cpp | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index de3f476af1..bc34c6118e 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -316,8 +316,16 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) - SubView *view = mSubViewFactory.makeSubView (id, *mDocument); - + const std::vector referenceables(CSMWorld::UniversalId::listReferenceableTypes()); + SubView *view = NULL; + if(std::find(referenceables.begin(), referenceables.end(), id.getType()) != referenceables.end()) + { + view = mSubViewFactory.makeSubView (CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Referenceable, id.getId()), *mDocument); + } else + { + view = mSubViewFactory.makeSubView (id, *mDocument); + } + assert(view); if (!hint.empty()) view->useHint (hint); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 3635ee1d40..766323d0b3 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -160,7 +160,9 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO { return new QTextEdit(parent); } - if (display == CSMWorld::ColumnBase::Display_String || display == CSMWorld::ColumnBase::Display_Skill) + if (display == CSMWorld::ColumnBase::Display_String || + display == CSMWorld::ColumnBase::Display_Skill || + display == CSMWorld::ColumnBase::Display_Script) { return new QLineEdit(parent); } From 81f32976e3a17d773de1e06ca6c970d9c29196d2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 18:38:37 +0100 Subject: [PATCH 109/240] changes --- apps/opencs/view/world/dialoguesubview.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 65db33cbfc..85cb946771 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -260,6 +260,10 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()) { + CSMWorld::IdTable* model = dynamic_cast(document.getData().getTableModel (id)); + const QModelIndex indexToFocus(model->getModelIndex (id.getId(), 0)); + const int focusedRow = indexToFocus.row(); + QWidget *widget = new QWidget (this); setWidget (widget); @@ -280,9 +284,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM widget->setLayout (mainLayout); - QAbstractItemModel *model = document.getData().getTableModel (id); - - int columns = model->columnCount(); + const int columns = model->columnCount(); mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel (model); @@ -301,7 +303,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mDispatcher.makeDelegate(display); - QWidget *editor = mDispatcher.makeEditor(display, (model->index (0, i))); + QWidget *editor = mDispatcher.makeEditor(display, (model->index (focusedRow, i))); if (editor) { @@ -325,8 +327,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM } } - mWidgetMapper->setCurrentModelIndex ( - dynamic_cast (*model).getModelIndex (id.getId(), 0)); + mWidgetMapper->setCurrentModelIndex (indexToFocus); } void CSVWorld::DialogueSubView::setEditLock (bool locked) From c422dc6eed94f9e8f283c9e41cea5019b3b079ea Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 18:38:37 +0100 Subject: [PATCH 110/240] Refactorisation. --- apps/opencs/view/world/dialoguesubview.cpp | 13 ++++++----- apps/opencs/view/world/enumdelegate.cpp | 27 ++++++++-------------- apps/opencs/view/world/enumdelegate.hpp | 7 +++--- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 65db33cbfc..85cb946771 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -260,6 +260,10 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()) { + CSMWorld::IdTable* model = dynamic_cast(document.getData().getTableModel (id)); + const QModelIndex indexToFocus(model->getModelIndex (id.getId(), 0)); + const int focusedRow = indexToFocus.row(); + QWidget *widget = new QWidget (this); setWidget (widget); @@ -280,9 +284,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM widget->setLayout (mainLayout); - QAbstractItemModel *model = document.getData().getTableModel (id); - - int columns = model->columnCount(); + const int columns = model->columnCount(); mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel (model); @@ -301,7 +303,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mDispatcher.makeDelegate(display); - QWidget *editor = mDispatcher.makeEditor(display, (model->index (0, i))); + QWidget *editor = mDispatcher.makeEditor(display, (model->index (focusedRow, i))); if (editor) { @@ -325,8 +327,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM } } - mWidgetMapper->setCurrentModelIndex ( - dynamic_cast (*model).getModelIndex (id.getId(), 0)); + mWidgetMapper->setCurrentModelIndex (indexToFocus); } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 40f447f9ea..6c46232a89 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -14,7 +14,7 @@ void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { - if (QComboBox *comboBox = qobject_cast (editor)) + if (QComboBox *comboBox = dynamic_cast (editor)) { QString value = comboBox->currentText(); @@ -42,6 +42,14 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector } +QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + return createEditor(parent, option, index, CSMWorld::ColumnBase::Display_None); + //overloading virtual functions is HARD +} + QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { @@ -57,24 +65,9 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio return comboBox; } -QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const -{ - if (!index.data(Qt::EditRole).isValid()) - return 0; - - QComboBox *comboBox = new QComboBox (parent); - - for (std::vector >::const_iterator iter (mValues.begin()); - iter!=mValues.end(); ++iter) - comboBox->addItem (iter->second); - - return comboBox; -} - void CSVWorld::EnumDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { - if (QComboBox *comboBox = qobject_cast (editor)) //qobject_cast is faster than dynamic_cast + if (QComboBox *comboBox = dynamic_cast (editor)) { QVariant data = index.data (Qt::EditRole); diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index e3ecc051db..cd749a4518 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -33,12 +34,12 @@ namespace CSVWorld virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index, - CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; + const QModelIndex& index) const; virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index) const; + const QModelIndex& index, + CSMWorld::ColumnBase::Display display = CSMWorld::ColumnBase::Display_None) const; virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; From 865e6b52ae62d4e1b8541e10ea47dcc418766682 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 11 Mar 2014 21:21:05 +0100 Subject: [PATCH 111/240] scroll area --- apps/opencs/view/world/dialoguesubview.cpp | 11 ++++++++--- apps/opencs/view/world/util.cpp | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 85cb946771..6a3e1d25d2 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" @@ -264,9 +265,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM const QModelIndex indexToFocus(model->getModelIndex (id.getId(), 0)); const int focusedRow = indexToFocus.row(); - QWidget *widget = new QWidget (this); - - setWidget (widget); + QScrollArea *scrollArea = new QScrollArea(this); + QWidget *widget = new QWidget (scrollArea); QFrame* line = new QFrame(this); line->setObjectName(QString::fromUtf8("line")); @@ -328,6 +328,11 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM } mWidgetMapper->setCurrentModelIndex (indexToFocus); + + scrollArea->setMinimumWidth(250); + scrollArea->setWidget(widget); + scrollArea->setWidgetResizable(true); + setWidget (scrollArea); } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 766323d0b3..85d8392ffe 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -162,7 +162,10 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO } if (display == CSMWorld::ColumnBase::Display_String || display == CSMWorld::ColumnBase::Display_Skill || - display == CSMWorld::ColumnBase::Display_Script) + display == CSMWorld::ColumnBase::Display_Script || + display == CSMWorld::ColumnBase::Display_Race || + display == CSMWorld::ColumnBase::Display_Class || + display == CSMWorld::ColumnBase::Display_Faction) { return new QLineEdit(parent); } From 8d50af547a1434d9aea71c73109de21c3ac9a9e2 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 10:21:52 +0100 Subject: [PATCH 112/240] created editwidget --- apps/opencs/view/world/dialoguesubview.cpp | 91 +++++++++++++--------- apps/opencs/view/world/dialoguesubview.hpp | 15 +++- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6a3e1d25d2..dd38e770f9 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -251,68 +251,75 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() } /* -==============================DialogueSubView========================================== +=============================================================EditWidget===================================================== */ -CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) : - - SubView (id), - mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()) - +CSVWorld::EditWidget::EditWidget(QWidget *parent, const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : +mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()), +QScrollArea(parent), +mWidgetMapper(NULL), +mMainWidget(NULL), +mUndoStack(document.getUndoStack()), +mTable(dynamic_cast(document.getData().getTableModel(id))) { - CSMWorld::IdTable* model = dynamic_cast(document.getData().getTableModel (id)); - const QModelIndex indexToFocus(model->getModelIndex (id.getId(), 0)); - const int focusedRow = indexToFocus.row(); + remake (id); +} - QScrollArea *scrollArea = new QScrollArea(this); - QWidget *widget = new QWidget (scrollArea); +void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) +{ + const QModelIndex indexToFocus(mTable->getModelIndex (id.getId(), 0)); - QFrame* line = new QFrame(this); + if (mMainWidget) + { + delete mMainWidget; + } + mMainWidget = new QWidget (this); + + if (mWidgetMapper) + { + delete mWidgetMapper; + } + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel(mTable); + mWidgetMapper->setItemDelegate(&mDispatcher); + + QFrame* line = new QFrame(mMainWidget); line->setObjectName(QString::fromUtf8("line")); line->setGeometry(QRect(320, 150, 118, 3)); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); - QVBoxLayout *mainLayout = new QVBoxLayout; - QGridLayout *unlockedLayout = new QGridLayout; - QGridLayout *lockedLayout = new QGridLayout; + QVBoxLayout *mainLayout = new QVBoxLayout(mMainWidget); + QGridLayout *unlockedLayout = new QGridLayout(); + QGridLayout *lockedLayout = new QGridLayout(); mainLayout->addLayout(lockedLayout, 0); mainLayout->addWidget(line, 1); mainLayout->addLayout(unlockedLayout, 2); mainLayout->addStretch(1); - widget->setLayout (mainLayout); - - const int columns = model->columnCount(); - - mWidgetMapper = new QDataWidgetMapper (this); - mWidgetMapper->setModel (model); - mWidgetMapper->setItemDelegate(&mDispatcher); - int unlocked = 0; int locked = 0; - std::vector editors; + const int focusedRow = indexToFocus.row(); + const int columns = mTable->columnCount(); for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + int flags = mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { CSMWorld::ColumnBase::Display display = static_cast - (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + (mTable->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mDispatcher.makeDelegate(display); - QWidget *editor = mDispatcher.makeEditor(display, (model->index (focusedRow, i))); + QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (focusedRow, i))); if (editor) { - editors.push_back(editor); mWidgetMapper->addMapping (editor, i); - QLabel* label = new QLabel(model->headerData (i, Qt::Horizontal).toString()); + QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - if (! (model->flags (model->index (0, i)) & Qt::ItemIsEditable)) + if (! (mTable->flags (mTable->index (0, i)) & Qt::ItemIsEditable)) { lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); @@ -329,10 +336,24 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mWidgetMapper->setCurrentModelIndex (indexToFocus); - scrollArea->setMinimumWidth(250); - scrollArea->setWidget(widget); - scrollArea->setWidgetResizable(true); - setWidget (scrollArea); + this->setMinimumWidth(300); + this->setWidget(mMainWidget); + this->setWidgetResizable(true); +} + +/* +==============================DialogueSubView========================================== +*/ + +CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + bool createAndDelete) : + + SubView (id) + +{ + EditWidget* widget = new EditWidget(this, id, document, false); + + setWidget (widget); } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 33514d205a..40c658b9e4 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "../doc/subview.hpp" #include "../../model/world/columnbase.hpp" @@ -119,11 +120,23 @@ namespace CSVWorld }; - class DialogueSubView : public CSVDoc::SubView + class EditWidget : public QScrollArea { QDataWidgetMapper *mWidgetMapper; DialogueDelegateDispatcher mDispatcher; + QWidget* mMainWidget; + CSMWorld::IdTable* mTable; + QUndoStack& mUndoStack; + public: + + EditWidget (QWidget *parent, const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); + + void remake(const CSMWorld::UniversalId& id); + }; + + class DialogueSubView : public CSVDoc::SubView + { public: DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); From 06e02ed77f231036192baddff9d79cc602ac8dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Sedej?= Date: Wed, 12 Mar 2014 10:53:56 +0100 Subject: [PATCH 113/240] Added version and revision number to mainmenu --- apps/openmw/mwgui/mainmenu.cpp | 10 ++++++++++ apps/openmw/mwgui/mainmenu.hpp | 1 + files/mygui/openmw_mainmenu.layout | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index da1992474b..2788c8f5ca 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -1,5 +1,7 @@ #include "mainmenu.hpp" +#include + #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -20,6 +22,14 @@ namespace MWGui , mButtonBox(0), mWidth (w), mHeight (h) , mSaveGameDialog(NULL) { + getWidget(mVersionText, "VersionText"); + std::string rev = OPENMW_VERSION_COMMITHASH; + rev = rev.substr(0,10); + std::stringstream sstream; + sstream << "OpenMW version: " << OPENMW_VERSION << "\nrevision: " << rev; + std::string output = sstream.str(); + mVersionText->setCaptionWithReplacing(output); + updateMenu(); } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 6d52f26d5d..a9453b2c82 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -24,6 +24,7 @@ namespace MWGui private: MyGUI::Widget* mButtonBox; + MyGUI::TextBox* mVersionText; std::map mButtons; diff --git a/files/mygui/openmw_mainmenu.layout b/files/mygui/openmw_mainmenu.layout index 4479a121f6..2dc43f1b7e 100644 --- a/files/mygui/openmw_mainmenu.layout +++ b/files/mygui/openmw_mainmenu.layout @@ -2,5 +2,9 @@ - + + + + + From 299b7a6ce6de170aba94a853d7a4bc28e478781d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 11:08:04 +0100 Subject: [PATCH 114/240] Added buttons, they don't do anything. --- apps/opencs/view/world/dialoguesubview.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index dd38e770f9..e34d744d93 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" @@ -317,7 +318,7 @@ void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) { mWidgetMapper->addMapping (editor, i); QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); - label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); if (! (mTable->flags (mTable->index (0, i)) & Qt::ItemIsEditable)) { @@ -351,9 +352,22 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM SubView (id) { - EditWidget* widget = new EditWidget(this, id, document, false); + QWidget *mainWidget = new QWidget(this); + + QHBoxLayout *buttonsLayout = new QHBoxLayout; + QPushButton* mPrevButton = new QPushButton(tr("Previous")); + QPushButton* mNextButton = new QPushButton(tr("Next")); + buttonsLayout->addWidget(mPrevButton); + buttonsLayout->addWidget(mNextButton); + + QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget); + + EditWidget* editWidget = new EditWidget(mainWidget, id, document, false); + mainLayout->addLayout(buttonsLayout); + mainLayout->addWidget(editWidget); + editWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + setWidget(mainWidget); - setWidget (widget); } void CSVWorld::DialogueSubView::setEditLock (bool locked) From 0cd40294a24cff680eb695c23d54021bd8be807f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Mar 2014 11:30:44 +0100 Subject: [PATCH 115/240] Fixed ranged combat for creatures --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/creatureanimation.cpp | 51 ++++++- apps/openmw/mwrender/creatureanimation.hpp | 21 ++- apps/openmw/mwrender/npcanimation.cpp | 148 +++---------------- apps/openmw/mwrender/npcanimation.hpp | 27 +--- apps/openmw/mwrender/weaponanimation.cpp | 159 +++++++++++++++++++++ apps/openmw/mwrender/weaponanimation.hpp | 56 ++++++++ 7 files changed, 307 insertions(+), 157 deletions(-) create mode 100644 apps/openmw/mwrender/weaponanimation.cpp create mode 100644 apps/openmw/mwrender/weaponanimation.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index eb502de384..5e108edaf7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,7 +15,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview globalmap videoplayer ripplesimulation refraction - terrainstorage renderconst effectmanager + terrainstorage renderconst effectmanager weaponanimation ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 8ef5841549..0eb883953e 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr) updateParts(); } + + mWeaponAnimationTime = Ogre::SharedPtr(new WeaponAnimationTime(this)); } void CreatureWeaponAnimation::showWeapons(bool showWeapon) @@ -110,6 +112,20 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0, !item.getClass().getEnchantment(item).empty(), &glowColor); + // Crossbows start out with a bolt attached + if (slot == MWWorld::InventoryStore::Slot_CarriedRight && + item.getTypeName() == typeid(ESM::Weapon).name() && + item.get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo != inv.end() && ammo->get()->mBase->mData.mType == ESM::Weapon::Bolt) + attachArrow(); + else + mAmmunition.setNull(); + } + else + mAmmunition.setNull(); + if(scene->mSkelBase) { Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton(); @@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo updateSkeletonInstance(mSkelBase->getSkeleton(), skel); } - // TODO: - // type == ESM::PRT_Weapon should get an animation source based on the current offset - // of the weapon attack animation (from its beginning, or start marker?) std::vector >::iterator ctrl(scene->mControllers.begin()); for(;ctrl != scene->mControllers.end();ctrl++) { if(ctrl->getSource().isNull()) - ctrl->setSource(Ogre::SharedPtr(new NullAnimationTime())); + { + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + ctrl->setSource(mWeaponAnimationTime); + else + ctrl->setSource(Ogre::SharedPtr(new NullAnimationTime())); + } } } +void CreatureWeaponAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) +{ + Ogre::Vector3 glowColor = getEnchantmentColor(ptr); + + setRenderProperties(object, RV_Actors, RQG_Main, RQG_Alpha, 0, + !ptr.getClass().getEnchantment(ptr).empty(), &glowColor); +} + +void CreatureWeaponAnimation::attachArrow() +{ + WeaponAnimation::attachArrow(mPtr); +} + +void CreatureWeaponAnimation::releaseArrow() +{ + WeaponAnimation::releaseArrow(mPtr); +} + +Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration) +{ + Ogre::Vector3 ret = Animation::runAnimation(duration); + pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton()); + return ret; +} + } diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index 37826673d8..d6cd8a5179 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -2,6 +2,7 @@ #define GAME_RENDER_CREATUREANIMATION_H #include "animation.hpp" +#include "weaponanimation.hpp" #include "../mwworld/inventorystore.hpp" namespace MWWorld @@ -21,7 +22,7 @@ namespace MWRender // For creatures with weapons and shields // Animation is already virtual anyway, so might as well make a separate class. // Most creatures don't need weapons/shields, so this will save some memory. - class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener + class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener { public: CreatureWeaponAnimation(const MWWorld::Ptr& ptr); @@ -36,11 +37,29 @@ namespace MWRender void updatePart(NifOgre::ObjectScenePtr& scene, int slot); + virtual void attachArrow(); + virtual void releaseArrow(); + + virtual Ogre::Vector3 runAnimation(float duration); + + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character. + virtual void setPitchFactor(float factor) { mPitchFactor = factor; } + + virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); } + + // WeaponAnimation + virtual NifOgre::ObjectScenePtr getWeapon() { return mWeapon; } + virtual void showWeapon(bool show) { showWeapons(show); } + virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot); + private: NifOgre::ObjectScenePtr mWeapon; NifOgre::ObjectScenePtr mShield; bool mShowWeapons; bool mShowCarriedLeft; + + Ogre::SharedPtr mWeaponAnimationTime; }; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 8f8e65cd02..a09a581910 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -76,27 +76,6 @@ float HeadAnimationTime::getValue() const return 1; } -float WeaponAnimationTime::getValue() const -{ - if (mWeaponGroup.empty()) - return 0; - float current = mAnimation->getCurrentTime(mWeaponGroup); - if (current == -1) - return 0; - return current - mStartTime; -} - -void WeaponAnimationTime::setGroup(const std::string &group) -{ - mWeaponGroup = group; - mStartTime = mAnimation->getStartTime(mWeaponGroup); -} - -void WeaponAnimationTime::updateStartTime() -{ - setGroup(mWeaponGroup); -} - static NpcAnimation::PartBoneMap createPartListMap() { NpcAnimation::PartBoneMap result; @@ -147,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), mAlpha(1.f), - mNpcType(Type_Normal), - mPitchFactor(0) + mNpcType(Type_Normal) { mNpc = mPtr.get()->mBase; @@ -538,14 +516,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) // updateSkeletonInstance, below, touches the hands. node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); } - else if (mPitchFactor > 0) + else { // In third person mode we may still need pitch for ranged weapon targeting - float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor; - Ogre::Node *node = baseinst->getBone("Bip01 Spine2"); - node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); - node = baseinst->getBone("Bip01 Spine1"); - node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); + pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst); } mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. @@ -695,13 +669,14 @@ void NpcAnimation::showWeapons(bool showWeapon) { MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if(weapon != inv.end()) // special case for weapons + if(weapon != inv.end()) { Ogre::Vector3 glowColor = getEnchantmentColor(*weapon); std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); + // Crossbows start out with a bolt attached if (weapon->getTypeName() == typeid(ESM::Weapon).name() && weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) { @@ -743,113 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show) removeIndividualPart(ESM::PRT_Shield); } +void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) +{ + Ogre::Vector3 glowColor = getEnchantmentColor(ptr); + setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, + !ptr.getClass().getEnchantment(ptr).empty(), &glowColor); + + std::for_each(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot)); + std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot)); +} + void NpcAnimation::attachArrow() { - MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weaponSlot != inv.end() && weaponSlot->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) - showWeapons(true); - else - { - NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon]; - - MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); - if (ammo == inv.end()) - return; - std::string model = ammo->getClass().getModel(*ammo); - - mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model); - Ogre::Vector3 glowColor = getEnchantmentColor(*ammo); - setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, - !ammo->getClass().getEnchantment(*ammo).empty(), &glowColor); - - std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); - std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition)); - } + WeaponAnimation::attachArrow(mPtr); } void NpcAnimation::releaseArrow() { - MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weapon == inv.end()) - return; - - // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. - Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * - Ogre::Quaternion(Ogre::Radian(mPtr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); - - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); - - // Reduce fatigue - // somewhat of a guess, but using the weapon weight makes sense - const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); - const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); - const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); - MWMechanics::CreatureStats& attackerStats = mPtr.getClass().getCreatureStats(mPtr); - MWMechanics::DynamicStat fatigue = attackerStats.getFatigue(); - const float normalizedEncumbrance = mPtr.getClass().getEncumbrance(mPtr) / mPtr.getClass().getCapacity(mPtr); - float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; - if (!weapon->isEmpty()) - fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult; - fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); - attackerStats.setFatigue(fatigue); - - if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) - { - // Thrown weapons get detached now - NifOgre::ObjectScenePtr objects = mObjectParts[ESM::PRT_Weapon]; - - Ogre::Vector3 launchPos(0,0,0); - if (objects->mSkelBase) - { - launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition(); - } - else if (objects->mEntities.size()) - { - objects->mEntities[0]->getParentNode()->needUpdate(true); - launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); - } - - float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); - float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); - float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * - mPtr.getClass().getCreatureStats(mPtr).getAttackStrength(); - - MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *weapon, launchPos, orient, *weapon, speed); - - showWeapons(false); - - inv.remove(*weapon, 1, mPtr); - } - else - { - // With bows and crossbows only the used arrow/bolt gets detached - MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); - if (ammo == inv.end()) - return; - - Ogre::Vector3 launchPos(0,0,0); - if (mAmmunition->mSkelBase) - { - launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition(); - } - else if (mAmmunition->mEntities.size()) - { - mAmmunition->mEntities[0]->getParentNode()->needUpdate(true); - launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); - } - - float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); - float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); - float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * mPtr.getClass().getCreatureStats(mPtr).getAttackStrength(); - - MWBase::Environment::get().getWorld()->launchProjectile(mPtr, *ammo, launchPos, orient, *weapon, speed); - - inv.remove(*ammo, 1, mPtr); - mAmmunition.setNull(); - } + WeaponAnimation::releaseArrow(mPtr); } void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 725fde01d8..8ec46facda 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -5,6 +5,8 @@ #include "../mwworld/inventorystore.hpp" +#include "weaponanimation.hpp" + namespace ESM { struct NPC; @@ -25,24 +27,7 @@ public: { } }; -class WeaponAnimationTime : public Ogre::ControllerValue -{ -private: - Animation* mAnimation; - std::string mWeaponGroup; - float mStartTime; -public: - WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {} - void setGroup(const std::string& group); - void updateStartTime(); - - virtual Ogre::Real getValue() const; - virtual void setValue(Ogre::Real value) - { } -}; - - -class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener +class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener { public: virtual void equipmentChanged() { updateParts(); } @@ -91,7 +76,6 @@ private: Ogre::SharedPtr mWeaponAnimationTime; float mAlpha; - float mPitchFactor; void updateNpcBase(); @@ -138,7 +122,10 @@ public: virtual void attachArrow(); virtual void releaseArrow(); - NifOgre::ObjectScenePtr mAmmunition; + // WeaponAnimation + virtual NifOgre::ObjectScenePtr getWeapon() { return mObjectParts[ESM::PRT_Weapon]; } + virtual void showWeapon(bool show) { showWeapons(show); } + virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot); void setViewMode(ViewMode viewMode); diff --git a/apps/openmw/mwrender/weaponanimation.cpp b/apps/openmw/mwrender/weaponanimation.cpp new file mode 100644 index 0000000000..5f953bd838 --- /dev/null +++ b/apps/openmw/mwrender/weaponanimation.cpp @@ -0,0 +1,159 @@ +#include "weaponanimation.hpp" + +#include +#include +#include +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "../mwmechanics/creaturestats.hpp" + +#include "animation.hpp" + +namespace MWRender +{ + +float WeaponAnimationTime::getValue() const +{ + if (mWeaponGroup.empty()) + return 0; + float current = mAnimation->getCurrentTime(mWeaponGroup); + if (current == -1) + return 0; + return current - mStartTime; +} + +void WeaponAnimationTime::setGroup(const std::string &group) +{ + mWeaponGroup = group; + mStartTime = mAnimation->getStartTime(mWeaponGroup); +} + +void WeaponAnimationTime::updateStartTime() +{ + setGroup(mWeaponGroup); +} + +void WeaponAnimation::attachArrow(MWWorld::Ptr actor) +{ + MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); + MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weaponSlot != inv.end() && weaponSlot->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + showWeapon(true); + else + { + NifOgre::ObjectScenePtr weapon = getWeapon(); + + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + std::string model = ammo->getClass().getModel(*ammo); + + mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", weapon->mSkelBase->getParentSceneNode(), model); + configureAddedObject(mAmmunition, *ammo, MWWorld::InventoryStore::Slot_Ammunition); + } +} + +void WeaponAnimation::releaseArrow(MWWorld::Ptr actor) +{ + MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon == inv.end()) + return; + + // The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise. + Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X); + + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + // Reduce fatigue + // somewhat of a guess, but using the weapon weight makes sense + const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); + const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); + const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); + MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor); + MWMechanics::DynamicStat fatigue = attackerStats.getFatigue(); + const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor); + float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; + if (!weapon->isEmpty()) + fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult; + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + attackerStats.setFatigue(fatigue); + + if (weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanThrown) + { + // Thrown weapons get detached now + NifOgre::ObjectScenePtr objects = getWeapon(); + + Ogre::Vector3 launchPos(0,0,0); + if (objects->mSkelBase) + { + launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (objects->mEntities.size()) + { + objects->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat(); + float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat(); + float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * + actor.getClass().getCreatureStats(actor).getAttackStrength(); + + MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed); + + showWeapon(false); + + inv.remove(*weapon, 1, actor); + } + else + { + // With bows and crossbows only the used arrow/bolt gets detached + MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition); + if (ammo == inv.end()) + return; + + Ogre::Vector3 launchPos(0,0,0); + if (mAmmunition->mSkelBase) + { + launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition(); + } + else if (mAmmunition->mEntities.size()) + { + mAmmunition->mEntities[0]->getParentNode()->needUpdate(true); + launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition(); + } + + float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat(); + float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat(); + float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * actor.getClass().getCreatureStats(actor).getAttackStrength(); + + MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed); + + inv.remove(*ammo, 1, actor); + mAmmunition.setNull(); + } +} + +void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel) +{ + if (mPitchFactor == 0) + return; + + float pitch = xrot * mPitchFactor; + Ogre::Node *node = skel->getBone("Bip01 Spine2"); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); + node = skel->getBone("Bip01 Spine1"); + node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD); +} + +} diff --git a/apps/openmw/mwrender/weaponanimation.hpp b/apps/openmw/mwrender/weaponanimation.hpp new file mode 100644 index 0000000000..c09aa65d98 --- /dev/null +++ b/apps/openmw/mwrender/weaponanimation.hpp @@ -0,0 +1,56 @@ +#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H +#define OPENMW_MWRENDER_WEAPONANIMATION_H + +#include + +#include + +#include "../mwworld/ptr.hpp" + +namespace MWRender +{ + + class Animation; + + class WeaponAnimationTime : public Ogre::ControllerValue + { + private: + Animation* mAnimation; + std::string mWeaponGroup; + float mStartTime; + public: + WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {} + void setGroup(const std::string& group); + void updateStartTime(); + + virtual Ogre::Real getValue() const; + virtual void setValue(Ogre::Real value) + { } + }; + + /// Handles attach & release of projectiles for ranged weapons + class WeaponAnimation + { + public: + WeaponAnimation() : mPitchFactor(0) {} + + virtual void attachArrow(MWWorld::Ptr actor); + virtual void releaseArrow(MWWorld::Ptr actor); + + protected: + NifOgre::ObjectScenePtr mAmmunition; + + virtual NifOgre::ObjectScenePtr getWeapon() = 0; + virtual void showWeapon(bool show) = 0; + virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) = 0; + + /// A relative factor (0-1) that decides if and how much the skeleton should be pitched + /// to indicate the facing orientation of the character, for ranged weapon aiming. + float mPitchFactor; + + void pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel); + }; + +} + +#endif From cb9bcc3cc118dbbbe9f4569f4c9d15562cef8dbb Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 12:25:37 +0100 Subject: [PATCH 116/240] buttons are functional --- apps/opencs/view/world/dialoguesubview.cpp | 85 ++++++++++++++++------ apps/opencs/view/world/dialoguesubview.hpp | 19 ++++- apps/opencs/view/world/enumdelegate.cpp | 1 - 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e34d744d93..212031a017 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -255,21 +255,19 @@ CSVWorld::DialogueDelegateDispatcher::~DialogueDelegateDispatcher() =============================================================EditWidget===================================================== */ -CSVWorld::EditWidget::EditWidget(QWidget *parent, const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : -mDispatcher(this, dynamic_cast(document.getData().getTableModel (id)), document.getUndoStack()), +CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, QUndoStack& undoStack, bool createAndDelete) : +mDispatcher(this, table, undoStack), QScrollArea(parent), mWidgetMapper(NULL), mMainWidget(NULL), -mUndoStack(document.getUndoStack()), -mTable(dynamic_cast(document.getData().getTableModel(id))) +mUndoStack(undoStack), +mTable(table) { - remake (id); + remake (row); } -void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) +void CSVWorld::EditWidget::remake(int row) { - const QModelIndex indexToFocus(mTable->getModelIndex (id.getId(), 0)); - if (mMainWidget) { delete mMainWidget; @@ -300,7 +298,6 @@ void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) int unlocked = 0; int locked = 0; - const int focusedRow = indexToFocus.row(); const int columns = mTable->columnCount(); for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); mDispatcher.makeDelegate(display); - QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (focusedRow, i))); + QWidget *editor = mDispatcher.makeEditor(display, (mTable->index (row, i))); if (editor) { @@ -335,7 +332,7 @@ void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) } } - mWidgetMapper->setCurrentModelIndex (indexToFocus); + mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); this->setMinimumWidth(300); this->setWidget(mMainWidget); @@ -349,25 +346,71 @@ void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : - SubView (id) + SubView (id), + mEditWidget(0), + mMainLayout(NULL), + mUndoStack(document.getUndoStack()), + mTable(dynamic_cast(document.getData().getTableModel(id))), + mRow (-1) { + mRow = mTable->getModelIndex (id.getId(), 0).row(); QWidget *mainWidget = new QWidget(this); QHBoxLayout *buttonsLayout = new QHBoxLayout; - QPushButton* mPrevButton = new QPushButton(tr("Previous")); - QPushButton* mNextButton = new QPushButton(tr("Next")); - buttonsLayout->addWidget(mPrevButton); - buttonsLayout->addWidget(mNextButton); + QPushButton* prevButton = new QPushButton(tr("Previous"), mainWidget); + QPushButton* nextButton = new QPushButton(tr("Next"), mainWidget); + buttonsLayout->addWidget(prevButton); + buttonsLayout->addWidget(nextButton); + connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); + connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); - QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget); + mMainLayout = new QVBoxLayout(mainWidget); + + mEditWidget = new EditWidget(mainWidget, mRow, mTable, mUndoStack, false); + mMainLayout->addLayout(buttonsLayout); + mMainLayout->addWidget(mEditWidget); + mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - EditWidget* editWidget = new EditWidget(mainWidget, id, document, false); - mainLayout->addLayout(buttonsLayout); - mainLayout->addWidget(editWidget); - editWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setWidget(mainWidget); +} +void CSVWorld::DialogueSubView::prevId() +{ + if (mRow < 1) + { + return; + } + + int newRow = mRow - 1; + QModelIndex newIndex(mTable->index(newRow, 0)); + + if (!newIndex.isValid()) + { + return; + } + + mEditWidget->remake(newRow); + mRow = newRow; +} + +void CSVWorld::DialogueSubView::nextId() +{ + if (mRow == -1) + { + return; + } + + int newRow = mRow + 1; + QModelIndex newIndex(mTable->index(newRow, 0)); + + if (!newIndex.isValid()) + { + return; + } + + mEditWidget->remake(newRow); + mRow = newRow; } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 40c658b9e4..0576c7aff7 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -14,6 +14,7 @@ class QDataWidgetMapper; class QSize; class QEvent; class QLabel; +class QVBoxLayout; namespace CSMWorld { @@ -130,18 +131,32 @@ namespace CSVWorld public: - EditWidget (QWidget *parent, const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); + EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, QUndoStack& undoStack, bool createAndDelete = false); - void remake(const CSMWorld::UniversalId& id); + void remake(int row); }; class DialogueSubView : public CSVDoc::SubView { + Q_OBJECT + + EditWidget* mEditWidget; + QVBoxLayout* mMainLayout; + CSMWorld::IdTable* mTable; + QUndoStack& mUndoStack; + int mRow; + public: DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); virtual void setEditLock (bool locked); + + private slots: + + void nextId(); + + void prevId(); }; } diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index 6c46232a89..377f479bff 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -9,7 +9,6 @@ #include #include "../../model/world/commands.hpp" -#include void CSVWorld::EnumDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const From d72a2f1ffb85d4e416c5bad19b974403c8f6c3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Sedej?= Date: Wed, 12 Mar 2014 12:33:42 +0100 Subject: [PATCH 117/240] Added code to test if git hash is availible --- apps/openmw/mwgui/mainmenu.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 2788c8f5ca..4ad260fd93 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -23,12 +23,20 @@ namespace MWGui , mSaveGameDialog(NULL) { getWidget(mVersionText, "VersionText"); - std::string rev = OPENMW_VERSION_COMMITHASH; - rev = rev.substr(0,10); std::stringstream sstream; - sstream << "OpenMW version: " << OPENMW_VERSION << "\nrevision: " << rev; + sstream << "OpenMW version: " << OPENMW_VERSION; + + // adding info about git hash if availible + std::string rev = OPENMW_VERSION_COMMITHASH; + std::string tag = OPENMW_VERSION_TAGHASH; + if (!rev.empty() && !tag.empty()) + { + rev = rev.substr(0,10); + sstream << "\nrevision: " << rev; + } + std::string output = sstream.str(); - mVersionText->setCaptionWithReplacing(output); + mVersionText->setCaption(output); updateMenu(); } From 2e226e63b696223a4061bc9f0ed5354a4bdc2b43 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 13:04:40 +0100 Subject: [PATCH 118/240] change universalid of base subview after pressing next or prev --- apps/opencs/view/doc/subview.cpp | 8 +++++++- apps/opencs/view/doc/subview.hpp | 2 ++ apps/opencs/view/world/dialoguesubview.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 6160f2673c..dcd85945fc 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -18,4 +18,10 @@ void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QSt void CSVDoc::SubView::setStatusBar (bool show) {} -void CSVDoc::SubView::useHint (const std::string& hint) {} \ No newline at end of file +void CSVDoc::SubView::useHint (const std::string& hint) {} + +void CSVDoc::SubView::setUniversalId (const CSMWorld::UniversalId& id) +{ + mUniversalId = id; + setWindowTitle (mUniversalId.toString().c_str()); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 59781f869c..85274a18db 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -27,6 +27,8 @@ namespace CSVDoc // not implemented SubView (const SubView&); SubView& operator= (SubView&); + protected: + void setUniversalId(const CSMWorld::UniversalId& id); public: diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 212031a017..f8cb81a8ff 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -391,6 +391,8 @@ void CSVWorld::DialogueSubView::prevId() } mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), + mTable->data (mTable->index (newRow, 0)).toString().toStdString())); mRow = newRow; } @@ -410,6 +412,8 @@ void CSVWorld::DialogueSubView::nextId() } mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), + mTable->data (mTable->index (newRow, 0)).toString().toStdString())); mRow = newRow; } From 43a12fffd58369726bb51bc5a0dfcc349e933b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Sedej?= Date: Wed, 12 Mar 2014 14:37:44 +0100 Subject: [PATCH 119/240] indentation issue... --- apps/openmw/mwgui/mainmenu.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index a9453b2c82..48b515d65b 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -24,7 +24,7 @@ namespace MWGui private: MyGUI::Widget* mButtonBox; - MyGUI::TextBox* mVersionText; + MyGUI::TextBox* mVersionText; std::map mButtons; From 1a9d4204d2a4750fc7dc9f944632dd36a2e622da Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 15:07:17 +0100 Subject: [PATCH 120/240] correction --- apps/opencs/view/world/dialoguesubview.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index f8cb81a8ff..2a5dd93c67 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -377,11 +377,6 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM void CSVWorld::DialogueSubView::prevId() { - if (mRow < 1) - { - return; - } - int newRow = mRow - 1; QModelIndex newIndex(mTable->index(newRow, 0)); @@ -398,11 +393,6 @@ void CSVWorld::DialogueSubView::prevId() void CSVWorld::DialogueSubView::nextId() { - if (mRow == -1) - { - return; - } - int newRow = mRow + 1; QModelIndex newIndex(mTable->index(newRow, 0)); From 2278eb5e9104db721424dff6c5fb60622e362e57 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 15:17:49 +0100 Subject: [PATCH 121/240] handle locking --- apps/opencs/view/world/dialoguesubview.cpp | 9 +++++++-- apps/opencs/view/world/dialoguesubview.hpp | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 2a5dd93c67..a8bd899c91 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -351,7 +351,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout(NULL), mUndoStack(document.getUndoStack()), mTable(dynamic_cast(document.getData().getTableModel(id))), - mRow (-1) + mRow (-1), + mLocked(false) { mRow = mTable->getModelIndex (id.getId(), 0).row(); @@ -373,6 +374,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setWidget(mainWidget); + mEditWidget->setDisabled(mLocked); } void CSVWorld::DialogueSubView::prevId() @@ -389,6 +391,7 @@ void CSVWorld::DialogueSubView::prevId() setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toStdString())); mRow = newRow; + mEditWidget->setDisabled(mLocked); } void CSVWorld::DialogueSubView::nextId() @@ -405,9 +408,11 @@ void CSVWorld::DialogueSubView::nextId() setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toStdString())); mRow = newRow; + mEditWidget->setDisabled(mLocked); } void CSVWorld::DialogueSubView::setEditLock (bool locked) { - + mLocked = locked; + mEditWidget->setDisabled(mLocked); } \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 0576c7aff7..b42b125559 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -145,6 +145,7 @@ namespace CSVWorld CSMWorld::IdTable* mTable; QUndoStack& mUndoStack; int mRow; + bool mLocked; public: From d1290ac5568704b15d3e85b711684ff40aa33d52 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 15:46:27 +0100 Subject: [PATCH 122/240] skip deleled or erased records --- apps/opencs/view/world/dialoguesubview.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index a8bd899c91..97f116c06c 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -23,6 +23,7 @@ #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/columns.hpp" +#include "../../model/world/record.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -387,6 +388,13 @@ void CSVWorld::DialogueSubView::prevId() return; } + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Deleted) + { + prevId(); + return; + } + mEditWidget->remake(newRow); setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toStdString())); @@ -404,6 +412,13 @@ void CSVWorld::DialogueSubView::nextId() return; } + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Deleted) + { + nextId(); + return; + } + mEditWidget->remake(newRow); setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toStdString())); From 919065db327b085383f19bb7bebba273fefd949b Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 16:06:59 +0100 Subject: [PATCH 123/240] fixed segfault --- apps/opencs/view/world/dialoguesubview.cpp | 37 ++++++++++++++++++++-- apps/opencs/view/world/dialoguesubview.hpp | 3 ++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 97f116c06c..bfb296a0e6 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -356,6 +356,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mLocked(false) { + connect(mTable, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex &)), this, SLOT(dataChanged())); mRow = mTable->getModelIndex (id.getId(), 0).row(); QWidget *mainWidget = new QWidget(this); @@ -375,12 +376,24 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setWidget(mainWidget); - mEditWidget->setDisabled(mLocked); + + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) + { + mEditWidget->setDisabled(true); + } else + { + mEditWidget->setDisabled(mLocked); + } } void CSVWorld::DialogueSubView::prevId() { int newRow = mRow - 1; + if (newRow < 0) + { + return; + } QModelIndex newIndex(mTable->index(newRow, 0)); if (!newIndex.isValid()) @@ -389,7 +402,7 @@ void CSVWorld::DialogueSubView::prevId() } CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Deleted) + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) { prevId(); return; @@ -405,6 +418,12 @@ void CSVWorld::DialogueSubView::prevId() void CSVWorld::DialogueSubView::nextId() { int newRow = mRow + 1; + + if (newRow > mTable->rowCount()) + { + return; + } + QModelIndex newIndex(mTable->index(newRow, 0)); if (!newIndex.isValid()) @@ -413,7 +432,7 @@ void CSVWorld::DialogueSubView::nextId() } CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Deleted) + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) { nextId(); return; @@ -430,4 +449,16 @@ void CSVWorld::DialogueSubView::setEditLock (bool locked) { mLocked = locked; mEditWidget->setDisabled(mLocked); +} + +void CSVWorld::DialogueSubView::dataChanged() +{ + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) + { + mEditWidget->setDisabled(true); + } else + { + mEditWidget->setDisabled(mLocked); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index b42b125559..62d95b6222 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -158,6 +158,9 @@ namespace CSVWorld void nextId(); void prevId(); + + void dataChanged(); + ///\brief we need to care for deleting currently edited record }; } From 30a0c82e2212f2e44e496c8526f8effe5593f3a6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 16:28:10 +0100 Subject: [PATCH 124/240] this code segfaults, but i don't know why --- apps/opencs/view/world/dialoguesubview.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index bfb296a0e6..c041dd1f4d 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -377,14 +377,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM setWidget(mainWidget); - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) - { - mEditWidget->setDisabled(true); - } else - { - mEditWidget->setDisabled(mLocked); - } + dataChanged(); } void CSVWorld::DialogueSubView::prevId() @@ -421,6 +414,7 @@ void CSVWorld::DialogueSubView::nextId() if (newRow > mTable->rowCount()) { + std::cout<<"test"< Date: Wed, 12 Mar 2014 19:36:46 +0100 Subject: [PATCH 125/240] added the new qlineedit subclass --- apps/opencs/view/world/table.cpp | 4 ++-- apps/opencs/view/world/util.cpp | 27 +++++++++++++++++++++++++-- apps/opencs/view/world/util.hpp | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4bb9955e6e..f7f42eb5f1 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -528,7 +528,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) { - event->acceptProposedAction(); + event->acceptProposedAction(); } void CSVWorld::Table::dropEvent(QDropEvent *event) @@ -560,7 +560,7 @@ void CSVWorld::Table::dropEvent(QDropEvent *event) void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) { - event->accept(); + event->accept(); } std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 85d8392ffe..cc9fac1cb3 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -12,9 +12,10 @@ #include #include #include +#include #include "../../model/world/commands.hpp" -#include +#include "../../model/world/tablemimedata.hpp" CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) : mModel (model) @@ -167,7 +168,7 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO display == CSMWorld::ColumnBase::Display_Class || display == CSMWorld::ColumnBase::Display_Faction) { - return new QLineEdit(parent); + return new DropLineEdit(parent); } if (display == CSMWorld::ColumnBase::Display_Boolean) { @@ -233,4 +234,26 @@ void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelInde editor->setProperty(n, v); } +} + +CSVWorld::DropLineEdit::DropLineEdit(QWidget* parent) : +QLineEdit(parent) +{ + setAcceptDrops(true); +} + +void CSVWorld::DropLineEdit::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::DropLineEdit::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) +{ + emit tableMimeDataDropped(dynamic_cast (event->mimeData())->getData()); + //WIP } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 814f09d3a2..a70af9dc8a 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -5,11 +5,18 @@ #include #include +#include #include "../../model/world/columnbase.hpp" class QUndoStack; +namespace CSMWorld +{ + class TableMimeData; + class UniversalId; +} + namespace CSVWorld { ///< \brief Getting the data out of an editor widget @@ -79,6 +86,24 @@ namespace CSVWorld }; + class DropLineEdit : public QLineEdit + { + Q_OBJECT + + public: + DropLineEdit(QWidget *parent); + + private: + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + + signals: + void tableMimeDataDropped(const std::vector& data); + }; + ///< \brief Use commands instead of manipulating the model directly class CommandDelegate : public QStyledItemDelegate { From fd4829141de2fe80112ac36776e56fae82e972db Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 19:55:51 +0100 Subject: [PATCH 126/240] succesfully connected the signals --- apps/opencs/view/world/dialoguesubview.cpp | 7 ++++++- apps/opencs/view/world/dialoguesubview.hpp | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index c041dd1f4d..a1c9de6125 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -113,6 +113,10 @@ QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const return mEditor; } +void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector& data) +{ + std::cout<<"Test!"<(editor)) + if (qobject_cast(editor)) { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + connect(editor, SIGNAL(tableMimeDataDropped(const std::vector&)), proxy, SLOT(tableMimeDataDropped(const std::vector&))); skip = true; } if(!skip && qobject_cast(editor)) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 62d95b6222..e133041b88 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -69,6 +69,7 @@ namespace CSVWorld CSMWorld::ColumnBase::Display mDisplay; std::auto_ptr mIndexWrapper; + public: DialogueDelegateDispatcherProxy(QWidget* editor, CSMWorld::ColumnBase::Display display); QWidget* getEditor() const; @@ -76,6 +77,7 @@ namespace CSVWorld public slots: void editorDataCommited(); void setIndex(const QModelIndex& index); + void tableMimeDataDropped(const std::vector& data); signals: void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); From cc1ceb35ce1c6173add9f738d8ea91434960b849 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 12 Mar 2014 20:34:55 +0100 Subject: [PATCH 127/240] drag and drop works --- apps/opencs/view/world/dialoguesubview.cpp | 20 +++++++++++++++++++- apps/opencs/view/world/dialoguesubview.hpp | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index a1c9de6125..65e9c5cb32 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -24,6 +24,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/record.hpp" +#include "../../model/world/tablemimedata.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -115,7 +116,19 @@ QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector& data) { - std::cout<<"Test!"<(mEditor); + if (lineEdit && mIndexWrapper.get()) + { + lineEdit->setText(data[i].getId().c_str()); + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + break; + } + } + } } /* ==============================DialogueDelegateDispatcher========================================== @@ -242,6 +255,11 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: connect(editor, SIGNAL(currentIndexChanged (int)), proxy, SLOT(editorDataCommited())); skip = true; } + if(!skip && qobject_cast(editor)) + { + connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); + skip = true; + } connect(proxy, SIGNAL(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display)), this, SLOT(editorDataCommited(QWidget*, const QModelIndex&, CSMWorld::ColumnBase::Display))); mProxys.push_back(proxy); //deleted in the destructor diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index e133041b88..df985d5f8b 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -90,7 +90,7 @@ namespace CSVWorld QObject* mParent; - CSMWorld::IdTable* mTable; //nor sure if it is needed TODO + CSMWorld::IdTable* mTable; QUndoStack& mUndoStack; From dd755a00e47bbfe1631f9eaaf6422ff62b829938 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Mar 2014 08:52:37 +0100 Subject: [PATCH 128/240] fix for the bug (?) --- apps/opencs/view/world/table.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index f7f42eb5f1..7f98801cb2 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -508,20 +508,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) drag->setMimeData (mime); drag->setPixmap (QString::fromStdString (mime->getIcon())); - - Qt::DropActions action = Qt::IgnoreAction; - switch (QApplication::keyboardModifiers()) - { - case Qt::ControlModifier: - action = Qt::CopyAction; - break; - - case Qt::ShiftModifier: - action = Qt::MoveAction; - break; - } - - drag->exec(action); + drag->exec(Qt::CopyAction); } } From 7bc97fb8b85de38df4d6d15f450a4069137512b2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Mar 2014 13:19:32 +0100 Subject: [PATCH 129/240] reworked code for player positioning on startup and new game --- apps/openmw/engine.cpp | 33 ++----------- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwstate/statemanagerimp.cpp | 5 +- apps/openmw/mwworld/worldimp.cpp | 61 +++++++++++++++++-------- apps/openmw/mwworld/worldimp.hpp | 9 +++- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4c3cadb3b6..7d06269132 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -404,7 +404,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, - mActivationDistanceOverride)); + mActivationDistanceOverride, mCellName)); MWBase::Environment::get().getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); @@ -440,31 +440,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mechanics->buildPlayer(); window->updatePlayer(); - // load cell - ESM::Position pos; - MWBase::World *world = MWBase::Environment::get().getWorld(); - - if (!mCellName.empty()) - { - if (world->findExteriorPosition(mCellName, pos)) { - world->changeToExteriorCell (pos); - } - else { - world->findInteriorPosition(mCellName, pos); - world->changeToInteriorCell (mCellName, pos); - } - } - else - { - pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; - pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; - world->changeToExteriorCell (pos); - } - - Ogre::FrameEvent event; - event.timeSinceLastEvent = 0; - event.timeSinceLastFrame = 0; - frameRenderingQueued(event); mOgre->getRoot()->addFrameListener (this); // scripts @@ -502,15 +477,15 @@ void OMW::Engine::go() // Play some good 'ol tunes MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); - if (!mStartupScript.empty()) - MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); - // start in main menu if (!mSkipMenu) MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); else MWBase::Environment::get().getStateManager()->newGame (true); + if (!mStartupScript.empty()) + MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); + // Start the main rendering loop while (!mEnvironment.get().getStateManager()->hasQuitRequest()) Ogre::Root::getSingleton().renderOneFrame(); diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5c2912a8f0..bb6f5741d9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -101,7 +101,8 @@ namespace MWBase virtual ~World() {} - virtual void startNewGame() = 0; + virtual void startNewGame (bool bypass) = 0; + ///< \param bypass Bypass regular game start. virtual void clear() = 0; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 05e9289377..d6309c1c9f 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -125,11 +125,10 @@ void MWState::StateManager::newGame (bool bypass) { cleanup(); + MWBase::Environment::get().getWorld()->startNewGame (bypass); + if (!bypass) - { - MWBase::Environment::get().getWorld()->startNewGame(); MWBase::Environment::get().getWindowManager()->setNewGame (true); - } else MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 62ef09ae45..992ecc9607 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -121,13 +121,15 @@ namespace MWWorld const Files::Collections& fileCollections, const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, - ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) + ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, + int activationDistanceOverride, const std::string& startCell) : mPlayer (0), mLocalScripts (mStore), mSky (true), mCells (mStore, mEsm), - mActivationDistanceOverride (mActivationDistanceOverride), + mActivationDistanceOverride (activationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(true), mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles), - mGoToJail(false) + mGoToJail(false), + mStartCell (startCell) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -168,7 +170,7 @@ namespace MWWorld mWorldScene = new Scene(*mRendering, mPhysics); } - void World::startNewGame() + void World::startNewGame (bool bypass) { mGoToJail = false; mLevitationEnabled = true; @@ -181,23 +183,44 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->updatePlayer(); - // FIXME: this will add cell 0,0 as visible on the global map - ESM::Position pos; - const int cellSize = 8192; - pos.pos[0] = cellSize/2; - pos.pos[1] = cellSize/2; - pos.pos[2] = 0; - pos.rot[0] = 0; - pos.rot[1] = 0; - pos.rot[2] = 0; - mWorldScene->changeToExteriorCell(pos); + if (bypass && !mStartCell.empty()) + { + ESM::Position pos; - // FIXME: should be set to 1, but the sound manager won't pause newly started sounds - mPlayIntro = 2; + if (findExteriorPosition (mStartCell, pos)) + { + changeToExteriorCell (pos); + } + else + { + findInteriorPosition (mStartCell, pos); + changeToInteriorCell (mStartCell, pos); + } + } + else + { + /// \todo if !bypass, do not add player location to global map for the duration of one + /// frame + ESM::Position pos; + const int cellSize = 8192; + pos.pos[0] = cellSize/2; + pos.pos[1] = cellSize/2; + pos.pos[2] = 0; + pos.rot[0] = 0; + pos.rot[1] = 0; + pos.rot[2] = 0; + mWorldScene->changeToExteriorCell(pos); + } - // set new game mark - mGlobalVariables["chargenstate"].setInteger (1); - mGlobalVariables["pcrace"].setInteger (3); + if (!bypass) + { + // FIXME: should be set to 1, but the sound manager won't pause newly started sounds + mPlayIntro = 2; + + // set new game mark + mGlobalVariables["chargenstate"].setInteger (1); + mGlobalVariables["pcrace"].setInteger (3); + } // we don't want old weather to persist on a new game delete mWeatherManager; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b694e00f75..42f52cb619 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -120,6 +120,9 @@ namespace MWWorld std::map mMagicBolts; std::map mProjectiles; + + std::string mStartCell; + void updateWeather(float duration); int getDaysPerMonth (int month) const; @@ -179,11 +182,13 @@ namespace MWWorld const Files::Collections& fileCollections, const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, - ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); + ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, + int activationDistanceOverride, const std::string& startCell); virtual ~World(); - virtual void startNewGame(); + virtual void startNewGame (bool bypass); + ///< \param bypass Bypass regular game start. virtual void clear(); From d92740efc9db3ab1569cd89c0e905942ab2f0ba7 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 13 Mar 2014 23:44:52 +1100 Subject: [PATCH 130/240] Bug #900 fix - only fixed AiWonder, AiCombat, AiTravel and others may need a different strategy to this. --- apps/openmw/mwmechanics/aiwander.cpp | 96 +++++++++++++++++++++++++++- apps/openmw/mwmechanics/aiwander.hpp | 14 ++++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 9a95822f5e..844eaf4900 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -27,6 +27,11 @@ namespace namespace MWMechanics { + // NOTE: determined empherically but probably need further tweaking + static const int COUNT_BEFORE_STUCK = 20; + static const int COUNT_BEFORE_RESET = 200; + static const int COUNT_EVADE = 7; + AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) , mCellX(std::numeric_limits::max()) @@ -36,6 +41,11 @@ namespace MWMechanics , mX(0) , mY(0) , mZ(0) + , mPrevX(0) + , mPrevY(0) + , mWalkState(State_Norm) + , mStuckCount(0) + , mEvadeCount(0) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -298,9 +308,91 @@ namespace MWMechanics } else { - zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + /* 1 n + * State_Norm <---> State_CheckStuck --> State_Evade + * ^ ^ | ^ | ^ | | + * | | | | | | | | + * | +---+ +---+ +---+ | m + * | any < n < m | + * +--------------------------------------------+ + */ + bool samePosition = (abs(pos.pos[0] - mPrevX) < 1) && (abs(pos.pos[1] - mPrevY) < 1); + switch(mWalkState) + { + case State_Norm: + { + if(!samePosition) + break; + else + mWalkState = State_CheckStuck; + } + /* FALL THROUGH */ + case State_CheckStuck: + { + if(!samePosition) + { + mWalkState = State_Norm; + // to do this properly need yet another variable, simply don't clear for now + //mStuckCount = 0; + break; + } + else + { + // consider stuck only if position unchanges consequitively + if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0) + mWalkState = State_Evade; + // NOTE: mStuckCount is purposely not cleared here + else + break; // still in the same state, but counter got incremented + } + } + /* FALL THROUGH */ + case State_Evade: + { + if(mEvadeCount++ < COUNT_EVADE) + break; + else + { + mWalkState = State_Norm; // tried to evade, assume all is ok and start again + // NOTE: mStuckCount is purposely not cleared here + mEvadeCount = 0; + } + } + /* NO DEFAULT CASE */ + } - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + if(mWalkState == State_Evade) + { + //std::cout << "Stuck \""<= COUNT_BEFORE_RESET) // something has gone wrong, reset + { + //std::cout << "Reset \""< Date: Fri, 14 Mar 2014 00:09:03 +1100 Subject: [PATCH 131/240] Bug #900 fix - minor update to comments --- apps/openmw/mwmechanics/aiwander.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index a893e257ef..6de0b81813 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -44,6 +44,9 @@ namespace MWMechanics float mYCell; // for checking if we're stuck (but don't check Z axis) + float mPrevX; + float mPrevY; + enum WalkState { State_Norm, @@ -52,8 +55,6 @@ namespace MWMechanics }; WalkState mWalkState; - float mPrevX; - float mPrevY; int mStuckCount; int mEvadeCount; From e1a4b64efb7172cd30271c56b003870f2ec8c76a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Mar 2014 16:50:04 +0100 Subject: [PATCH 132/240] fixed segfault --- apps/opencs/model/world/tablemimedata.cpp | 5 + apps/opencs/model/world/tablemimedata.hpp | 2 + apps/opencs/view/world/dialoguesubview.cpp | 103 +++++++++++++-------- apps/opencs/view/world/dialoguesubview.hpp | 24 ++++- apps/opencs/view/world/util.cpp | 4 +- apps/opencs/view/world/util.hpp | 3 +- 6 files changed, 98 insertions(+), 43 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b56c9c8c25..9dcecf3bc1 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -443,4 +443,9 @@ CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::U default: return CSMWorld::ColumnBase::Display_None; } +} + +const CSMDoc::Document* CSMWorld::TableMimeData::getDocumentPtr() const +{ + return &mDocument; } \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 7687f3555f..44ac0f5f64 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -48,6 +48,8 @@ namespace CSMWorld UniversalId returnMatching(UniversalId::Type type) const; + const CSMDoc::Document* getDocumentPtr() const; + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 65e9c5cb32..50d19378d2 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -25,6 +25,7 @@ #include "../../model/world/columns.hpp" #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/doc/document.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -114,7 +115,7 @@ QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const return mEditor; } -void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector& data) +void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector& data, const CSMDoc::Document* document) { for (unsigned i = 0; i < data.size(); ++i) { @@ -123,8 +124,7 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: QLineEdit* lineEdit = qobject_cast(mEditor); if (lineEdit && mIndexWrapper.get()) { - lineEdit->setText(data[i].getId().c_str()); - emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); break; } } @@ -237,7 +237,10 @@ QWidget* CSVWorld::DialogueDelegateDispatcher::makeEditor(CSMWorld::ColumnBase:: if (qobject_cast(editor)) { connect(editor, SIGNAL(editingFinished()), proxy, SLOT(editorDataCommited())); - connect(editor, SIGNAL(tableMimeDataDropped(const std::vector&)), proxy, SLOT(tableMimeDataDropped(const std::vector&))); + connect(editor, SIGNAL(tableMimeDataDropped(const std::vector&, const CSMDoc::Document*)), + proxy, SLOT(tableMimeDataDropped(const std::vector&, const CSMDoc::Document*))); + connect(proxy, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); skip = true; } if(!skip && qobject_cast(editor)) @@ -288,6 +291,7 @@ mUndoStack(undoStack), mTable(table) { remake (row); + connect(&mDispatcher, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); } void CSVWorld::EditWidget::remake(int row) @@ -376,7 +380,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mUndoStack(document.getUndoStack()), mTable(dynamic_cast(document.getData().getTableModel(id))), mRow (-1), - mLocked(false) + mLocked(false), + mDocument(document) { connect(mTable, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex &)), this, SLOT(dataChanged())); @@ -394,6 +399,10 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout = new QVBoxLayout(mainWidget); mEditWidget = new EditWidget(mainWidget, mRow, mTable, mUndoStack, false); + connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); + + mMainLayout->addLayout(buttonsLayout); mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); @@ -410,56 +419,59 @@ void CSVWorld::DialogueSubView::prevId() { return; } - QModelIndex newIndex(mTable->index(newRow, 0)); - - if (!newIndex.isValid()) + while (newRow >= 0) { - return; - } + QModelIndex newIndex(mTable->index(newRow, 0)); - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) - { - prevId(); - return; - } + if (!newIndex.isValid()) + { + return; + } - mEditWidget->remake(newRow); - setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toStdString())); - mRow = newRow; - mEditWidget->setDisabled(mLocked); + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); + if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) + { + mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), + mTable->data (mTable->index (newRow, 0)).toString().toStdString())); + mRow = newRow; + mEditWidget->setDisabled(mLocked); + return; + } + --newRow; + } } void CSVWorld::DialogueSubView::nextId() { int newRow = mRow + 1; - if (newRow > mTable->rowCount()) - { - std::cout<<"test"<index(newRow, 0)); - - if (!newIndex.isValid()) + if (newRow >= mTable->rowCount()) { return; } - CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) + while (newRow < mTable->rowCount()) { - nextId(); - return; - } + QModelIndex newIndex(mTable->index(newRow, 0)); - mEditWidget->remake(newRow); - setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), + if (!newIndex.isValid()) + { + return; + } + + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (newRow, 1)).toInt()); + if (!(state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)) + { + mEditWidget->remake(newRow); + setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), mTable->data (mTable->index (newRow, 0)).toString().toStdString())); - mRow = newRow; - mEditWidget->setDisabled(mLocked); + mRow = newRow; + mEditWidget->setDisabled(mLocked); + return; + } + ++newRow; + } } void CSVWorld::DialogueSubView::setEditLock (bool locked) @@ -478,4 +490,15 @@ void CSVWorld::DialogueSubView::dataChanged() { mEditWidget->setDisabled(mLocked); } -} \ No newline at end of file +} + +void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, + const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document) +{ + if (document == &mDocument) + { + qobject_cast(editor)->setText(id.getId().c_str()); + } +} diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index df985d5f8b..1c8aa5971d 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -77,10 +77,15 @@ namespace CSVWorld public slots: void editorDataCommited(); void setIndex(const QModelIndex& index); - void tableMimeDataDropped(const std::vector& data); + void tableMimeDataDropped(const std::vector& data, const CSMDoc::Document* document); signals: void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); + + void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document); + }; class DialogueDelegateDispatcher : public QAbstractItemDelegate @@ -121,10 +126,17 @@ namespace CSVWorld private slots: void editorDataCommited(QWidget* editor, const QModelIndex& index, CSMWorld::ColumnBase::Display display); + signals: + void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document); + + }; class EditWidget : public QScrollArea { + Q_OBJECT QDataWidgetMapper *mWidgetMapper; DialogueDelegateDispatcher mDispatcher; QWidget* mMainWidget; @@ -136,6 +148,11 @@ namespace CSVWorld EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, QUndoStack& undoStack, bool createAndDelete = false); void remake(int row); + + signals: + void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document); }; class DialogueSubView : public CSVDoc::SubView @@ -148,6 +165,7 @@ namespace CSVWorld QUndoStack& mUndoStack; int mRow; bool mLocked; + const CSMDoc::Document& mDocument; public: @@ -163,6 +181,10 @@ namespace CSVWorld void dataChanged(); ///\brief we need to care for deleting currently edited record + + void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, + const CSMWorld::UniversalId& id, + const CSMDoc::Document* document); }; } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index cc9fac1cb3..7278f34715 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -254,6 +254,8 @@ void CSVWorld::DropLineEdit::dragMoveEvent(QDragMoveEvent *event) void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) { - emit tableMimeDataDropped(dynamic_cast (event->mimeData())->getData()); + const CSMWorld::TableMimeData* data(dynamic_cast(event->mimeData())); + emit tableMimeDataDropped(data->getData(), data->getDocumentPtr()); + emit editingFinished (); //WIP } \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index a70af9dc8a..7664f3eae2 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -8,6 +8,7 @@ #include #include "../../model/world/columnbase.hpp" +#include "../../model/doc/document.hpp" class QUndoStack; @@ -101,7 +102,7 @@ namespace CSVWorld void dropEvent(QDropEvent *event); signals: - void tableMimeDataDropped(const std::vector& data); + void tableMimeDataDropped(const std::vector& data, const CSMDoc::Document* document); }; ///< \brief Use commands instead of manipulating the model directly From c97172d89c66c411c2f3b550c6b5119e47f3654a Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Mar 2014 17:00:14 +0100 Subject: [PATCH 133/240] fixed segfault and prevent drops from other documents --- apps/opencs/view/world/dialoguesubview.cpp | 1 + apps/opencs/view/world/util.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 50d19378d2..fdef57c41b 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -125,6 +125,7 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: if (lineEdit && mIndexWrapper.get()) { emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); break; } } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 7278f34715..a71937b085 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -256,6 +256,5 @@ void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) { const CSMWorld::TableMimeData* data(dynamic_cast(event->mimeData())); emit tableMimeDataDropped(data->getData(), data->getDocumentPtr()); - emit editingFinished (); //WIP } \ No newline at end of file From 4a623ace7159589e8ea1b1180d95305f86e9f5ce Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 13 Mar 2014 18:26:59 +0100 Subject: [PATCH 134/240] disabling works --- apps/opencs/view/world/dialoguesubview.cpp | 28 ++++++++++++++-------- apps/opencs/view/world/dialoguesubview.hpp | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index fdef57c41b..17b665d46f 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -363,7 +363,7 @@ void CSVWorld::EditWidget::remake(int row) mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); - this->setMinimumWidth(300); + this->setMinimumWidth(325); //TODO find better way to set the width this->setWidget(mMainWidget); this->setWidgetResizable(true); } @@ -385,7 +385,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mDocument(document) { - connect(mTable, SIGNAL(dataChanged ( const QModelIndex &, const QModelIndex &)), this, SLOT(dataChanged())); + connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); mRow = mTable->getModelIndex (id.getId(), 0).row(); QWidget *mainWidget = new QWidget(this); @@ -403,14 +403,12 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); - mMainLayout->addLayout(buttonsLayout); mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + dataChanged(mTable->index(mRow, 0)); setWidget(mainWidget); - - dataChanged(); } void CSVWorld::DialogueSubView::prevId() @@ -478,11 +476,6 @@ void CSVWorld::DialogueSubView::nextId() void CSVWorld::DialogueSubView::setEditLock (bool locked) { mLocked = locked; - mEditWidget->setDisabled(mLocked); -} - -void CSVWorld::DialogueSubView::dataChanged() -{ CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) { @@ -493,6 +486,21 @@ void CSVWorld::DialogueSubView::dataChanged() } } +void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index) +{ + if (index.row() == mRow) + { + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) + { + mEditWidget->setDisabled(true); + } else + { + mEditWidget->setDisabled(mLocked); + } + } +} + void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, const QModelIndex& index, const CSMWorld::UniversalId& id, diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 1c8aa5971d..c2e7da3382 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -179,7 +179,7 @@ namespace CSVWorld void prevId(); - void dataChanged(); + void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, From b2e3fa70c2e860dfba3061e6cf4d36e72add3a09 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 14 Mar 2014 07:04:39 +1100 Subject: [PATCH 135/240] Fix spelling errors in comments. --- apps/openmw/mwmechanics/aiwander.cpp | 4 ++-- apps/openmw/mwscript/transformationextensions.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 844eaf4900..a51092fea3 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -27,7 +27,7 @@ namespace namespace MWMechanics { - // NOTE: determined empherically but probably need further tweaking + // NOTE: determined empirically but probably need further tweaking static const int COUNT_BEFORE_STUCK = 20; static const int COUNT_BEFORE_RESET = 200; static const int COUNT_EVADE = 7; @@ -338,7 +338,7 @@ namespace MWMechanics } else { - // consider stuck only if position unchanges consequitively + // consider stuck only if position unchanges consecutively if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0) mWalkState = State_Evade; // NOTE: mStuckCount is purposely not cleared here diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 9a9175f191..1efc796436 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -99,7 +99,7 @@ namespace MWScript MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle); } else - throw std::runtime_error ("invalid ration axis: " + axis); + throw std::runtime_error ("invalid rotation axis: " + axis); } }; @@ -128,7 +128,7 @@ namespace MWScript runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()); } else - throw std::runtime_error ("invalid ration axis: " + axis); + throw std::runtime_error ("invalid rotation axis: " + axis); } }; @@ -157,7 +157,7 @@ namespace MWScript runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees()); } else - throw std::runtime_error ("invalid ration axis: " + axis); + throw std::runtime_error ("invalid rotation axis: " + axis); } }; @@ -186,7 +186,7 @@ namespace MWScript runtime.push(ptr.getRefData().getPosition().pos[2]); } else - throw std::runtime_error ("invalid rotation axis: " + axis); + throw std::runtime_error ("invalid axis: " + axis); } }; From e6977d00e812840dd6d8d8f9dba420db198999c7 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 15 Mar 2014 08:16:35 +1100 Subject: [PATCH 136/240] Oops. Fix typo picked up by Zini. --- apps/openmw/mwmechanics/aiwander.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index a51092fea3..4da325abd6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -391,8 +391,8 @@ namespace MWMechanics // update position ESM::Position updatedPos = actor.getRefData().getPosition(); - mPrevX = pos.pos[0]; - mPrevY = pos.pos[1]; + mPrevX = updatedPos.pos[0]; + mPrevY = updatedPos.pos[1]; } } From cd0c283795ae576db6d2f01fed3229576afba9fd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Mar 2014 15:14:59 +0100 Subject: [PATCH 137/240] Add text shadow to version text for better readability --- files/mygui/openmw_mainmenu.layout | 2 ++ 1 file changed, 2 insertions(+) diff --git a/files/mygui/openmw_mainmenu.layout b/files/mygui/openmw_mainmenu.layout index 2dc43f1b7e..e8cb23b773 100644 --- a/files/mygui/openmw_mainmenu.layout +++ b/files/mygui/openmw_mainmenu.layout @@ -5,6 +5,8 @@ + + From 3880247017dfb88aa286e32154125d5734068e9c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Mar 2014 20:28:48 +0100 Subject: [PATCH 138/240] Fixes #1206: effect texture override was not accounted for --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a0d06f0682..15d56b8a99 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1073,7 +1073,7 @@ float RenderingManager::getCameraDistance() const void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) { - mEffectManager->addEffect(model, "", worldPosition, scale); + mEffectManager->addEffect(model, texture, worldPosition, scale); } } // namespace From 93c21b5ef22ab3543b427775bf17a1f0010d6a3b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Mar 2014 20:34:12 +0100 Subject: [PATCH 139/240] Fixes #1197: incorrect mouse wheel step --- apps/openmw/mwgui/race.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 3dff1b7e41..499c1e191a 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -41,9 +41,13 @@ namespace MWGui getWidget(mPreviewImage, "PreviewImage"); getWidget(mHeadRotate, "HeadRotate"); - mHeadRotate->setScrollRange(50); - mHeadRotate->setScrollPosition(25); - mHeadRotate->setScrollViewPage(10); + + // Mouse wheel step is hardcoded to 50 in MyGUI 3.2 ("FIXME"). + // Give other steps the same value to accomodate. + mHeadRotate->setScrollRange(1000); + mHeadRotate->setScrollPosition(500); + mHeadRotate->setScrollViewPage(50); + mHeadRotate->setScrollPage(50); mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate); // Set up next/previous buttons @@ -171,9 +175,9 @@ namespace MWGui eventBack(); } - void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) + void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position) { - float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2; float diff = angle - mCurrentAngle; mPreview->update (diff); mPreviewDirty = true; From 61955111f1f6e10af7dc4d45de61cd90a0489548 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Mar 2014 20:48:54 +0100 Subject: [PATCH 140/240] Fixes #1204: Any health value < 1 should show as empty life bar. --- apps/openmw/mwgui/hud.cpp | 17 +++++++++++------ apps/openmw/mwgui/hud.hpp | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index a6ad43ce58..06a228a1fb 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -604,15 +604,22 @@ namespace MWGui mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop()); } + void HUD::updateEnemyHealthBar() + { + MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); + mEnemyHealth->setProgressRange(100); + // Health is usually cast to int before displaying. Actors die whenever they are < 1 health. + // Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :) + mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100); + } + void HUD::update() { mSpellIcons->updateWidgets(mEffectBox, true); if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) { - MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); - mEnemyHealth->setProgressRange(100); - mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); + updateEnemyHealthBar(); } if (mIsDrowning) @@ -629,9 +636,7 @@ namespace MWGui if (!mEnemyHealth->getVisible()) mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); mEnemyHealth->setVisible(true); - MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); - mEnemyHealth->setProgressRange(100); - mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); + updateEnemyHealthBar(); } } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 04206fbc88..6d1ffd03c1 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -112,6 +112,8 @@ namespace MWGui void onMagicClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender); + void updateEnemyHealthBar(); + void updatePositions(); }; } From d08394bf787a411e82dd58ca97a0538f52a737dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Mar 2014 21:00:25 +0100 Subject: [PATCH 141/240] Fixes #1205: apparently creature and hand-to-hand (health) attacks should also level up armor skill (tested in vanilla) --- apps/openmw/mwclass/npc.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 50e002113a..a1d2ef8481 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -673,12 +673,7 @@ namespace MWClass else getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? - if(object.isEmpty()) - { - if(ishealth) - damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); - } - else if(ishealth) + if(ishealth) { // Hit percentages: // cuirass = 30% From 43757efdc4af643aeedc99c43e170b324c062830 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 16 Mar 2014 08:09:14 +1100 Subject: [PATCH 142/240] Feature #1030 - partial fix to stop creatures unable to walk/fly to come out of water. Does not necessarily handle situations where they are already out of water, however. --- apps/openmw/mwclass/creature.cpp | 28 +++++++- apps/openmw/mwclass/creature.hpp | 5 +- apps/openmw/mwworld/class.cpp | 17 ++++- apps/openmw/mwworld/class.hpp | 5 +- apps/openmw/mwworld/physicssystem.cpp | 93 ++++++++++++++++++++------- apps/openmw/mwworld/worldimp.cpp | 2 +- 6 files changed, 120 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 33c4390a0a..c66bda09fa 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -525,7 +525,7 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && + else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && world->isLevitationEnabled())) { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + @@ -678,7 +678,15 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - bool Creature::isFlying(const MWWorld::Ptr &ptr) const + bool Creature::isBipedal(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Bipedal; + } + + bool Creature::canFly(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -686,6 +694,22 @@ namespace MWClass return ref->mBase->mFlags & ESM::Creature::Flies; } + bool Creature::canSwim(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Swims; + } + + bool Creature::canWalk(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Walks; + } + int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 6df6db297f..c1bcb8739e 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -124,7 +124,10 @@ namespace MWClass return true; } - virtual bool isFlying (const MWWorld::Ptr &ptr) const; + virtual bool isBipedal (const MWWorld::Ptr &ptr) const; + virtual bool canFly (const MWWorld::Ptr &ptr) const; + virtual bool canSwim (const MWWorld::Ptr &ptr) const; + virtual bool canWalk (const MWWorld::Ptr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 2110086d37..39d48f95b8 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -363,7 +363,22 @@ namespace MWWorld return newPtr; } - bool Class::isFlying(const Ptr &ptr) const + bool Class::isBipedal(const Ptr &ptr) const + { + return false; + } + + bool Class::canFly(const Ptr &ptr) const + { + return false; + } + + bool Class::canSwim(const Ptr &ptr) const + { + return false; + } + + bool Class::canWalk(const Ptr &ptr) const { return false; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ad2cc3af47..739fd5942e 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -307,7 +307,10 @@ namespace MWWorld return false; } - virtual bool isFlying(const MWWorld::Ptr& ptr) const; + virtual bool isBipedal(const MWWorld::Ptr& ptr) const; + virtual bool canFly(const MWWorld::Ptr& ptr) const; + virtual bool canSwim(const MWWorld::Ptr& ptr) const; + virtual bool canWalk(const MWWorld::Ptr& ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1657cf2670..1c68389396 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -116,8 +116,9 @@ namespace MWWorld const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); - /* Anything to collide with? */ OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); + + // If no need to check for collision simply return the new position. if(!physicActor || !physicActor->getCollisionMode()) { return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * @@ -125,36 +126,67 @@ namespace MWWorld * movement * time; } + /* Anything to collide with? */ btCollisionObject *colobj = physicActor->getCollisionBody(); Ogre::Vector3 halfExtents = physicActor->getHalfExtents(); - position.z += halfExtents.z; + Ogre::Vector3 newPosition = position; // FIXME: not sure if a copy is needed + newPosition.z += halfExtents.z; // NOTE: remember to restore before returning + float actorWaterlevel = waterlevel - halfExtents.z * 0.5; - waterlevel -= halfExtents.z * 0.5; + /* + * A 3/4 submerged example + * + * +---+ + * | | + * | | <- waterlevel + * | | + * | | <- newPosition <- actorWaterlevel + * | | + * | | + * | | + * +---+ <- position + */ OEngine::Physic::ActorTracer tracer; bool wasOnGround = false; bool isOnGround = false; Ogre::Vector3 inertia(0.0f); Ogre::Vector3 velocity; - if(position.z < waterlevel || isFlying) + + bool canWalk = ptr.getClass().canWalk(ptr); + bool isBipedal = ptr.getClass().isBipedal(ptr); + bool isNpc = ptr.getClass().isNpc(); + + //if(!canWalk && !isBipedal && !isNpc && (position.z >= waterlevel)) + //{ + //std::cout << "Swim Creature \""<()); + mDocumentManager.setResourceDir (mResources = variables["resources"].as()); mFsStrict = variables["fs-strict"].as(); @@ -225,6 +228,15 @@ int CS::Editor::run() if (mLocal.empty()) return 1; + mStartup.show(); + + QApplication::setQuitOnLastWindowClosed (true); + + return QApplication::exec(); +} + +std::auto_ptr CS::Editor::setupGraphics() +{ // TODO: setting Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); @@ -242,9 +254,36 @@ int CS::Editor::run() Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); - mStartup.show(); + sh::OgrePlatform* platform = + new sh::OgrePlatform ("General", (mResources / "materials").string()); - QApplication::setQuitOnLastWindowClosed (true); + if (!boost::filesystem::exists (mCfgMgr.getCachePath())) + boost::filesystem::create_directories (mCfgMgr.getCachePath()); - return QApplication::exec(); + platform->setCacheFolder (mCfgMgr.getCachePath().string()); + + std::auto_ptr factory (new sh::Factory (platform)); + + factory->setCurrentLanguage (sh::Language_GLSL); /// \todo make this configurable + factory->setWriteSourceCache (true); + factory->setReadSourceCache (true); + factory->setReadMicrocodeCache (true); + factory->setWriteMicrocodeCache (true); + + factory->loadAllFiles(); + + sh::Factory::getInstance().setGlobalSetting ("fog", "true"); + + sh::Factory::getInstance().setGlobalSetting ("shadows", "false"); + sh::Factory::getInstance().setGlobalSetting ("shadows_pssm", "false"); + + sh::Factory::getInstance ().setGlobalSetting ("render_refraction", "false"); + + sh::Factory::getInstance ().setGlobalSetting ("viewproj_fix", "false"); + + sh::Factory::getInstance ().setGlobalSetting ("num_lights", "8"); + + /// \todo add more configurable shiny settings + + return factory; } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index ec417ba8e0..164398fb73 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -1,11 +1,15 @@ #ifndef CS_EDITOR_H #define CS_EDITOR_H +#include + #include #include #include #include +#include + #ifndef Q_MOC_RUN #include #endif @@ -42,6 +46,7 @@ namespace CS CSVSettings::UserSettingsDialog mSettings; CSVDoc::FileDialog mFileDialog; boost::filesystem::path mLocal; + boost::filesystem::path mResources; bool mFsStrict; void setupDataFiles (const Files::PathContainer& dataDirs); @@ -63,6 +68,9 @@ namespace CS int run(); ///< \return error status + std::auto_ptr setupGraphics(); + ///< The returned factory must persist at least as long as *this. + private slots: void createGame(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 212ed08367..eded36394a 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #ifdef Q_OS_MAC @@ -42,6 +44,8 @@ int main(int argc, char *argv[]) OgreInit::OgreInit ogreInit; + std::auto_ptr shinyFactory; + Application application (argc, argv); #ifdef Q_OS_MAC @@ -73,5 +77,7 @@ int main(int argc, char *argv[]) // return 0; } + shinyFactory = editor.setupGraphics(); + return editor.run(); } diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp new file mode 100644 index 0000000000..160240b172 --- /dev/null +++ b/apps/opencs/view/render/previewwidget.cpp @@ -0,0 +1,45 @@ + +#include "previewwidget.hpp" + +#include + +#include "../../model/world/data.hpp" + +void CSVRender::PreviewWidget::setup (const std::string& id) +{ + setNavigation (&mOrbit); + + int column = mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); + + int row = mData.getReferenceables().getIndex (id); + + QVariant value = mData.getReferenceables().getData (row, column); + + if (!value.isValid()) + return; + + std::string model = value.toString().toUtf8().constData(); + + if (model.empty()) + return; + + Ogre::SceneNode* node = getSceneManager()->getRootSceneNode()->createChildSceneNode(); + node->setPosition (Ogre::Vector3 (0, 0, 0)); + + mObject = NifOgre::Loader::createObjects (node, "Meshes\\" + model); +} + +CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, + const std::string& referenceableId, QWidget *parent) +: SceneWidget (parent), mData (data) +{ + setup (referenceableId); +} + +CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, + const std::string& referenceableId, const std::string& referenceId, QWidget *parent) +: SceneWidget (parent), mData (data) +{ + setup (referenceableId); + /// \todo apply reference modifications (scale, rotation) +} diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp new file mode 100644 index 0000000000..01d40050fb --- /dev/null +++ b/apps/opencs/view/render/previewwidget.hpp @@ -0,0 +1,42 @@ +#ifndef OPENCS_VIEW_PREVIEWWIDGET_H +#define OPENCS_VIEW_PREVIEWWIDGET_H + +#include + +#include "scenewidget.hpp" + +#include "navigationorbit.hpp" + +namespace CSMWorld +{ + class Data; +} + +namespace CSVRender +{ + class PreviewWidget : public SceneWidget + { + Q_OBJECT + + const CSMWorld::Data& mData; + CSVRender::NavigationOrbit mOrbit; + NifOgre::ObjectScenePtr mObject; + + void setup (const std::string& id); + ///< \param id ID of the referenceable to be viewed + + public: + + PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, + QWidget *parent = 0); + + PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, + const std::string& referenceId, QWidget *parent = 0); + + signals: + + void closeRequest(); + }; +} + +#endif diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp new file mode 100644 index 0000000000..0fa1d3b174 --- /dev/null +++ b/apps/opencs/view/world/previewsubview.cpp @@ -0,0 +1,50 @@ + +#include "previewsubview.hpp" + +#include + +#include "../render/scenewidget.hpp" + +#include "scenetoolbar.hpp" + +#include "../render/previewwidget.hpp" + +CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) +: SubView (id) +{ + QHBoxLayout *layout = new QHBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + if (document.getData().getReferenceables().searchId (id.getId())==-1) + { + std::string referenceableId = + document.getData().getReferences().getRecord (id.getId()).get().mRefID; + + mScene = + new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); + } + else + mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), this); + + SceneToolbar *toolbar = new SceneToolbar (48, this); + + layout->addWidget (toolbar, 0); + + layout->addWidget (mScene, 1); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); + + connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); +} + +void CSVWorld::PreviewSubView::setEditLock (bool locked) {} + +void CSVWorld::PreviewSubView::closeRequest() +{ + deleteLater(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/previewsubview.hpp b/apps/opencs/view/world/previewsubview.hpp new file mode 100644 index 0000000000..e7a2a261ed --- /dev/null +++ b/apps/opencs/view/world/previewsubview.hpp @@ -0,0 +1,36 @@ +#ifndef CSV_WORLD_PREVIEWSUBVIEW_H +#define CSV_WORLD_PREVIEWSUBVIEW_H + +#include "../doc/subview.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSVRender +{ + class PreviewWidget; +} + +namespace CSVWorld +{ + class PreviewSubView : public CSVDoc::SubView + { + Q_OBJECT + + CSVRender::PreviewWidget *mScene; + + public: + + PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + + private slots: + + void closeRequest(); + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 74ce03cce6..33a91330df 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -16,6 +16,7 @@ #include "scenesubview.hpp" #include "dialoguecreator.hpp" #include "infocreator.hpp" +#include "previewsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -78,4 +79,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CreatorFactory >); manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); + + manager.add (CSMWorld::UniversalId::Type_Preview, new CSVDoc::SubViewFactory); } \ No newline at end of file From fa042a8aca3ca1ed8e11506763f8c6fd5987995f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Mar 2014 13:22:32 +0100 Subject: [PATCH 148/240] use proper title for reference prewview subviews (avoid internal ID) --- apps/opencs/view/render/previewwidget.hpp | 2 +- apps/opencs/view/world/previewsubview.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index 01d40050fb..c8834775b5 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -20,7 +20,7 @@ namespace CSVRender const CSMWorld::Data& mData; CSVRender::NavigationOrbit mOrbit; - NifOgre::ObjectScenePtr mObject; + NifOgre::ObjectScenePtr mObject; void setup (const std::string& id); ///< \param id ID of the referenceable to be viewed diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 0fa1d3b174..587af561fb 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -21,6 +21,8 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo std::string referenceableId = document.getData().getReferences().getRecord (id.getId()).get().mRefID; + setWindowTitle (("Preview: Reference to " + referenceableId).c_str()); + mScene = new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); } From b0a1e1198af8ba7bb526f61ac6c5eeef00dfe9de Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Mar 2014 16:30:17 +0100 Subject: [PATCH 149/240] Yet another terrain fix --- components/terrain/quadtreenode.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 21c1becb0a..a14fe1f842 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -282,9 +282,6 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) size_t wantedLod = 0; float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - if (!mTerrain->getDistantLandEnabled() && dist > cellWorldSize) - return true; - if (dist > cellWorldSize*64) wantedLod = 6; else if (dist > cellWorldSize*32) @@ -392,6 +389,8 @@ void QuadTreeNode::load(const LoadResponseData &data) mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mChunk->setCastShadows(true); + if (!mTerrain->getDistantLandEnabled()) + mChunk->setRenderingDistance(8192); mSceneNode->attachObject(mChunk); mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); From 4a0d1ba2729f6b7a4ffbbc289ea48f01f8172899 Mon Sep 17 00:00:00 2001 From: sirherrbatka Date: Sun, 16 Mar 2014 17:06:11 +0100 Subject: [PATCH 150/240] do not cast and check multiple times --- apps/opencs/view/world/dialoguesubview.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 17b665d46f..f0a4ac131e 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -117,17 +117,20 @@ QWidget* CSVWorld::DialogueDelegateDispatcherProxy::getEditor() const void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std::vector& data, const CSMDoc::Document* document) { + QLineEdit* lineEdit = qobject_cast(mEditor); + { + if (!lineEdit or !mIndexWrapper.get()) + { + return; + } + } for (unsigned i = 0; i < data.size(); ++i) { if (mDisplay == CSMWorld::TableMimeData::convertEnums(data[i].getType())) { - QLineEdit* lineEdit = qobject_cast(mEditor); - if (lineEdit && mIndexWrapper.get()) - { - emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); - emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); - break; - } + emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + break; } } } From 48ea93d8907e2a7ba22cd555e6f35bfdf0ffef4d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Mar 2014 17:06:30 +0100 Subject: [PATCH 151/240] less random camera start position --- apps/opencs/view/render/scenewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 7b790fd581..5a2d93385c 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -45,7 +45,7 @@ namespace CSVRender mCamera = mSceneMgr->createCamera("foo"); - mCamera->setPosition(300,300,300); + mCamera->setPosition(300,0,000); mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); mCamera->setFarClipDistance(3000); From 6b11265fbc0b4d78dcd89554a2af3472bb0fb9d4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Mar 2014 17:06:45 +0100 Subject: [PATCH 152/240] consider scale and orientation when previewing reference --- apps/opencs/view/render/previewwidget.cpp | 37 ++++++++++++++++++++--- apps/opencs/view/render/previewwidget.hpp | 4 +++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 160240b172..43d45f4cb9 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -23,15 +23,42 @@ void CSVRender::PreviewWidget::setup (const std::string& id) if (model.empty()) return; - Ogre::SceneNode* node = getSceneManager()->getRootSceneNode()->createChildSceneNode(); - node->setPosition (Ogre::Vector3 (0, 0, 0)); + mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(); + mNode->setPosition (Ogre::Vector3 (0, 0, 0)); - mObject = NifOgre::Loader::createObjects (node, "Meshes\\" + model); + mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); +} + +void CSVRender::PreviewWidget::adjust (const std::string& id) +{ + if (mNode) + { + int row = mData.getReferences().getIndex (id); + + float scale = mData.getReferences().getData (row, mData.getReferences(). + findColumnIndex (CSMWorld::Columns::ColumnId_Scale)).toFloat(); + float rotX = mData.getReferences().getData (row, mData.getReferences(). + findColumnIndex (CSMWorld::Columns::ColumnId_PositionXRot)).toFloat(); + float rotY = mData.getReferences().getData (row, mData.getReferences(). + findColumnIndex (CSMWorld::Columns::ColumnId_PositionYRot)).toFloat(); + float rotZ = mData.getReferences().getData (row, mData.getReferences(). + findColumnIndex (CSMWorld::Columns::ColumnId_PositionZRot)).toFloat(); + + mNode->setScale (scale, scale, scale); + + Ogre::Quaternion xr (Ogre::Radian(-rotX), Ogre::Vector3::UNIT_X); + + Ogre::Quaternion yr (Ogre::Radian(-rotY), Ogre::Vector3::UNIT_Y); + + Ogre::Quaternion zr (Ogre::Radian(-rotZ), Ogre::Vector3::UNIT_Z); + + mNode->setOrientation (xr*yr*zr); + } } CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, QWidget *parent) -: SceneWidget (parent), mData (data) +: SceneWidget (parent), mData (data), mNode (0) { setup (referenceableId); } @@ -41,5 +68,5 @@ CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, : SceneWidget (parent), mData (data) { setup (referenceableId); - /// \todo apply reference modifications (scale, rotation) + adjust (referenceId); } diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index c8834775b5..b3abd55879 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -21,10 +21,14 @@ namespace CSVRender const CSMWorld::Data& mData; CSVRender::NavigationOrbit mOrbit; NifOgre::ObjectScenePtr mObject; + Ogre::SceneNode *mNode; void setup (const std::string& id); ///< \param id ID of the referenceable to be viewed + void adjust (const std::string& id); + ///< \param id ID of the reference to be viewed + public: PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, From 3c8eeb8c48caf472d6c0d25c8b5de0165dcc9bbc Mon Sep 17 00:00:00 2001 From: sirherrbatka Date: Sun, 16 Mar 2014 17:11:13 +0100 Subject: [PATCH 153/240] added comment --- apps/opencs/view/world/dialoguesubview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index f0a4ac131e..2e77cf29e0 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -306,6 +306,7 @@ void CSVWorld::EditWidget::remake(int row) } mMainWidget = new QWidget (this); + //not sure if widget mapper can handle deleting the widgets that were mapped if (mWidgetMapper) { delete mWidgetMapper; From 20ea859aac470906a1410fbf0602e9a52fc2c656 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Mar 2014 17:14:44 +0100 Subject: [PATCH 154/240] adjusted the far clip distance --- apps/opencs/view/render/scenewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 5a2d93385c..5eec702d3d 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -48,7 +48,7 @@ namespace CSVRender mCamera->setPosition(300,0,000); mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); - mCamera->setFarClipDistance(3000); + mCamera->setFarClipDistance(30000); QTimer *timer = new QTimer (this); From 81dfc26d269587edf6be080acbf803db0b9cbdaf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Mar 2014 21:39:46 +0100 Subject: [PATCH 155/240] mwscript/locals was added twice --- apps/openmw/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 5e108edaf7..20011b0d99 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -44,7 +44,7 @@ add_openmw_dir (mwscript locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions guiextensions soundextensions skyextensions statsextensions containerextensions aiextensions controlextensions extensions globalscripts ref dialogueextensions - animationextensions transformationextensions consoleextensions userextensions locals + animationextensions transformationextensions consoleextensions userextensions ) add_openmw_dir (mwsound From 49ef976b3dcaaf1e86639b1730fe8cb7f400b377 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Mar 2014 23:30:45 +0100 Subject: [PATCH 156/240] Remove reference to no longer used font --- OFL.txt | 93 ------------------------------------------------------ readme.txt | 1 - 2 files changed, 94 deletions(-) delete mode 100644 OFL.txt diff --git a/OFL.txt b/OFL.txt deleted file mode 100644 index 043e85e83b..0000000000 --- a/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/readme.txt b/readme.txt index bb17a68e70..a054626ddb 100644 --- a/readme.txt +++ b/readme.txt @@ -8,7 +8,6 @@ License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org Font Licenses: -EBGaramond-Regular.ttf: OFL (see OFL.txt for more information) DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information) From c8c0e5de3870202d0b2be92c330d1e3289d6f249 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Mar 2014 23:38:51 +0100 Subject: [PATCH 157/240] Fixed code issues found with unity build. Missing include guards, duplicated functions, ... --- apps/openmw/mwclass/container.cpp | 14 +++++----- apps/openmw/mwclass/creature.cpp | 22 +++++++-------- apps/openmw/mwclass/creaturelevlist.cpp | 8 +++--- apps/openmw/mwclass/light.cpp | 16 +++++------ apps/openmw/mwclass/npc.cpp | 28 ++++++++++---------- apps/openmw/mwgui/alchemywindow.cpp | 12 +-------- apps/openmw/mwgui/container.cpp | 15 +---------- apps/openmw/mwgui/dialogue.cpp | 13 +-------- apps/openmw/mwgui/hud.hpp | 5 ++++ apps/openmw/mwgui/itemselection.hpp | 5 ++++ apps/openmw/mwgui/itemview.cpp | 24 +++++++---------- apps/openmw/mwgui/itemview.hpp | 2 ++ apps/openmw/mwgui/journalbooks.cpp | 9 ------- apps/openmw/mwgui/journalbooks.hpp | 9 +++++++ apps/openmw/mwgui/mainmenu.hpp | 5 ++++ apps/openmw/mwgui/quickkeysmenu.cpp | 22 --------------- apps/openmw/mwgui/spellwindow.cpp | 22 --------------- apps/openmw/mwgui/spellwindow.hpp | 19 +++++++++++++ apps/openmw/mwmechanics/aiactivate.cpp | 14 ++-------- apps/openmw/mwmechanics/aiescort.cpp | 14 ++-------- apps/openmw/mwmechanics/aitravel.cpp | 14 ++-------- apps/openmw/mwmechanics/aiwander.cpp | 10 ------- apps/openmw/mwmechanics/pathfinding.cpp | 7 ----- apps/openmw/mwmechanics/pathfinding.hpp | 16 +++++++++++ apps/openmw/mwscript/containerextensions.cpp | 8 +++--- apps/openmw/mwscript/globalscripts.cpp | 8 +++--- apps/openmw/mwscript/guiextensions.cpp | 4 +-- apps/openmw/mwscript/statsextensions.cpp | 20 +++++++------- apps/openmw/mwsound/ffmpeg_decoder.cpp | 7 ++--- apps/openmw/mwsound/ffmpeg_decoder.hpp | 2 ++ components/bsa/bsa_file.cpp | 2 +- components/compiler/declarationparser.cpp | 4 +-- components/interpreter/runtime.cpp | 2 +- 33 files changed, 154 insertions(+), 228 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 715784b7cf..604b51990a 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -28,16 +28,16 @@ namespace { - struct CustomData : public MWWorld::CustomData + struct ContainerCustomData : public MWWorld::CustomData { MWWorld::ContainerStore mContainerStore; virtual MWWorld::CustomData *clone() const; }; - MWWorld::CustomData *CustomData::clone() const + MWWorld::CustomData *ContainerCustomData::clone() const { - return new CustomData (*this); + return new ContainerCustomData (*this); } } @@ -47,7 +47,7 @@ namespace MWClass { if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data (new CustomData); + std::auto_ptr data (new ContainerCustomData); MWWorld::LiveCellRef *ref = ptr.get(); @@ -174,7 +174,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Container::getScript (const MWWorld::Ptr& ptr) const @@ -267,7 +267,7 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. readState (state2.mInventory); } @@ -278,7 +278,7 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. + dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore. writeState (state2.mInventory); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index c66bda09fa..0f7ffdc480 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -37,7 +37,7 @@ namespace { - struct CustomData : public MWWorld::CustomData + struct CreatureCustomData : public MWWorld::CustomData { MWMechanics::CreatureStats mCreatureStats; MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures @@ -45,13 +45,13 @@ namespace virtual MWWorld::CustomData *clone() const; - CustomData() : mContainerStore(0) {} - virtual ~CustomData() { delete mContainerStore; } + CreatureCustomData() : mContainerStore(0) {} + virtual ~CreatureCustomData() { delete mContainerStore; } }; - MWWorld::CustomData *CustomData::clone() const + MWWorld::CustomData *CreatureCustomData::clone() const { - CustomData* cloned = new CustomData (*this); + CreatureCustomData* cloned = new CreatureCustomData (*this); cloned->mContainerStore = mContainerStore->clone(); return cloned; } @@ -63,7 +63,7 @@ namespace MWClass { if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data (new CustomData); + std::auto_ptr data (new CreatureCustomData); static bool inited = false; if(!inited) @@ -192,7 +192,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; + return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } @@ -456,7 +456,7 @@ namespace MWClass { ensureCustomData (ptr); - return *dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return *dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const @@ -559,7 +559,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; } Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const @@ -786,7 +786,7 @@ namespace MWClass ensureCustomData (ptr); - CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); @@ -800,7 +800,7 @@ namespace MWClass ensureCustomData (ptr); - CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mContainerStore->writeState (state2.mInventory); customData.mCreatureStats.writeState (state2.mCreatureStats); diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index caef521afb..732038b2fc 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -9,15 +9,15 @@ namespace { - struct CustomData : public MWWorld::CustomData + struct CreatureLevListCustomData : public MWWorld::CustomData { // TODO: save the creature we spawned here virtual MWWorld::CustomData *clone() const; }; - MWWorld::CustomData *CustomData::clone() const + MWWorld::CustomData *CreatureLevListCustomData::clone() const { - return new CustomData (*this); + return new CreatureLevListCustomData (*this); } } @@ -44,7 +44,7 @@ namespace MWClass { if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data (new CustomData); + std::auto_ptr data (new CreatureLevListCustomData); MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 72de620e44..bd25b66b2c 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,12 +26,12 @@ namespace { - struct CustomData : public MWWorld::CustomData + struct LightCustomData : public MWWorld::CustomData { float mTime; ///< Time remaining - CustomData(MWWorld::Ptr ptr) + LightCustomData(MWWorld::Ptr ptr) { MWWorld::LiveCellRef *ref = ptr.get(); mTime = ref->mBase->mData.mTime; @@ -40,7 +40,7 @@ namespace virtual MWWorld::CustomData *clone() const { - return new CustomData (*this); + return new LightCustomData (*this); } }; } @@ -210,7 +210,7 @@ namespace MWClass { ensureCustomData(ptr); - float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; timeRemaining = duration; } @@ -218,7 +218,7 @@ namespace MWClass { ensureCustomData(ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; } MWWorld::Ptr @@ -233,7 +233,7 @@ namespace MWClass void Light::ensureCustomData (const MWWorld::Ptr& ptr) const { if (!ptr.getRefData().getCustomData()) - ptr.getRefData().setCustomData(new CustomData(ptr)); + ptr.getRefData().setCustomData(new LightCustomData(ptr)); } bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const @@ -278,7 +278,7 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; } void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -288,6 +288,6 @@ namespace MWClass ensureCustomData (ptr); - state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index a1d2ef8481..3fbd0d5b21 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -39,7 +39,7 @@ namespace { - struct CustomData : public MWWorld::CustomData + struct NpcCustomData : public MWWorld::CustomData { MWMechanics::NpcStats mNpcStats; MWMechanics::Movement mMovement; @@ -48,9 +48,9 @@ namespace virtual MWWorld::CustomData *clone() const; }; - MWWorld::CustomData *CustomData::clone() const + MWWorld::CustomData *NpcCustomData::clone() const { - return new CustomData (*this); + return new NpcCustomData (*this); } void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats) @@ -262,7 +262,7 @@ namespace MWClass } if (!ptr.getRefData().getCustomData()) { - std::auto_ptr data(new CustomData); + std::auto_ptr data(new NpcCustomData); MWWorld::LiveCellRef *ref = ptr.get(); @@ -436,14 +436,14 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; + return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; + return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } @@ -819,7 +819,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; } MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) @@ -827,7 +827,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; } std::string Npc::getScript (const MWWorld::Ptr& ptr) const @@ -841,7 +841,7 @@ namespace MWClass float Npc::getSpeed(const MWWorld::Ptr& ptr) const { const MWBase::World *world = MWBase::Environment::get().getWorld(); - const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); @@ -896,7 +896,7 @@ namespace MWClass float Npc::getJump(const MWWorld::Ptr &ptr) const { - const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + fJumpEncumbranceMultiplier->getFloat() * @@ -935,7 +935,7 @@ namespace MWClass if (fallHeight >= fallDistanceMin) { const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); - const CustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); + const NpcCustomData *npcdata = static_cast(ptr.getRefData().getCustomData()); const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); @@ -960,7 +960,7 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; } Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const @@ -1266,7 +1266,7 @@ namespace MWClass ensureCustomData (ptr); - CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mInventoryStore.readState (state2.mInventory); customData.mNpcStats.readState (state2.mNpcStats); @@ -1280,7 +1280,7 @@ namespace MWClass ensureCustomData (ptr); - CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); customData.mInventoryStore.writeState (state2.mInventory); customData.mNpcStats.writeState (state2.mNpcStats); diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index ddbd3f120d..a6880ffcb5 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -26,16 +26,6 @@ namespace return path; } - std::string getCountString(const int count) - { - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); - } - } namespace MWGui @@ -226,7 +216,7 @@ namespace MWGui text->setNeedMouseFocus(false); text->setTextShadow(true); text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(ingredient->getUserData()->getRefData().getCount())); + text->setCaption(ItemView::getCountString(ingredient->getUserData()->getRefData().getCount())); } mItemView->update(); diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 7e7ad5ec26..34ac8d9f4d 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -23,19 +23,6 @@ #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" -namespace -{ - std::string getCountString(const int count) - { - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); - } -} - namespace MWGui { @@ -79,7 +66,7 @@ namespace MWGui text->setNeedMouseFocus(false); text->setTextShadow(true); text->setTextShadowColour(MyGUI::Colour(0,0,0)); - text->setCaption(getCountString(count)); + text->setCaption(ItemView::getCountString(count)); sourceView->update(); diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 481c983142..6b913f24ae 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -23,18 +23,7 @@ #include "travelwindow.hpp" #include "bookpage.hpp" - -namespace -{ - MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) - { - typedef MWGui::BookTypesetter::Utf8Point point; - - point begin = reinterpret_cast (text); - - return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); - } -} +#include "journalbooks.hpp" // to_utf8_span namespace MWGui { diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 6d1ffd03c1..1645d8db01 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -1,3 +1,6 @@ +#ifndef OPENMW_GAME_MWGUI_HUD_H +#define OPENMW_GAME_MWGUI_HUD_H + #include "mapwindow.hpp" #include "../mwmechanics/stat.hpp" @@ -117,3 +120,5 @@ namespace MWGui void updatePositions(); }; } + +#endif diff --git a/apps/openmw/mwgui/itemselection.hpp b/apps/openmw/mwgui/itemselection.hpp index d6d19d9e1a..c9ec23cfae 100644 --- a/apps/openmw/mwgui/itemselection.hpp +++ b/apps/openmw/mwgui/itemselection.hpp @@ -1,3 +1,6 @@ +#ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H +#define OPENMW_GAME_MWGUI_ITEMSELECTION_H + #include "container.hpp" namespace MWGui @@ -32,3 +35,5 @@ namespace MWGui }; } + +#endif diff --git a/apps/openmw/mwgui/itemview.cpp b/apps/openmw/mwgui/itemview.cpp index f9a900ebac..027c3201fc 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -13,23 +13,19 @@ #include "itemmodel.hpp" -namespace -{ - std::string getCountString(const int count) - { - if (count == 1) - return ""; - if (count > 9999) - return boost::lexical_cast(int(count/1000.f)) + "k"; - else - return boost::lexical_cast(count); - } -} - - namespace MWGui { +std::string ItemView::getCountString(int count) +{ + if (count == 1) + return ""; + if (count > 9999) + return boost::lexical_cast(int(count/1000.f)) + "k"; + else + return boost::lexical_cast(count); +} + ItemView::ItemView() : mModel(NULL) , mScrollView(NULL) diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 17f609f2b8..74bc66ea0d 100644 --- a/apps/openmw/mwgui/itemview.hpp +++ b/apps/openmw/mwgui/itemview.hpp @@ -30,6 +30,8 @@ namespace MWGui void update(); + static std::string getCountString(int count); + private: virtual void initialiseOverride(); diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 8caea770ee..2682323cea 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -2,15 +2,6 @@ namespace { - MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) - { - typedef MWGui::BookTypesetter::Utf8Point point; - - point begin = reinterpret_cast (text); - - return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); - } - const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f); const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f); const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f); diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index 09d3cf1a85..b9c0a60b3c 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -6,6 +6,15 @@ namespace MWGui { + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) + { + typedef MWGui::BookTypesetter::Utf8Point point; + + point begin = reinterpret_cast (text); + + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); + } + struct JournalBooks { typedef TypesetBook::Ptr Book; diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 48b515d65b..722b329de1 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,3 +1,6 @@ +#ifndef OPENMW_GAME_MWGUI_MAINMENU_H +#define OPENMW_GAME_MWGUI_MAINMENU_H + #include #include "imagebutton.hpp" @@ -36,3 +39,5 @@ namespace MWGui }; } + +#endif diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index ff13ae1afa..ba4fdb86ab 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -20,28 +20,6 @@ #include "itemselection.hpp" -namespace -{ - bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) - { - int cmp = left.getClass().getName(left).compare( - right.getClass().getName(right)); - return cmp < 0; - } - - bool sortSpells(const std::string& left, const std::string& right) - { - const MWWorld::Store &spells = - MWBase::Environment::get().getWorld()->getStore().get(); - - const ESM::Spell* a = spells.find(left); - const ESM::Spell* b = spells.find(right); - - int cmp = a->mName.compare(b->mName); - return cmp < 0; - } -} - namespace MWGui { diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 6b261a799c..19bd2e939c 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -18,28 +18,6 @@ #include "inventorywindow.hpp" #include "confirmationdialog.hpp" -namespace -{ - bool sortSpells(const std::string& left, const std::string& right) - { - const MWWorld::Store &spells = - MWBase::Environment::get().getWorld()->getStore().get(); - - const ESM::Spell* a = spells.find(left); - const ESM::Spell* b = spells.find(right); - - int cmp = a->mName.compare(b->mName); - return cmp < 0; - } - - bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) - { - int cmp = MWWorld::Class::get(left).getName(left).compare( - MWWorld::Class::get(right).getName(right)); - return cmp < 0; - } -} - namespace MWGui { SpellWindow::SpellWindow(DragAndDrop* drag) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 38a7619318..4699cc4455 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -7,6 +7,25 @@ namespace MWGui { class SpellIcons; + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + int cmp = left.getClass().getName(left).compare( + right.getClass().getName(right)); + return cmp < 0; + } + + bool sortSpells(const std::string& left, const std::string& right) + { + const MWWorld::Store &spells = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Spell* a = spells.find(left); + const ESM::Spell* b = spells.find(right); + + int cmp = a->mName.compare(b->mName); + return cmp < 0; + } + class SpellWindow : public WindowPinnableBase, public NoDrop { public: diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index bdd7c8f3be..8610cf4b2d 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -10,16 +10,6 @@ #include "steering.hpp" #include "movement.hpp" -namespace -{ - float sgn(float a) - { - if(a > 0) - return 1.0; - return -1.0; - } -} - MWMechanics::AiActivate::AiActivate(const std::string &objectId) : mObjectId(objectId) { @@ -38,7 +28,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); + int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) @@ -49,7 +39,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) } if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); + int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index b54f1d39ca..f27fada39f 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -11,16 +11,6 @@ #include "steering.hpp" #include "movement.hpp" -namespace -{ - float sgn(float a) - { - if(a > 0) - return 1.0; - return -1.0; - } -} - /* TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0. TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z. @@ -91,7 +81,7 @@ namespace MWMechanics if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX); + int sideX = PathFinder::sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX); // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE / 2.0 - 200)) @@ -102,7 +92,7 @@ namespace MWMechanics } if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY); + int sideY = PathFinder::sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY); // Check if actor is near the border of an inactive cell. If so, pause walking. if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE / 2.0 - 200)) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 1fd719c608..c62c4e9702 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -9,16 +9,6 @@ #include "steering.hpp" #include "movement.hpp" -namespace -{ - float sgn(float a) - { - if(a > 0) - return 1.0; - return -1.0; - } -} - namespace MWMechanics { AiTravel::AiTravel(float x, float y, float z) @@ -43,7 +33,7 @@ namespace MWMechanics MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { - int sideX = sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); + int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) @@ -54,7 +44,7 @@ namespace MWMechanics } if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { - int sideY = sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); + int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 4da325abd6..2db875a01b 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -15,16 +15,6 @@ #include "steering.hpp" #include "movement.hpp" -namespace -{ - float sgn(float a) - { - if(a > 0) - return 1.0; - return -1.0; - } -} - namespace MWMechanics { // NOTE: determined empirically but probably need further tweaking diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 5314b5919c..3ecd407431 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -37,13 +37,6 @@ namespace return sqrt(x * x + y * y + z * z); } - static float sgn(Ogre::Radian a) - { - if(a.valueRadians() > 0) - return 1.0; - return -1.0; - } - int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) { if(!grid || grid->mPoints.empty()) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index 8771ef0cad..ecaaef568f 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace MWWorld { class CellStore; @@ -16,6 +18,20 @@ namespace MWMechanics public: PathFinder(); + static float sgn(Ogre::Radian a) + { + if(a.valueRadians() > 0) + return 1.0; + return -1.0; + } + + static float sgn(float a) + { + if(a > 0) + return 1.0; + return -1.0; + } + void clearPath(); void buildPathgridGraph(const ESM::Pathgrid* pathGrid); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index d489bfaf15..66c8d44687 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -121,7 +121,7 @@ namespace MWScript std::string itemName; for (MWWorld::ContainerStoreIterator iter(store.begin()); iter != store.end(); ++iter) - if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) + if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) itemName = iter->getClass().getName(*iter); int numRemoved = store.remove(item, count, ptr); @@ -165,7 +165,7 @@ namespace MWScript MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) + if (::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) break; } if (it == invStore.end()) @@ -268,7 +268,7 @@ namespace MWScript for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); - if (it != invStore.end() && Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) + if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) { runtime.push(1); return; @@ -295,7 +295,7 @@ namespace MWScript it != invStore.end(); ++it) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name)) + if (::Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name)) { runtime.push(1); return; diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 179e2bb0bb..527c576cc7 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -24,7 +24,7 @@ namespace MWScript void GlobalScripts::addScript (const std::string& name) { std::map >::iterator iter = - mScripts.find (Misc::StringUtils::lowerCase (name)); + mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) { @@ -44,7 +44,7 @@ namespace MWScript void GlobalScripts::removeScript (const std::string& name) { std::map >::iterator iter = - mScripts.find (Misc::StringUtils::lowerCase (name)); + mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter!=mScripts.end()) iter->second.first = false; @@ -53,7 +53,7 @@ namespace MWScript bool GlobalScripts::isRunning (const std::string& name) const { std::map >::const_iterator iter = - mScripts.find (Misc::StringUtils::lowerCase (name)); + mScripts.find (::Misc::StringUtils::lowerCase (name)); if (iter==mScripts.end()) return false; @@ -151,7 +151,7 @@ namespace MWScript Locals& GlobalScripts::getLocals (const std::string& name) { - std::string name2 = Misc::StringUtils::lowerCase (name); + std::string name2 = ::Misc::StringUtils::lowerCase (name); std::map >::iterator iter = mScripts.find (name2); diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index ab8901881b..57fc2d4707 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -113,7 +113,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); - Misc::StringUtils::toLower(cell); + ::Misc::StringUtils::toLower(cell); runtime.pop(); // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." @@ -126,7 +126,7 @@ namespace MWScript for (; it != cells.extEnd(); ++it) { std::string name = it->mName; - Misc::StringUtils::toLower(name); + ::Misc::StringUtils::toLower(name); if (name.find(cell) != std::string::npos) MWBase::Environment::get().getWindowManager()->addVisitedLocation ( it->mName, diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c4f672dc76..80467f58ad 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -540,7 +540,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -569,7 +569,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -602,7 +602,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); @@ -640,7 +640,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::toLower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { @@ -742,7 +742,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); runtime.push ( @@ -778,7 +778,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value); @@ -813,7 +813,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - Misc::StringUtils::toLower (factionId); + ::Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, @@ -858,11 +858,11 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); std::string race = runtime.getStringLiteral(runtime[0].mInteger); - Misc::StringUtils::toLower(race); + ::Misc::StringUtils::toLower(race); runtime.pop(); std::string npcRace = ptr.get()->mBase->mRace; - Misc::StringUtils::toLower(npcRace); + ::Misc::StringUtils::toLower(npcRace); runtime.push (npcRace == race); } @@ -906,7 +906,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - Misc::StringUtils::toLower(factionID); + ::Misc::StringUtils::toLower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); if(factionID!="") { diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index c836974425..c595de5aec 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -11,9 +11,10 @@ namespace MWSound { -static void fail(const std::string &msg) -{ throw std::runtime_error("FFmpeg exception: "+msg); } - +void FFmpeg_Decoder::fail(const std::string &msg) +{ + throw std::runtime_error("FFmpeg exception: "+msg); +} int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) { diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index d0d73379d3..8276b45c77 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -61,6 +61,8 @@ namespace MWSound virtual void rewind(); virtual size_t getSampleOffset(); + void fail(const std::string &msg); + FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs); diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 8db4fa8888..25b006fb3e 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -111,7 +111,7 @@ void BSAFile::readHeader() fail("Directory information larger than entire archive"); // Read the offset info into a temporary buffer - vector offsets(3*filenum); + std::vector offsets(3*filenum); input.read(reinterpret_cast(&offsets[0]), 12*filenum); // Read the string table diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp index d17f49caf0..7961b8f411 100644 --- a/components/compiler/declarationparser.cpp +++ b/components/compiler/declarationparser.cpp @@ -18,7 +18,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke { if (mState==State_Name) { - std::string name2 = Misc::StringUtils::lowerCase (name); + std::string name2 = ::Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); @@ -80,4 +80,4 @@ bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, S void Compiler::DeclarationParser::reset() { mState = State_Begin; -} \ No newline at end of file +} diff --git a/components/interpreter/runtime.cpp b/components/interpreter/runtime.cpp index bb0dffb87f..c71aef95c5 100644 --- a/components/interpreter/runtime.cpp +++ b/components/interpreter/runtime.cpp @@ -50,7 +50,7 @@ namespace Interpreter return literalBlock+offset; } - void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context) + void Runtime::configure (const Type_Code *code, int codeSize, Context& context) { clear(); From 9089df30807e8479b7a87aeba31ee6bce21f6a4d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Mar 2014 23:40:59 +0100 Subject: [PATCH 158/240] Add unity build option Each component and each MW-subsystem will be used as a single compilation unit. --- CMakeLists.txt | 2 + cmake/OpenMWMacros.cmake | 85 +++++++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 954e161a1d..154f30161b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE) +option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE) + # Apps and tools option(BUILD_BSATOOL "build BSA extractor" OFF) option(BUILD_ESMTOOL "build ESM inspector" ON) diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index f66dbf2c47..c5669fa70c 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -1,26 +1,75 @@ +function(enable_unity_build UB_SUFFIX SOURCE_VARIABLE_NAME) + set(files ${SOURCE_VARIABLE_NAME}) + # Generate a unique filename for the unity build translation unit + set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${UB_SUFFIX}.cpp) + # Exclude all translation units from compilation + set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true) + # Open the ub file + FILE(WRITE ${unit_build_file} "// Unity Build generated by CMake\n") + # Add include statement for each translation unit + foreach(source_file ${files} ) + FILE( APPEND ${unit_build_file} "#include <${source_file}>\n") + endforeach(source_file) + # Complement list of translation units with the name of ub + set(${SOURCE_VARIABLE_NAME} ${${SOURCE_VARIABLE_NAME}} ${unit_build_file} PARENT_SCOPE) +endfunction(enable_unity_build) + + macro (add_openmw_dir dir) -set (files) -foreach (u ${ARGN}) -file (GLOB ALL "${dir}/${u}.[ch]pp") -foreach (f ${ALL}) -list (APPEND files "${f}") -list (APPEND OPENMW_FILES "${f}") -endforeach (f) -endforeach (u) -source_group ("apps\\openmw\\${dir}" FILES ${files}) + set (files) + set (cppfiles) + foreach (u ${ARGN}) + + # Add cpp and hpp to OPENMW_FILES + file (GLOB ALL "${dir}/${u}.[ch]pp") + foreach (f ${ALL}) + list (APPEND files "${f}") + list (APPEND OPENMW_FILES "${f}") + endforeach (f) + + # Add cpp to unity build + file (GLOB ALL "${dir}/${u}.cpp") + foreach (f ${ALL}) + list (APPEND cppfiles "${f}") + endforeach (f) + + endforeach (u) + + if (OPENMW_UNITY_BUILD) + enable_unity_build(${dir} "${cppfiles}") + list (APPEND OPENMW_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp) + endif() + + source_group ("apps\\openmw\\${dir}" FILES ${files}) endmacro (add_openmw_dir) macro (add_component_dir dir) -set (files) -foreach (u ${ARGN}) -file (GLOB ALL "${dir}/${u}.[ch]pp") -foreach (f ${ALL}) -list (APPEND files "${f}") -list (APPEND COMPONENT_FILES "${f}") -endforeach (f) -endforeach (u) -source_group ("components\\${dir}" FILES ${files}) + set (files) + set (cppfiles) + + foreach (u ${ARGN}) + file (GLOB ALL "${dir}/${u}.[ch]pp") + + foreach (f ${ALL}) + list (APPEND files "${f}") + list (APPEND COMPONENT_FILES "${f}") + endforeach (f) + + # Add cpp to unity build + file (GLOB ALL "${dir}/${u}.cpp") + foreach (f ${ALL}) + list (APPEND cppfiles "${f}") + endforeach (f) + + endforeach (u) + + if (OPENMW_UNITY_BUILD) + enable_unity_build(${dir} "${cppfiles}") + list (APPEND COMPONENT_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp) + endif() + + source_group ("components\\${dir}" FILES ${files}) endmacro (add_component_dir) macro (add_component_qt_dir dir) From 4443e22387611bea47a7050a7df35ddf364fa738 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 17 Mar 2014 13:21:28 +0100 Subject: [PATCH 159/240] Build fix --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 ++ apps/openmw/mwgui/spellwindow.cpp | 20 ++++++++++++++++++++ apps/openmw/mwgui/spellwindow.hpp | 20 +++----------------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index ba4fdb86ab..4c0faeac1f 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -19,6 +19,8 @@ #include "windowmanagerimp.hpp" #include "itemselection.hpp" +#include "spellwindow.hpp" + namespace MWGui { diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index 19bd2e939c..b052739bda 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -20,6 +20,26 @@ namespace MWGui { + + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) + { + int cmp = left.getClass().getName(left).compare( + right.getClass().getName(right)); + return cmp < 0; + } + + bool sortSpells(const std::string& left, const std::string& right) + { + const MWWorld::Store &spells = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Spell* a = spells.find(left); + const ESM::Spell* b = spells.find(right); + + int cmp = a->mName.compare(b->mName); + return cmp < 0; + } + SpellWindow::SpellWindow(DragAndDrop* drag) : WindowPinnableBase("openmw_spell_window.layout") , NoDrop(drag, mMainWidget) diff --git a/apps/openmw/mwgui/spellwindow.hpp b/apps/openmw/mwgui/spellwindow.hpp index 4699cc4455..53eed1ba14 100644 --- a/apps/openmw/mwgui/spellwindow.hpp +++ b/apps/openmw/mwgui/spellwindow.hpp @@ -2,29 +2,15 @@ #define MWGUI_SPELLWINDOW_H #include "windowpinnablebase.hpp" +#include "../mwworld/ptr.hpp" namespace MWGui { class SpellIcons; - bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right) - { - int cmp = left.getClass().getName(left).compare( - right.getClass().getName(right)); - return cmp < 0; - } + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right); - bool sortSpells(const std::string& left, const std::string& right) - { - const MWWorld::Store &spells = - MWBase::Environment::get().getWorld()->getStore().get(); - - const ESM::Spell* a = spells.find(left); - const ESM::Spell* b = spells.find(right); - - int cmp = a->mName.compare(b->mName); - return cmp < 0; - } + bool sortSpells(const std::string& left, const std::string& right); class SpellWindow : public WindowPinnableBase, public NoDrop { From 128a47570e07e7d862f3f59bb66f8153e34481e0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 17 Mar 2014 14:15:52 +0100 Subject: [PATCH 160/240] One more fix --- apps/openmw/mwgui/journalbooks.cpp | 9 +++++++++ apps/openmw/mwgui/journalbooks.hpp | 9 +-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/journalbooks.cpp b/apps/openmw/mwgui/journalbooks.cpp index 2682323cea..683fe9208f 100644 --- a/apps/openmw/mwgui/journalbooks.cpp +++ b/apps/openmw/mwgui/journalbooks.cpp @@ -169,6 +169,15 @@ namespace namespace MWGui { +MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) +{ + typedef MWGui::BookTypesetter::Utf8Point point; + + point begin = reinterpret_cast (text); + + return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); +} + typedef TypesetBook::Ptr book; JournalBooks::JournalBooks (JournalViewModel::Ptr model) : diff --git a/apps/openmw/mwgui/journalbooks.hpp b/apps/openmw/mwgui/journalbooks.hpp index b9c0a60b3c..819bda0fd2 100644 --- a/apps/openmw/mwgui/journalbooks.hpp +++ b/apps/openmw/mwgui/journalbooks.hpp @@ -6,14 +6,7 @@ namespace MWGui { - MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text) - { - typedef MWGui::BookTypesetter::Utf8Point point; - - point begin = reinterpret_cast (text); - - return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text)); - } + MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text); struct JournalBooks { From 09ba54763024059bc179d06ffafd89d23a520eec Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 17 Mar 2014 15:35:36 +0100 Subject: [PATCH 161/240] use tool buttons with icons --- apps/opencs/view/world/dialoguesubview.cpp | 12 ++++++++---- files/opencs/go-next.png | Bin 0 -> 930 bytes files/opencs/go-previous.png | Bin 0 -> 955 bytes files/opencs/resources.qrc | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 files/opencs/go-next.png create mode 100644 files/opencs/go-previous.png diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 2e77cf29e0..660bb305fb 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "../../model/world/columnbase.hpp" #include "../../model/world/idtable.hpp" @@ -394,10 +395,13 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM QWidget *mainWidget = new QWidget(this); QHBoxLayout *buttonsLayout = new QHBoxLayout; - QPushButton* prevButton = new QPushButton(tr("Previous"), mainWidget); - QPushButton* nextButton = new QPushButton(tr("Next"), mainWidget); - buttonsLayout->addWidget(prevButton); - buttonsLayout->addWidget(nextButton); + QToolButton* prevButton = new QToolButton(mainWidget); + prevButton->setIcon(QIcon(":/go-previous.png")); + QToolButton* nextButton = new QToolButton(mainWidget); + nextButton->setIcon(QIcon(":/go-next.png")); + buttonsLayout->addWidget(prevButton, 0); + buttonsLayout->addWidget(nextButton, 1); + buttonsLayout->addStretch(2); connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); diff --git a/files/opencs/go-next.png b/files/opencs/go-next.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3f65d33d303315f0d8d9dd03c7e7e2427d4950 GIT binary patch literal 930 zcmV;T16}-yP)b zm0M^XRTPH5HM8gBV(mpKDJa$_Z|#GH3N?w)f>s1UMXmZGQl%7pX-P>7f}&tiq(1aT zP)ljdLx@@mDPl`Bh9;&enie%Q#!J+svBm_`ljO{0&t5*9IdzgY(T8-w&&-B3-`fA$ zi`k;8T#fLq3ibbj8=Hf!`X=+>l7rQ;d2QV-?>9CVb}uOyV8_ERRIj^vt-Jr}^J`7#mgi;JV)-V&XlM@pz)^Qubt9p3w))KZP8v6Cty-~c zRl~=9ZT*|}%9hiw>&TVBk{`E*J%R4u*)k_V)uAX(9bH4M2p)H=X<%%8)E(<;`P*81 z@8<{N{{IBOvg2J&{nVa86~&Rd6sHcSmeg5NwcJ*BHyV2!{p9dWDJ(v9>J8g5$L0J= zsk213L}W|EX-H&4k~AVoLt+~ery;Q|@y+iixR{RdYHa50=@ zc;Z`>a*MNG;dVYeab$LCdg4%TZMthga6bIN!w)=6m3OItX9Q0?*00)%Kf9c^j>EIlQ#1Qc zA57l?;H7zB5ee#>%FD+$>_Y(0NEvPf-v}qi-lVpshC8o+i1CprJ$AfhX7ZQl+3(t2 zH&6>05s6eaF&WeV1%>gq+V-!$Z0APe-Os&PsP>n$@w!Jj_x-uF^Rtr^!@atFw9gFz zHT*jWDk5oR$3Ecq9kAr&;el~2&q-4lO(_RF6KKCrtkj_?;y0348J zvVd8%$N30LAs>C(5f2Uy_MAE5UP~ip7D;y%aK4(KDFy_}Bhr4nHU9O7(L-n7bFEo! zk}1x<%d6Z9Rkb2Q1|nz80wzuLs~`JES_Y1~?yQi@K`QVBC;_3W>YRUtl<7g%+ko=a zR2)dofzERo0gb zl}%_|RTze!@0@e*&7V^HBN&Y|1&xTHg2tGcEZQg$sloamid{(QrYma`p)R6`8xe$x zE=)JAVs%lCB0?>;8x67IczQ|E_9Y~NZ8{U=yn5|bNt zZ{FN-`>T5f542e0(zO_A*m+=BT65yYyKmi^zpwkzroe@3VKxTmpO<#Qo$9}PN9Ubg z_Xgiie@@TlL5!nPUT}-K{lP!##V{MR`u1A(tAcYQ;T_gAofzD<^P1~6-C!rCJ_VE{ zO?Y9~Tg^rcf*`V-pKddeGBNcDMl99@d@*?#BMu`5 zYb=66M96P>ym1t{;b7mot2ey7qkmV+`Nb)IK6?gh0-Om*H6=|GtZ^8z_|#xB$4MHa zD6$$jKjIFyZ|rzxApcm)!!tZo00$ zhq5n{niALwE`qSx617;GS}jp)^&B)CQmI@XtfQKK{hHtR*X)JiW5*8{*M}YSbUs87 z1lYjfLPHRlDtrlQago7TL4k#H%e?#y8Bg9ja)0mkw!Y3Mr7-(3 zYAQUqh^+o!nj9Hs){H z=}ylcLsNMD&@1I~F^*P_E-*KL76XJLlGGgAOs=NizD)j@|COP{pFB8!alR|pGZbnf z#AOW%WB!5iMHYZ^rc$=oSgzquXnvZ7!rRH-LgDnu&rXb&7mKAv<@GDAIZiV9I`7ewn;fJhF#0jl@vQT*g_QU!iIgs_I1~i>lhOu8{-W_eYbl%HYh8>o&{o zBxWuf-PgfmU=b*(s!IKhkt#g^qHK3{6G$e_R6k`ID61~NDss8UR75P*@7&g@Hcf4! d1W0rh$lr11Sh~C<&aD6d002ovPDHLkV1hiw#cluq literal 0 HcmV?d00001 diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 2b1e65ff03..fb3c1eb3e9 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -57,6 +57,8 @@ static.png weapon.png multitype.png + go-next.png + go-previous.png raster/startup/big/create-addon.png raster/startup/big/new-game.png raster/startup/big/edit-content.png From 7296b09357b2aa3bfec4ed2a375404a96b3130bd Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 17 Mar 2014 19:18:18 +0100 Subject: [PATCH 162/240] revert button --- apps/opencs/view/world/dialoguesubview.cpp | 43 ++++++++++++++++++++++ apps/opencs/view/world/dialoguesubview.hpp | 2 + 2 files changed, 45 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 660bb305fb..8f602aa8d3 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -27,6 +27,7 @@ #include "../../model/world/record.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/doc/document.hpp" +#include "../../model/world/commands.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -402,9 +403,22 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM buttonsLayout->addWidget(prevButton, 0); buttonsLayout->addWidget(nextButton, 1); buttonsLayout->addStretch(2); + + QToolButton* cloneButton = new QToolButton(mainWidget); + QToolButton* addButton = new QToolButton(mainWidget); + QToolButton* deleteButton = new QToolButton(mainWidget); + QToolButton* revertButton = new QToolButton(mainWidget); + + buttonsLayout->addWidget(cloneButton); + buttonsLayout->addWidget(addButton); + buttonsLayout->addWidget(deleteButton); + buttonsLayout->addWidget(revertButton); + connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); + connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord())); + mMainLayout = new QVBoxLayout(mainWidget); mEditWidget = new EditWidget(mainWidget, mRow, mTable, mUndoStack, false); @@ -519,3 +533,32 @@ void CSVWorld::DialogueSubView::tableMimeDataDropped(QWidget* editor, qobject_cast(editor)->setText(id.getId().c_str()); } } + +void CSVWorld::DialogueSubView::revertRecord() +{ + int rows = mTable->rowCount(); + if (!mLocked && mTable->columnCount() > 0 && mRow < mTable->rowCount() ) + { + CSMWorld::RecordBase::State state = + static_cast (mTable->data (mTable->index (mRow, 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + mUndoStack.push(new CSMWorld::RevertCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); + } + if (rows != mTable->rowCount()) + { + if (mTable->rowCount() == 0) + { + mEditWidget->setDisabled(true); + return; + } + if (mRow >= mTable->rowCount()) + { + prevId(); + } else { + dataChanged(mTable->index(mRow, 0)); + } + } + } +} \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index c2e7da3382..b7920a3874 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -179,6 +179,8 @@ namespace CSVWorld void prevId(); + void revertRecord(); + void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record From 95afca0558c732af60a2fb5c4b5589185f6d7a26 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 17 Mar 2014 19:28:41 +0100 Subject: [PATCH 163/240] delete record button --- apps/opencs/view/world/dialoguesubview.cpp | 26 +++++++++++++++++++++- apps/opencs/view/world/dialoguesubview.hpp | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 8f602aa8d3..6bd8001c7a 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -418,6 +418,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord())); + connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteRecord())); mMainLayout = new QVBoxLayout(mainWidget); @@ -550,7 +551,30 @@ void CSVWorld::DialogueSubView::revertRecord() { if (mTable->rowCount() == 0) { - mEditWidget->setDisabled(true); + mEditWidget->setDisabled(true); //closing the editor is other option + return; + } + if (mRow >= mTable->rowCount()) + { + prevId(); + } else { + dataChanged(mTable->index(mRow, 0)); + } + } + } +} + +void CSVWorld::DialogueSubView::deleteRecord() +{ + int rows = mTable->rowCount(); + if (!mLocked && mTable->columnCount() > 0 && mRow < mTable->rowCount() ) + { + mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); + if (rows != mTable->rowCount()) + { + if (mTable->rowCount() == 0) + { + mEditWidget->setDisabled(true); //closing the editor is other option return; } if (mRow >= mTable->rowCount()) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index b7920a3874..bf6e70e0d3 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -180,6 +180,7 @@ namespace CSVWorld void prevId(); void revertRecord(); + void deleteRecord(); void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record From 5f71843e2c6afd111b57fee3d8e7a52f7033b344 Mon Sep 17 00:00:00 2001 From: pvdk Date: Tue, 18 Mar 2014 00:00:13 +0100 Subject: [PATCH 164/240] Updated credits.txt --- credits.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/credits.txt b/credits.txt index 6012557638..28a30e907e 100644 --- a/credits.txt +++ b/credits.txt @@ -82,12 +82,14 @@ Sandy Carter (bwrsandman) - Arch Linux Public Relations and Translations: +Alex McKibben (WeirdSexy) - Podcaster Artem Kotsynyak (greye) - Russian News Writer +Jim Clauwaert (Zedd) - Public Outreach Julien Voisin (jvoisin/ap0) - French News Writer +Lukasz Gromanowski (lgro) - English News Writer Mickey Lyle (raevol) - Release Manager Pithorn - Chinese News Writer -sir_herrbatka - English/Polish News Writer -Alex McKibben (WeirdSexy) - Podcaster +sir_herrbatka - Polish News Writer Website: From db774b02d75116b60e5116b3553be0391cb908e4 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 18 Mar 2014 09:36:22 +0100 Subject: [PATCH 165/240] clone and create buttons work --- apps/opencs/view/world/dialoguesubview.cpp | 38 ++++++++++-- apps/opencs/view/world/dialoguesubview.hpp | 12 +++- apps/opencs/view/world/subviews.cpp | 72 ++++++++++++++++------ 3 files changed, 99 insertions(+), 23 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6bd8001c7a..1692cdf80b 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -31,6 +31,7 @@ #include "recordstatusdelegate.hpp" #include "util.hpp" +#include "tablebottombox.hpp" /* ==============================NotEditableSubDelegate========================================== */ @@ -379,7 +380,7 @@ void CSVWorld::EditWidget::remake(int row) */ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) : + const CreatorFactoryBase& creatorFactory, bool sorting) : SubView (id), mEditWidget(0), @@ -416,7 +417,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); - + connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); connect(revertButton, SIGNAL(clicked()), this, SLOT(revertRecord())); connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteRecord())); @@ -428,7 +429,21 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout->addLayout(buttonsLayout); mMainLayout->addWidget(mEditWidget); - mEditWidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + mMainLayout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this)); + + mBottom->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + connect(mBottom, SIGNAL(requestFocus(const std::string&)), this, SLOT(requestFocus(const std::string&))); + connect(addButton, SIGNAL(clicked()), mBottom, SLOT(createRequest())); + + if(!mBottom->canCreateAndDelete()) + { + cloneButton->setDisabled(true); + addButton->setDisabled(true); + deleteButton->setDisabled(true); + } dataChanged(mTable->index(mRow, 0)); setWidget(mainWidget); @@ -567,7 +582,10 @@ void CSVWorld::DialogueSubView::revertRecord() void CSVWorld::DialogueSubView::deleteRecord() { int rows = mTable->rowCount(); - if (!mLocked && mTable->columnCount() > 0 && mRow < mTable->rowCount() ) + if (!mLocked && + mTable->columnCount() > 0 && + mRow < mTable->rowCount() && + mBottom->canCreateAndDelete()) { mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); if (rows != mTable->rowCount()) @@ -585,4 +603,16 @@ void CSVWorld::DialogueSubView::deleteRecord() } } } +} + +void CSVWorld::DialogueSubView::requestFocus (const std::string& id) +{ + mRow = mTable->getModelIndex (id, 0).row(); + mEditWidget->remake(mRow); +} + +void CSVWorld::DialogueSubView::cloneRequest () +{ + mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toStdString(), + static_cast(mTable->data(mTable->index(mRow, 2)).toInt())); } \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index bf6e70e0d3..af07d28f4c 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -29,6 +29,8 @@ namespace CSMDoc namespace CSVWorld { class CommandDelegate; + class CreatorFactoryBase; + class TableBottomBox; class NotEditableSubDelegate : public QAbstractItemDelegate { @@ -166,10 +168,14 @@ namespace CSVWorld int mRow; bool mLocked; const CSMDoc::Document& mDocument; + TableBottomBox* mBottom; public: - DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete = false); + DialogueSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document, + const CreatorFactoryBase& creatorFactory, + bool sorting = false); virtual void setEditLock (bool locked); @@ -182,12 +188,16 @@ namespace CSVWorld void revertRecord(); void deleteRecord(); + void cloneRequest(); + void dataChanged(const QModelIndex & index); ///\brief we need to care for deleting currently edited record void tableMimeDataDropped(QWidget* editor, const QModelIndex& index, const CSMWorld::UniversalId& id, const CSMDoc::Document* document); + + void requestFocus (const std::string& id); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index d4d9565a4a..f647a9261f 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -79,22 +79,58 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Region, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Spell, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Referenceable, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Birthsign, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Global, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Gmst, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Race, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Class, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Filter, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Sound, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Faction, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Skill, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_JournalInfo, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_TopicInfo, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactory); - manager.add(CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactory); + //edit subviews + manager.add (CSMWorld::UniversalId::Type_Region, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Spell, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Referenceable, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Birthsign, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Global, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Gmst, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Race, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Class, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Reference, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Cell, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Filter, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Sound, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Faction, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Skill, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_JournalInfo, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_TopicInfo, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Topic, + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Journal, + new CSVDoc::SubViewFactoryWithCreator > (false)); } \ No newline at end of file From c0b2b783cb5cb235c5c0b575e656c2ae008aba50 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 18 Mar 2014 20:53:21 +0100 Subject: [PATCH 166/240] use more proper creators --- apps/opencs/view/world/subviews.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index f647a9261f..6c832e179d 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -87,7 +87,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Referenceable, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Birthsign, new CSVDoc::SubViewFactoryWithCreator > (false)); @@ -108,7 +108,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Cell, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Filter, new CSVDoc::SubViewFactoryWithCreator > (false)); @@ -123,14 +123,14 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_JournalInfo, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_TopicInfo, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator(false)); manager.add (CSMWorld::UniversalId::Type_Topic, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Journal, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator (false)); } \ No newline at end of file From 010160d04793616eacc294eb9e821101609ef128 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 18 Mar 2014 20:56:55 +0100 Subject: [PATCH 167/240] missed those :/ --- apps/opencs/view/world/subviews.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 6c832e179d..116f1997b7 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -105,7 +105,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Reference, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactoryWithCreator > (false)); @@ -126,10 +126,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_TopicInfo, - new CSVDoc::SubViewFactoryWithCreator(false)); + new CSVDoc::SubViewFactoryWithCreator >(false)); manager.add (CSMWorld::UniversalId::Type_Topic, - new CSVDoc::SubViewFactoryWithCreator > (false)); + new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Journal, new CSVDoc::SubViewFactoryWithCreator (false)); From 8f3103ce655d5089396fbaeb94c3c1c924115961 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 19 Mar 2014 11:42:43 +0100 Subject: [PATCH 168/240] added preview button --- apps/opencs/view/world/dialoguesubview.cpp | 13 +++++++++++++ apps/opencs/view/world/dialoguesubview.hpp | 3 +++ apps/opencs/view/world/table.cpp | 1 + 3 files changed, 17 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 1692cdf80b..9f86a8ce38 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -405,16 +405,21 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM buttonsLayout->addWidget(nextButton, 1); buttonsLayout->addStretch(2); + QToolButton* previewButton = new QToolButton(mainWidget); QToolButton* cloneButton = new QToolButton(mainWidget); QToolButton* addButton = new QToolButton(mainWidget); QToolButton* deleteButton = new QToolButton(mainWidget); QToolButton* revertButton = new QToolButton(mainWidget); + previewButton->setEnabled(mTable->hasPreview()); + + buttonsLayout->addWidget(previewButton); buttonsLayout->addWidget(cloneButton); buttonsLayout->addWidget(addButton); buttonsLayout->addWidget(deleteButton); buttonsLayout->addWidget(revertButton); + connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); @@ -615,4 +620,12 @@ void CSVWorld::DialogueSubView::cloneRequest () { mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toStdString(), static_cast(mTable->data(mTable->index(mRow, 2)).toInt())); +} + +void CSVWorld::DialogueSubView::showPreview () +{ + if (mTable->hasPreview()) + { + emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toStdString()), ""); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index af07d28f4c..8a96e3ec0d 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -185,7 +185,10 @@ namespace CSVWorld void prevId(); + void showPreview(); + void revertRecord(); + void deleteRecord(); void cloneRequest(); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4ce18ce68e..cd9138c357 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -442,6 +442,7 @@ void CSVWorld::Table::previewRecord() emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , ""); } } + void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) { int columns = mModel->columnCount(); From ea9b6fa37e265ac7b896f204f42fa321f962b14f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 19 Mar 2014 12:01:36 +0100 Subject: [PATCH 169/240] view button --- apps/opencs/view/world/dialoguesubview.cpp | 30 ++++++++++++++++++---- apps/opencs/view/world/dialoguesubview.hpp | 2 ++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 9f86a8ce38..53e53e0e67 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -405,21 +405,30 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM buttonsLayout->addWidget(nextButton, 1); buttonsLayout->addStretch(2); - QToolButton* previewButton = new QToolButton(mainWidget); QToolButton* cloneButton = new QToolButton(mainWidget); QToolButton* addButton = new QToolButton(mainWidget); QToolButton* deleteButton = new QToolButton(mainWidget); QToolButton* revertButton = new QToolButton(mainWidget); - previewButton->setEnabled(mTable->hasPreview()); + if (mTable->hasPreview()) + { + QToolButton* previewButton = new QToolButton(mainWidget); + buttonsLayout->addWidget(previewButton); + connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); + } + + if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None) + { + QToolButton* viewButton = new QToolButton(mainWidget); + buttonsLayout->addWidget(viewButton); + connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); + } - buttonsLayout->addWidget(previewButton); buttonsLayout->addWidget(cloneButton); buttonsLayout->addWidget(addButton); buttonsLayout->addWidget(deleteButton); buttonsLayout->addWidget(revertButton); - connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); connect(nextButton, SIGNAL(clicked()), this, SLOT(nextId())); connect(prevButton, SIGNAL(clicked()), this, SLOT(prevId())); connect(cloneButton, SIGNAL(clicked()), this, SLOT(cloneRequest())); @@ -624,8 +633,19 @@ void CSVWorld::DialogueSubView::cloneRequest () void CSVWorld::DialogueSubView::showPreview () { - if (mTable->hasPreview()) + if (mTable->hasPreview() && mRow < mTable->rowCount()) { emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toStdString()), ""); } +} + +void CSVWorld::DialogueSubView::viewRecord() +{ + if (mRow < mTable->rowCount()) + { + std::pair params = mTable->view (mRow); + + if (params.first.getType()!=CSMWorld::UniversalId::Type_None) + emit focusId (params.first, params.second); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 8a96e3ec0d..5642f46a0b 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -187,6 +187,8 @@ namespace CSVWorld void showPreview(); + void viewRecord(); + void revertRecord(); void deleteRecord(); From 7ed3effc92ef92b945cc980983a53b56103da47e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Wed, 19 Mar 2014 12:01:53 +0100 Subject: [PATCH 170/240] Fix windows packaging issue now that the OFL license doesn't exist anymore. --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 154f30161b..fb3827588b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,7 +465,6 @@ if(WIN32) INSTALL(FILES "${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/GPL3.txt" - "${OpenMW_SOURCE_DIR}/OFL.txt" "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" From e4c4747a27b9597cfb3c9489cbd74e9fc9fd3d76 Mon Sep 17 00:00:00 2001 From: Rohit Nirmal Date: Wed, 19 Mar 2014 20:08:01 -0500 Subject: [PATCH 171/240] Play item sound even if merchant doesn't purchase it. --- apps/openmw/mwgui/inventorywindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 5fa1d3bb16..16ddab584a 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -162,6 +162,7 @@ namespace MWGui } const ItemStack& item = mTradeModel->getItem(index); + std::string sound = MWWorld::Class::get(item.mBase).getDownSoundId(item.mBase); MWWorld::Ptr object = item.mBase; int count = item.mCount; @@ -213,6 +214,7 @@ namespace MWGui int services = MWBase::Environment::get().getWindowManager()->getTradeWindow()->getMerchantServices(); if (!MWWorld::Class::get(object).canSell(object, services)) { + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()-> messageBox("#{sBarterDialog4}"); return; From 1acd1bd913c8dc68483c8ce82c0a55678fd13219 Mon Sep 17 00:00:00 2001 From: slothlife Date: Thu, 20 Mar 2014 01:25:52 -0500 Subject: [PATCH 172/240] Feature #1173: Saved Game: include weather state Removed some unused state in and changed Ogre::String to std::string in WeatherManager. --- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/weather.cpp | 77 ++++++++++++++++++++++--- apps/openmw/mwworld/weather.hpp | 37 +++++++----- apps/openmw/mwworld/worldimp.cpp | 4 +- components/esm/defs.hpp | 1 + 5 files changed, 93 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d6309c1c9f..c0860b7849 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -266,6 +266,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_GLOB: case ESM::REC_PLAY: case ESM::REC_CSTA: + case ESM::REC_WTHR: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 5f38ee2863..b74ca63935 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,5 +1,8 @@ #include "weather.hpp" +#include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -15,6 +18,15 @@ using namespace Ogre; using namespace MWWorld; using namespace MWSound; +#define HOUR "HOUR" +#define WINDSPEED "WNSP" +#define CURRENTWEATHER "CWTH" +#define NEXTWEATHER "NWTH" +#define CURRENTREGION "CREG" +#define FIRSTUPDATE "FUPD" +#define REMAININGTRANSITIONTIME "RTTM" +#define TIMEPASSED "TMPS" + namespace { float lerp (float x, float y, float factor) @@ -91,8 +103,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering,MWWorld::Fa mHour(14), mCurrentWeather("clear"), mNextWeather(""), mFirstUpdate(true), mWeatherUpdateTime(0), mThunderFlash(0), mThunderChance(0), mThunderChanceNeeded(50), mThunderSoundDelay(0), mRemainingTransitionTime(0), - mMonth(0), mDay(0), mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), - mRendering(rendering) + mTimePassed(0), mFallback(fallback), mWindSpeed(0.f), mRendering(rendering) { //Globals mThunderSoundID0 = mFallback->getFallbackString("Weather_Thunderstorm_Thunder_Sound_ID_0"); @@ -530,7 +541,7 @@ void WeatherManager::stopSounds(bool stopAll) } } -Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const +std::string WeatherManager::nextWeather(const ESM::Region* region) const { std::vector probability; @@ -599,12 +610,6 @@ void WeatherManager::setHour(const float hour) mHour = hour; } -void WeatherManager::setDate(const int day, const int month) -{ - mDay = day; - mMonth = month; -} - unsigned int WeatherManager::getWeatherID() const { // Source: http://www.uesp.net/wiki/Tes3Mod:GetCurrentWeather @@ -691,6 +696,60 @@ bool WeatherManager::isDark() const return exterior && (mHour < mSunriseTime || mHour > mNightStart - 1); } +void WeatherManager::write(ESM::ESMWriter& writer) +{ + writer.startRecord(ESM::REC_WTHR); + writer.writeHNT(HOUR, mHour); + writer.writeHNT(WINDSPEED, mWindSpeed); + writer.writeHNCString(CURRENTWEATHER, mCurrentWeather.c_str()); + writer.writeHNCString(NEXTWEATHER, mNextWeather.c_str()); + writer.writeHNCString(CURRENTREGION, mCurrentRegion.c_str()); + writer.writeHNT(FIRSTUPDATE, mFirstUpdate); + writer.writeHNT(REMAININGTRANSITIONTIME, mRemainingTransitionTime); + writer.writeHNT(TIMEPASSED, mTimePassed); + writer.endRecord(ESM::REC_WTHR); +} + +bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) +{ + if(ESM::REC_WTHR == type) + { + // store state in locals so that if we fail to load, we don't leave the manager in a half-way state + float newHour = 0.0; + reader.getHNT(newHour, HOUR); + float newWindSpeed = 0.0; + reader.getHNT(newWindSpeed, WINDSPEED); + std::string newCurrentWeather = reader.getHNString(CURRENTWEATHER); + std::string newNextWeather = reader.getHNString(NEXTWEATHER); + std::string newCurrentRegion = reader.getHNString(CURRENTREGION); + bool newFirstUpdate = false; + reader.getHNT(newFirstUpdate, FIRSTUPDATE); + float newRemainingTransitionTime = 0.0; + reader.getHNT(newRemainingTransitionTime, REMAININGTRANSITIONTIME); + double newTimePassed = 0.0; + reader.getHNT(newTimePassed, TIMEPASSED); + + // reset other temporary state + mRegionOverrides.clear(); + stopSounds(true); // TODO: inconsistent state if this throws... + mRegionMods.clear(); + + // swap in new values, now that we can't fail + mHour = newHour; + mWindSpeed = newWindSpeed; + mCurrentWeather.swap(newCurrentWeather); + mNextWeather.swap(newNextWeather); + mCurrentRegion.swap(newCurrentRegion); + mFirstUpdate = newFirstUpdate; + mRemainingTransitionTime = newRemainingTransitionTime; + mTimePassed = newTimePassed; + + return true; + } + + return false; +} + void WeatherManager::switchToNextWeather(bool instantly) { MWBase::World* world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index fa2d9bd8e1..3167a64c2a 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -1,12 +1,16 @@ #ifndef GAME_MWWORLD_WEATHER_H #define GAME_MWWORLD_WEATHER_H -#include +#include +#include + #include namespace ESM { struct Region; + class ESMWriter; + class ESMReader; } namespace MWRender @@ -21,8 +25,8 @@ namespace MWWorld /// Defines the actual weather that results from weather setting (see below), time of day and weather transition struct WeatherResult { - Ogre::String mCloudTexture; - Ogre::String mNextCloudTexture; + std::string mCloudTexture; + std::string mNextCloudTexture; float mCloudBlendFactor; Ogre::ColourValue mFogColor; @@ -48,14 +52,14 @@ namespace MWWorld bool mNight; // use night skybox float mNightFade; // fading factor for night skybox - Ogre::String mAmbientLoopSoundID; + std::string mAmbientLoopSoundID; }; /// Defines a single weather setting (according to INI) struct Weather { - Ogre::String mCloudTexture; + std::string mCloudTexture; // Sky (atmosphere) colors Ogre::ColourValue mSkySunriseColor, @@ -105,10 +109,10 @@ namespace MWWorld // Sound effect // This is used for Blight, Ashstorm and Blizzard (Bloodmoon) - Ogre::String mAmbientLoopSoundID; + std::string mAmbientLoopSoundID; // Rain sound effect - Ogre::String mRainLoopSoundID; + std::string mRainLoopSoundID; /// \todo disease chance }; @@ -142,8 +146,6 @@ namespace MWWorld float getWindSpeed() const; - void setDate(const int day, const int month); - void advanceTime(double hours) { mTimePassed += hours*3600; @@ -156,22 +158,25 @@ namespace MWWorld /// @see World::isDark bool isDark() const; + void write(ESM::ESMWriter& writer); + + bool readRecord(ESM::ESMReader& reader, int32_t type); + private: float mHour; - int mDay, mMonth; float mWindSpeed; MWWorld::Fallback* mFallback; void setFallbackWeather(Weather& weather,const std::string& name); MWRender::RenderingManager* mRendering; - std::map mWeatherSettings; + std::map mWeatherSettings; std::map mRegionOverrides; std::vector mSoundsPlaying; - Ogre::String mCurrentWeather; - Ogre::String mNextWeather; + std::string mCurrentWeather; + std::string mNextWeather; std::string mCurrentRegion; @@ -186,13 +191,13 @@ namespace MWWorld double mTimePassed; // time passed since last update void transition(const float factor); - void setResult(const Ogre::String& weatherType); + void setResult(const std::string& weatherType); float calculateHourFade (const std::string& moonName) const; float calculateAngleFade (const std::string& moonName, float angle) const; - void setWeather(const Ogre::String& weatherType, bool instant=false); - Ogre::String nextWeather(const ESM::Region* region) const; + void setWeather(const std::string& weatherType, bool instant=false); + std::string nextWeather(const ESM::Region* region) const; WeatherResult mResult; typedef std::map > RegionModMap; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 62bdd38ea1..81afc394a6 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -279,6 +279,7 @@ namespace MWWorld mGlobalVariables.write (writer); mCells.write (writer); mPlayer->write (writer); + mWeatherManager->write (writer); } void World::readRecord (ESM::ESMReader& reader, int32_t type, @@ -287,6 +288,7 @@ namespace MWWorld if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && + !mWeatherManager->readRecord (reader, type) && !mCells.readRecord (reader, type, contentFileMap)) { throw std::runtime_error ("unknown record in saved game"); @@ -680,8 +682,6 @@ namespace MWWorld mGlobalVariables["month"].setInteger (month); mRendering->skySetDate (day, month); - - mWeatherManager->setDate (day, month); } void World::setMonth (int month) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 57842796f5..4d5b36c740 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -92,6 +92,7 @@ enum RecNameInts REC_CSTA = 0x41545343, REC_GMAP = 0x50414d47, REC_DIAS = 0x53414944, + REC_WTHR = 0x52485457, // format 1 REC_FILT = 0x544C4946 From ad2f9a69a1aaf2abd4a42d170cec472f1cf9feba Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 08:59:04 +0100 Subject: [PATCH 173/240] prevent deleting already deleted record --- apps/opencs/view/world/dialoguesubview.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 53e53e0e67..5c718ad7f8 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -596,9 +596,15 @@ void CSVWorld::DialogueSubView::revertRecord() void CSVWorld::DialogueSubView::deleteRecord() { int rows = mTable->rowCount(); + + //easier than disabling the button + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); + bool deledetedOrErased = (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased); + if (!mLocked && mTable->columnCount() > 0 && - mRow < mTable->rowCount() && + !deledetedOrErased && + mRow < rows && mBottom->canCreateAndDelete()) { mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); From 5c34823bb077ac4ffaeb947d345a2169b4114c90 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 13:59:00 +0100 Subject: [PATCH 174/240] added some placeholder icons --- apps/opencs/view/world/dialoguesubview.cpp | 6 ++++++ files/opencs/add.png | Bin 0 -> 520 bytes files/opencs/edit-clone.png | Bin 0 -> 472 bytes files/opencs/edit-delete.png | Bin 0 -> 680 bytes files/opencs/edit-preview.png | Bin 0 -> 525 bytes files/opencs/edit-undo.png | Bin 0 -> 650 bytes files/opencs/go-next.png | Bin 930 -> 676 bytes files/opencs/go-previous.png | Bin 955 -> 655 bytes files/opencs/resources.qrc | 5 +++++ 9 files changed, 11 insertions(+) create mode 100644 files/opencs/add.png create mode 100644 files/opencs/edit-clone.png create mode 100644 files/opencs/edit-delete.png create mode 100644 files/opencs/edit-preview.png create mode 100644 files/opencs/edit-undo.png diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 5c718ad7f8..4962acfce6 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -406,13 +406,18 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM buttonsLayout->addStretch(2); QToolButton* cloneButton = new QToolButton(mainWidget); + cloneButton->setIcon(QIcon(":/edit-clone.png")); QToolButton* addButton = new QToolButton(mainWidget); + addButton->setIcon(QIcon(":/add.png")); QToolButton* deleteButton = new QToolButton(mainWidget); + deleteButton->setIcon(QIcon(":/edit-delete.png")); QToolButton* revertButton = new QToolButton(mainWidget); + revertButton->setIcon(QIcon(":/edit-undo.png")); if (mTable->hasPreview()) { QToolButton* previewButton = new QToolButton(mainWidget); + previewButton->setIcon(QIcon(":/edit-preview.png")); buttonsLayout->addWidget(previewButton); connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); } @@ -420,6 +425,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None) { QToolButton* viewButton = new QToolButton(mainWidget); + viewButton->setIcon(QIcon(":/cell.png")); buttonsLayout->addWidget(viewButton); connect(viewButton, SIGNAL(clicked()), this, SLOT(viewRecord())); } diff --git a/files/opencs/add.png b/files/opencs/add.png new file mode 100644 index 0000000000000000000000000000000000000000..3f1347e2452820b6687dc1128f6e9e8d9f2ef827 GIT binary patch literal 520 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf2?p zUk71ECym(^Ktah8*NBqf{Irtt#G+J&^73-M%)IR4Bl#B&opTdp z4dzN(xKt@=ZBuK4^6eIr=!mY~Z(P&F>KZg8SQK47IP4Z}Q&aoFvAg>HxriT1Cq$i> zNzE{BFMg&tL-KCRn^n>cn?#MW+bwgG_!)#weLP?GY_qIpN6DL9ot~9ncLs{SKy>c|Kn;tJhp&ZSDqn*2mhvEt5|uD;+)KvAc7@PoW)K)3}>7 zB%zi?r<_YaegC>6EtPzOSmRfMHzpLaUS0 zZ5iIOxMiEfImLv%W90DAR9MhiljuO*(b|p7p1FMbhW2n*Y09zyE_Tsb;OjL!;ZExMuKl^>bP0 Hl+XkK6MoQ? literal 0 HcmV?d00001 diff --git a/files/opencs/edit-clone.png b/files/opencs/edit-clone.png new file mode 100644 index 0000000000000000000000000000000000000000..3398e8d18265c6944d605cd3fbb6c6b02597119e GIT binary patch literal 472 zcmV;}0Vn>6P)ZSXqf8r_B|Y z&f3OC(MALdv9OU;Hlm!RSXgNzB!ytW(%9UQTogI=ZQkxnVvz(l$*yLanK$pBpP7H& zGx{gjE3{$R<0Zo{F96mSXWlGc-uxYSC+flF?Xxdn_*?gzQs zvpNSrNjns7GCUUk02K_=q$|=#@4$CL9bo_#R`y#708TRx^xD^q4SgXtKMjD^np7&q z*=3r%HORzJ#J_{Ob?SSf(OHtoBvMK&%Oa6TkWQx?aArMd7R#iJ5CSPB*^e=7+r}_j zH)%ABv$Hyfy820vI!C!sRA8%`cdD`7J<7<`K~)3q1F4hssB^}DT|oISCHD^P6mZ=f zZZU^^x@UL$bz6ZNK#0oI7FzwsQI9xI)zcBD?CII`D|IzMIheaat3U$X7J=^wx&%}d zSU(7b{vkvj!Vef9B=XsQpTPJWXKy3kpng$7sW#Q8uhCbmx3Jx>?X?$ht)1;%?%O#4 O0000&~9SKOY%f0nX^lS0%q#YP1;Cv`C~wifvVWH7q5i>VkBIIeP6 z?s|6)au-91QGBQO`{sE*@B4fS?A7A^!aG@(#_<=$akkoxxl9xQXmcpb()eup8Wcsb zgoa^snGM6hljX9thz?EX=KSV+MNuFK0-`7)n@QnL;d#!A@UhW?AP8tQ8h?ynYKO=6BACOv97OzXrAtf@ z2O)x1X8|H-V>nD%fY=%S{{Wx9i2r094LW{B44uo@Kw#`p>kX`0Uo?09s&V;JTzyn7 zDU*{^%-Y&}CK{d4s?`b^i%n^@T9ulJ#`L$9H{{~N1F9)O#I0o&NqHSeDc6cDqz8 zK1}bwAaOHui(g)OtONMGwY4emZr<;5I_X`d&8)9~I0*nN&z?HdsoA#LQO@rNluJuR z`2EL^Bopb;WFp!lvI6;RN#5=* z4F5rJ!QSPQfg+p*9+AZi4BWyX%*Zfnjs#GUy~NYkmHies6E_bB&t#W1Kp~G3*N775 z{M_8syb=bDWK9Jl0|PS!uf*)qB!%FT)XcopA}+4vqSVBa)D(sC%#sX+l8jUZcTe8{ z1uH9sjFOT9D}DX+%-jOK{GxQ_{}B!h42));E{-7CVLZPN@n~aG&Ys+HJt9Jx+odjH(FI|Z8R`B=8%G_}@Q?qi} zKSsxRi-PkzzyG#uuYPdeyHfb}Mz%)F1QCP%^3RQ0XYXU^k7Ryo$Gdo^&%$@QJQe41 zi#^Ta`>{e!`ZSm9`+4vr?gKS2DO*2^U{>lFJfR}s(W{*@X*2x@gv+C>cX=# z)-ukRE0G!RbKd9EN6n12ANze7uk2#DSyK1+%bq5~wi~aM4({+Z$YZ`xRe99hrp2(W z?YEEFT>a9&eH|_?OOmXo?Z^uj+t!zIc-NGzbB!-{GOvug-}N>^N$I$f(7T;(f}Tv? z)xUS&-WxW-_qjiVyJqQq;Sa`~6BuXRSC#+3x20iG(5i#`t-i1_bQNB&`VuoA7%L2( Lu6{1-oD!M<@FT~7 literal 0 HcmV?d00001 diff --git a/files/opencs/edit-undo.png b/files/opencs/edit-undo.png new file mode 100644 index 0000000000000000000000000000000000000000..8b0fef9a8319452009247864c41e033694859278 GIT binary patch literal 650 zcmV;50(Jd~P)kSC&LBJ zfVRcM$?V1P3B6PcAX~#mZX7Dr0E-up6>u&Ai1M4+$6px*d6g)50zul{6fOZfD5 zx^(<(+NuITAZTdtF*`7sbB~Mx0Ae$D>fg&k;5u>{h0Ft(%sL2>YF!TkK|`BOGW-0q z$<55rD9F$#2!Mm7ygZedyZydz;2gN<0Fly!zty-=mBaVpcsEVu>hcW{d1#Z{npnGY zBY*fjmK0%?8^t(;YR@+XG=oH^Fxc%lEVS$r0NiT$nf{@pT|4*Ae2zV-96O_3`)`13 k>$j#pJbyuT?P=Bf1qBx3B`dxp6aWAK07*qoM6N<$g68)pi~s-t literal 0 HcmV?d00001 diff --git a/files/opencs/go-next.png b/files/opencs/go-next.png index 6f3f65d33d303315f0d8d9dd03c7e7e2427d4950..6ef8de76e0f5bf01c09da24a07c61cfe558d7a4b 100644 GIT binary patch delta 634 zcmV-=0)_pe2c!idiBL{Q4GJ0x0000DNk~Le0000G0000G2nGNE03Y-JVUZy>e*!j1 zL_t(Ijg^v1NK|1Q#ed)9&cjB-6jH=qL?CDs1clQ^Xwf2q1Y1UdLb%Ctj5b0mH;s^s zC~^}Ny`)8AK5!8&X3#056gSZ#EHj<4fga;{@7(*f2wyW3)Pb}4{)fZ)pZ|w3hJ1#* zbgZ#cw-uY^Q&I%Rqj7ijfxd8;e>D(}SfOH?H54@fxYE)aJbCo&X#}(3h}H1l21;t0 z4xjP0H(##}`a=`ph}Bk1CmJLAD~6q09u?V}?Sp}-&tp`T?ZYRx@p$~!%7^)xVPiNS zi#k6y3_7*xxp*Hb6iO(BkO%|-iBCg#xRm*Ku&cbD`1H6lHaW0>F{ffte`f|j1Grv} z*vDrmsZm0K5C{Q60m5V1o+G_9&%wGQR!!BO+NWc8C&Ce{BNlb~H9*c=C7oHocC*-S z7Ns<$C1sQZLijB|M!48sj(1C=)P(9pYjao0(5pv%$FEw)G{Dl2Io>BSu?9kCluf4;V$ zgEcrgo5WONb$WJc(c2WcptHXvs3@<@eEXgnHZB)7NB*Y-LK0t2r|!j~g^}~|8$sR) Uv~G6gwg3PC07*qoM6N<$f}Qpxc>n+a delta 890 zcmV-=1BLvg1)>KbiBL{Q4GJ0x0000DNk~Le0000M0000M2nGNE0K~LxJCPwbe*-j0 zL_t(IjkT3qXdP7)hQBql=j3ATMJXvL)+cZ6gM|t;iO_;p1VKfu`XW-L6ntq(NehCa zU{Rz#^hHohY0N{2S_&y*OEiWirYf2iH8jRc)TFV-1k;n`%w^ABKAbsqk~YzYbivQe zhBe>X|JsY$qN-et@U9B=|AHHve}k_2CiCEugVnKlZQU*JH#Qe`FDV#c$HOmFue*8U ztM}|RZ*SWs{*r-xKj4wg+iNzhyZ`C)Yfa~t=VjSq`6j<;Xb%3sQFmE&BcXG)`po%G z8aHgMTCr?Z!^eGX{hRj6mea56$d$m7AGd}*f$rbgGABXRp(surT|=!1e;#+NX<%%8 z)E(<;`P*81@8<{N{{IBOvg2J&{nVa86~&Rd6sHcSmeg5NwcJ*BHyV2!{p9dWDJ(v9 z>J8g5$L0J=sk213L}W|EX-H&4k~AVoLt+~ery;Q|@y+iixR{Rdx?DQcqfQxHUMSP-1JXbv=}@-T(6 zJR^9%1uV`fx2~*bP2qMvJaJ@pYI@>OZ*973L2y3&z{3wbOqF-3e}HEMPdwJI+KNBB zoVJd`v(r;E`%WKB-vQvId0-I<>YK{T$2aUl0MAGnZUo;5C&%8Twx)(VuYZW~ktscP zyk%zcm+0B=+FdtL3m6fJR5dXf)BpvA@weLcufA;OM&jMiy;!LBm$UJ@M>+TXxwP}M zlM}(^&B57sEKH&EquYRUtl<7g%+ko=aR2)dofzERo0ge*z&% zL_t(IjfIm>NEC4t$G>l8ezQBfQetea8?{0R5eb5oZd-@kB39E(L?Va|C3Lc)ExLM% z5Cc2J9whM4B?^MDZXG07ijEZ?B1`vBxoncV?ac1X@AvC4nXZiPgLis=_`L6X4^m3h z@}oDkEAd-e#w(^_3yY^%9T6s@f8pk?g&&^{uN-BI;ua7-ojly%b8Xui)eNX&j06fK*uV6q8legr6j(T!&0k zi9>yj9S1wv*Iyq{u=3DUhFDV{h!hY22|!8#AqDQFFSrEp6dMW}+aL6wf4JnYuWz9D zi?6_?1E%N@l4EB68Hfl3f`qGnd$iXb;n;2VsHt)P`R?O`My|Agci-NCF&}hI2Ui3v zQGjU{P_$N1wo6b*g`Wk{x;wF^0p~U>`wvX#H%c-OZ~=##s0b5Wa!4-09SO%4;Ep?T zu9$V#I5TQr&Mtkq{`h6ue}wImt$P!YdKp45fdv46K!qMq!3+h=uRztPTF3Pwl7gwW zmRPvGEvC;EUL#+bhmgSO$PI{p6x?eHgfRMW1er0LHs*Ssa%Rk)JFqL2j^{6)W@l&L zmdoXeS6a0OUbZ??!?sW|znlwr5!8I;TJQW9ZSypA?8?gt00000NkvXXu0mjf6K)rf delta 915 zcmV;E18n?{1-l0!iBL{Q4GJ0x0000DNk~Le0000M0000M2nGNE0K~LxJCPwbe*;ZP zL_t(IjkT3cXk1kohM(`8bMMWcQu`wqjWh*~h@gVTn3*ivC=sc_`X7p2Na?04YZIX^ zqKF$2go-XqH?3lIQH>%(Ew&pKBAqHIh(VNC(}8mwWCJRpm-7b493t`#;6KfBS<05e|fHzW>yy`bM>DcHB1-?!A6f=Z8jY-&zd) zCsWPwHRsGd0<#tbK=IkZ{3=|ulv!az=dmJHU{UPmv+IO z>c4wO=bc^m2H#G9PS55+jH6OsaErP9!9VK7FdMY`_FDFpKddeGBNcDMl99@ zd@*?#BMu`5Yb=66M96P>ym1t{;b7mot2ey7qkmV+`Nb)IK6?gh0-Om*H6=|GtZ^8z z_|#xB$4MHaD6$$jKjIFye{bw~W+4Aq%jEfQn4bG-8K?{WP; zS$Tt(9UIy)h>{Td5@lZ`7K@_T5Wr(lj1|OGy=kgw35w~3e?_sB{#VV(6X*W?{m@4z z-@SCxhHkp9y@#?dlA03O3NC`M*%Gx_np!PUYxNv78&auU9;~C9e}4U%-}l$-h2dkz z4;R;m9rScQL=Xhnz~Dkd5Sc1`32JeX!B|0og>%cir6@!s0HTRE{0Fx^Rh$`5-aB%C z@AkI7&L^cX`!Q-NJh@uhW!c5T*? ze*B%>UE4P1Z`tWif6pF6Q+WN*E9G)Aj#iE?FgJe|1B4=y)EwJPuBP9gyvWGN{#8Av!##xlhOu8{-W z_eYbl%HYh8>o&{oBxWuf-PgfmU=b*(s!IKhkt#g^qHK3{6G$e_R6k`ID61~NDss8U pR75P*@7&g@Hcf4!1W0rh$lr11Sh~C<&aD6d002ovPDHLkV1m7OyDR_z diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index fb3c1eb3e9..2031e54cce 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -59,6 +59,11 @@ multitype.png go-next.png go-previous.png + edit-delete.png + edit-undo.png + edit-preview.png + edit-clone.png + add.png raster/startup/big/create-addon.png raster/startup/big/new-game.png raster/startup/big/edit-content.png From 1a1d52c51308995e505f7c39028661b74f4306b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 20 Mar 2014 14:19:50 +0100 Subject: [PATCH 175/240] fixed a column ID problem --- apps/opencs/model/world/columnimp.hpp | 5 ++--- apps/opencs/model/world/columns.cpp | 3 ++- apps/opencs/model/world/columns.hpp | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index def225018f..b1c61b76a5 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -37,7 +37,6 @@ namespace CSMWorld } }; - /// \note Shares ID with IdColumn. A table can not have both. template struct StringIdColumn : public Column { @@ -838,11 +837,11 @@ namespace CSMWorld } }; - /// \note Shares ID with StringIdColumn. A table can not have both. template struct IdColumn : public Column { - IdColumn() : Column (Columns::ColumnId_Id, ColumnBase::Display_String) {} + IdColumn() : Column (Columns::ColumnId_ReferenceableId, + ColumnBase::Display_Referenceable) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 2f3911270e..7410780e08 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -172,7 +172,8 @@ namespace CSMWorld { ColumnId_Rank, "Rank" }, { ColumnId_Gender, "Gender" }, { ColumnId_PcRank, "PC Rank" }, - { ColumnId_Scope, "Scope", }, + { ColumnId_Scope, "Scope" }, + { ColumnId_ReferenceableId, "Referenceable ID" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 52e022e789..855e89cade 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -166,6 +166,7 @@ namespace CSMWorld ColumnId_Gender = 153, ColumnId_PcRank = 154, ColumnId_Scope = 155, + ColumnId_ReferenceableId = 156, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. From c9b8c69f032ea1474fdfb92a990e4347bcad344f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 15:14:37 +0100 Subject: [PATCH 176/240] reverted changes in the cmake --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f13ede52b..154f30161b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,8 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") -# if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) -# message(FATAL_ERROR "Silly Zini forgot to update the version again...") + if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + message(FATAL_ERROR "Silly Zini forgot to update the version again...") else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) From 832a10b2ac2d7997c94b8634cde596a389102f51 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 18:25:01 +0100 Subject: [PATCH 177/240] solving problems --- CMakeLists.txt | 4 ++-- apps/opencs/model/world/columnimp.hpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 4 ++-- apps/opencs/view/world/util.cpp | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 154f30161b..5f13ede52b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,8 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") - if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) - message(FATAL_ERROR "Silly Zini forgot to update the version again...") +# if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) +# message(FATAL_ERROR "Silly Zini forgot to update the version again...") else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 6311562a6a..4c053bfd99 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1560,7 +1560,7 @@ namespace CSMWorld template struct ResponseColumn : public Column { - ResponseColumn() : Column (Columns::ColumnId_Response, ColumnBase::Display_String) {} + ResponseColumn() : Column (Columns::ColumnId_Response, ColumnBase::Display_LongString) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 4962acfce6..6be5da6352 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -353,7 +353,7 @@ void CSVWorld::EditWidget::remake(int row) QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - if (! (mTable->flags (mTable->index (0, i)) & Qt::ItemIsEditable)) + if (!mTable->flags (mTable->index (0, i))) { lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); @@ -370,7 +370,7 @@ void CSVWorld::EditWidget::remake(int row) mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); - this->setMinimumWidth(325); //TODO find better way to set the width + this->setMinimumWidth(325); //TODO find better way to set the width or make it customizable this->setWidget(mMainWidget); this->setWidgetResizable(true); } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index a71937b085..227c5c9c5a 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -166,7 +166,9 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO display == CSMWorld::ColumnBase::Display_Script || display == CSMWorld::ColumnBase::Display_Race || display == CSMWorld::ColumnBase::Display_Class || - display == CSMWorld::ColumnBase::Display_Faction) + display == CSMWorld::ColumnBase::Display_Faction || + display == CSMWorld::ColumnBase::Display_Miscellaneous || + display == CSMWorld::ColumnBase::Display_Sound) { return new DropLineEdit(parent); } From ccd5e549cd9de58b61f79c7e26eb88721208d5cf Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 19:51:11 +0100 Subject: [PATCH 178/240] moved buttons down --- CMakeLists.txt | 4 ++-- apps/opencs/view/world/dialoguesubview.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f13ede52b..154f30161b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,8 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/.git) set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") -# if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) -# message(FATAL_ERROR "Silly Zini forgot to update the version again...") + if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + message(FATAL_ERROR "Silly Zini forgot to update the version again...") else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6be5da6352..492fa920fe 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -447,7 +447,6 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); - mMainLayout->addLayout(buttonsLayout); mMainLayout->addWidget(mEditWidget); mEditWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -466,6 +465,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM } dataChanged(mTable->index(mRow, 0)); + mMainLayout->addLayout(buttonsLayout); setWidget(mainWidget); } From a692ce99fa82f313a803cae30bbff13204225d99 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 20:26:53 +0100 Subject: [PATCH 179/240] =?UTF-8?q?where=20was=20my=20mind=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/opencs/model/world/columnimp.hpp | 4 ++-- apps/opencs/view/world/dialoguesubview.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 4c053bfd99..ccbeaf6948 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -834,7 +834,7 @@ namespace CSMWorld virtual bool isUserEditable() const { - return false; + return true; } }; @@ -1114,7 +1114,7 @@ namespace CSMWorld virtual bool isUserEditable() const { - return false; + return true; } }; diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 492fa920fe..b29d6695b7 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -353,7 +353,7 @@ void CSVWorld::EditWidget::remake(int row) QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - if (!mTable->flags (mTable->index (0, i))) + if (! (mTable->flags (index) & Qt::ItemIsEditable)) { lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); From fbb619db73a145f02c23fb3c8ef5938ede48709f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 20 Mar 2014 21:41:11 +0100 Subject: [PATCH 180/240] =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/opencs/view/world/dialoguesubview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index b29d6695b7..ae7c5923b1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -353,7 +353,7 @@ void CSVWorld::EditWidget::remake(int row) QLabel* label = new QLabel(mTable->headerData (i, Qt::Horizontal).toString(), mMainWidget); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - if (! (mTable->flags (index) & Qt::ItemIsEditable)) + if (! (mTable->flags (mTable->index (row, i)) & Qt::ItemIsEditable)) { lockedLayout->addWidget (label, locked, 0); lockedLayout->addWidget (editor, locked, 1); From e3a9daf921f30c5a8c7540b216e0760070e6bef6 Mon Sep 17 00:00:00 2001 From: Rohit Nirmal Date: Thu, 20 Mar 2014 22:22:31 -0500 Subject: [PATCH 181/240] Play item sound when trying to sell a bound item, too. --- apps/openmw/mwgui/inventorywindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 16ddab584a..e9efe75e7f 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -171,6 +171,7 @@ namespace MWGui if (item.mBase.getCellRef().mRefID.size() > 6 && item.mBase.getCellRef().mRefID.substr(0,6) == "bound_") { + MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getWindowManager()->messageBox("#{sBarterDialog12}"); return; } From 6eab9c5179725ae234a504ba10c67ab35de9ed48 Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:19:40 -0500 Subject: [PATCH 182/240] Move weather state save/load to a new class --- apps/openmw/mwworld/weather.cpp | 73 +++++++++++++-------------------- components/CMakeLists.txt | 2 +- components/esm/weatherstate.cpp | 59 ++++++++++++++++++++++++++ components/esm/weatherstate.hpp | 27 ++++++++++++ 4 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 components/esm/weatherstate.cpp create mode 100644 components/esm/weatherstate.hpp diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b74ca63935..335702c665 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -1,7 +1,6 @@ #include "weather.hpp" -#include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -18,15 +17,6 @@ using namespace Ogre; using namespace MWWorld; using namespace MWSound; -#define HOUR "HOUR" -#define WINDSPEED "WNSP" -#define CURRENTWEATHER "CWTH" -#define NEXTWEATHER "NWTH" -#define CURRENTREGION "CREG" -#define FIRSTUPDATE "FUPD" -#define REMAININGTRANSITIONTIME "RTTM" -#define TIMEPASSED "TMPS" - namespace { float lerp (float x, float y, float factor) @@ -698,15 +688,18 @@ bool WeatherManager::isDark() const void WeatherManager::write(ESM::ESMWriter& writer) { + ESM::WeatherState state; + state.mHour = mHour; + state.mWindSpeed = mWindSpeed; + state.mCurrentWeather = mCurrentWeather; + state.mNextWeather = mNextWeather; + state.mCurrentRegion = mCurrentRegion; + state.mFirstUpdate = mFirstUpdate; + state.mRemainingTransitionTime = mRemainingTransitionTime; + state.mTimePassed = mTimePassed; + writer.startRecord(ESM::REC_WTHR); - writer.writeHNT(HOUR, mHour); - writer.writeHNT(WINDSPEED, mWindSpeed); - writer.writeHNCString(CURRENTWEATHER, mCurrentWeather.c_str()); - writer.writeHNCString(NEXTWEATHER, mNextWeather.c_str()); - writer.writeHNCString(CURRENTREGION, mCurrentRegion.c_str()); - writer.writeHNT(FIRSTUPDATE, mFirstUpdate); - writer.writeHNT(REMAININGTRANSITIONTIME, mRemainingTransitionTime); - writer.writeHNT(TIMEPASSED, mTimePassed); + state.save(writer); writer.endRecord(ESM::REC_WTHR); } @@ -714,35 +707,27 @@ bool WeatherManager::readRecord(ESM::ESMReader& reader, int32_t type) { if(ESM::REC_WTHR == type) { - // store state in locals so that if we fail to load, we don't leave the manager in a half-way state - float newHour = 0.0; - reader.getHNT(newHour, HOUR); - float newWindSpeed = 0.0; - reader.getHNT(newWindSpeed, WINDSPEED); - std::string newCurrentWeather = reader.getHNString(CURRENTWEATHER); - std::string newNextWeather = reader.getHNString(NEXTWEATHER); - std::string newCurrentRegion = reader.getHNString(CURRENTREGION); - bool newFirstUpdate = false; - reader.getHNT(newFirstUpdate, FIRSTUPDATE); - float newRemainingTransitionTime = 0.0; - reader.getHNT(newRemainingTransitionTime, REMAININGTRANSITIONTIME); - double newTimePassed = 0.0; - reader.getHNT(newTimePassed, TIMEPASSED); + // load first so that if it fails, we haven't accidentally reset the state below + ESM::WeatherState state; + state.load(reader); - // reset other temporary state + // reset other temporary state, now that we loaded successfully + stopSounds(true); // let's hope this never throws mRegionOverrides.clear(); - stopSounds(true); // TODO: inconsistent state if this throws... mRegionMods.clear(); + mThunderFlash = 0.0; + mThunderChance = 0.0; + mThunderChanceNeeded = 50.0; - // swap in new values, now that we can't fail - mHour = newHour; - mWindSpeed = newWindSpeed; - mCurrentWeather.swap(newCurrentWeather); - mNextWeather.swap(newNextWeather); - mCurrentRegion.swap(newCurrentRegion); - mFirstUpdate = newFirstUpdate; - mRemainingTransitionTime = newRemainingTransitionTime; - mTimePassed = newTimePassed; + // swap in the loaded values now that we can't fail + mHour = state.mHour; + mWindSpeed = state.mWindSpeed; + mCurrentWeather.swap(state.mCurrentWeather); + mNextWeather.swap(state.mNextWeather); + mCurrentRegion.swap(state.mCurrentRegion); + mFirstUpdate = state.mFirstUpdate; + mRemainingTransitionTime = state.mRemainingTransitionTime; + mTimePassed = state.mTimePassed; return true; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index db4ecad0b1..c4d13170d0 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -45,7 +45,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats + npcstats creaturestats weatherstate ) add_component_dir (misc diff --git a/components/esm/weatherstate.cpp b/components/esm/weatherstate.cpp new file mode 100644 index 0000000000..48cf55a600 --- /dev/null +++ b/components/esm/weatherstate.cpp @@ -0,0 +1,59 @@ +#include "weatherstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace +{ + const char* hourRecord = "HOUR"; + const char* windSpeedRecord = "WNSP"; + const char* currentWeatherRecord = "CWTH"; + const char* nextWeatherRecord = "NWTH"; + const char* currentRegionRecord = "CREG"; + const char* firstUpdateRecord = "FUPD"; + const char* remainingTransitionTimeRecord = "RTTM"; + const char* timePassedRecord = "TMPS"; +} + +namespace ESM +{ + void WeatherState::load(ESMReader& esm) + { + // store values locally so that a failed load can't leave the state half set + float newHour = 0.0; + esm.getHNT(newHour, hourRecord); + float newWindSpeed = 0.0; + esm.getHNT(newWindSpeed, windSpeedRecord); + std::string newCurrentWeather = esm.getHNString(currentWeatherRecord); + std::string newNextWeather = esm.getHNString(nextWeatherRecord); + std::string newCurrentRegion = esm.getHNString(currentRegionRecord); + bool newFirstUpdate = false; + esm.getHNT(newFirstUpdate, firstUpdateRecord); + float newRemainingTransitionTime = 0.0; + esm.getHNT(newRemainingTransitionTime, remainingTransitionTimeRecord); + double newTimePassed = 0.0; + esm.getHNT(newTimePassed, timePassedRecord); + + // swap values now that it is safe to do so + mHour = newHour; + mWindSpeed = newWindSpeed; + mCurrentWeather.swap(newCurrentWeather); + mNextWeather.swap(newNextWeather); + mCurrentRegion.swap(newCurrentRegion); + mFirstUpdate = newFirstUpdate; + mRemainingTransitionTime = newRemainingTransitionTime; + mTimePassed = newTimePassed; + } + + void WeatherState::save(ESMWriter& esm) const + { + esm.writeHNT(hourRecord, mHour); + esm.writeHNT(windSpeedRecord, mWindSpeed); + esm.writeHNCString(currentWeatherRecord, mCurrentWeather.c_str()); + esm.writeHNCString(nextWeatherRecord, mNextWeather.c_str()); + esm.writeHNCString(currentRegionRecord, mCurrentRegion.c_str()); + esm.writeHNT(firstUpdateRecord, mFirstUpdate); + esm.writeHNT(remainingTransitionTimeRecord, mRemainingTransitionTime); + esm.writeHNT(timePassedRecord, mTimePassed); + } +} diff --git a/components/esm/weatherstate.hpp b/components/esm/weatherstate.hpp new file mode 100644 index 0000000000..5457512252 --- /dev/null +++ b/components/esm/weatherstate.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_WEATHER_H +#define OPENMW_ESM_WEATHER_H + +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + struct WeatherState + { + float mHour; + float mWindSpeed; + std::string mCurrentWeather; + std::string mNextWeather; + std::string mCurrentRegion; + bool mFirstUpdate; + float mRemainingTransitionTime; + double mTimePassed; + + void load(ESMReader& esm); + void save(ESMWriter& esm) const; + }; +} + +#endif From 00eac7d53adc272424fa029a4eaaed262396750e Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:22:54 -0500 Subject: [PATCH 183/240] Fixed header include guard to match filename --- components/esm/weatherstate.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/weatherstate.hpp b/components/esm/weatherstate.hpp index 5457512252..d0cd59c160 100644 --- a/components/esm/weatherstate.hpp +++ b/components/esm/weatherstate.hpp @@ -1,5 +1,5 @@ -#ifndef OPENMW_ESM_WEATHER_H -#define OPENMW_ESM_WEATHER_H +#ifndef OPENMW_ESM_WEATHERSTATE_H +#define OPENMW_ESM_WEATHERSTATE_H #include From 6dce3e51864e9fc8799690c132b5e7d4e868ddf2 Mon Sep 17 00:00:00 2001 From: slothlife Date: Fri, 21 Mar 2014 01:33:11 -0500 Subject: [PATCH 184/240] Fixed include to use C99 header, not C++11 header --- apps/openmw/mwworld/weather.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 3167a64c2a..cad3a4492e 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWWORLD_WEATHER_H #define GAME_MWWORLD_WEATHER_H -#include +#include #include #include From 10ce47938b3de5f6a556b72acfbf4aef41c6a491 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 11:22:43 +0100 Subject: [PATCH 185/240] update preview on reference/referenceable changes --- apps/opencs/view/render/previewwidget.cpp | 134 ++++++++++++++++++---- apps/opencs/view/render/previewwidget.hpp | 30 +++-- apps/opencs/view/render/scenewidget.cpp | 5 + apps/opencs/view/render/scenewidget.hpp | 2 + 4 files changed, 139 insertions(+), 32 deletions(-) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 43d45f4cb9..9b64cf6f4e 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -4,36 +4,55 @@ #include #include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" -void CSVRender::PreviewWidget::setup (const std::string& id) +void CSVRender::PreviewWidget::setup() { setNavigation (&mOrbit); - int column = mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); - - int row = mData.getReferenceables().getIndex (id); - - QVariant value = mData.getReferenceables().getData (row, column); - - if (!value.isValid()) - return; - - std::string model = value.toString().toUtf8().constData(); - - if (model.empty()) - return; - mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(); mNode->setPosition (Ogre::Vector3 (0, 0, 0)); - mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); + setModel(); + + QAbstractItemModel *referenceables = + mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); + + connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (ReferenceableDataChanged (const QModelIndex&, const QModelIndex&))); + connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (ReferenceableAboutToBeRemoved (const QModelIndex&, int, int))); } -void CSVRender::PreviewWidget::adjust (const std::string& id) +void CSVRender::PreviewWidget::setModel() { if (mNode) { - int row = mData.getReferences().getIndex (id); + mObject.setNull(); + + int column = mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); + + int row = mData.getReferenceables().getIndex (mReferenceableId); + + QVariant value = mData.getReferenceables().getData (row, column); + + if (!value.isValid()) + return; + + std::string model = value.toString().toUtf8().constData(); + + if (model.empty()) + return; + + mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); + } +} + +void CSVRender::PreviewWidget::adjust() +{ + if (mNode) + { + int row = mData.getReferences().getIndex (mReferenceId); float scale = mData.getReferences().getData (row, mData.getReferences(). findColumnIndex (CSMWorld::Columns::ColumnId_Scale)).toFloat(); @@ -56,17 +75,82 @@ void CSVRender::PreviewWidget::adjust (const std::string& id) } } -CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, +CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, QWidget *parent) -: SceneWidget (parent), mData (data), mNode (0) +: SceneWidget (parent), mData (data), mNode (0), mReferenceableId (referenceableId) { - setup (referenceableId); + setup(); } -CSVRender::PreviewWidget::PreviewWidget (const CSMWorld::Data& data, +CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, const std::string& referenceId, QWidget *parent) -: SceneWidget (parent), mData (data) +: SceneWidget (parent), mData (data), mReferenceableId (referenceableId), + mReferenceId (referenceId) { - setup (referenceableId); - adjust (referenceId); + setup(); + + adjust(); + + QAbstractItemModel *references = + mData.getTableModel (CSMWorld::UniversalId::Type_References); + + connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (ReferenceDataChanged (const QModelIndex&, const QModelIndex&))); + connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (ReferenceAboutToBeRemoved (const QModelIndex&, int, int))); +} + +void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + CSMWorld::IdTable& referenceables = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); + + QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0); + + if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) + { + /// \todo possible optimisation; check columns and only update if relevant columns have + /// changed + setModel(); + flagAsModified(); + } +} + +void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, + int end) +{ + +} + +void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); + + QModelIndex index = references.getModelIndex (mReferenceId, columnIndex); + + if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) + { + /// \todo possible optimisation; check columns and only update if relevant columns have + /// changed + adjust(); + + if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) + { + mReferenceableId = references.data (index).toString().toUtf8().constData(); + setModel(); + } + + flagAsModified(); + } +} + +void CSVRender::PreviewWidget::ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, + int end) +{ + } diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index b3abd55879..2b6517e715 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -7,6 +7,8 @@ #include "navigationorbit.hpp" +class QModelIndex; + namespace CSMWorld { class Data; @@ -18,28 +20,42 @@ namespace CSVRender { Q_OBJECT - const CSMWorld::Data& mData; + CSMWorld::Data& mData; CSVRender::NavigationOrbit mOrbit; NifOgre::ObjectScenePtr mObject; Ogre::SceneNode *mNode; + std::string mReferenceId; + std::string mReferenceableId; - void setup (const std::string& id); - ///< \param id ID of the referenceable to be viewed + void setup(); - void adjust (const std::string& id); - ///< \param id ID of the reference to be viewed + void setModel(); + + void adjust(); + ///< Adjust referenceable preview according to the reference public: - PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, + PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, QWidget *parent = 0); - PreviewWidget (const CSMWorld::Data& data, const std::string& referenceableId, + PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, const std::string& referenceId, QWidget *parent = 0); signals: void closeRequest(); + + private slots: + + void ReferenceableDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight); + + void ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); }; } diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 5eec702d3d..6b2ca85b5e 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -118,6 +118,11 @@ namespace CSVRender return mSceneMgr; } + void SceneWidget::flagAsModified() + { + mUpdate = true; + } + void SceneWidget::paintEvent(QPaintEvent* e) { if (!mWindow) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 05b06b2873..375c877d22 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -36,6 +36,8 @@ namespace CSVRender Ogre::SceneManager *getSceneManager(); + void flagAsModified(); + private: void paintEvent(QPaintEvent* e); void resizeEvent(QResizeEvent* e); From fc8ae2b9b563d7713b909333ad2c228764b0e53b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 11:56:48 +0100 Subject: [PATCH 186/240] fixed encoding problems --- apps/opencs/editor.cpp | 8 ++++---- apps/opencs/model/tools/tools.cpp | 2 +- apps/opencs/model/world/tablemimedata.cpp | 4 ++-- apps/opencs/view/doc/opendialog.cpp | 2 +- apps/opencs/view/doc/subview.cpp | 2 +- apps/opencs/view/filter/editwidget.cpp | 4 ++-- apps/opencs/view/world/dialoguesubview.cpp | 14 +++++++------- apps/opencs/view/world/scriptedit.cpp | 6 +++--- apps/opencs/view/world/table.cpp | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 942780c32b..87660a60bc 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -61,7 +61,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs) { for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { - QString path = QString::fromStdString(iter->string()); + QString path = QString::fromUtf8 (iter->string().c_str()); mFileDialog.addFiles(path); } } @@ -148,7 +148,7 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath) std::vector files; foreach (const QString &path, mFileDialog.selectedFilePaths()) - files.push_back(path.toStdString()); + files.push_back(path.toUtf8().constData()); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); @@ -161,10 +161,10 @@ void CS::Editor::createNewFile (const boost::filesystem::path &savePath) std::vector files; foreach (const QString &path, mFileDialog.selectedFilePaths()) { - files.push_back(path.toStdString()); + files.push_back(path.toUtf8().constData()); } - files.push_back(mFileDialog.filename().toStdString()); + files.push_back(mFileDialog.filename().toUtf8().constData()); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 79a09dcb48..d3d8f5fadd 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -143,6 +143,6 @@ void CSMTools::Tools::verifierMessage (const QString& message, int type) std::map::iterator iter = mActiveReports.find (type); if (iter!=mActiveReports.end()) - mReports[iter->second]->add (message.toStdString()); + mReports[iter->second]->add (message.toUtf8().constData()); } diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 9dcecf3bc1..ccd6fb4964 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -8,7 +8,7 @@ CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& mDocument(document) { mUniversalId.push_back (id); - mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); + mObjectsFormats << QString::fromUtf8 (("tabledata/" + id.getTypeName()).c_str()); } CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : @@ -16,7 +16,7 @@ CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id { for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) { - mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + mObjectsFormats << QString::fromUtf8 (("tabledata/" + it->getTypeName()).c_str()); } } diff --git a/apps/opencs/view/doc/opendialog.cpp b/apps/opencs/view/doc/opendialog.cpp index 7b62aafa31..d107b198c6 100644 --- a/apps/opencs/view/doc/opendialog.cpp +++ b/apps/opencs/view/doc/opendialog.cpp @@ -40,7 +40,7 @@ OpenDialog::OpenDialog(QWidget * parent) : QDialog(parent) mCfgMgr.processPaths(mDataLocal); // Set the charset for reading the esm/esp files - QString encoding = QString::fromStdString(variables["encoding"].as()); + QString encoding = QString::fromUtf8 (variables["encoding"].as().c_str()); Files::PathContainer dataDirs; dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end()); diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index dcd85945fc..7fd0057173 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -4,7 +4,7 @@ CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view - setWindowTitle (mUniversalId.toString().c_str()); + setWindowTitle (QString::fromUtf8 (mUniversalId.toString().c_str())); } CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index cc1578bdd9..b163297f9a 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -120,7 +120,7 @@ void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::st { ss<<"!or("; } else { - ss << orAnd << oldContent.toStdString() << ','; + ss << orAnd << oldContent.toUtf8().constData() << ','; } for (unsigned i = 0; i < count; ++i) @@ -137,7 +137,7 @@ void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::st } else { if (!replaceMode) { - ss << orAnd << oldContent.toStdString() <<','; + ss << orAnd << oldContent.toUtf8().constData() <<','; } else { ss<<'!'; } diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index ae7c5923b1..1627bc5f4f 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -444,7 +444,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout = new QVBoxLayout(mainWidget); mEditWidget = new EditWidget(mainWidget, mRow, mTable, mUndoStack, false); - connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), + connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); mMainLayout->addWidget(mEditWidget); @@ -490,7 +490,7 @@ void CSVWorld::DialogueSubView::prevId() { mEditWidget->remake(newRow); setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toStdString())); + mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); mRow = newRow; mEditWidget->setDisabled(mLocked); return; @@ -522,7 +522,7 @@ void CSVWorld::DialogueSubView::nextId() { mEditWidget->remake(newRow); setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), - mTable->data (mTable->index (newRow, 0)).toString().toStdString())); + mTable->data (mTable->index (newRow, 0)).toString().toUtf8().constData())); mRow = newRow; mEditWidget->setDisabled(mLocked); return; @@ -580,7 +580,7 @@ void CSVWorld::DialogueSubView::revertRecord() if (state!=CSMWorld::RecordBase::State_BaseOnly) { - mUndoStack.push(new CSMWorld::RevertCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); + mUndoStack.push(new CSMWorld::RevertCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData())); } if (rows != mTable->rowCount()) { @@ -613,7 +613,7 @@ void CSVWorld::DialogueSubView::deleteRecord() mRow < rows && mBottom->canCreateAndDelete()) { - mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toStdString())); + mUndoStack.push(new CSMWorld::DeleteCommand(*mTable, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData())); if (rows != mTable->rowCount()) { if (mTable->rowCount() == 0) @@ -639,7 +639,7 @@ void CSVWorld::DialogueSubView::requestFocus (const std::string& id) void CSVWorld::DialogueSubView::cloneRequest () { - mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toStdString(), + mBottom->cloneRequest(mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData(), static_cast(mTable->data(mTable->index(mRow, 2)).toInt())); } @@ -647,7 +647,7 @@ void CSVWorld::DialogueSubView::showPreview () { if (mTable->hasPreview() && mRow < mTable->rowCount()) { - emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toStdString()), ""); + emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData()), ""); } } diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp index fccac75b41..b1528d5254 100644 --- a/apps/opencs/view/world/scriptedit.cpp +++ b/apps/opencs/view/world/scriptedit.cpp @@ -71,9 +71,9 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) { if (stringNeedsQuote(it->getId())) { - insertPlainText(QString::fromStdString ('"' + it->getId() + '"')); + insertPlainText(QString::fromUtf8 (('"' + it->getId() + '"').c_str())); } else { - insertPlainText(QString::fromStdString (it->getId())); + insertPlainText(QString::fromUtf8 (it->getId().c_str())); } } } @@ -82,7 +82,7 @@ void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const { - const QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. + const QString string(QString::fromUtf8(id.c_str())); // is only for c++11, so let's use qregexp for now. //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… return !(string.contains(mWhiteListQoutes)); } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index cd9138c357..a2927c2f09 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -289,7 +289,7 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const { return CSMWorld::UniversalId ( static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); + mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData()); } void CSVWorld::Table::revertRecord() @@ -533,7 +533,7 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) } drag->setMimeData (mime); - drag->setPixmap (QString::fromStdString (mime->getIcon())); + drag->setPixmap (QString::fromUtf8 (mime->getIcon().c_str())); drag->exec(Qt::CopyAction); } @@ -588,7 +588,7 @@ std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column if (display == columndisplay) { - titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toUtf8().constData()); } } return titles; From 2a26ff2299923f9c0baab0e316408e9adc4ce0ec Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 21 Mar 2014 12:13:54 +0100 Subject: [PATCH 187/240] better fix --- apps/opencs/model/world/tablemimedata.cpp | 99 ++++++++++++++++++++-- apps/opencs/model/world/tablemimedata.hpp | 8 +- apps/opencs/view/world/dialoguesubview.cpp | 43 ++++++++-- 3 files changed, 131 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index 9dcecf3bc1..f6256c2634 100644 --- a/apps/opencs/model/world/tablemimedata.cpp +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -64,14 +64,69 @@ std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const return mUniversalId; } +bool CSMWorld::TableMimeData::isReferencable(CSMWorld::ColumnBase::Display type) const +{ +return ( type == CSMWorld::ColumnBase::Display_Activator + || type == CSMWorld::ColumnBase::Display_Potion + || type == CSMWorld::ColumnBase::Display_Apparatus + || type == CSMWorld::ColumnBase::Display_Armor + || type == CSMWorld::ColumnBase::Display_Book + || type == CSMWorld::ColumnBase::Display_Clothing + || type == CSMWorld::ColumnBase::Display_Container + || type == CSMWorld::ColumnBase::Display_Creature + || type == CSMWorld::ColumnBase::Display_Door + || type == CSMWorld::ColumnBase::Display_Ingredient + || type == CSMWorld::ColumnBase::Display_CreatureLevelledList + || type == CSMWorld::ColumnBase::Display_ItemLevelledList + || type == CSMWorld::ColumnBase::Display_Light + || type == CSMWorld::ColumnBase::Display_Lockpick + || type == CSMWorld::ColumnBase::Display_Miscellaneous + || type == CSMWorld::ColumnBase::Display_Npc + || type == CSMWorld::ColumnBase::Display_Probe + || type == CSMWorld::ColumnBase::Display_Repair + || type == CSMWorld::ColumnBase::Display_Static + || type == CSMWorld::ColumnBase::Display_Weapon); +} +bool CSMWorld::TableMimeData::isReferencable(CSMWorld::UniversalId::Type type) const +{ + return ( type == CSMWorld::UniversalId::Type_Activator + || type == CSMWorld::UniversalId::Type_Potion + || type == CSMWorld::UniversalId::Type_Apparatus + || type == CSMWorld::UniversalId::Type_Armor + || type == CSMWorld::UniversalId::Type_Book + || type == CSMWorld::UniversalId::Type_Clothing + || type == CSMWorld::UniversalId::Type_Container + || type == CSMWorld::UniversalId::Type_Creature + || type == CSMWorld::UniversalId::Type_Door + || type == CSMWorld::UniversalId::Type_Ingredient + || type == CSMWorld::UniversalId::Type_CreatureLevelledList + || type == CSMWorld::UniversalId::Type_ItemLevelledList + || type == CSMWorld::UniversalId::Type_Light + || type == CSMWorld::UniversalId::Type_Lockpick + || type == CSMWorld::UniversalId::Type_Miscellaneous + || type == CSMWorld::UniversalId::Type_Npc + || type == CSMWorld::UniversalId::Type_Probe + || type == CSMWorld::UniversalId::Type_Repair + || type == CSMWorld::UniversalId::Type_Static + || type == CSMWorld::UniversalId::Type_Weapon); +} bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const { + bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { - if (it->getType() == type) + if (referencable) { - return true; + if (isReferencable(it->getType())) + { + return true; + } + } else { + if (it->getType() == type) + { + return true; + } } } @@ -80,11 +135,20 @@ bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const { + bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { - if (it->getType() == convertEnums (type)) + if (referencable) { - return true; + if (isReferencable(it->getType())) + { + return true; + } + } else { + if (it->getType() == convertEnums (type)) + { + return true; + } } } @@ -93,11 +157,21 @@ bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) con CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const { + bool referencable = (type == CSMWorld::UniversalId::Type_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { - if (it->getType() == type) + if (referencable) { - return *it; + if (isReferencable(it->getType())) + { + return *it; + } + } else + { + if (it->getType() == type) + { + return *it; + } } } @@ -106,11 +180,20 @@ CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::Univers CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const { + bool referencable = (type == CSMWorld::ColumnBase::Display_Referenceable); for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) { - if (it->getType() == convertEnums (type)) + if (referencable) { - return *it; + if (isReferencable(it->getType())) + { + return *it; + } + } else { + if (it->getType() == convertEnums (type)) + { + return *it; + } } } diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp index 44ac0f5f64..adcb147c18 100644 --- a/apps/opencs/model/world/tablemimedata.hpp +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -27,6 +27,9 @@ namespace CSMWorld class TableMimeData : public QMimeData { + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; public: TableMimeData(UniversalId id, const CSMDoc::Document& document); @@ -56,9 +59,8 @@ namespace CSMWorld static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); private: - std::vector mUniversalId; - QStringList mObjectsFormats; - const CSMDoc::Document& mDocument; + bool isReferencable(CSMWorld::UniversalId::Type type) const; + bool isReferencable(CSMWorld::ColumnBase::Display type) const; }; } diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index ae7c5923b1..cf932aba11 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -122,18 +122,45 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: { QLineEdit* lineEdit = qobject_cast(mEditor); { - if (!lineEdit or !mIndexWrapper.get()) - { - return; - } + if (!lineEdit or !mIndexWrapper.get()) + { + return; + } } for (unsigned i = 0; i < data.size(); ++i) { - if (mDisplay == CSMWorld::TableMimeData::convertEnums(data[i].getType())) + CSMWorld::UniversalId::Type type = data[i].getType(); + if (mDisplay == CSMWorld::ColumnBase::Display_Referenceable) { - emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); - emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); - break; + if ( type == CSMWorld::UniversalId::Type_Activator + || type == CSMWorld::UniversalId::Type_Potion + || type == CSMWorld::UniversalId::Type_Apparatus + || type == CSMWorld::UniversalId::Type_Armor + || type == CSMWorld::UniversalId::Type_Book + || type == CSMWorld::UniversalId::Type_Clothing + || type == CSMWorld::UniversalId::Type_Container + || type == CSMWorld::UniversalId::Type_Creature + || type == CSMWorld::UniversalId::Type_Door + || type == CSMWorld::UniversalId::Type_Ingredient + || type == CSMWorld::UniversalId::Type_CreatureLevelledList + || type == CSMWorld::UniversalId::Type_ItemLevelledList + || type == CSMWorld::UniversalId::Type_Light + || type == CSMWorld::UniversalId::Type_Lockpick + || type == CSMWorld::UniversalId::Type_Miscellaneous + || type == CSMWorld::UniversalId::Type_Npc + || type == CSMWorld::UniversalId::Type_Probe + || type == CSMWorld::UniversalId::Type_Repair + || type == CSMWorld::UniversalId::Type_Static + || type == CSMWorld::UniversalId::Type_Weapon) + { + type = CSMWorld::UniversalId::Type_Referenceable; + } + } + if (mDisplay == CSMWorld::TableMimeData::convertEnums(type)) + { + emit tableMimeDataDropped(mEditor, mIndexWrapper->mIndex, data[i], document); + emit editorDataCommited(mEditor, mIndexWrapper->mIndex, mDisplay); + break; } } } From 58daf52a675da61d87e9fee305e82aa4cf93d664 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 12:44:06 +0100 Subject: [PATCH 188/240] deal with empty and invalid referenceable ID fields --- apps/opencs/view/render/previewwidget.cpp | 12 ++++++++++-- apps/opencs/view/world/previewsubview.cpp | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 9b64cf6f4e..94cb7ce6eb 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -30,9 +30,16 @@ void CSVRender::PreviewWidget::setModel() { mObject.setNull(); - int column = mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); + if (mReferenceableId.empty()) + return; - int row = mData.getReferenceables().getIndex (mReferenceableId); + int column = + mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); + + int row = mData.getReferenceables().searchId (mReferenceableId); + + if (row==-1) + return; QVariant value = mData.getReferenceables().getData (row, column); @@ -142,6 +149,7 @@ void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) { mReferenceableId = references.data (index).toString().toUtf8().constData(); + /// \todo update title setModel(); } diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 587af561fb..769ff3ddce 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -21,7 +21,10 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo std::string referenceableId = document.getData().getReferences().getRecord (id.getId()).get().mRefID; - setWindowTitle (("Preview: Reference to " + referenceableId).c_str()); + if (referenceableId.empty()) + setWindowTitle ("Preview: Reference to "); + else + setWindowTitle (("Preview: Reference to " + referenceableId).c_str()); mScene = new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); From 698fe8cce3208c54c682ee3ed19f2c757f80fc05 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 12:52:46 +0100 Subject: [PATCH 189/240] update preview title on changed referenceableID (preview for references only) --- apps/opencs/view/render/previewwidget.cpp | 2 +- apps/opencs/view/render/previewwidget.hpp | 2 ++ apps/opencs/view/world/previewsubview.cpp | 15 +++++++++++---- apps/opencs/view/world/previewsubview.hpp | 2 ++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 94cb7ce6eb..3e70fd9990 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -149,7 +149,7 @@ void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) { mReferenceableId = references.data (index).toString().toUtf8().constData(); - /// \todo update title + emit referenceableIdChanged (mReferenceableId); setModel(); } diff --git a/apps/opencs/view/render/previewwidget.hpp b/apps/opencs/view/render/previewwidget.hpp index 2b6517e715..7a63d8fb12 100644 --- a/apps/opencs/view/render/previewwidget.hpp +++ b/apps/opencs/view/render/previewwidget.hpp @@ -46,6 +46,8 @@ namespace CSVRender void closeRequest(); + void referenceableIdChanged (const std::string& id); + private slots: void ReferenceableDataChanged (const QModelIndex& topLeft, diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index 769ff3ddce..df9c3276cb 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -21,10 +21,7 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo std::string referenceableId = document.getData().getReferences().getRecord (id.getId()).get().mRefID; - if (referenceableId.empty()) - setWindowTitle ("Preview: Reference to "); - else - setWindowTitle (("Preview: Reference to " + referenceableId).c_str()); + referenceableIdChanged (referenceableId); mScene = new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); @@ -45,6 +42,8 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo setWidget (widget); connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); + connect (mScene, SIGNAL (referenceableIdChanged (const std::string&)), + this, SLOT (referenceableIdChanged (const std::string&))); } void CSVWorld::PreviewSubView::setEditLock (bool locked) {} @@ -52,4 +51,12 @@ void CSVWorld::PreviewSubView::setEditLock (bool locked) {} void CSVWorld::PreviewSubView::closeRequest() { deleteLater(); +} + +void CSVWorld::PreviewSubView::referenceableIdChanged (const std::string& id) +{ + if (id.empty()) + setWindowTitle ("Preview: Reference to "); + else + setWindowTitle (("Preview: Reference to " + id).c_str()); } \ No newline at end of file diff --git a/apps/opencs/view/world/previewsubview.hpp b/apps/opencs/view/world/previewsubview.hpp index e7a2a261ed..4ca25c3cbe 100644 --- a/apps/opencs/view/world/previewsubview.hpp +++ b/apps/opencs/view/world/previewsubview.hpp @@ -30,6 +30,8 @@ namespace CSVWorld private slots: void closeRequest(); + + void referenceableIdChanged (const std::string& id); }; } From 1e57d557172a99672a1a065f878738cf1439d047 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 13:27:46 +0100 Subject: [PATCH 190/240] update/close on deletion of reference/referenceable --- apps/opencs/view/render/previewwidget.cpp | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 3e70fd9990..be7f66016d 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -110,6 +110,9 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data, void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { + if (mReferenceableId.empty()) + return; + CSMWorld::IdTable& referenceables = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); @@ -127,12 +130,36 @@ void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topL void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + if (mReferenceableId.empty()) + return; + CSMWorld::IdTable& referenceables = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); + + QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0); + + if (index.row()>=start && index.row()<=end) + { + if (mReferenceId.empty()) + { + // this is a preview for a referenceble + emit closeRequest(); + } + else + { + // this is a preview for a reference + mObject.setNull(); + flagAsModified(); + } + } } void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { + if (mReferenceId.empty()) + return; + CSMWorld::IdTable& references = dynamic_cast ( *mData.getTableModel (CSMWorld::UniversalId::Type_References)); @@ -160,5 +187,14 @@ void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, void CSVRender::PreviewWidget::ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + if (mReferenceId.empty()) + return; + CSMWorld::IdTable& references = dynamic_cast ( + *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + + QModelIndex index = references.getModelIndex (mReferenceId, 0); + + if (index.row()>=start && index.row()<=end) + emit closeRequest(); } From 41fd94b1604c65199776968f477b27109bd201e8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 21 Mar 2014 13:39:22 +0100 Subject: [PATCH 191/240] compensated for different coordinate systems --- apps/opencs/view/render/navigation1st.cpp | 2 +- apps/opencs/view/render/scenewidget.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/render/navigation1st.cpp b/apps/opencs/view/render/navigation1st.cpp index b892d3e3e7..91f88634af 100644 --- a/apps/opencs/view/render/navigation1st.cpp +++ b/apps/opencs/view/render/navigation1st.cpp @@ -10,7 +10,7 @@ CSVRender::Navigation1st::Navigation1st() : mCamera (0) {} bool CSVRender::Navigation1st::activate (Ogre::Camera *camera) { mCamera = camera; - mCamera->setFixedYawAxis (true); + mCamera->setFixedYawAxis (true, Ogre::Vector3::UNIT_Z); Ogre::Radian pitch = mCamera->getOrientation().getPitch(); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6b2ca85b5e..5a3c14b49c 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -49,6 +49,7 @@ namespace CSVRender mCamera->lookAt(0,0,0); mCamera->setNearClipDistance(0.1); mCamera->setFarClipDistance(30000); + mCamera->roll (Ogre::Degree (90)); QTimer *timer = new QTimer (this); From 317f1e0b63afca99acc1d1442c63db47719f17bb Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 21 Mar 2014 13:44:01 +0100 Subject: [PATCH 192/240] Update dialoguesubview.cpp That doesn't work on Windows. (Are and/or allowed in C++ in any other compiler?) --- apps/opencs/view/world/dialoguesubview.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 3bd09bb50c..abdc331039 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -1,4 +1,3 @@ - #include "dialoguesubview.hpp" #include @@ -122,7 +121,7 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: { QLineEdit* lineEdit = qobject_cast(mEditor); { - if (!lineEdit or !mIndexWrapper.get()) + if (!lineEdit || !mIndexWrapper.get()) { return; } @@ -687,4 +686,4 @@ void CSVWorld::DialogueSubView::viewRecord() if (params.first.getType()!=CSMWorld::UniversalId::Type_None) emit focusId (params.first, params.second); } -} \ No newline at end of file +} From 394284d0f839bf4c135e842534161c8505112c97 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Mar 2014 00:46:47 +0100 Subject: [PATCH 193/240] Fixes #1209: Tarhiel never falls There are a few pitfalls with this code: - Gravity is only considered when applying queued movement. Therefore, make sure to queue some movement every frame. (Could be refactored in the future?) - The character controller never detects being in free fall (!World::isOnGround) unless movement has been applied. --- apps/openmw/mwmechanics/character.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 04d909b497..2db3bacf0e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1245,12 +1245,8 @@ void CharacterController::update(float duration) else //avoid z-rotating for knockdown world->rotateObject(mPtr, rot.x, rot.y, 0.0f, true); - // always control actual movement by animation unless this: - // FIXME: actor falling/landing should be controlled by physics engine - if(mMovementAnimVelocity == 0.0f && (vec.length() > 0.0f || mJumpState != JumpState_None)) - { + if (mMovementAnimVelocity == 0) world->queueMovement(mPtr, vec); - } } movement = vec; @@ -1290,7 +1286,7 @@ void CharacterController::update(float duration) } // Update movement - if(moved.squaredLength() > 1.0f) + if(mMovementAnimVelocity > 0) world->queueMovement(mPtr, moved); } mSkipAnim = false; From 0c207f7212a50ac8fa603054337ad2aa092963df Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Mar 2014 01:06:43 +0100 Subject: [PATCH 194/240] Fix build error --- apps/opencs/view/render/previewwidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 43d45f4cb9..1cd39c89b1 100644 --- a/apps/opencs/view/render/previewwidget.cpp +++ b/apps/opencs/view/render/previewwidget.cpp @@ -2,6 +2,7 @@ #include "previewwidget.hpp" #include +#include #include "../../model/world/data.hpp" From 6bd3b3ee78bb03af1901e75ccfe9209feb05ed66 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 22 Mar 2014 15:00:49 +0100 Subject: [PATCH 195/240] include local variable state in saved games --- apps/openmw/mwworld/livecellref.cpp | 10 +++++++++- apps/openmw/mwworld/refdata.cpp | 14 ++++++++++++-- apps/openmw/mwworld/refdata.hpp | 8 +++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index d71704fd70..c15f63abe1 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -10,16 +10,24 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) { mRef = state.mRef; mData = RefData (state); + Ptr ptr (this); + + if (state.mHasLocals) + mData.setLocals (state.mLocals, mClass->getScript (ptr)); + mClass->readAdditionalState (ptr, state); } void MWWorld::LiveCellRefBase::saveImp (ESM::ObjectState& state) const { state.mRef = mRef; - mData.write (state); + /// \todo get rid of this cast once const-correct Ptr are available Ptr ptr (const_cast (this)); + + mData.write (state, mClass->getScript (ptr)); + mClass->writeAdditionalState (ptr, state); } diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 8d48078b1d..907204739b 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -76,9 +76,13 @@ namespace MWWorld } } - void RefData::write (ESM::ObjectState& objectState) const + void RefData::write (ESM::ObjectState& objectState, const std::string& scriptId) const { - objectState.mHasLocals = false; + objectState.mHasLocals = mHasLocals; + + if (mHasLocals) + mLocals.write (objectState.mLocals, scriptId); + objectState.mEnabled = mEnabled; objectState.mCount = mCount; objectState.mPosition = mPosition; @@ -148,6 +152,12 @@ namespace MWWorld } } + void RefData::setLocals (const ESM::Locals& locals, const std::string& scriptId) + { + mHasLocals = true; + mLocals.read (locals, scriptId); + } + void RefData::setCount (int count) { if(count == 0) diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 19e3d48822..a74eb838cf 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -64,9 +64,9 @@ namespace MWWorld ~RefData(); - void write (ESM::ObjectState& objectState) const; - ///< Ignores local variables and custom data (not enough context available here to - /// perform these operations). + void write (ESM::ObjectState& objectState, const std::string& scriptId = "") const; + ///< Ignores custom data (not enough context available here to + /// perform this operations). RefData& operator= (const RefData& refData); @@ -83,6 +83,8 @@ namespace MWWorld void setLocals (const ESM::Script& script); + void setLocals (const ESM::Locals& locals, const std::string& scriptId); + void setCount (int count); /// Set object count (an object pile is a simple object with a count >1). /// From 87ae03b5d4273b4358b67416ae97faa51ec57163 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 22 Mar 2014 16:39:24 +0100 Subject: [PATCH 196/240] fix for previous commit --- apps/openmw/mwworld/livecellref.cpp | 12 +++++++++++- apps/openmw/mwworld/refdata.cpp | 6 ------ apps/openmw/mwworld/refdata.hpp | 2 -- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/livecellref.cpp b/apps/openmw/mwworld/livecellref.cpp index c15f63abe1..0fbb26c845 100644 --- a/apps/openmw/mwworld/livecellref.cpp +++ b/apps/openmw/mwworld/livecellref.cpp @@ -3,8 +3,12 @@ #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + #include "ptr.hpp" #include "class.hpp" +#include "esmstore.hpp" void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) { @@ -14,7 +18,13 @@ void MWWorld::LiveCellRefBase::loadImp (const ESM::ObjectState& state) Ptr ptr (this); if (state.mHasLocals) - mData.setLocals (state.mLocals, mClass->getScript (ptr)); + { + std::string scriptId = mClass->getScript (ptr); + + mData.setLocals (*MWBase::Environment::get().getWorld()->getStore(). + get().search (scriptId)); + mData.getLocals().read (state.mLocals, scriptId); + } mClass->readAdditionalState (ptr, state); } diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 907204739b..008782130b 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -152,12 +152,6 @@ namespace MWWorld } } - void RefData::setLocals (const ESM::Locals& locals, const std::string& scriptId) - { - mHasLocals = true; - mLocals.read (locals, scriptId); - } - void RefData::setCount (int count) { if(count == 0) diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index a74eb838cf..82371b056a 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -83,8 +83,6 @@ namespace MWWorld void setLocals (const ESM::Script& script); - void setLocals (const ESM::Locals& locals, const std::string& scriptId); - void setCount (int count); /// Set object count (an object pile is a simple object with a count >1). /// From 452b522bc3105b3aba95aeab86124ddc0839347e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Mar 2014 19:01:41 +0100 Subject: [PATCH 197/240] Fix typos --- components/compiler/lineparser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 5457d76255..98bd63ba1e 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -86,7 +86,7 @@ namespace Compiler { if (mState==PotentialEndState) { - getErrorHandler().warning ("stay string argument (ignoring it)", loc); + getErrorHandler().warning ("stray string argument (ignoring it)", loc); mState = EndState; return true; } @@ -377,19 +377,19 @@ namespace Compiler case Scanner::K_else: - getErrorHandler().warning ("stay else (ignoring it)", loc); + getErrorHandler().warning ("stray else (ignoring it)", loc); mState = EndState; return true; case Scanner::K_endif: - getErrorHandler().warning ("stay endif (ignoring it)", loc); + getErrorHandler().warning ("stray endif (ignoring it)", loc); mState = EndState; return true; case Scanner::K_begin: - getErrorHandler().warning ("stay begin (ignoring it)", loc); + getErrorHandler().warning ("stray begin (ignoring it)", loc); mState = EndState; return true; } From 0a17245633a10d3583d684fa92a5ac0b61c0fa83 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 22 Mar 2014 19:02:14 +0100 Subject: [PATCH 198/240] Refactored settings window --- CMakeLists.txt | 1 + apps/openmw/mwgui/settingswindow.cpp | 380 +++++++++------------- apps/openmw/mwgui/settingswindow.hpp | 37 +-- apps/openmw/mwrender/water.cpp | 6 +- files/mygui/openmw_settings_window.layout | 170 ++++++++-- files/settings-default.cfg | 2 - 6 files changed, 295 insertions(+), 301 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb3827588b..392fdfc66c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,7 @@ set(OENGINE_OGRE set(OENGINE_GUI ${LIBDIR}/openengine/gui/manager.cpp + ${LIBDIR}/openengine/gui/layout.hpp ) set(OENGINE_BULLET diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index c99e2d0de3..78adecd3ef 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -83,91 +83,116 @@ namespace } return false; } + + const char* checkButtonType = "CheckButton"; + const char* sliderType = "Slider"; + + std::string getSettingType(MyGUI::Widget* widget) + { + return widget->getUserString("SettingType"); + } + + std::string getSettingName(MyGUI::Widget* widget) + { + return widget->getUserString("SettingName"); + } + + std::string getSettingCategory(MyGUI::Widget* widget) + { + return widget->getUserString("SettingCategory"); + } + + std::string getSettingValueType(MyGUI::Widget* widget) + { + return widget->getUserString("SettingValueType"); + } + + void getSettingMinMax(MyGUI::Widget* widget, float& min, float& max) + { + const char* settingMin = "SettingMin"; + const char* settingMax = "SettingMax"; + min = 0.f; + max = 1.f; + if (!widget->getUserString(settingMin).empty()) + min = boost::lexical_cast(widget->getUserString(settingMin)); + if (!widget->getUserString(settingMax).empty()) + max = boost::lexical_cast(widget->getUserString(settingMax)); + } } namespace MWGui { + void SettingsWindow::configureWidgets(MyGUI::Widget* widget) + { + MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator(); + while (widgets.next()) + { + MyGUI::Widget* current = widgets.current(); + + std::string type = getSettingType(current); + if (type == checkButtonType) + { + std::string initialValue = Settings::Manager::getBool(getSettingName(current), + getSettingCategory(current)) + ? "#{sOn}" : "#{sOff}"; + current->castType()->setCaptionWithReplacing(initialValue); + current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + } + if (type == sliderType) + { + MyGUI::ScrollBar* scroll = current->castType(); + if (getSettingValueType(current) == "Float") + { + // TODO: ScrollBar isn't meant for this. should probably use a dedicated FloatSlider widget + float min,max; + getSettingMinMax(scroll, min, max); + float value = Settings::Manager::getFloat(getSettingName(current), getSettingCategory(current)); + value = (value-min)/(max-min); + + scroll->setScrollPosition( value * (scroll->getScrollRange()-1)); + } + else + { + int value = Settings::Manager::getFloat(getSettingName(current), getSettingCategory(current)); + scroll->setScrollPosition(value); + } + scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); + } + + configureWidgets(current); + } + } + SettingsWindow::SettingsWindow() : WindowBase("openmw_settings_window.layout") { + configureWidgets(mMainWidget); + getWidget(mOkButton, "OkButton"); - getWidget(mBestAttackButton, "BestAttackButton"); - getWidget(mGrabCursorButton, "GrabCursorButton"); - getWidget(mSubtitlesButton, "SubtitlesButton"); - getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); - getWidget(mMenuTransparencySlider, "MenuTransparencySlider"); - getWidget(mToolTipDelaySlider, "ToolTipDelaySlider"); - getWidget(mViewDistanceSlider, "ViewDistanceSlider"); getWidget(mFullscreenButton, "FullscreenButton"); getWidget(mVSyncButton, "VSyncButton"); getWidget(mFPSButton, "FPSButton"); getWidget(mFOVSlider, "FOVSlider"); - getWidget(mMasterVolumeSlider, "MasterVolume"); - getWidget(mVoiceVolumeSlider, "VoiceVolume"); - getWidget(mEffectsVolumeSlider, "EffectsVolume"); - getWidget(mFootstepsVolumeSlider, "FootstepsVolume"); - getWidget(mMusicVolumeSlider, "MusicVolume"); getWidget(mAnisotropySlider, "AnisotropySlider"); getWidget(mTextureFilteringButton, "TextureFilteringButton"); getWidget(mAnisotropyLabel, "AnisotropyLabel"); getWidget(mAnisotropyBox, "AnisotropyBox"); - getWidget(mWaterShaderButton, "WaterShaderButton"); - getWidget(mReflectObjectsButton, "ReflectObjectsButton"); - getWidget(mReflectActorsButton, "ReflectActorsButton"); - getWidget(mReflectTerrainButton, "ReflectTerrainButton"); getWidget(mShadersButton, "ShadersButton"); getWidget(mShaderModeButton, "ShaderModeButton"); getWidget(mShadowsEnabledButton, "ShadowsEnabledButton"); - getWidget(mShadowsLargeDistance, "ShadowsLargeDistance"); getWidget(mShadowsTextureSize, "ShadowsTextureSize"); - getWidget(mActorShadows, "ActorShadows"); - getWidget(mStaticsShadows, "StaticsShadows"); - getWidget(mMiscShadows, "MiscShadows"); - getWidget(mTerrainShadows, "TerrainShadows"); getWidget(mControlsBox, "ControlsBox"); getWidget(mResetControlsButton, "ResetControlsButton"); - getWidget(mInvertYButton, "InvertYButton"); - getWidget(mCameraSensitivitySlider, "CameraSensitivitySlider"); getWidget(mRefractionButton, "RefractionButton"); - mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mGrabCursorButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); - mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled); - mFullscreenButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mWaterShaderButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mRefractionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mReflectObjectsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mReflectTerrainButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mReflectActorsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mTextureFilteringButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringChanged); - mVSyncButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mFPSButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onFpsToggled); - mMenuTransparencySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mFOVSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mToolTipDelaySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mViewDistanceSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); mResolutionList->eventListChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onResolutionSelected); - mAnisotropySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mShadowsEnabledButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mShadowsLargeDistance->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadowsTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSizeChanged); - mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mMiscShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mTerrainShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - - mMasterVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mVoiceVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mEffectsVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mFootstepsVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - mMusicVolumeSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); center(); @@ -194,73 +219,25 @@ namespace MWGui mResolutionList->addItem(str); } - // read settings - int menu_transparency = (mMenuTransparencySlider->getScrollRange()-1) * Settings::Manager::getFloat("menu transparency", "GUI"); - mMenuTransparencySlider->setScrollPosition(menu_transparency); - int tooltip_delay = (mToolTipDelaySlider->getScrollRange()-1) * Settings::Manager::getFloat("tooltip delay", "GUI"); - mToolTipDelaySlider->setScrollPosition(tooltip_delay); - - mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); - mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); - mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}"); - mGrabCursorButton->setCaptionWithReplacing(Settings::Manager::getBool("grab cursor", "Input") ? "#{sOn}" : "#{sOff}"); - - float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); - mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); - MyGUI::TextBox* fovText; - getWidget(fovText, "FovText"); - fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getFloat("field of view", "General"))) + ")"); - - float anisotropyVal = Settings::Manager::getInt("anisotropy", "General") / 16.0; - mAnisotropySlider->setScrollPosition(anisotropyVal * (mAnisotropySlider->getScrollRange()-1)); std::string tf = Settings::Manager::getString("texture filtering", "General"); mTextureFilteringButton->setCaption(textureFilteringToStr(tf)); mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(Settings::Manager::getInt("anisotropy", "General")) + ")"); - float val = (Settings::Manager::getFloat("max viewing distance", "Viewing distance")-sViewDistMin)/(sViewDistMax-sViewDistMin); - int viewdist = (mViewDistanceSlider->getScrollRange()-1) * val; - mViewDistanceSlider->setScrollPosition(viewdist); - - mMasterVolumeSlider->setScrollPosition(Settings::Manager::getFloat("master volume", "Sound") * (mMasterVolumeSlider->getScrollRange()-1)); - mMusicVolumeSlider->setScrollPosition(Settings::Manager::getFloat("music volume", "Sound") * (mMusicVolumeSlider->getScrollRange()-1)); - mEffectsVolumeSlider->setScrollPosition(Settings::Manager::getFloat("sfx volume", "Sound") * (mEffectsVolumeSlider->getScrollRange()-1)); - mFootstepsVolumeSlider->setScrollPosition(Settings::Manager::getFloat("footsteps volume", "Sound") * (mFootstepsVolumeSlider->getScrollRange()-1)); - mVoiceVolumeSlider->setScrollPosition(Settings::Manager::getFloat("voice volume", "Sound") * (mVoiceVolumeSlider->getScrollRange()-1)); - - mWaterShaderButton->setCaptionWithReplacing(Settings::Manager::getBool("shader", "Water") ? "#{sOn}" : "#{sOff}"); - mReflectObjectsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect statics", "Water") ? "#{sOn}" : "#{sOff}"); - mReflectActorsButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect actors", "Water") ? "#{sOn}" : "#{sOff}"); - mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}"); - mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows")); - mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}"); - mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}"); - mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); - mStaticsShadows->setCaptionWithReplacing(Settings::Manager::getBool("statics shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); - mMiscShadows->setCaptionWithReplacing(Settings::Manager::getBool("misc shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); - mTerrainShadows->setCaptionWithReplacing(Settings::Manager::getBool("terrain shadows", "Shadows") ? "#{sOn}" : "#{sOff}"); - - float cameraSens = (Settings::Manager::getFloat("camera sensitivity", "Input")-0.2)/(5.0-0.2); - mCameraSensitivitySlider->setScrollPosition (cameraSens * (mCameraSensitivitySlider->getScrollRange()-1)); - mCameraSensitivitySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); - - mInvertYButton->setCaptionWithReplacing(Settings::Manager::getBool("invert y axis", "Input") ? "#{sOn}" : "#{sOff}"); - - mShadersButton->setCaptionWithReplacing (Settings::Manager::getBool("shaders", "Objects") ? "#{sOn}" : "#{sOff}"); mShaderModeButton->setCaption (Settings::Manager::getString("shader mode", "General")); - mRefractionButton->setCaptionWithReplacing (Settings::Manager::getBool("refraction", "Water") ? "#{sOn}" : "#{sOff}"); - if (!Settings::Manager::getBool("shaders", "Objects")) { mRefractionButton->setEnabled(false); mShadowsEnabledButton->setEnabled(false); } - mFullscreenButton->setCaptionWithReplacing(Settings::Manager::getBool("fullscreen", "Video") ? "#{sOn}" : "#{sOff}"); - mVSyncButton->setCaptionWithReplacing(Settings::Manager::getBool("vsync", "Video") ? "#{sOn}": "#{sOff}"); mFPSButton->setCaptionWithReplacing(fpsLevelToStr(Settings::Manager::getInt("fps", "HUD"))); + + MyGUI::TextBox* fovText; + getWidget(fovText, "FovText"); + fovText->setCaption("Field of View (" + boost::lexical_cast(int(Settings::Manager::getInt("field of view", "General"))) + ")"); } void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) @@ -320,6 +297,39 @@ namespace MWGui newState = true; } + if (_sender == mVSyncButton) + { + // Ogre::Window::setVSyncEnabled is bugged in 1.8 +#if OGRE_VERSION < (1 << 16 | 9 << 8 | 0) + MWBase::Environment::get().getWindowManager()-> + messageBox("VSync will be applied after a restart", std::vector()); +#endif + } + + if (_sender == mShadersButton) + { + if (newState == false) + { + // refraction needs shaders to display underwater fog + mRefractionButton->setCaptionWithReplacing("#{sOff}"); + mRefractionButton->setEnabled(false); + + Settings::Manager::setBool("refraction", "Water", false); + + // shadows not supported + mShadowsEnabledButton->setEnabled(false); + mShadowsEnabledButton->setCaptionWithReplacing("#{sOff}"); + Settings::Manager::setBool("enabled", "Shadows", false); + } + else + { + // re-enable + mRefractionButton->setEnabled(true); + + mShadowsEnabledButton->setEnabled(true); + } + } + if (_sender == mFullscreenButton) { // check if this resolution is supported in fullscreen @@ -341,64 +351,15 @@ namespace MWGui MWBase::Environment::get().getWindowManager()-> messageBox(msg); _sender->castType()->setCaption(off); - } - else - { - Settings::Manager::setBool("fullscreen", "Video", newState); - apply(); + return; } } - else if (_sender == mVSyncButton) + + if (getSettingType(_sender) == checkButtonType) { - Settings::Manager::setBool("vsync", "Video", newState); - // Ogre::Window::setVSyncEnabled is bugged in 1.8 -#if OGRE_VERSION < (1 << 16 | 9 << 8 | 0) - MWBase::Environment::get().getWindowManager()-> - messageBox("VSync will be applied after a restart", std::vector()); -#endif - apply(); - } - else - { - if (_sender == mVSyncButton) - Settings::Manager::setBool("vsync", "Video", newState); - if (_sender == mWaterShaderButton) - Settings::Manager::setBool("shader", "Water", newState); - else if (_sender == mRefractionButton) - Settings::Manager::setBool("refraction", "Water", newState); - else if (_sender == mReflectObjectsButton) - { - Settings::Manager::setBool("reflect misc", "Water", newState); - Settings::Manager::setBool("reflect statics", "Water", newState); - Settings::Manager::setBool("reflect statics small", "Water", newState); - } - else if (_sender == mReflectActorsButton) - Settings::Manager::setBool("reflect actors", "Water", newState); - else if (_sender == mReflectTerrainButton) - Settings::Manager::setBool("reflect terrain", "Water", newState); - else if (_sender == mShadowsEnabledButton) - Settings::Manager::setBool("enabled", "Shadows", newState); - else if (_sender == mShadowsLargeDistance) - Settings::Manager::setBool("split", "Shadows", newState); - else if (_sender == mActorShadows) - Settings::Manager::setBool("actor shadows", "Shadows", newState); - else if (_sender == mStaticsShadows) - Settings::Manager::setBool("statics shadows", "Shadows", newState); - else if (_sender == mMiscShadows) - Settings::Manager::setBool("misc shadows", "Shadows", newState); - else if (_sender == mTerrainShadows) - Settings::Manager::setBool("terrain shadows", "Shadows", newState); - else if (_sender == mInvertYButton) - Settings::Manager::setBool("invert y axis", "Input", newState); - else if (_sender == mCrosshairButton) - Settings::Manager::setBool("crosshair", "HUD", newState); - else if (_sender == mSubtitlesButton) - Settings::Manager::setBool("subtitles", "GUI", newState); - else if (_sender == mBestAttackButton) - Settings::Manager::setBool("best attack", "Game", newState); - else if (_sender == mGrabCursorButton) - Settings::Manager::setBool("grab cursor", "Input", newState); + Settings::Manager::setBool(getSettingName(_sender), getSettingCategory(_sender), newState); apply(); + return; } } @@ -419,50 +380,6 @@ namespace MWGui apply(); } - void SettingsWindow::onShadersToggled(MyGUI::Widget* _sender) - { - std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On"); - std::string off = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOff", "On"); - - std::string val = static_cast(_sender)->getCaption(); - if (val == off) - val = on; - else - val = off; - static_cast(_sender)->setCaptionWithReplacing (val); - - if (val == off) - { - Settings::Manager::setBool("shaders", "Objects", false); - - // refraction needs shaders to display underwater fog - mRefractionButton->setCaptionWithReplacing("#{sOff}"); - mRefractionButton->setEnabled(false); - - Settings::Manager::setBool("refraction", "Water", false); - Settings::Manager::setBool("underwater effect", "Water", false); - - // shadows not supported - mShadowsEnabledButton->setEnabled(false); - mShadowsEnabledButton->setCaptionWithReplacing("#{sOff}"); - Settings::Manager::setBool("enabled", "Shadows", false); - } - else - { - Settings::Manager::setBool("shaders", "Objects", true); - - // re-enable - mReflectObjectsButton->setEnabled(true); - mReflectActorsButton->setEnabled(true); - mReflectTerrainButton->setEnabled(true); - mRefractionButton->setEnabled(true); - - mShadowsEnabledButton->setEnabled(true); - } - - apply(); - } - void SettingsWindow::onFpsToggled(MyGUI::Widget* _sender) { int newLevel = (Settings::Manager::getInt("fps", "HUD") + 1) % 3; @@ -479,39 +396,34 @@ namespace MWGui void SettingsWindow::onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos) { - float val = pos / float(scroller->getScrollRange()-1); - if (scroller == mMenuTransparencySlider) - Settings::Manager::setFloat("menu transparency", "GUI", val); - else if (scroller == mToolTipDelaySlider) - Settings::Manager::setFloat("tooltip delay", "GUI", val); - else if (scroller == mViewDistanceSlider) - Settings::Manager::setFloat("max viewing distance", "Viewing distance", (1-val) * sViewDistMin + val * sViewDistMax); - else if (scroller == mFOVSlider) + if (getSettingType(scroller) == "Slider") { - MyGUI::TextBox* fovText; - getWidget(fovText, "FovText"); - fovText->setCaption("Field of View (" + boost::lexical_cast(int((1-val) * sFovMin + val * sFovMax)) + ")"); - Settings::Manager::setFloat("field of view", "General", (1-val) * sFovMin + val * sFovMax); - } - else if (scroller == mAnisotropySlider) - { - mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(int(val*16)) + ")"); - Settings::Manager::setInt("anisotropy", "General", val * 16); - } - else if (scroller == mMasterVolumeSlider) - Settings::Manager::setFloat("master volume", "Sound", val); - else if (scroller == mVoiceVolumeSlider) - Settings::Manager::setFloat("voice volume", "Sound", val); - else if (scroller == mEffectsVolumeSlider) - Settings::Manager::setFloat("sfx volume", "Sound", val); - else if (scroller == mFootstepsVolumeSlider) - Settings::Manager::setFloat("footsteps volume", "Sound", val); - else if (scroller == mMusicVolumeSlider) - Settings::Manager::setFloat("music volume", "Sound", val); - else if (scroller == mCameraSensitivitySlider) - Settings::Manager::setFloat("camera sensitivity", "Input", (1-val) * 0.2 + val * 5.f); + if (getSettingValueType(scroller) == "Float") + { + float value = pos / float(scroller->getScrollRange()-1); - apply(); + float min,max; + getSettingMinMax(scroller, min, max); + value = min + (max-min) * value; + Settings::Manager::setFloat(getSettingName(scroller), getSettingCategory(scroller), value); + + if (scroller == mFOVSlider) + { + MyGUI::TextBox* fovText; + getWidget(fovText, "FovText"); + fovText->setCaption("Field of View (" + boost::lexical_cast(int(value)) + ")"); + } + } + else + { + Settings::Manager::setInt(getSettingName(scroller), getSettingCategory(scroller), pos); + if (scroller == mAnisotropySlider) + { + mAnisotropyLabel->setCaption("Anisotropy (" + boost::lexical_cast(pos) + ")"); + } + } + apply(); + } } void SettingsWindow::apply() diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 6b9ce414b8..7a6c1a5ed5 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -19,61 +19,29 @@ namespace MWGui void updateControlsBox(); - private: - static int const sFovMin = 30; - static int const sFovMax = 140; - static int const sViewDistMin = 2000; - static int const sViewDistMax = 5600; - - protected: + protected: MyGUI::Button* mOkButton; - MyGUI::ScrollBar* mMenuTransparencySlider; - MyGUI::ScrollBar* mToolTipDelaySlider; - MyGUI::Button* mSubtitlesButton; - MyGUI::Button* mCrosshairButton; - MyGUI::Button* mBestAttackButton; - MyGUI::Button* mGrabCursorButton; - // graphics MyGUI::ListBox* mResolutionList; MyGUI::Button* mFullscreenButton; MyGUI::Button* mVSyncButton; MyGUI::Button* mFPSButton; - MyGUI::ScrollBar* mViewDistanceSlider; MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mAnisotropySlider; MyGUI::ComboBox* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; MyGUI::Widget* mAnisotropyBox; - MyGUI::Button* mWaterShaderButton; - MyGUI::Button* mReflectObjectsButton; - MyGUI::Button* mReflectActorsButton; - MyGUI::Button* mReflectTerrainButton; MyGUI::Button* mShadersButton; MyGUI::Button* mShaderModeButton; MyGUI::Button* mRefractionButton; MyGUI::Button* mShadowsEnabledButton; - MyGUI::Button* mShadowsLargeDistance; MyGUI::ComboBox* mShadowsTextureSize; - MyGUI::Button* mActorShadows; - MyGUI::Button* mStaticsShadows; - MyGUI::Button* mMiscShadows; - MyGUI::Button* mTerrainShadows; - - // audio - MyGUI::ScrollBar* mMasterVolumeSlider; - MyGUI::ScrollBar* mVoiceVolumeSlider; - MyGUI::ScrollBar* mEffectsVolumeSlider; - MyGUI::ScrollBar* mFootstepsVolumeSlider; - MyGUI::ScrollBar* mMusicVolumeSlider; // controls MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; - MyGUI::Button* mInvertYButton; - MyGUI::ScrollBar* mCameraSensitivitySlider; void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); @@ -84,7 +52,6 @@ namespace MWGui void onResolutionAccept(); void onResolutionCancel(); - void onShadersToggled(MyGUI::Widget* _sender); void onShaderModeToggled(MyGUI::Widget* _sender); void onShadowTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); @@ -94,6 +61,8 @@ namespace MWGui void onResetDefaultBindingsAccept (); void apply(); + + void configureWidgets(MyGUI::Widget* widget); }; } diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 1fa5d88341..5368cbe687 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -418,10 +418,8 @@ void Water::applyRTT() void Water::applyVisibilityMask() { mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water") - + RV_Statics * Settings::Manager::getBool("reflect statics", "Water") - + RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water") + + (RV_Statics + RV_StaticsSmall + RV_Misc) * Settings::Manager::getBool("reflect statics", "Water") + RV_Actors * Settings::Manager::getBool("reflect actors", "Water") - + RV_Misc * Settings::Manager::getBool("reflect misc", "Water") + RV_Sky; if (mReflection) @@ -444,8 +442,6 @@ void Water::processChangedSettings(const Settings::CategorySettingVector& settin if ( it->first == "Water" && ( it->second == "reflect actors" || it->second == "reflect terrain" - || it->second == "reflect misc" - || it->second == "reflect small statics" || it->second == "reflect statics")) applyVisMask = true; } diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index 61103963db..adf9f1557f 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -18,6 +18,10 @@ + + + + @@ -34,6 +38,10 @@ + + + + @@ -45,28 +53,44 @@ - + + + + + - + + + + + - + + + + + - + + + + + @@ -81,6 +105,10 @@ + + + + @@ -89,6 +117,10 @@ + + + + @@ -97,6 +129,10 @@ + + + + @@ -105,6 +141,10 @@ + + + + @@ -113,6 +153,10 @@ + + + + @@ -127,7 +171,11 @@ - + + + + + @@ -139,6 +187,12 @@ + + + + + + @@ -149,8 +203,6 @@ - - @@ -163,16 +215,23 @@ - - + + + + + - + + + + + @@ -186,7 +245,11 @@ - + + + + + @@ -205,6 +268,12 @@ + + + + + + @@ -233,8 +302,10 @@ - - + + + + @@ -244,6 +315,12 @@ + + + + + + @@ -260,7 +337,11 @@ - + + + + + @@ -269,21 +350,33 @@ - + + + + + - + + + + + - + + + + + @@ -291,7 +384,11 @@ - + + + + + @@ -302,7 +399,11 @@ - + + + + + @@ -311,35 +412,52 @@ - + + + + + - - - + + + + - + + + + + - + + + + + - + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4fb7097f84..5dfc054553 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -137,9 +137,7 @@ refraction = true rtt size = 512 reflect terrain = true reflect statics = false -reflect small statics = false reflect actors = false -reflect misc = false [Sound] # Device name. Blank means default From 772a49b2a641b2f3dc0f9a10408d58b85cc85ddd Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 23 Mar 2014 13:24:04 +1100 Subject: [PATCH 199/240] Just some notes for future maintenance. --- apps/openmw/mwworld/physicssystem.cpp | 102 +++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3c0c3ffaa3..c2d902d697 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -50,25 +50,102 @@ namespace MWWorld const Ogre::Vector3 &velocity, float &remainingTime, OEngine::Physic::PhysicEngine *engine) { + /* + * Slide up an incline or set of stairs. Should be called only after a + * collision detection otherwise unnecessary tracing will be performed. + * + * NOTE: with a small change this method can be used to step over an obstacle + * of height sStepSize. + * + * If successful return 'true' and update 'position' to the new possible + * location and adjust 'remainingTime'. + * + * If not successful return 'false'. May fail for these reasons: + * - can't move directly up from current position + * - having moved up by between epsilon() and sStepSize, can't move forward + * - having moved forward by between epsilon() and velocity*remainingTime, + * = moved down between 0 and just under sStepSize but slope was too steep, or + * = moved the full sStepSize down (FIXME: this could be a bug) + * + * + * + * Starting position. Obstacle or stairs with height upto sStepSize in front. + * + * +--+ +--+ |XX + * | | -------> velocity | | +--+XX + * | | | | |XXXXX + * | | +--+ | | +--+XXXXX + * | | |XX| | | |XXXXXXXX + * +--+ +--+ +--+ +-------- + * ============================================== + */ + + /* + * Try moving up sStepSize using stepper. + * FIXME: does not work in case there is no front obstacle but there is one above + * + * +--+ +--+ + * | | | | + * | | | | |XX + * | | | | +--+XX + * | | | | |XXXXX + * +--+ +--+ +--+ +--+XXXXX + * |XX| |XXXXXXXX + * +--+ +-------- + * ============================================== + */ OEngine::Physic::ActorTracer tracer, stepper; stepper.doTrace(colobj, position, position+Ogre::Vector3(0.0f,0.0f,sStepSize), engine); if(stepper.mFraction < std::numeric_limits::epsilon()) - return false; + return false; // didn't even move the smallest representable amount + // (TODO: shouldn't this be larger? Why bother with such a small amount?) + /* + * Try moving from the elevated position using tracer. + * + * +--+ +--+ + * | | |YY| FIXME: collision with object YY + * | | +--+ + * | | + * <------------------->| | + * +--+ +--+ + * |XX| the moved amount is velocity*remainingTime*tracer.mFraction + * +--+ + * ============================================== + */ tracer.doTrace(colobj, stepper.mEndPos, stepper.mEndPos + velocity*remainingTime, engine); if(tracer.mFraction < std::numeric_limits::epsilon()) - return false; + return false; // didn't even move the smallest representable amount + /* + * Try moving back down sStepSize using stepper. + * NOTE: if there is an obstacle below (e.g. stairs), we'll be "stepping up". + * Below diagram is the case where we "stepped over" an obstacle in front. + * + * +--+ + * |YY| + * +--+ +--+ + * | | + * | | + * +--+ | | + * |XX| | | + * +--+ +--+ + * ============================================== + */ stepper.doTrace(colobj, tracer.mEndPos, tracer.mEndPos-Ogre::Vector3(0.0f,0.0f,sStepSize), engine); if(stepper.mFraction < 1.0f && getSlope(stepper.mPlaneNormal) <= sMaxSlope) { // only step down onto semi-horizontal surfaces. don't step down onto the side of a house or a wall. + // TODO: stepper.mPlaneNormal does not appear to be reliable - needs more testing + // NOTE: caller's variables 'position' & 'remainingTime' are modified here position = stepper.mEndPos; - remainingTime *= (1.0f-tracer.mFraction); + remainingTime *= (1.0f-tracer.mFraction); // remaining time is proportional to remaining distance return true; } + // moved between 0 and just under sStepSize distance but slope was too great, + // or moved full sStepSize distance (FIXME: is this a bug?) return false; } @@ -164,26 +241,27 @@ namespace MWWorld { velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; // not in water nor can fly, so need to deal with gravity - if(!physicActor->getOnGround()) + if(!physicActor->getOnGround()) // if current OnGround status is false, must be falling or jumping { // If falling, add part of the incoming velocity with the current inertia + // TODO: but we could be jumping up? velocity = velocity * time + physicActor->getInertialForce(); } - inertia = velocity; // REM velocity is for z axis only in this code block + inertia = velocity; // NOTE: velocity is for z axis only in this code block - if(!(movement.z > 0.0f)) + if(!(movement.z > 0.0f)) // falling or moving horizontally (or stationary?) check if we're on ground now { - wasOnGround = physicActor->getOnGround(); - // TODO: Find out if there is a significance with the value 2 used here - tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); + wasOnGround = physicActor->getOnGround(); // store current state + tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); // check if down 2 possible if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) isOnGround = true; } } + // NOTE: isOnGround was initialised false, so should stay false if falling or sliding horizontally if(isOnGround) { - // if we're on the ground, don't try to fall + // if we're on the ground, don't try to fall any more velocity.z = std::max(0.0f, velocity.z); // NOTE: two different velocity assignments above } @@ -196,6 +274,7 @@ namespace MWWorld float remainingTime = time; for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { + // NOTE: velocity is either z axis only or x & z axis Ogre::Vector3 nextpos = newPosition + velocity * remainingTime; // If not able to fly, walk or bipedal don't allow to move out of water @@ -229,8 +308,9 @@ namespace MWWorld break; } - // We hit something. Try to step up onto it. + // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // NOTE: May need to stop slaughterfish step out of the water. + // NOTE: stepMove may modify newPosition if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine)) isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity else From 205354ba30d18bc711ee5d9e106b9f5f447b468c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Mar 2014 13:40:56 +0100 Subject: [PATCH 200/240] some ambient light cleanup --- apps/opencs/view/render/scenewidget.cpp | 8 ++------ apps/opencs/view/render/scenewidget.hpp | 6 +++--- apps/opencs/view/render/unpagedworldspacewidget.cpp | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 5a3c14b49c..c59c142fc9 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -32,11 +32,7 @@ namespace CSVRender mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - // Throw in a random color just to make sure multiple scenes work - Ogre::Real r = Ogre::Math::RangeRandom(0, 1); - Ogre::Real g = Ogre::Math::RangeRandom(0, 1); - Ogre::Real b = Ogre::Math::RangeRandom(0, 1); - mSceneMgr->setAmbientLight(Ogre::ColourValue(r,g,b,1)); + mSceneMgr->setAmbientLight (Ogre::ColourValue (0,0,0,1)); Ogre::Light* l = mSceneMgr->createLight(); l->setType (Ogre::Light::LT_DIRECTIONAL); @@ -57,7 +53,7 @@ namespace CSVRender timer->start (20); /// \todo make this configurable } - void SceneWidget::setAmbient (const Ogre::ColourValue& colour) + void SceneWidget::setDefaultAmbient (const Ogre::ColourValue& colour) { mSceneMgr->setAmbientLight (colour); } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 375c877d22..51d3464edb 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -26,9 +26,6 @@ namespace CSVRender QPaintEngine* paintEngine() const; - void setAmbient (const Ogre::ColourValue& colour); - ///< \note The actual ambient colour may differ based on lighting settings. - protected: void setNavigation (Navigation *navigation); @@ -38,6 +35,9 @@ namespace CSVRender void flagAsModified(); + void setDefaultAmbient (const Ogre::ColourValue& colour); + ///< \note The actual ambient colour may differ based on lighting settings. + private: void paintEvent(QPaintEvent* e); void resizeEvent(QResizeEvent* e); diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index c7edbe79b1..fb74788cc2 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -15,7 +15,7 @@ void CSVRender::UnpagedWorldspaceWidget::update() Ogre::ColourValue colour; colour.setAsABGR (record.get().mAmbi.mAmbient); - setAmbient (colour); + setDefaultAmbient (colour); /// \todo deal with mSunlight and mFog/mForDensity } From d5506172e873836939495aae2805bf2472ec95c4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 23 Mar 2014 15:14:26 +0100 Subject: [PATCH 201/240] added lighting switching --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/lighting.cpp | 4 ++ apps/opencs/view/render/lighting.hpp | 27 +++++++++++++ apps/opencs/view/render/scenewidget.cpp | 40 +++++++++++++++++++- apps/opencs/view/render/scenewidget.hpp | 22 ++++++++++- apps/opencs/view/render/worldspacewidget.hpp | 2 +- apps/opencs/view/world/previewsubview.cpp | 8 ++-- apps/opencs/view/world/scenesubview.cpp | 7 +++- 8 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 apps/opencs/view/render/lighting.cpp create mode 100644 apps/opencs/view/render/lighting.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index b2b534031b..ee7887f3ef 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -69,7 +69,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation navigation1st navigationfree navigationorbit + navigation navigation1st navigationfree navigationorbit lighting ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/lighting.cpp b/apps/opencs/view/render/lighting.cpp new file mode 100644 index 0000000000..d57570d695 --- /dev/null +++ b/apps/opencs/view/render/lighting.cpp @@ -0,0 +1,4 @@ + +#include "lighting.hpp" + +CSVRender::Lighting::~Lighting() {} \ No newline at end of file diff --git a/apps/opencs/view/render/lighting.hpp b/apps/opencs/view/render/lighting.hpp new file mode 100644 index 0000000000..a1da9f7e38 --- /dev/null +++ b/apps/opencs/view/render/lighting.hpp @@ -0,0 +1,27 @@ +#ifndef OPENCS_VIEW_LIGHTING_H +#define OPENCS_VIEW_LIGHTING_H + +namespace Ogre +{ + class SceneManager; + class ColourValue; +} + +namespace CSVRender +{ + class Lighting + { + public: + + virtual ~Lighting(); + + virtual void activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient = 0) = 0; + + virtual void deactivate() = 0; + + virtual void setDefaultAmbient (const Ogre::ColourValue& colour) = 0; + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index c59c142fc9..8a1db1da06 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -11,7 +11,10 @@ #include #include +#include "../world/scenetoolmode.hpp" + #include "navigation.hpp" +#include "lighting.hpp" namespace CSVRender { @@ -19,11 +22,12 @@ namespace CSVRender : QWidget(parent) , mWindow(NULL) , mCamera(NULL) - , mSceneMgr(NULL), mNavigation (0), mUpdate (false) + , mSceneMgr(NULL), mNavigation (0), mLighting (0), mUpdate (false) , mKeyForward (false), mKeyBackward (false), mKeyLeft (false), mKeyRight (false) , mKeyRollLeft (false), mKeyRollRight (false) , mFast (false), mDragging (false), mMod1 (false) , mFastFactor (4) /// \todo make this configurable + , mDefaultAmbient (0, 0, 0, 0), mHasDefaultAmbient (false) { setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); @@ -53,9 +57,27 @@ namespace CSVRender timer->start (20); /// \todo make this configurable } + CSVWorld::SceneToolMode *SceneWidget::makeLightingSelector (CSVWorld::SceneToolbar *parent) + { + CSVWorld::SceneToolMode *tool = new CSVWorld::SceneToolMode (parent); + + tool->addButton (":door.png", "day"); /// \todo replace icons + tool->addButton (":GMST.png", "night"); + tool->addButton (":Info.png", "bright"); + + connect (tool, SIGNAL (modeChanged (const std::string&)), + this, SLOT (selectLightingMode (const std::string&))); + + return tool; + } + void SceneWidget::setDefaultAmbient (const Ogre::ColourValue& colour) { - mSceneMgr->setAmbientLight (colour); + mDefaultAmbient = colour; + mHasDefaultAmbient = true; + + if (mLighting) + mLighting->setDefaultAmbient (colour); } void SceneWidget::updateOgreWindow() @@ -312,4 +334,18 @@ namespace CSVRender { return mFast ? mFastFactor : 1; } + + void SceneWidget::setLighting (Lighting *lighting) + { + if (mLighting) + mLighting->deactivate(); + + mLighting = lighting; + mLighting->activate (mSceneManager, mHasDefaultAmbient ? &mDefaultAmbient : 0); + } + + void SceneWidget::selectLightingMode (const std::string& mode) + { + + } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 51d3464edb..5058fb860c 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -3,17 +3,25 @@ #include +#include + namespace Ogre { class Camera; class SceneManager; class RenderWindow; - class ColourValue; +} + +namespace CSVWorld +{ + class SceneToolMode; + class SceneToolbar; } namespace CSVRender { class Navigation; + class Lighting; class SceneWidget : public QWidget { @@ -26,6 +34,10 @@ namespace CSVRender QPaintEngine* paintEngine() const; + CSVWorld::SceneToolMode *makeLightingSelector (CSVWorld::SceneToolbar *parent); + ///< \attention The created tool is not added to the toolbar (via addTool). Doing that + /// is the responsibility of the calling function. + protected: void setNavigation (Navigation *navigation); @@ -61,11 +73,15 @@ namespace CSVRender int getFastFactor() const; + void setLighting (Lighting *lighting); + ///< \attention The ownership of \a lighting is not transferred to *this. + Ogre::Camera* mCamera; Ogre::SceneManager* mSceneMgr; Ogre::RenderWindow* mWindow; Navigation *mNavigation; + Lighting *mLighting; bool mUpdate; bool mKeyForward; bool mKeyBackward; @@ -78,10 +94,14 @@ namespace CSVRender bool mMod1; QPoint mOldPos; int mFastFactor; + Ogre::ColourValue mDefaultAmbient; + bool mHasDefaultAmbient; private slots: void update(); + + void selectLightingMode (const std::string& mode); }; } diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 2eccca3bf4..7921c3560c 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -28,7 +28,7 @@ namespace CSVRender WorldspaceWidget (QWidget *parent = 0); CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); - ///< \important The created tool is not added to the toolbar (via addTool). Doing that + ///< \attention The created tool is not added to the toolbar (via addTool). Doing that /// is the responsibility of the calling function. void selectDefaultNavigationMode(); diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index df9c3276cb..ac9776d222 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -3,11 +3,10 @@ #include -#include "../render/scenewidget.hpp" +#include "../render/previewwidget.hpp" #include "scenetoolbar.hpp" - -#include "../render/previewwidget.hpp" +#include "scenetoolmode.hpp" CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id) @@ -31,6 +30,9 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo SceneToolbar *toolbar = new SceneToolbar (48, this); + SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); + toolbar->addTool (lightingTool); + layout->addWidget (toolbar, 0); layout->addWidget (mScene, 1); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 66e0266046..10e8b40715 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -39,8 +39,11 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); - SceneToolMode *tool = mScene->makeNavigationSelector (toolbar); - toolbar->addTool (tool); + SceneToolMode *navigationTool = mScene->makeNavigationSelector (toolbar); + toolbar->addTool (navigationTool); + + SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); + toolbar->addTool (lightingTool); layout2->addWidget (toolbar, 0); From 235dbdf18ef66678bd1ccae9aefc6d2ba4b41ed5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Mar 2014 17:07:43 +0100 Subject: [PATCH 202/240] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 28a30e907e..7d2a5ca42f 100644 --- a/credits.txt +++ b/credits.txt @@ -21,6 +21,7 @@ athile Britt Mathis (galdor557) BrotherBrick cc9cii +Chris Boyce (slothlife) Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) From fa29942b27bc44c0f3e9ce8c97f250541d66d3b7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Mar 2014 17:47:56 +0100 Subject: [PATCH 203/240] added day lighting mode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/lightingday.cpp | 36 +++++++++++++++++++++++++ apps/opencs/view/render/lightingday.hpp | 31 +++++++++++++++++++++ apps/opencs/view/render/scenewidget.cpp | 19 +++++++------ apps/opencs/view/render/scenewidget.hpp | 3 +++ 5 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 apps/opencs/view/render/lightingday.cpp create mode 100644 apps/opencs/view/render/lightingday.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ee7887f3ef..608f01d12c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -69,7 +69,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation navigation1st navigationfree navigationorbit lighting + navigation navigation1st navigationfree navigationorbit lighting lightingday ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/lightingday.cpp b/apps/opencs/view/render/lightingday.cpp new file mode 100644 index 0000000000..ab0257c0c7 --- /dev/null +++ b/apps/opencs/view/render/lightingday.cpp @@ -0,0 +1,36 @@ + +#include "lightingday.hpp" + +#include + +CSVRender::LightingDay::LightingDay() : mSceneManager (0), mLight (0) {} + +void CSVRender::LightingDay::activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient) +{ + mSceneManager = sceneManager; + + if (defaultAmbient) + mSceneManager->setAmbientLight (*defaultAmbient); + else + mSceneManager->setAmbientLight (Ogre::ColourValue (0.7, 0.7, 0.7, 1)); + + mLight = mSceneManager->createLight(); + mLight->setType (Ogre::Light::LT_DIRECTIONAL); + mLight->setDirection (Ogre::Vector3 (0, 0, -1)); + mLight->setDiffuseColour (Ogre::ColourValue (1, 1, 1)); +} + +void CSVRender::LightingDay::deactivate() +{ + if (mLight) + { + mSceneManager->destroyLight (mLight); + mLight = 0; + } +} + +void CSVRender::LightingDay::setDefaultAmbient (const Ogre::ColourValue& colour) +{ + mSceneManager->setAmbientLight (colour); +} \ No newline at end of file diff --git a/apps/opencs/view/render/lightingday.hpp b/apps/opencs/view/render/lightingday.hpp new file mode 100644 index 0000000000..8638146e23 --- /dev/null +++ b/apps/opencs/view/render/lightingday.hpp @@ -0,0 +1,31 @@ +#ifndef OPENCS_VIEW_LIGHTING_DAY_H +#define OPENCS_VIEW_LIGHTING_DAY_H + +#include "lighting.hpp" + +namespace Ogre +{ + class Light; +} + +namespace CSVRender +{ + class LightingDay : public Lighting + { + Ogre::SceneManager *mSceneManager; + Ogre::Light *mLight; + + public: + + LightingDay(); + + virtual void activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient = 0); + + virtual void deactivate(); + + virtual void setDefaultAmbient (const Ogre::ColourValue& colour); + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 8a1db1da06..77d9ea1e07 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -38,19 +38,16 @@ namespace CSVRender mSceneMgr->setAmbientLight (Ogre::ColourValue (0,0,0,1)); - Ogre::Light* l = mSceneMgr->createLight(); - l->setType (Ogre::Light::LT_DIRECTIONAL); - l->setDirection (Ogre::Vector3(-0.4, -0.7, 0.3)); - l->setDiffuseColour (Ogre::ColourValue(0.7,0.7,0.7)); - mCamera = mSceneMgr->createCamera("foo"); - mCamera->setPosition(300,0,000); - mCamera->lookAt(0,0,0); - mCamera->setNearClipDistance(0.1); - mCamera->setFarClipDistance(30000); + mCamera->setPosition (300, 0, 0); + mCamera->lookAt (0, 0, 0); + mCamera->setNearClipDistance (0.1); + mCamera->setFarClipDistance (30000); mCamera->roll (Ogre::Degree (90)); + setLighting (&mLightingDay); + QTimer *timer = new QTimer (this); connect (timer, SIGNAL (timeout()), this, SLOT (update())); @@ -341,11 +338,13 @@ namespace CSVRender mLighting->deactivate(); mLighting = lighting; - mLighting->activate (mSceneManager, mHasDefaultAmbient ? &mDefaultAmbient : 0); + mLighting->activate (mSceneMgr, mHasDefaultAmbient ? &mDefaultAmbient : 0); } void SceneWidget::selectLightingMode (const std::string& mode) { + if (mode=="day") + setLighting (&mLightingDay); } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 5058fb860c..a1ecd733b9 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -5,6 +5,8 @@ #include +#include "lightingday.hpp" + namespace Ogre { class Camera; @@ -96,6 +98,7 @@ namespace CSVRender int mFastFactor; Ogre::ColourValue mDefaultAmbient; bool mHasDefaultAmbient; + LightingDay mLightingDay; private slots: From 9746f8bd32487c3de66bec834072c012454ca7ad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Mar 2014 17:59:42 +0100 Subject: [PATCH 204/240] added night lighting mode --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/lightingnight.cpp | 36 +++++++++++++++++++++++ apps/opencs/view/render/lightingnight.hpp | 31 +++++++++++++++++++ apps/opencs/view/render/scenewidget.cpp | 2 ++ apps/opencs/view/render/scenewidget.hpp | 2 ++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/render/lightingnight.cpp create mode 100644 apps/opencs/view/render/lightingnight.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 608f01d12c..a6200062da 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -69,7 +69,7 @@ opencs_units (view/render ) opencs_units_noqt (view/render - navigation navigation1st navigationfree navigationorbit lighting lightingday + navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/lightingnight.cpp b/apps/opencs/view/render/lightingnight.cpp new file mode 100644 index 0000000000..516bb3f408 --- /dev/null +++ b/apps/opencs/view/render/lightingnight.cpp @@ -0,0 +1,36 @@ + +#include "lightingnight.hpp" + +#include + +CSVRender::LightingNight::LightingNight() : mSceneManager (0), mLight (0) {} + +void CSVRender::LightingNight::activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient) +{ + mSceneManager = sceneManager; + + if (defaultAmbient) + mSceneManager->setAmbientLight (*defaultAmbient); + else + mSceneManager->setAmbientLight (Ogre::ColourValue (0.2, 0.2, 0.2, 1)); + + mLight = mSceneManager->createLight(); + mLight->setType (Ogre::Light::LT_DIRECTIONAL); + mLight->setDirection (Ogre::Vector3 (0, 0, -1)); + mLight->setDiffuseColour (Ogre::ColourValue (0.2, 0.2, 0.2)); +} + +void CSVRender::LightingNight::deactivate() +{ + if (mLight) + { + mSceneManager->destroyLight (mLight); + mLight = 0; + } +} + +void CSVRender::LightingNight::setDefaultAmbient (const Ogre::ColourValue& colour) +{ + mSceneManager->setAmbientLight (colour); +} \ No newline at end of file diff --git a/apps/opencs/view/render/lightingnight.hpp b/apps/opencs/view/render/lightingnight.hpp new file mode 100644 index 0000000000..47d1d7ce82 --- /dev/null +++ b/apps/opencs/view/render/lightingnight.hpp @@ -0,0 +1,31 @@ +#ifndef OPENCS_VIEW_LIGHTING_NIGHT_H +#define OPENCS_VIEW_LIGHTING_NIGHT_H + +#include "lighting.hpp" + +namespace Ogre +{ + class Light; +} + +namespace CSVRender +{ + class LightingNight : public Lighting + { + Ogre::SceneManager *mSceneManager; + Ogre::Light *mLight; + + public: + + LightingNight(); + + virtual void activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient = 0); + + virtual void deactivate(); + + virtual void setDefaultAmbient (const Ogre::ColourValue& colour); + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 77d9ea1e07..f1371041ca 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -345,6 +345,8 @@ namespace CSVRender { if (mode=="day") setLighting (&mLightingDay); + else if (mode=="night") + setLighting (&mLightingNight); } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index a1ecd733b9..96e7d7bb2c 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -6,6 +6,7 @@ #include #include "lightingday.hpp" +#include "lightingnight.hpp" namespace Ogre { @@ -99,6 +100,7 @@ namespace CSVRender Ogre::ColourValue mDefaultAmbient; bool mHasDefaultAmbient; LightingDay mLightingDay; + LightingNight mLightingNight; private slots: From ed524b2fd45e735ae8532ca7f07ded2a3c688882 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Mar 2014 18:29:15 +0100 Subject: [PATCH 205/240] added bright lighting mode --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/view/render/lightingbright.cpp | 30 +++++++++++++++++++++ apps/opencs/view/render/lightingbright.hpp | 31 ++++++++++++++++++++++ apps/opencs/view/render/scenewidget.cpp | 3 ++- apps/opencs/view/render/scenewidget.hpp | 2 ++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/render/lightingbright.cpp create mode 100644 apps/opencs/view/render/lightingbright.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a6200062da..a7a694463e 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -70,6 +70,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight + lightingbright ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/render/lightingbright.cpp b/apps/opencs/view/render/lightingbright.cpp new file mode 100644 index 0000000000..ab845b924f --- /dev/null +++ b/apps/opencs/view/render/lightingbright.cpp @@ -0,0 +1,30 @@ + +#include "lightingbright.hpp" + +#include + +CSVRender::LightingBright::LightingBright() : mSceneManager (0), mLight (0) {} + +void CSVRender::LightingBright::activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient) +{ + mSceneManager = sceneManager; + + mSceneManager->setAmbientLight (Ogre::ColourValue (1.0, 1.0, 1.0, 1)); + + mLight = mSceneManager->createLight(); + mLight->setType (Ogre::Light::LT_DIRECTIONAL); + mLight->setDirection (Ogre::Vector3 (0, 0, -1)); + mLight->setDiffuseColour (Ogre::ColourValue (1.0, 1.0, 1.0)); +} + +void CSVRender::LightingBright::deactivate() +{ + if (mLight) + { + mSceneManager->destroyLight (mLight); + mLight = 0; + } +} + +void CSVRender::LightingBright::setDefaultAmbient (const Ogre::ColourValue& colour) {} \ No newline at end of file diff --git a/apps/opencs/view/render/lightingbright.hpp b/apps/opencs/view/render/lightingbright.hpp new file mode 100644 index 0000000000..bc01899cb0 --- /dev/null +++ b/apps/opencs/view/render/lightingbright.hpp @@ -0,0 +1,31 @@ +#ifndef OPENCS_VIEW_LIGHTING_BRIGHT_H +#define OPENCS_VIEW_LIGHTING_BRIGHT_H + +#include "lighting.hpp" + +namespace Ogre +{ + class Light; +} + +namespace CSVRender +{ + class LightingBright : public Lighting + { + Ogre::SceneManager *mSceneManager; + Ogre::Light *mLight; + + public: + + LightingBright(); + + virtual void activate (Ogre::SceneManager *sceneManager, + const Ogre::ColourValue *defaultAmbient = 0); + + virtual void deactivate(); + + virtual void setDefaultAmbient (const Ogre::ColourValue& colour); + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index f1371041ca..1aee421ed7 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -347,6 +347,7 @@ namespace CSVRender setLighting (&mLightingDay); else if (mode=="night") setLighting (&mLightingNight); - + else if (mode=="bright") + setLighting (&mLightingBright); } } diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 96e7d7bb2c..8df9cf3471 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -7,6 +7,7 @@ #include "lightingday.hpp" #include "lightingnight.hpp" +#include "lightingbright.hpp" namespace Ogre { @@ -101,6 +102,7 @@ namespace CSVRender bool mHasDefaultAmbient; LightingDay mLightingDay; LightingNight mLightingNight; + LightingBright mLightingBright; private slots: From 688415ce5476aeeb9bf91962f2294868e9d858e6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 18:55:16 +0100 Subject: [PATCH 206/240] Play deathknockout/deathknockdown animations when appropriate --- apps/openmw/mwmechanics/character.cpp | 10 ++++++++++ apps/openmw/mwmechanics/character.hpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2db3bacf0e..93c789af1a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -414,6 +414,16 @@ void CharacterController::playRandomDeath(float startpoint) mDeathState = CharState_SwimDeath; mCurrentDeath = "swimdeath"; } + else if (mHitState == CharState_KnockDown) + { + mDeathState = CharState_DeathKnockDown; + mCurrentDeath = "deathknockdown"; + } + else if (mHitState == CharState_KnockOut) + { + mDeathState = CharState_DeathKnockOut; + mCurrentDeath = "deathknockout"; + } else { int selected=0; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4009744efb..5aea0210f2 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -90,6 +90,8 @@ enum CharacterState { CharState_Death4, CharState_Death5, CharState_SwimDeath, + CharState_DeathKnockDown, + CharState_DeathKnockOut, CharState_Hit, CharState_KnockDown, From 793649c854d759a40fcb8b0421295a6f23c3ac2f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 19:55:52 +0100 Subject: [PATCH 207/240] ToggleAI: Report current status on toggle --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 ++- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 2 +- apps/openmw/mwscript/aiextensions.cpp | 7 ++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 22dda0ce09..e4c480a8c0 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -156,7 +156,7 @@ namespace MWBase /// paused we may want to do it manually (after equipping permanent enchantment) virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; - virtual void toggleAI() = 0; + virtual bool toggleAI() = 0; virtual bool isAIActive() = 0; virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4c8f35edb0..3164ca1550 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -750,9 +750,10 @@ namespace MWMechanics mActors.updateMagicEffects(ptr); } - void MechanicsManager::toggleAI() + bool MechanicsManager::toggleAI() { mAI = !mAI; + return mAI; } bool MechanicsManager::isAIActive() diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 761caf586c..5dd7583779 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -137,7 +137,7 @@ namespace MWMechanics virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); - virtual void toggleAI(); + virtual bool toggleAI(); virtual bool isAIActive(); virtual void playerLoaded(); diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8314d011a9..a34c5476c9 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -464,7 +464,12 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { - MWBase::Environment::get().getMechanicsManager()->toggleAI(); + InterpreterContext& context + = static_cast (runtime.getContext()); + + bool enabled = MWBase::Environment::get().getMechanicsManager()->toggleAI(); + + context.report (enabled ? "AI -> On" : "AI -> Off"); } }; From 53ba23e3031e20ebd71d3af178cd9fcb95461702 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 19:56:12 +0100 Subject: [PATCH 208/240] Fixes #1224: Store class name instead of id in savegame header --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwstate/statemanagerimp.cpp | 2 +- components/esm/savedgame.cpp | 4 ++-- components/esm/savedgame.hpp | 7 ++++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 77ad98121b..894cbe0a3e 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -86,7 +86,7 @@ namespace MWGui { std::stringstream title; title << it->getSignature().mPlayerName; - title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClass << ")"; + title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClassName << ")"; mCharacterSelection->addItem (title.str()); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d6309c1c9f..a0d482f0a0 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -155,7 +155,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClass = player.get()->mBase->mClass; + profile.mPlayerClassName = world.getStore().get().find(player.get()->mBase->mClass)->mName; profile.mPlayerCell = world.getCellName(); diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index d6887f1704..813865e253 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -11,7 +11,7 @@ void ESM::SavedGame::load (ESMReader &esm) { mPlayerName = esm.getHNString("PLNA"); esm.getHNOT (mPlayerLevel, "PLLE"); - mPlayerClass = esm.getHNString("PLCL"); + mPlayerClassName = esm.getHNString("PLCL"); mPlayerCell = esm.getHNString("PLCE"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); @@ -30,7 +30,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const { esm.writeHNString ("PLNA", mPlayerName); esm.writeHNT ("PLLE", mPlayerLevel); - esm.writeHNString ("PLCL", mPlayerClass); + esm.writeHNString ("PLCL", mPlayerClassName); esm.writeHNString ("PLCE", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index 9c7bf551d7..b8615a6bc4 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -26,7 +26,12 @@ namespace ESM std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; - std::string mPlayerClass; // this is the ID and not the name of the class + // The (translated) name of the player class. So it will be displayed in the MW language + // the savegame was made in, not the currently running language of MW. + // However, savegames from a different MW language are not compatible anyway. + // And if only the ID was stored here, we would need to + // peek into the savegame to look for a class record in case it is a custom class. + std::string mPlayerClassName; std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; From ecfaa41e93699b0c20a717d76daf1f0e4ebf7661 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 20:56:39 +0100 Subject: [PATCH 209/240] Savegame dialog: use locale-aware datetime format --- apps/openmw/mwgui/savegamedialog.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 894cbe0a3e..e897c5a6bc 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -241,7 +241,13 @@ namespace MWGui struct tm* timeinfo; timeinfo = localtime(&time); - text << asctime(timeinfo) << "\n"; + // Use system/environment locale settings for datetime formatting + std::setlocale(LC_TIME, ""); + + const int size=1024; + char buffer[size]; + if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) + text << buffer << "\n"; text << "Level " << slot->mProfile.mPlayerLevel << "\n"; text << slot->mProfile.mPlayerCell << "\n"; // text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; From 5562c78ac4933456b4e17e197b98b813bfa747e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 21:11:39 +0100 Subject: [PATCH 210/240] Ask for confirmation when selecting 'New Game' --- apps/openmw/mwgui/mainmenu.cpp | 17 ++++++++++++++++- apps/openmw/mwgui/mainmenu.hpp | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 4ad260fd93..655663eb68 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -13,6 +13,7 @@ #include "../mwstate/character.hpp" #include "savegamedialog.hpp" +#include "confirmationdialog.hpp" namespace MWGui { @@ -62,6 +63,11 @@ namespace MWGui OEngine::GUI::Layout::setVisible (visible); } + void MainMenu::onNewGameConfirmed() + { + MWBase::Environment::get().getStateManager()->newGame(); + } + void MainMenu::onButtonClicked(MyGUI::Widget *sender) { std::string name = *sender->getUserData(); @@ -77,7 +83,16 @@ namespace MWGui MWBase::Environment::get().getStateManager()->requestQuit(); else if (name == "newgame") { - MWBase::Environment::get().getStateManager()->newGame(); + if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) + onNewGameConfirmed(); + else + { + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + dialog->open("#{sNotifyMessage54}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onNewGameConfirmed); + dialog->eventCancelClicked.clear(); + } } else diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 722b329de1..f38b2b751b 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -32,6 +32,7 @@ namespace MWGui std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); + void onNewGameConfirmed(); void updateMenu(); From 23f4bbc5b00c569ff542e86c13da827ce79b5b6c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 21:36:57 +0100 Subject: [PATCH 211/240] Videoplayer: only pause previous sounds if there is an audio stream --- apps/openmw/mwrender/videoplayer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index f3c0971e76..7abc8140a0 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -994,6 +994,9 @@ void VideoState::init(const std::string& resourceName) audio_index = i; } + if (audio_index != -1) + MWBase::Environment::get().getSoundManager()->pauseSounds(); + this->external_clock_base = av_gettime(); if(audio_index >= 0) this->stream_open(audio_index, this->format_ctx); @@ -1164,8 +1167,6 @@ void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - MWBase::Environment::get().getSoundManager()->pauseSounds(); - try { mState = new VideoState; mState->init(resourceName); From a5598e9c2e5e6f9b74a938ae45a9da87569fc628 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 21:38:57 +0100 Subject: [PATCH 212/240] Add Credits button to main menu (plays mw_credits.bik) --- apps/openmw/mwgui/mainmenu.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 655663eb68..4e4599e064 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -27,7 +27,7 @@ namespace MWGui std::stringstream sstream; sstream << "OpenMW version: " << OPENMW_VERSION; - // adding info about git hash if availible + // adding info about git hash if available std::string rev = OPENMW_VERSION_COMMITHASH; std::string tag = OPENMW_VERSION_TAGHASH; if (!rev.empty() && !tag.empty()) @@ -79,6 +79,8 @@ namespace MWGui } else if (name == "options") MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + else if (name == "credits") + MWBase::Environment::get().getWorld()->playVideo("mw_credits.bik", true); else if (name == "exitgame") MWBase::Environment::get().getStateManager()->requestQuit(); else if (name == "newgame") @@ -135,7 +137,10 @@ namespace MWGui buttons.push_back("savegame"); buttons.push_back("options"); - //buttons.push_back("credits"); + + if (state==MWBase::StateManager::State_NoGame) + buttons.push_back("credits"); + buttons.push_back("exitgame"); // Create new buttons if needed From e5e1e84c5270f9120a39e7b7d676ba8c7298a516 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Mar 2014 22:05:21 +0100 Subject: [PATCH 213/240] Ask for confirmation to exit OpenMW --- apps/openmw/mwgui/mainmenu.cpp | 18 +++++++++++++++++- apps/openmw/mwgui/mainmenu.hpp | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 4e4599e064..25821663df 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -68,6 +68,11 @@ namespace MWGui MWBase::Environment::get().getStateManager()->newGame(); } + void MainMenu::onExitConfirmed() + { + MWBase::Environment::get().getStateManager()->requestQuit(); + } + void MainMenu::onButtonClicked(MyGUI::Widget *sender) { std::string name = *sender->getUserData(); @@ -82,7 +87,18 @@ namespace MWGui else if (name == "credits") MWBase::Environment::get().getWorld()->playVideo("mw_credits.bik", true); else if (name == "exitgame") - MWBase::Environment::get().getStateManager()->requestQuit(); + { + if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) + onExitConfirmed(); + else + { + ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); + dialog->open("#{sMessage2}"); + dialog->eventOkClicked.clear(); + dialog->eventOkClicked += MyGUI::newDelegate(this, &MainMenu::onExitConfirmed); + dialog->eventCancelClicked.clear(); + } + } else if (name == "newgame") { if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index f38b2b751b..1850c473c3 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -33,6 +33,7 @@ namespace MWGui void onButtonClicked (MyGUI::Widget* sender); void onNewGameConfirmed(); + void onExitConfirmed(); void updateMenu(); From 0e78ffe8c0b317e38e358d0b57504403ab5acf8b Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 00:10:46 +0100 Subject: [PATCH 214/240] Add option to disable SDL's "minimize on focus loss" in fullscreen --- files/settings-default.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 5dfc054553..6361476e34 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -8,6 +8,9 @@ resolution y = 600 fullscreen = false screen = 0 +# Minimize the window if it loses key focus? +minimize on focus loss = true + # Render system # blank means default # Valid values: From 27956f362431127d92f2bafc7008c55d37241db6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 02:36:21 +0100 Subject: [PATCH 215/240] Savegame dialog: Hide main menu after successful load --- apps/openmw/mwgui/savegamedialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index e897c5a6bc..6971358c17 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -169,7 +169,10 @@ namespace MWGui else { if (mCurrentCharacter && slot) + { MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); + MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu); + } } setVisible(false); From bcacdc49afc039c4fcbdbc9d12cb2cfb508e682d Mon Sep 17 00:00:00 2001 From: Rohit Nirmal Date: Wed, 26 Mar 2014 21:08:31 -0500 Subject: [PATCH 216/240] Update weapon icon in HUD when save is loaded. --- apps/openmw/mwstate/statemanagerimp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c0860b7849..c807c5e235 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -25,6 +25,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" @@ -303,6 +304,10 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getMechanicsManager()->playerLoaded(); MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + //Update the weapon icon in the hud with whatever the player is currently holding. + MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight)); ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); From e695890e014e0d2778e7c16720ed70ed11f354f9 Mon Sep 17 00:00:00 2001 From: Rohit Nirmal Date: Wed, 26 Mar 2014 21:53:35 -0500 Subject: [PATCH 217/240] Only update the icon if player is NOT unarmed, to avoid bad dereference. --- apps/openmw/mwstate/statemanagerimp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index c807c5e235..5430caae56 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -307,7 +307,10 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl //Update the weapon icon in the hud with whatever the player is currently holding. MWWorld::InventoryStore& invStore = ptr.getClass().getInventoryStore(ptr); - MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight)); + MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if (item != invStore.end()) + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*item); ESM::CellId cellId = ptr.getCell()->getCell()->getCellId(); From 2a8bf46607429dfa788338689cb92d06f6ad6904 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 27 Mar 2014 01:23:56 -0400 Subject: [PATCH 218/240] Trader Gold Reset Delay Implemented traded gold reset delay. Note: Traders gold pool is still in inventory. --- apps/openmw/mwclass/creature.cpp | 7 ++++- apps/openmw/mwclass/npc.cpp | 7 +++++ apps/openmw/mwgui/tradewindow.cpp | 35 +++++++++++++++++++++++ apps/openmw/mwgui/tradewindow.hpp | 4 +++ apps/openmw/mwmechanics/creaturestats.cpp | 20 +++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 12 ++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f7ffdc480..a224de71ef 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -123,6 +123,11 @@ namespace MWClass else data->mContainerStore = new MWWorld::ContainerStore(); + // Relates to NPC gold reset delay + data->mCreatureStats.setTradeTime (MWBase::Environment::get().getWorld()->getTimeStamp()); + + data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); + // store ptr.getRefData().setCustomData (data.release()); @@ -134,7 +139,7 @@ namespace MWClass getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); if (ref->mBase->mFlags & ESM::Creature::Weapon) - getInventoryStore(ptr).autoEquip(ptr); + getInventoryStore(ptr).autoEquip(ptr); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3fbd0d5b21..502ff6ef6c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -357,6 +357,11 @@ namespace MWClass data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); + // Relates to NPC gold reset delay + data->mNpcStats.setTradeTime(MWBase::Environment::get().getWorld()->getTimeStamp()); + + data->mNpcStats.setGoldPool(gold); + // store ptr.getRefData().setCustomData (data.release()); @@ -365,6 +370,8 @@ namespace MWClass getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); getInventoryStore(ptr).autoEquip(ptr); + + } } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 92ba9470d4..41c3cd197a 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -84,8 +84,11 @@ namespace MWGui mCurrentBalance = 0; mCurrentMerchantOffer = 0; + checkTradeTime(); + std::vector itemSources; MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources); + // Important: actor goes last, so that items purchased by the merchant go into his inventory itemSources.push_back(actor); std::vector worldItems; @@ -360,6 +363,8 @@ namespace MWGui addOrRemoveGold(-mCurrentBalance, mPtr); } + updateTradeTime(); + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse( MWBase::Environment::get().getWorld()->getStore().get().find("sBarterDialog5")->getString()); @@ -474,4 +479,34 @@ namespace MWGui } return merchantGold; } + + // Relates to NPC gold reset delay + void TradeWindow::checkTradeTime() + { + MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); + MWWorld::LiveCellRef *ref = mPtr.get(); + const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + double delay = boost::lexical_cast(MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getInt()); + + // if time stamp longer than gold reset delay, reset gold. + if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() + delay) + { + addOrRemoveGold(-store.count(MWWorld::ContainerStore::sGoldId), mPtr); + addOrRemoveGold(+ref->mBase->mNpdt52.mGold, mPtr); + } + } + + void TradeWindow::updateTradeTime() + { + MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); + MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + double delay = boost::lexical_cast(MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getInt()); + + // If trade timestamp is within reset delay don't set + if ( ! (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() && + MWBase::Environment::get().getWorld()->getTimeStamp() < sellerStats.getTradeTime() + delay) ) + { + sellerStats.setTradeTime(MWBase::Environment::get().getWorld()->getTimeStamp()); + } + } } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index 1a8999e6e5..5c154d4259 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -100,6 +100,10 @@ namespace MWGui virtual void onReferenceUnavailable(); int getMerchantGold(); + + // Relates to NPC gold reset delay + void checkTradeTime(); + void updateTradeTime(); }; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index d61b967390..feed8d182b 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -476,4 +476,24 @@ namespace MWMechanics for (int i=0; i<3; ++i) mDynamic[i].readState (state.mDynamic[i]); } + + // Relates to NPC gold reset delay + void CreatureStats::setTradeTime(MWWorld::TimeStamp tradeTime) + { + mTradeTime = tradeTime; + } + + MWWorld::TimeStamp CreatureStats::getTradeTime() const + { + return mTradeTime; + } + + void CreatureStats::setGoldPool(int pool) + { + mGoldPool = pool; + } + int CreatureStats::getGoldPool() const + { + return mGoldPool; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 94e506fc4f..20a9a5799e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -54,6 +54,11 @@ namespace MWMechanics bool mRecalcDynamicStats; std::map mUsedPowers; + + MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay + + int mGoldPool; // the pool of merchant gold not in inventory + protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; @@ -221,6 +226,13 @@ namespace MWMechanics void writeState (ESM::CreatureStats& state) const; void readState (const ESM::CreatureStats& state); + + // Relates to NPC gold reset delay + void setTradeTime(MWWorld::TimeStamp tradeTime); + MWWorld::TimeStamp getTradeTime() const; + + void setGoldPool(int pool); + int getGoldPool() const; }; } From 53c4878d5257d139c63e5ae72b4fecdeb17aa8f7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Mar 2014 10:01:42 +0100 Subject: [PATCH 219/240] fix for inconsistent case in topic records --- apps/opencs/model/world/collection.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9d97e36c76..1fb3e1f1db 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -448,7 +448,8 @@ namespace CSMWorld template void Collection::setRecord (int index, const Record& record) { - if (IdAccessorT().getId (mRecords.at (index).get())!=IdAccessorT().getId (record.get())) + if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!= + Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get()))) throw std::runtime_error ("attempt to change the ID of a record"); mRecords.at (index) = record; From 6896142db17a53f5df20a156df8ac136c67b6b59 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 27 Mar 2014 07:30:24 -0400 Subject: [PATCH 220/240] Trader Gold Reset Delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trade Time initialized to 0 Gold pot set correctly for npc’s and creatures --- apps/openmw/mwclass/creature.cpp | 4 ++-- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a224de71ef..72b3154845 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -124,12 +124,12 @@ namespace MWClass data->mContainerStore = new MWWorld::ContainerStore(); // Relates to NPC gold reset delay - data->mCreatureStats.setTradeTime (MWBase::Environment::get().getWorld()->getTimeStamp()); + data->mCreatureStats.setTradeTime(MWWorld::TimeStamp(0.0, 0)); data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); // store - ptr.getRefData().setCustomData (data.release()); + ptr.getRefData().setCustomData(data.release()); getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 502ff6ef6c..020f3b3af2 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -358,7 +358,7 @@ namespace MWClass MWBase::Environment::get().getWorld()->getStore()); // Relates to NPC gold reset delay - data->mNpcStats.setTradeTime(MWBase::Environment::get().getWorld()->getTimeStamp()); + data->mNpcStats.setTradeTime(MWWorld::TimeStamp(0.0, 0)); data->mNpcStats.setGoldPool(gold); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 41c3cd197a..0525a97aee 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -484,7 +484,6 @@ namespace MWGui void TradeWindow::checkTradeTime() { MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); - MWWorld::LiveCellRef *ref = mPtr.get(); const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); double delay = boost::lexical_cast(MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getInt()); @@ -492,7 +491,7 @@ namespace MWGui if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() + delay) { addOrRemoveGold(-store.count(MWWorld::ContainerStore::sGoldId), mPtr); - addOrRemoveGold(+ref->mBase->mNpdt52.mGold, mPtr); + addOrRemoveGold(+sellerStats.getGoldPool(), mPtr); } } From 2e4ef93b28630f6f2ec8ccbe7eb2627e3912d6d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 04:15:47 +0100 Subject: [PATCH 221/240] Add proper main menu (background image, logo animation, title music) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 31 +++++++++++++++-- apps/openmw/mwgui/mainmenu.cpp | 61 +++++++++++++++++++++++++++++++--- apps/openmw/mwgui/mainmenu.hpp | 4 +++ files/mygui/openmw_layers.xml | 2 +- 5 files changed, 91 insertions(+), 9 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 20011b0d99..19f3473f79 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -33,7 +33,7 @@ add_openmw_dir (mwgui merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog - recharge + recharge mode ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 47e0d016f5..a710ce59a7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -339,6 +339,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) std::string aa = settings.getString("antialiasing", "Video"); windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0"; + SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, + settings.getBool("minimize on focus loss", "Video") ? "1" : "0"); + mOgre->createWindow("OpenMW", windowSettings); Bsa::registerResources (mFileCollections, mArchives, true, mFSStrict); @@ -356,6 +359,18 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding); mEnvironment.setWindowManager (window); + // Create sound system + mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); + + // TODO: play pre-load intro videos. Need to find a way to have them receive input. + // Make videoplayer a MyGUI widget? + /* + { + MWRender::VideoPlayer player(mOgre->getScene(), mOgre->getWindow()); + player.playVideo("mw_logo.bik", 1); + } + */ + // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, @@ -373,9 +388,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) Compiler::registerExtensions (mExtensions); - // Create sound system - mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); - // Create script system mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full); mScriptContext->setExtensions (&mExtensions); @@ -434,7 +446,20 @@ void OMW::Engine::go() // start in main menu if (!mSkipMenu) + { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + try + { + // Is there an ini setting for this filename or something? + MWBase::Environment::get().getSoundManager()->streamMusic("Special/morrowind title.mp3"); + + // TODO: there are other intro videos, too. They need to be imported from Morrowind.ini. + // Unfortunately those must play BEFORE any loading is done, which will currently not work. + // The videoplayer is created by World, so all content files must be loaded first... + MWBase::Environment::get().getWorld()->playVideo("mw_logo.bik", true); + } + catch (...) {} + } else MWBase::Environment::get().getStateManager()->newGame (true); diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 25821663df..0bc8b07d1d 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -22,6 +22,7 @@ namespace MWGui : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0), mWidth (w), mHeight (h) , mSaveGameDialog(NULL) + , mBackground(NULL) { getWidget(mVersionText, "VersionText"); std::stringstream sstream; @@ -59,6 +60,10 @@ namespace MWGui { if (visible) updateMenu(); + else + showBackground( + MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && + MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); OEngine::GUI::Layout::setVisible (visible); } @@ -125,11 +130,42 @@ namespace MWGui } } + void MainMenu::showBackground(bool show) + { + if (mBackground) + { + MyGUI::Gui::getInstance().destroyWidget(mBackground); + mBackground = NULL; + } + if (show) + { + if (!mBackground) + { + mBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Stretch, "Menu"); + mBackground->setImageTexture("black.png"); + + // Use black bars to correct aspect ratio. The video player also does it, so we need to do it + // for mw_logo.bik to align correctly with menu_morrowind.dds. + MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); + + // No way to un-hardcode this right now, menu_morrowind.dds is 1024x512 but was designed for 4:3 + double imageaspect = 4.0/3.0; + + int leftPadding = std::max(0.0, (screenSize.width - screenSize.height * imageaspect) / 2); + int topPadding = std::max(0.0, (screenSize.height - screenSize.width / imageaspect) / 2); + + MyGUI::ImageBox* image = mBackground->createWidget("ImageBox", + leftPadding, topPadding, screenSize.width - leftPadding*2, screenSize.height - topPadding*2, MyGUI::Align::Default); + image->setImageTexture("textures\\menu_morrowind.dds"); + } + } + } + void MainMenu::updateMenu() { setCoord(0,0, mWidth, mHeight); - if (!mButtonBox) mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); @@ -137,6 +173,8 @@ namespace MWGui MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); + showBackground(state == MWBase::StateManager::State_NoGame); + std::vector buttons; if (state==MWBase::StateManager::State_Running) @@ -191,12 +229,27 @@ namespace MWGui assert(mButtons.find(*it) != mButtons.end()); MWGui::ImageButton* button = mButtons[*it]; button->setVisible(true); + MyGUI::IntSize requested = button->getRequestedSize(); - button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, requested.height); - curH += requested.height; + + // Trim off some of the excessive padding + // TODO: perhaps do this within ImageButton? + int trim = 8; + button->setImageCoord(MyGUI::IntCoord(0, trim, requested.width, requested.height-trim)); + int height = requested.height-trim*2; + button->setImageTile(MyGUI::IntSize(requested.width, height)); + button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height); + curH += height; } - mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); + if (state == MWBase::StateManager::State_NoGame) + { + // Align with the background image + int bottomPadding=48; + mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight - curH - bottomPadding, maxwidth, curH); + } + else + mButtonBox->setCoord (mWidth/2 - maxwidth/2, mHeight/2 - curH/2, maxwidth, curH); } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 1850c473c3..c571fda861 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -29,12 +29,16 @@ namespace MWGui MyGUI::Widget* mButtonBox; MyGUI::TextBox* mVersionText; + MyGUI::ImageBox* mBackground; + std::map mButtons; void onButtonClicked (MyGUI::Widget* sender); void onNewGameConfirmed(); void onExitConfirmed(); + void showBackground(bool show); + void updateMenu(); SaveGameDialog* mSaveGameDialog; diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 84ec6f7c50..8bd95e2cd3 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -1,8 +1,8 @@ - + From 7026a0ceee3fe0a667bd21863e5c69c2af4244dd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Mar 2014 15:46:49 +0100 Subject: [PATCH 222/240] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 7d2a5ca42f..cd533de3a2 100644 --- a/credits.txt +++ b/credits.txt @@ -37,6 +37,7 @@ gugus/gus Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) +Jeffrey Haines (Jyby) Joel Graff (graffy) John Blomberg (fstp) Jordan Milne From 5906d795c0dc936a17e51a3fe37b12faada56160 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 19:10:15 +0100 Subject: [PATCH 223/240] Refactored video player (now a MyGUI widget) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 4 + apps/openmw/mwbase/world.hpp | 2 - apps/openmw/mwgui/mainmenu.cpp | 2 +- apps/openmw/mwgui/mode.hpp | 4 +- apps/openmw/mwgui/videowidget.cpp | 45 ++++++ apps/openmw/mwgui/videowidget.hpp | 39 +++++ apps/openmw/mwgui/windowmanagerimp.cpp | 71 +++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 10 ++ apps/openmw/mwinput/inputmanagerimp.cpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 19 --- apps/openmw/mwrender/renderingmanager.hpp | 5 - apps/openmw/mwrender/videoplayer.cpp | 188 +++++++--------------- apps/openmw/mwrender/videoplayer.hpp | 31 ++-- apps/openmw/mwscript/miscextensions.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 12 +- apps/openmw/mwworld/worldimp.hpp | 2 - files/mygui/openmw_layers.xml | 1 + 19 files changed, 242 insertions(+), 203 deletions(-) create mode 100644 apps/openmw/mwgui/videowidget.cpp create mode 100644 apps/openmw/mwgui/videowidget.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 19f3473f79..511435108e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -33,7 +33,7 @@ add_openmw_dir (mwgui merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog - recharge mode + recharge mode videowidget ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a710ce59a7..a118152edd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -456,7 +456,7 @@ void OMW::Engine::go() // TODO: there are other intro videos, too. They need to be imported from Morrowind.ini. // Unfortunately those must play BEFORE any loading is done, which will currently not work. // The videoplayer is created by World, so all content files must be loaded first... - MWBase::Environment::get().getWorld()->playVideo("mw_logo.bik", true); + MWBase::Environment::get().getWindowManager()->playVideo("mw_logo.bik", true); } catch (...) {} } diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 4fce19e337..e3bd428e22 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -96,6 +96,10 @@ namespace MWBase */ virtual void update() = 0; + /// @note This method will block until the video finishes playing + /// (and will continually update the window while doing so) + virtual void playVideo(const std::string& name, bool allowSkipping) = 0; + virtual void setNewGame(bool newgame) = 0; virtual void pushGuiMode (MWGui::GuiMode mode) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bb6f5741d9..f03a9197d9 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -420,8 +420,6 @@ namespace MWBase virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; /// \todo this does not belong here - virtual void playVideo(const std::string& name, bool allowSkipping) = 0; - virtual void stopVideo() = 0; virtual void frameStarted (float dt, bool paused) = 0; virtual void screenshot (Ogre::Image& image, int w, int h) = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 0bc8b07d1d..00e124f6c2 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -90,7 +90,7 @@ namespace MWGui else if (name == "options") MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (name == "credits") - MWBase::Environment::get().getWorld()->playVideo("mw_credits.bik", true); + MWBase::Environment::get().getWindowManager()->playVideo("mw_credits.bik", true); else if (name == "exitgame") { if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame) diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 50d53abacd..a1688d2e53 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -47,9 +47,7 @@ namespace MWGui GM_Loading, GM_LoadingWallpaper, - GM_QuickKeysMenu, - - GM_Video + GM_QuickKeysMenu }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/videowidget.cpp b/apps/openmw/mwgui/videowidget.cpp new file mode 100644 index 0000000000..566c7cadbb --- /dev/null +++ b/apps/openmw/mwgui/videowidget.cpp @@ -0,0 +1,45 @@ +#include "videowidget.hpp" + +namespace MWGui +{ + +VideoWidget::VideoWidget() + : mAllowSkipping(true) +{ + eventKeyButtonPressed += MyGUI::newDelegate(this, &VideoWidget::onKeyPressed); + + setNeedKeyFocus(true); +} + +void VideoWidget::playVideo(const std::string &video, bool allowSkipping) +{ + mAllowSkipping = allowSkipping; + + mPlayer.playVideo(video); + + setImageTexture(mPlayer.getTextureName()); +} + +int VideoWidget::getVideoWidth() +{ + return mPlayer.getVideoWidth(); +} + +int VideoWidget::getVideoHeight() +{ + return mPlayer.getVideoHeight(); +} + +void VideoWidget::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) +{ + if (_key == MyGUI::KeyCode::Escape && mAllowSkipping) + mPlayer.stopVideo(); +} + +bool VideoWidget::update() +{ + mPlayer.update(); + return mPlayer.isPlaying(); +} + +} diff --git a/apps/openmw/mwgui/videowidget.hpp b/apps/openmw/mwgui/videowidget.hpp new file mode 100644 index 0000000000..16a71d367d --- /dev/null +++ b/apps/openmw/mwgui/videowidget.hpp @@ -0,0 +1,39 @@ +#ifndef OPENMW_MWGUI_VIDEOWIDGET_H +#define OPENMW_MWGUI_VIDEOWIDGET_H + +#include + +#include "../mwrender/videoplayer.hpp" + +namespace MWGui +{ + + /** + * Widget that plays a video. Can be skipped by pressing Esc. + */ + class VideoWidget : public MyGUI::ImageBox + { + public: + MYGUI_RTTI_DERIVED(VideoWidget) + + VideoWidget(); + + void playVideo (const std::string& video, bool allowSkipping); + + int getVideoWidth(); + int getVideoHeight(); + + /// @return Is the video still playing? + bool update(); + + private: + bool mAllowSkipping; + + MWRender::VideoPlayer mPlayer; + + void onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1b71157a74..ca8459fa1e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "MyGUI_UString.h" #include "MyGUI_IPointer.h" @@ -59,6 +60,7 @@ #include "bookpage.hpp" #include "itemview.hpp" #include "fontloader.hpp" +#include "videowidget.hpp" namespace MWGui { @@ -104,6 +106,8 @@ namespace MWGui , mRecharge(NULL) , mRepair(NULL) , mCompanionWindow(NULL) + , mVideoBackground(NULL) + , mVideoWidget(NULL) , mTranslationDataStorage (translationDataStorage) , mCharGen(NULL) , mInputBlocker(NULL) @@ -155,6 +159,7 @@ namespace MWGui MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); BookPage::registerMyGUIComponents (); ItemView::registerComponents(); @@ -186,6 +191,13 @@ namespace MWGui // hide mygui's pointer MyGUI::PointerManager::getInstance().setVisible(false); + + mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Default, "Overlay"); + mVideoBackground->setImageTexture("black.png"); + mVideoBackground->setVisible(false); + + mVideoWidget = mVideoBackground->createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Default); } void WindowManager::initUI() @@ -391,6 +403,7 @@ namespace MWGui mCompanionWindow->setVisible(false); mInventoryWindow->setTrading(false); mRecharge->setVisible(false); + mVideoBackground->setVisible(false); mHud->setVisible(mHudEnabled); @@ -539,10 +552,6 @@ namespace MWGui setCursorVisible(false); break; - case GM_Video: - setCursorVisible(false); - mHud->setVisible(false); - break; default: // Unsupported mode, switch back to game break; @@ -894,6 +903,7 @@ namespace MWGui void WindowManager::windowResized(int x, int y) { + sizeVideo(x, y); mGuiManager->windowResized(); mLoadingScreen->onResChange (x,y); if (!mHud) @@ -1401,4 +1411,57 @@ namespace MWGui mMap->readRecord(reader, type); } + void WindowManager::playVideo(const std::string &name, bool allowSkipping) + { + mVideoWidget->playVideo("video\\" + name, allowSkipping); + + // Turn off all rendering except for the GUI + mRendering->getScene()->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work? + for(int i = 0;i < Ogre::RENDER_QUEUE_MAX;++i) + { + if(i > 0 && i < 96) + mRendering->getScene()->addSpecialCaseRenderQueue(i); + } + mRendering->getScene()->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); + sizeVideo(screenSize.width, screenSize.height); + + setKeyFocusWidget(mVideoWidget); + + mVideoBackground->setVisible(true); + + bool cursorWasVisible = mCursorVisible; + setCursorVisible(false); + + while (mVideoWidget->update()) + { + MWBase::Environment::get().getInputManager()->update(0, false); + + mRendering->getWindow()->update(); + } + + setCursorVisible(cursorWasVisible); + + // Restore normal rendering + mRendering->getScene()->clearSpecialCaseRenderQueues(); + mRendering->getScene()->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + mVideoBackground->setVisible(false); + } + + void WindowManager::sizeVideo(int screenWidth, int screenHeight) + { + // Use black bars to correct aspect ratio + mVideoBackground->setSize(screenWidth, screenHeight); + + double imageaspect = static_cast(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight(); + + int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2); + int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2); + + mVideoWidget->setCoord(leftPadding, topPadding, + screenWidth - leftPadding*2, screenHeight - topPadding*2); + } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index dafb65e47b..ab9770a41b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -18,6 +18,7 @@ namespace MyGUI class Widget; class Window; class UString; + class ImageBox; } namespace Compiler @@ -80,6 +81,7 @@ namespace MWGui class SoulgemDialog; class Recharge; class CompanionWindow; + class VideoWidget; class WindowManager : public MWBase::WindowManager { @@ -98,6 +100,10 @@ namespace MWGui virtual Loading::Listener* getLoadingScreen(); + /// @note This method will block until the video finishes playing + /// (and will continually update the window while doing so) + virtual void playVideo(const std::string& name, bool allowSkipping); + /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening @@ -332,6 +338,8 @@ namespace MWGui Repair* mRepair; Recharge* mRecharge; CompanionWindow* mCompanionWindow; + MyGUI::ImageBox* mVideoBackground; + VideoWidget* mVideoWidget; Translation::Storage& mTranslationDataStorage; Cursor* mSoftwareCursor; @@ -390,6 +398,8 @@ namespace MWGui void onCursorChange(const std::string& name); void onKeyFocusChanged(MyGUI::Widget* widget); + + void sizeVideo(int screenWidth, int screenHeight); }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4bfd3f4651..840a3f71a9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -626,9 +626,7 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().isModalAny()) return; - if (MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_Video) - MWBase::Environment::get().getWorld ()->stopVideo (); - else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)) + if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)) { MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getSoundManager()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 15d56b8a99..fa7b17a7c1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -43,7 +43,6 @@ #include "water.hpp" #include "npcanimation.hpp" #include "globalmap.hpp" -#include "videoplayer.hpp" #include "terrainstorage.hpp" #include "effectmanager.hpp" @@ -171,9 +170,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mVideoPlayer = new VideoPlayer(mRendering.getScene (), mRendering.getWindow()); - mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video")); - mSun = 0; mDebugging = new Debugging(mRootNode, engine); @@ -197,7 +193,6 @@ RenderingManager::~RenderingManager () delete mLocalMap; delete mOcclusionQuery; delete mWater; - delete mVideoPlayer; delete mActors; delete mObjects; delete mEffectManager; @@ -333,8 +328,6 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) void RenderingManager::update (float duration, bool paused) { - mVideoPlayer->update (); - if (MWBase::Environment::get().getStateManager()->getState()== MWBase::StateManager::State_NoGame) return; @@ -884,8 +877,6 @@ void RenderingManager::windowResized(int x, int y) Settings::Manager::setInt("resolution y", "Video", y); mRendering.adjustViewport(); - mVideoPlayer->setResolution (x, y); - MWBase::Environment::get().getWindowManager()->windowResized(x,y); } @@ -1001,16 +992,6 @@ void RenderingManager::screenshot(Image &image, int w, int h) mRendering.getCamera()->setAspectRatio(oldAspect); } -void RenderingManager::playVideo(const std::string& name, bool allowSkipping) -{ - mVideoPlayer->playVideo ("video/" + name, allowSkipping); -} - -void RenderingManager::stopVideo() -{ - mVideoPlayer->stopVideo (); -} - void RenderingManager::addWaterRippleEmitter (const MWWorld::Ptr& ptr, float scale, float force) { mWater->addEmitter (ptr, scale, force); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 115a94786e..423a7078ab 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -46,7 +46,6 @@ namespace MWRender class LocalMap; class Water; class GlobalMap; - class VideoPlayer; class Animation; class EffectManager; @@ -209,8 +208,6 @@ public: Animation* getAnimation(const MWWorld::Ptr &ptr); - void playVideo(const std::string& name, bool allowSkipping); - void stopVideo(); void frameStarted(float dt, bool paused); void screenshot(Ogre::Image& image, int w, int h); @@ -271,8 +268,6 @@ private: MWRender::LocalMap* mLocalMap; MWRender::Shadows* mShadows; - - VideoPlayer* mVideoPlayer; }; } diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7abc8140a0..80704ca7c9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -17,7 +17,6 @@ #include -#include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwsound/sound_decoder.hpp" @@ -126,7 +125,7 @@ struct VideoState { int stream_open(int stream_index, AVFormatContext *pFormatCtx); - bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height); + bool update(); static void video_thread_loop(VideoState *is); static void decode_thread_loop(VideoState *is); @@ -163,6 +162,7 @@ struct VideoState { static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); + Ogre::TexturePtr mTexture; Ogre::DataStreamPtr stream; AVFormatContext* format_ctx; @@ -599,22 +599,17 @@ void VideoState::video_display() if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) { - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); - if(texture.isNull() || static_cast(texture->getWidth()) != (*this->video_st)->codec->width - || static_cast(texture->getHeight()) != (*this->video_st)->codec->height) + + if(static_cast(mTexture->getWidth()) != (*this->video_st)->codec->width || + static_cast(mTexture->getHeight()) != (*this->video_st)->codec->height) { - Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); - texture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - (*this->video_st)->codec->width, (*this->video_st)->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + mTexture->unload(); + mTexture->setWidth((*this->video_st)->codec->width); + mTexture->setHeight((*this->video_st)->codec->height); + mTexture->createInternalResources(); } Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); - Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + Ogre::HardwarePixelBufferSharedPtr buffer = mTexture->getBuffer(); buffer->blitFromMemory(pb); this->display_ready = true; } @@ -851,7 +846,7 @@ void VideoState::decode_thread_loop(VideoState *self) } -bool VideoState::update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) +bool VideoState::update() { if(this->quit) return false; @@ -860,21 +855,6 @@ bool VideoState::update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int scr { this->refresh = false; this->video_refresh_timer(); - // Would be nice not to do this all the time... - if(this->display_ready) - mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); - - // Correct aspect ratio by adding black bars - double videoaspect = av_q2d((*this->video_st)->codec->sample_aspect_ratio); - if(videoaspect == 0.0) - videoaspect = 1.0; - videoaspect *= static_cast((*this->video_st)->codec->width) / (*this->video_st)->codec->height; - - double screenaspect = static_cast(screen_width) / screen_height; - double aspect_correction = videoaspect / screenaspect; - - rect->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), - std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); } return true; } @@ -1001,8 +981,29 @@ void VideoState::init(const std::string& resourceName) if(audio_index >= 0) this->stream_open(audio_index, this->format_ctx); if(video_index >= 0) + { this->stream_open(video_index, this->format_ctx); + int width = (*this->video_st)->codec->width; + int height = (*this->video_st)->codec->height; + static int i = 0; + this->mTexture = Ogre::TextureManager::getSingleton().createManual( + "OpenMW/VideoTexture" + Ogre::StringConverter::toString(++i), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + width, height, // TEST + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + + // initialize to (0,0,0,0) + std::vector buffer; + buffer.resize(width * height, 0); + Ogre::PixelBox pb(width, height, 1, Ogre::PF_BYTE_RGBA, &buffer[0]); + this->mTexture->getBuffer()->blitFromMemory(pb); + } + + this->parse_thread = boost::thread(decode_thread_loop, this); } @@ -1073,111 +1074,26 @@ public: #endif // defined OPENMW_USE_FFMPEG -VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window) +VideoPlayer::VideoPlayer() : mState(NULL) - , mSceneMgr(sceneMgr) - , mRectangle(NULL) - , mNode(NULL) - , mAllowSkipping(false) - , mWindow(window) - , mWidth(0) - , mHeight(0) { - mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); - if (mVideoMaterial.isNull ()) - { - mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - } - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); - Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General"); - if (blackMaterial.isNull ()) - { - blackMaterial = Ogre::MaterialManager::getSingleton().create("BlackBarsMaterial", "General"); - blackMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - blackMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - blackMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - blackMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); - } - - mRectangle = new Ogre::Rectangle2D(true); - mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+2); - mBackgroundRectangle = new Ogre::Rectangle2D(true); - mBackgroundRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mBackgroundRectangle->setMaterial("BlackBarsMaterial"); - mBackgroundRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); - - // Use infinite AAB to always stay visible - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - mRectangle->setBoundingBox(aabInf); - mBackgroundRectangle->setBoundingBox(aabInf); - - // Attach background to the scene - mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); - mNode->attachObject(mRectangle); - mBackgroundNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); - mBackgroundNode->attachObject(mBackgroundRectangle); - - mRectangle->setVisible(false); - mRectangle->setVisibilityFlags(RV_Overlay); - mBackgroundRectangle->setVisible(false); - mBackgroundRectangle->setVisibilityFlags(RV_Overlay); } VideoPlayer::~VideoPlayer() { if(mState) close(); - - mSceneMgr->destroySceneNode(mNode); - mSceneMgr->destroySceneNode(mBackgroundNode); - - delete mRectangle; - delete mBackgroundRectangle; } -void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) +void VideoPlayer::playVideo(const std::string &resourceName) { - mAllowSkipping = allowSkipping; - if(mState) close(); - mRectangle->setVisible(true); - mBackgroundRectangle->setVisible(true); - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); - - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); - - // Turn off rendering except the GUI - mSceneMgr->clearSpecialCaseRenderQueues(); - // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. - for(int i = 0;i < Ogre::RENDER_QUEUE_MAX;++i) - { - if(i > 0 && i < 96) - mSceneMgr->addSpecialCaseRenderQueue(i); - } - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - try { mState = new VideoState; mState->init(resourceName); - - while (isPlaying()) - { - MWBase::Environment::get().getInputManager()->update(0, false); - update(); - mWindow->update(); - } - } catch(std::exception& e) { std::cerr<< "Failed to play video: "<update(mVideoMaterial, mRectangle, mWidth, mHeight)) + if(!mState->update()) close(); } } +std::string VideoPlayer::getTextureName() +{ + std::string name; + if (mState) + name = mState->mTexture->getName(); + return name; +} + +int VideoPlayer::getVideoWidth() +{ + int width=0; + if (mState) + width = mState->mTexture->getWidth(); + return width; +} + +int VideoPlayer::getVideoHeight() +{ + int height=0; + if (mState) + height = mState->mTexture->getHeight(); + return height; +} + void VideoPlayer::stopVideo () { - if (mAllowSkipping) - close(); + close(); } void VideoPlayer::close() @@ -1211,13 +1150,6 @@ void VideoPlayer::close() } MWBase::Environment::get().getSoundManager()->resumeSounds(); - - mRectangle->setVisible(false); - mBackgroundRectangle->setVisible(false); - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); - - mSceneMgr->clearSpecialCaseRenderQueues(); - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); } bool VideoPlayer::isPlaying () diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 0e548e23e4..47e252cc15 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -1,27 +1,22 @@ #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H -#include - -namespace Ogre -{ - class SceneManager; - class SceneNode; - class Rectangle2D; - class RenderWindow; -} +#include namespace MWRender { struct VideoState; + /** + * @brief Plays a video on an Ogre texture. + */ class VideoPlayer { public: - VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window); + VideoPlayer(); ~VideoPlayer(); - void playVideo (const std::string& resourceName, bool allowSkipping); + void playVideo (const std::string& resourceName); void update(); @@ -30,22 +25,14 @@ namespace MWRender bool isPlaying(); - void setResolution (int w, int h) { mWidth = w; mHeight = h; } + std::string getTextureName(); + int getVideoWidth(); + int getVideoHeight(); private: VideoState* mState; - bool mAllowSkipping; - - Ogre::SceneManager* mSceneMgr; - Ogre::MaterialPtr mVideoMaterial; - Ogre::Rectangle2D* mRectangle; - Ogre::Rectangle2D* mBackgroundRectangle; - Ogre::SceneNode* mNode; - Ogre::SceneNode* mBackgroundNode; - Ogre::RenderWindow* mWindow; - int mWidth; int mHeight; }; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index fa8441aa57..89a5ceec1e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -47,7 +47,7 @@ namespace MWScript bool allowSkipping = runtime[0].mInteger; runtime.pop(); - MWBase::Environment::get().getWorld ()->playVideo (name, allowSkipping); + MWBase::Environment::get().getWindowManager()->playVideo (name, allowSkipping); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 62bdd38ea1..f884f69af2 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1305,7 +1305,7 @@ namespace MWWorld { --mPlayIntro; if (mPlayIntro == 0) - mRendering->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); + MWBase::Environment::get().getWindowManager()->playVideo(mFallback.getFallbackString("Movies_New_Game"), true); } if (mGoToJail && !paused) @@ -1776,16 +1776,6 @@ namespace MWWorld return mRendering->getAnimation(ptr); } - void World::playVideo (const std::string &name, bool allowSkipping) - { - mRendering->playVideo(name, allowSkipping); - } - - void World::stopVideo () - { - mRendering->stopVideo(); - } - void World::frameStarted (float dt, bool paused) { mRendering->frameStarted(dt, paused); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 42f52cb619..f1e89bf6bc 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -523,8 +523,6 @@ namespace MWWorld virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr); /// \todo this does not belong here - virtual void playVideo(const std::string& name, bool allowSkipping); - virtual void stopVideo(); virtual void frameStarted (float dt, bool paused); virtual void screenshot (Ogre::Image& image, int w, int h); diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 8bd95e2cd3..6f77369949 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -9,5 +9,6 @@ + From f2cd37edd0ee8903f0d4f2bd53214bf16a7290dd Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 19:51:48 +0100 Subject: [PATCH 224/240] Play company logo movie --- apps/openmw/engine.cpp | 18 ++++++------------ apps/openmw/mwbase/inputmanager.hpp | 2 +- apps/openmw/mwgui/loadingscreen.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 16 ++++++++-------- apps/openmw/mwinput/inputmanagerimp.hpp | 2 +- files/mygui/openmw_layers.xml | 2 +- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a118152edd..f02432ba86 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -362,14 +362,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); - // TODO: play pre-load intro videos. Need to find a way to have them receive input. - // Make videoplayer a MyGUI widget? - /* - { - MWRender::VideoPlayer player(mOgre->getScene(), mOgre->getWindow()); - player.playVideo("mw_logo.bik", 1); - } - */ + std::string logo = mFallbackMap["Movies_Company_Logo"]; + if (!logo.empty()) + window->playVideo(logo, 1); // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, @@ -453,10 +448,9 @@ void OMW::Engine::go() // Is there an ini setting for this filename or something? MWBase::Environment::get().getSoundManager()->streamMusic("Special/morrowind title.mp3"); - // TODO: there are other intro videos, too. They need to be imported from Morrowind.ini. - // Unfortunately those must play BEFORE any loading is done, which will currently not work. - // The videoplayer is created by World, so all content files must be loaded first... - MWBase::Environment::get().getWindowManager()->playVideo("mw_logo.bik", true); + std::string logo = mFallbackMap["Movies_Morrowind_Logo"]; + if (!logo.empty()) + MWBase::Environment::get().getWindowManager()->playVideo(logo, true); } catch (...) {} } diff --git a/apps/openmw/mwbase/inputmanager.hpp b/apps/openmw/mwbase/inputmanager.hpp index 42922a5b3d..d44da4974d 100644 --- a/apps/openmw/mwbase/inputmanager.hpp +++ b/apps/openmw/mwbase/inputmanager.hpp @@ -25,7 +25,7 @@ namespace MWBase virtual ~InputManager() {} - virtual void update(float dt, bool loading) = 0; + virtual void update(float dt, bool disableControls, bool disableEvents=false) = 0; virtual void changeInputMode(bool guiMode) = 0; diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 37e29591b8..b3f70a5ab0 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -226,7 +226,7 @@ namespace MWGui } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - MWBase::Environment::get().getInputManager()->update(0, true); + MWBase::Environment::get().getInputManager()->update(0, true, true); // First, swap buffers from last draw, then, queue an update of the // window contents, but don't swap buffers (which would have diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ca8459fa1e..1e019aaa95 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1437,7 +1437,7 @@ namespace MWGui while (mVideoWidget->update()) { - MWBase::Environment::get().getInputManager()->update(0, false); + MWBase::Environment::get().getInputManager()->update(0, true, false); mRendering->getWindow()->update(); } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 840a3f71a9..e2d4f8cb2d 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -96,12 +96,12 @@ namespace MWInput : mOgre(ogre) , mPlayer(NULL) , mEngine(engine) - , mMouseLookEnabled(true) + , mMouseLookEnabled(false) , mMouseX(ogre.getWindow()->getWidth ()/2.f) , mMouseY(ogre.getWindow()->getHeight ()/2.f) , mMouseWheel(0) , mDragDrop(false) - , mGuiCursorEnabled(false) + , mGuiCursorEnabled(true) , mUserFile(userFile) , mUserFileExists(userFileExists) , mInvertY (Settings::Manager::getBool("invert y axis", "Input")) @@ -256,18 +256,21 @@ namespace MWInput } } - void InputManager::update(float dt, bool loading) + void InputManager::update(float dt, bool disableControls, bool disableEvents) { mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); - mInputManager->capture(loading); + mInputManager->capture(disableEvents); // inject some fake mouse movement to force updating MyGUI's widget states MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); // update values of channels (as a result of pressed keys) - if (!loading) + if (!disableControls) mInputBinder->update(dt); + if (disableControls) + return; + bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console; @@ -288,9 +291,6 @@ namespace MWInput mInputManager->warpMouse(mMouseX, mMouseY); } - if (loading) - return; - // Disable movement in Gui mode if (MWBase::Environment::get().getWindowManager()->isGuiMode() || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 2eab03a34e..87fbda25cd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -68,7 +68,7 @@ namespace MWInput /// Clear all savegame-specific data virtual void clear(); - virtual void update(float dt, bool loading); + virtual void update(float dt, bool disableControls, bool disableEvents=false); void setPlayer (MWWorld::Player* player) { mPlayer = player; } diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index 6f77369949..e66f3fc017 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -9,6 +9,6 @@ - + From 4f852daf45cfd9ee5e199edd25b924909e08f103 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 19:59:33 +0100 Subject: [PATCH 225/240] Don't play company logo when using --skip-menu --- apps/openmw/engine.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index f02432ba86..508b195e92 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -362,9 +362,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); - std::string logo = mFallbackMap["Movies_Company_Logo"]; - if (!logo.empty()) - window->playVideo(logo, 1); + if (!mSkipMenu) + { + std::string logo = mFallbackMap["Movies_Company_Logo"]; + if (!logo.empty()) + window->playVideo(logo, 1); + } // Create the world mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, From ea357cfed086154766808d54da9d62db9da9eb14 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 20:39:56 +0100 Subject: [PATCH 226/240] Fix crash when resizing while company logo is playing --- libs/openengine/ogre/renderer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index c816f2060c..5f95789884 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -157,5 +157,6 @@ void OgreRenderer::setFov(float fov) void OgreRenderer::windowResized(int x, int y) { - mWindowListener->windowResized(x,y); + if (mWindowListener) + mWindowListener->windowResized(x,y); } From d7df9cae21dd4946901f01769a5f4f702cc01d09 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 27 Mar 2014 22:32:42 +0100 Subject: [PATCH 227/240] Bug #1224: Changed fix to potentially allow for language independent saves --- apps/openmw/mwgui/savegamedialog.cpp | 16 +++++++++++++++- apps/openmw/mwstate/statemanagerimp.cpp | 7 ++++++- apps/openmw/mwworld/store.hpp | 8 ++++++++ components/esm/savedgame.cpp | 12 ++++++++++-- components/esm/savedgame.hpp | 11 ++++++----- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 6971358c17..caa082646e 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -86,7 +86,21 @@ namespace MWGui { std::stringstream title; title << it->getSignature().mPlayerName; - title << " (Level " << it->getSignature().mPlayerLevel << " " << it->getSignature().mPlayerClassName << ")"; + + // For a custom class, we will not find it in the store (unless we loaded the savegame first). + // Fall back to name stored in savegame header in that case. + std::string className; + if (it->getSignature().mPlayerClassId.empty()) + className = it->getSignature().mPlayerClassName; + else + { + // Find the localised name for this class from the store + const ESM::Class* class_ = MWBase::Environment::get().getWorld()->getStore().get().find( + it->getSignature().mPlayerClassId); + className = class_->mName; + } + + title << " (Level " << it->getSignature().mPlayerLevel << " " << className << ")"; mCharacterSelection->addItem (title.str()); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a0d482f0a0..7793cc9608 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -155,7 +155,12 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerName = player.getClass().getName (player); profile.mPlayerLevel = player.getClass().getNpcStats (player).getLevel(); - profile.mPlayerClassName = world.getStore().get().find(player.get()->mBase->mClass)->mName; + + std::string classId = player.get()->mBase->mClass; + if (world.getStore().get().isDynamic(classId)) + profile.mPlayerClassName = world.getStore().get().find(classId)->mName; + else + profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 6b99c0a0c4..0fc2d547cb 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -157,6 +157,14 @@ namespace MWWorld return 0; } + /** + * Does the record with this ID come from the dynamic store? + */ + bool isDynamic(const std::string &id) const { + typename Dynamic::const_iterator dit = mDynamic.find(id); + return (dit != mDynamic.end()); + } + /** Returns a random record that starts with the named ID, or NULL if not found. */ const T *searchRandom(const std::string &id) const { diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index 813865e253..b5e0810dbc 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -11,7 +11,10 @@ void ESM::SavedGame::load (ESMReader &esm) { mPlayerName = esm.getHNString("PLNA"); esm.getHNOT (mPlayerLevel, "PLLE"); - mPlayerClassName = esm.getHNString("PLCL"); + + mPlayerClassId = esm.getHNOString("PLCL"); + mPlayerClassName = esm.getHNOString("PLCN"); + mPlayerCell = esm.getHNString("PLCE"); esm.getHNT (mInGameTime, "TSTM", 16); esm.getHNT (mTimePlayed, "TIME"); @@ -30,7 +33,12 @@ void ESM::SavedGame::save (ESMWriter &esm) const { esm.writeHNString ("PLNA", mPlayerName); esm.writeHNT ("PLLE", mPlayerLevel); - esm.writeHNString ("PLCL", mPlayerClassName); + + if (!mPlayerClassId.empty()) + esm.writeHNString ("PLCL", mPlayerClassId); + else + esm.writeHNString ("PLCN", mPlayerClassName); + esm.writeHNString ("PLCE", mPlayerCell); esm.writeHNT ("TSTM", mInGameTime, 16); esm.writeHNT ("TIME", mTimePlayed); diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index b8615a6bc4..3e7cae775a 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -26,12 +26,13 @@ namespace ESM std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; - // The (translated) name of the player class. So it will be displayed in the MW language - // the savegame was made in, not the currently running language of MW. - // However, savegames from a different MW language are not compatible anyway. - // And if only the ID was stored here, we would need to - // peek into the savegame to look for a class record in case it is a custom class. + + // ID of class + std::string mPlayerClassId; + // Name of the class. When using a custom class, the ID is not really meaningful prior + // to loading the savegame, so the name is stored separately. std::string mPlayerClassName; + std::string mPlayerCell; TimeStamp mInGameTime; double mTimePlayed; From 895748f18d2f5d66e0f0033f4a49ad7fd44857bb Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 28 Mar 2014 12:01:56 -0400 Subject: [PATCH 228/240] Gold Pool implemented for Vendors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It appears that my solution breaks persuasion gold for some reason. I may be wrong. I can’t see where this could be happening as the files I’ve changes should not affect persuasion at all. --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwgui/tradewindow.cpp | 21 +++++++++------------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 72b3154845..2af134a38a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -136,7 +136,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); + //getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); if (ref->mBase->mFlags & ESM::Creature::Weapon) getInventoryStore(ptr).autoEquip(ptr); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 020f3b3af2..f7b280a799 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -367,7 +367,7 @@ namespace MWClass // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // (except for gold you gave him) - getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); + //getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); getInventoryStore(ptr).autoEquip(ptr); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0525a97aee..93f53a11f4 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -360,7 +360,8 @@ namespace MWGui if (mCurrentBalance != 0) { addOrRemoveGold(mCurrentBalance, player); - addOrRemoveGold(-mCurrentBalance, mPtr); + mPtr.getClass().getCreatureStats(mPtr).setGoldPool( + mPtr.getClass().getCreatureStats(mPtr).getGoldPool() - mCurrentBalance ); } updateTradeTime(); @@ -470,28 +471,24 @@ namespace MWGui int TradeWindow::getMerchantGold() { - int merchantGold = 0; - MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); - for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) - { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, MWWorld::ContainerStore::sGoldId)) - merchantGold += it->getRefData().getCount(); - } + int merchantGold = mPtr.getClass().getCreatureStats(mPtr).getGoldPool(); return merchantGold; } // Relates to NPC gold reset delay void TradeWindow::checkTradeTime() { - MWWorld::ContainerStore store = mPtr.getClass().getContainerStore(mPtr); - const MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); + MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr); double delay = boost::lexical_cast(MWBase::Environment::get().getWorld()->getStore().get().find("fBarterGoldResetDelay")->getInt()); // if time stamp longer than gold reset delay, reset gold. if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() + delay) { - addOrRemoveGold(-store.count(MWWorld::ContainerStore::sGoldId), mPtr); - addOrRemoveGold(+sellerStats.getGoldPool(), mPtr); + // reset gold to the base gold + if ( mPtr.getClass().isNpc() ) + sellerStats.setGoldPool(mPtr.get()->mBase->mNpdt52.mGold); + else + sellerStats.setGoldPool(mPtr.get()->mBase->mData.mGold); } } From 401d21b4ee1693b13a4770961899dcbb6ab30cfd Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 28 Mar 2014 14:21:38 -0400 Subject: [PATCH 229/240] getBaseGold implemented in MWWorld::Class for NPC and Creature Implemented a getBaseGold() to get the vendor gold base NPC gold base now can come from mNpdt12 and mNpdt52 --- apps/openmw/mwclass/creature.cpp | 5 +++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 9 +++++++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwgui/tradewindow.cpp | 6 +----- apps/openmw/mwworld/class.cpp | 2 ++ apps/openmw/mwworld/class.hpp | 2 ++ 7 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 2af134a38a..d347b2c044 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -811,6 +811,11 @@ namespace MWClass customData.mCreatureStats.writeState (state2.mCreatureStats); } + int Creature::getBaseGold(const MWWorld::Ptr& ptr) const + { + return ptr.get()->mBase->mData.mGold; + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index c1bcb8739e..04c010c830 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -141,6 +141,8 @@ namespace MWClass virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. + + virtual int getBaseGold(const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f7b280a799..c06e315560 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1294,6 +1294,15 @@ namespace MWClass static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); } + int Npc::getBaseGold(const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) + return ref->mBase->mNpdt52.mGold; + else + return ref->mBase->mNpdt12.mGold; + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index c54dd339a4..fb45a2f1f6 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -166,6 +166,8 @@ namespace MWClass virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const; ///< Write additional state from \a ptr into \a state. + + virtual int getBaseGold(const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 93f53a11f4..3e15bcd78c 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -484,11 +484,7 @@ namespace MWGui // if time stamp longer than gold reset delay, reset gold. if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getTradeTime() + delay) { - // reset gold to the base gold - if ( mPtr.getClass().isNpc() ) - sellerStats.setGoldPool(mPtr.get()->mBase->mNpdt52.mGold); - else - sellerStats.setGoldPool(mPtr.get()->mBase->mData.mGold); + sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr)); } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 39d48f95b8..af8de13264 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -396,4 +396,6 @@ namespace MWWorld void Class::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const {} void Class::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const {} + + int Class::getBaseGold(const MWWorld::Ptr& ptr) const {} } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 739fd5942e..cee1b171de 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -332,6 +332,8 @@ namespace MWWorld ///< If there is no class for this pointer, an exception is thrown. static void registerClass (const std::string& key, boost::shared_ptr instance); + + virtual int getBaseGold(const MWWorld::Ptr& ptr) const; }; } From 9efef31bb81ec944401cfa5e025207c69ef384f3 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 28 Mar 2014 17:27:23 -0400 Subject: [PATCH 230/240] Feature #953 Trader Gold - Unused code/warning resolved Removed unused code getBaseGold throws proper error --- apps/openmw/mwclass/creature.cpp | 4 ---- apps/openmw/mwclass/npc.cpp | 8 +------- apps/openmw/mwworld/class.cpp | 5 ++++- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d347b2c044..94238e6d53 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -134,10 +134,6 @@ namespace MWClass getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", MWBase::Environment::get().getWorld()->getStore()); - // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. - // (except for gold you gave him) - //getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); - if (ref->mBase->mFlags & ESM::Creature::Weapon) getInventoryStore(ptr).autoEquip(ptr); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c06e315560..d41f7002af 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -365,13 +365,7 @@ namespace MWClass // store ptr.getRefData().setCustomData (data.release()); - // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. - // (except for gold you gave him) - //getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, gold, ptr); - - getInventoryStore(ptr).autoEquip(ptr); - - + getInventoryStore(ptr).autoEquip(ptr); } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index af8de13264..8c61e32f44 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -397,5 +397,8 @@ namespace MWWorld void Class::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const {} - int Class::getBaseGold(const MWWorld::Ptr& ptr) const {} + int Class::getBaseGold(const MWWorld::Ptr& ptr) const + { + throw std::runtime_error("class does not support base gold"); + } } From 13d330e42744e6200a91d922c12d1d35d6dcccd8 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 28 Mar 2014 21:16:42 -0400 Subject: [PATCH 231/240] Feature #1233 Bribe gold not in inventory Gold is now put into inventory receivers when bribing. --- apps/openmw/mwgui/dialogue.cpp | 10 ++++++++++ apps/openmw/mwgui/dialogue.hpp | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6b913f24ae..6cc580fb63 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -64,16 +64,19 @@ namespace MWGui else if (sender == mBribe10Button) { player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 10, player); + mReceiver.getClass().getContainerStore(mReceiver).add(MWWorld::ContainerStore::sGoldId, 10, mReceiver); type = MWBase::MechanicsManager::PT_Bribe10; } else if (sender == mBribe100Button) { player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 100, player); + mReceiver.getClass().getContainerStore(mReceiver).add(MWWorld::ContainerStore::sGoldId, 100, mReceiver); type = MWBase::MechanicsManager::PT_Bribe100; } else /*if (sender == mBribe1000Button)*/ { player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 1000, player); + mReceiver.getClass().getContainerStore(mReceiver).add(MWWorld::ContainerStore::sGoldId, 10000, mReceiver); type = MWBase::MechanicsManager::PT_Bribe1000; } @@ -97,6 +100,12 @@ namespace MWGui mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast(playerGold)); } + // The receiver of the bribe + void PersuasionDialog::setReceiver(MWWorld::Ptr receiver) + { + mReceiver = receiver; + } + // -------------------------------------------------------------------------------------------------- Response::Response(const std::string &text, const std::string &title) @@ -371,6 +380,7 @@ namespace MWGui mPtr = actor; mTopicsList->setEnabled(true); setTitle(npcName); + mPersuasionDialog.setReceiver(mPtr); mTopicsList->clear(); diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index befbd6eeeb..242dae2995 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -35,6 +35,10 @@ namespace MWGui virtual void open(); + // The receiver of the bribe + MWWorld::Ptr mReceiver; + void setReceiver(MWWorld::Ptr receiver); + private: MyGUI::Button* mCancelButton; MyGUI::Button* mAdmireButton; From 3e8f7b3bae9a3e23ad6953fbe79c199da79213aa Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 02:59:47 -0400 Subject: [PATCH 232/240] made mReceiver private --- apps/openmw/mwgui/dialogue.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 242dae2995..3681405208 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -36,7 +36,6 @@ namespace MWGui virtual void open(); // The receiver of the bribe - MWWorld::Ptr mReceiver; void setReceiver(MWWorld::Ptr receiver); private: @@ -51,6 +50,9 @@ namespace MWGui void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); + + // The receiver of the bribe + MWWorld::Ptr mReceiver; }; From 90a813ad2cf1f55a54b018ebe1ad80d6007c49a5 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 24 Mar 2014 23:20:25 +1100 Subject: [PATCH 233/240] Allow interrupting a walking NPC to trigger a greeting. --- apps/openmw/mwmechanics/aiwander.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 2db875a01b..26975ff1e4 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -69,6 +69,7 @@ namespace MWMechanics return new AiWander(*this); } + // TODO: duration is passed in but never used, check if it is needed bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); @@ -102,20 +103,21 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); + // Once off initialization to discover & store allowed node points for this actor. if(!mStoredAvailableNodes) { - mStoredAvailableNodes = true; mPathgrid = world->getStore().get().search(*actor.getCell()->getCell()); mCellX = actor.getCell()->getCell()->mData.mX; mCellY = actor.getCell()->getCell()->mData.mY; + // TODO: If there is no path does this actor get stuck forever? if(!mPathgrid) mDistance = 0; else if(mPathgrid->mPoints.empty()) mDistance = 0; - if(mDistance) + if(mDistance) // A distance value is initially passed into the constructor. { mXCell = 0; mYCell = 0; @@ -151,10 +153,13 @@ namespace MWMechanics } mCurrentNode = mAllowedNodes[index]; mAllowedNodes.erase(mAllowedNodes.begin() + index); + + mStoredAvailableNodes = true; // set only if successful in finding allowed nodes } } } + // TODO: Does this actor stay in one spot forever while in AiWander? if(mAllowedNodes.empty()) mDistance = 0; @@ -162,7 +167,7 @@ namespace MWMechanics if(mDistance && (mCellX != actor.getCell()->getCell()->mData.mX || mCellY != actor.getCell()->getCell()->mData.mY)) mDistance = 0; - if(mChooseAction) + if(mChooseAction) // Initially set true by the constructor. { mPlayedIdle = 0; unsigned short idleRoll = 0; @@ -208,7 +213,8 @@ namespace MWMechanics } } - if(mIdleNow) + // Allow interrupting a walking actor to trigger a greeting + if(mIdleNow || (mWalking && (mWalkState != State_Norm))) { // Play a random voice greeting if the player gets too close const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -222,6 +228,14 @@ namespace MWMechanics float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).distance( Ogre::Vector3(actor.getRefData().getPosition().pos)); + if(mWalking && playerDist <= helloDistance) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mWalkState = State_Norm; + } + if (!mSaidGreeting) { // TODO: check if actor is aware / has line of sight @@ -353,7 +367,7 @@ namespace MWMechanics if(mWalkState == State_Evade) { - //std::cout << "Stuck \""<= COUNT_BEFORE_RESET) // something has gone wrong, reset { - //std::cout << "Reset \""< Date: Thu, 27 Mar 2014 08:37:05 +1100 Subject: [PATCH 234/240] Prevent NPC suicides off silt the strider platform in Seyda Neen. Added some comments as well. There may be opportunities for some optimization but left that out for now. --- apps/openmw/mwmechanics/pathfinding.cpp | 208 +++++++++++------------- 1 file changed, 98 insertions(+), 110 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 3ecd407431..c0b78f3c77 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -57,97 +57,6 @@ namespace return closestIndex; } - /*std::list reconstructPath(const std::vector& graph,const ESM::Pathgrid* pathgrid, int lastNode,float xCell, float yCell) - { - std::list path; - while(graph[lastNode].parent != -1) - { - //std::cout << "not empty" << xCell; - ESM::Pathgrid::Point pt = pathgrid->mPoints[lastNode]; - pt.mX += xCell; - pt.mY += yCell; - path.push_front(pt); - lastNode = graph[lastNode].parent; - } - return path; - }*/ - - - - /*std::list buildPath2(const ESM::Pathgrid* pathgrid,int start,int goal,float xCell = 0, float yCell = 0) - { - std::vector graph; - for(unsigned int i = 0; i < pathgrid->mPoints.size(); i++) - { - Node node; - node.label = i; - node.parent = -1; - graph.push_back(node); - } - for(unsigned int i = 0; i < pathgrid->mEdges.size(); i++) - { - Edge edge; - edge.destination = pathgrid->mEdges[i].mV1; - edge.cost = distance(pathgrid->mPoints[pathgrid->mEdges[i].mV0],pathgrid->mPoints[pathgrid->mEdges[i].mV1]); - graph[pathgrid->mEdges[i].mV0].edges.push_back(edge); - edge.destination = pathgrid->mEdges[i].mV0; - graph[pathgrid->mEdges[i].mV1].edges.push_back(edge); - } - - std::vector g_score(pathgrid->mPoints.size(),-1.); - std::vector f_score(pathgrid->mPoints.size(),-1.); - - g_score[start] = 0; - f_score[start] = distance(pathgrid->mPoints[start],pathgrid->mPoints[goal]); - - std::list openset; - std::list closedset; - openset.push_back(start); - - int current = -1; - - while(!openset.empty()) - { - current = openset.front(); - openset.pop_front(); - - if(current == goal) break; - - closedset.push_back(current); - - for(int j = 0;jmPoints[dest],pathgrid->mPoints[goal]); - if(!isInOpenSet) - { - std::list::iterator it = openset.begin(); - for(it = openset.begin();it!= openset.end();it++) - { - if(g_score[*it]>g_score[dest]) - break; - } - openset.insert(it,dest); - } - } - } - } - - } - return reconstructPath(graph,pathgrid,current,xCell,yCell); - - }*/ - } namespace MWMechanics @@ -166,37 +75,83 @@ namespace MWMechanics mIsPathConstructed = false; } + /* + * NOTE: based on buildPath2(), please check git history if interested + * + * Populate mGraph with the cost of each allowed edge (measured in distance ^2) + * Any existing data in mGraph is wiped clean first. The node's parent is + * set with initial value of -1. The parent values are populated by aStarSearch(). + * mGSore and mFScore are also resized. + * + * + * mGraph[f].edges[n].destination = t + * + * f = point index of location "from" + * t = point index of location "to" + * n = index of edges from point f + * + * + * Example: (note from p(0) to p(2) not allowed) + * + * mGraph[0].edges[0].destination = 1 + * .edges[1].destination = 3 + * + * mGraph[1].edges[0].destination = 0 + * .edges[1].destination = 2 + * .edges[2].destination = 3 + * + * mGraph[2].edges[0].destination = 1 + * + * (etc, etc) + * + * + * low + * cost + * p(0) <---> p(1) <------------> p(2) + * ^ ^ + * | | + * | +-----> p(3) + * +----------------> + * high cost + */ void PathFinder::buildPathgridGraph(const ESM::Pathgrid* pathGrid) { mGraph.clear(); - mGScore.resize(pathGrid->mPoints.size(),-1); - mFScore.resize(pathGrid->mPoints.size(),-1); + // resize lists + mGScore.resize(pathGrid->mPoints.size(), -1); + mFScore.resize(pathGrid->mPoints.size(), -1); Node defaultNode; defaultNode.label = -1; defaultNode.parent = -1; mGraph.resize(pathGrid->mPoints.size(),defaultNode); + // initialise mGraph for(unsigned int i = 0; i < pathGrid->mPoints.size(); i++) { Node node; node.label = i; node.parent = -1; - mGraph[i] = node; + mGraph[i] = node; // TODO: old code used push_back(node), check if any difference } + // store the costs (measured in distance ^2) of each edge, in both directions for(unsigned int i = 0; i < pathGrid->mEdges.size(); i++) { Edge edge; + edge.cost = distance(pathGrid->mPoints[pathGrid->mEdges[i].mV0], + pathGrid->mPoints[pathGrid->mEdges[i].mV1]); + // forward path of the edge edge.destination = pathGrid->mEdges[i].mV1; - edge.cost = distance(pathGrid->mPoints[pathGrid->mEdges[i].mV0],pathGrid->mPoints[pathGrid->mEdges[i].mV1]); mGraph[pathGrid->mEdges[i].mV0].edges.push_back(edge); - edge.destination = pathGrid->mEdges[i].mV0; - mGraph[pathGrid->mEdges[i].mV1].edges.push_back(edge); + // reverse path of the edge + // NOTE: These are redundant, ESM already contains the required reverse paths + //edge.destination = pathGrid->mEdges[i].mV0; + //mGraph[pathGrid->mEdges[i].mV1].edges.push_back(edge); } mIsGraphConstructed = true; } void PathFinder::cleanUpAStar() { - for(int i=0;i (mGraph.size());i++) + for(int i = 0; i < static_cast (mGraph.size()); i++) { mGraph[i].parent = -1; mGScore[i] = -1; @@ -204,6 +159,25 @@ namespace MWMechanics } } + /* + * NOTE: based on buildPath2(), please check git history if interested + * + * Find the shortest path to the target goal using a well known algorithm. + * Uses mGraph which has pre-computed costs for allowed edges. It is assumed + * that mGraph is already constructed. The caller, i.e. buildPath(), needs + * to ensure this. + * + * Returns path (a list of pathgrid point indexes) which may be empty. + * + * openset - point indexes to be traversed, lowest cost at the front + * closedset - point indexes already traversed + * + * mGScore - past accumulated costs vector indexed by point index + * mFScore - future estimated costs vector indexed by point index + * these are resized by buildPathgridGraph() + * + * The heuristics used is distance^2 from current position to the final goal. + */ std::list PathFinder::aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell, float yCell) { cleanUpAStar(); @@ -218,18 +192,19 @@ namespace MWMechanics while(!openset.empty()) { - current = openset.front(); + current = openset.front(); // front has the lowest cost openset.pop_front(); if(current == goal) break; closedset.push_back(current); - for(int j = 0;j (mGraph[current].edges.size());j++) + // check all edges for the "current" point index + for(int j = 0; j < static_cast (mGraph[current].edges.size()); j++) { - //int next = mGraph[current].edges[j].destination if(std::find(closedset.begin(),closedset.end(),mGraph[current].edges[j].destination) == closedset.end()) { + // not in closedset - i.e. have not traversed this edge destination int dest = mGraph[current].edges[j].destination; float tentative_g = mGScore[current] + mGraph[current].edges[j].cost; bool isInOpenSet = std::find(openset.begin(),openset.end(),dest) != openset.end(); @@ -241,20 +216,22 @@ namespace MWMechanics mFScore[dest] = tentative_g + distance(pathGrid->mPoints[dest],pathGrid->mPoints[goal]); if(!isInOpenSet) { + // add this edge to openset, lowest cost goes to the front + // TODO: if this causes performance problems a hash table may help (apparently) std::list::iterator it = openset.begin(); for(it = openset.begin();it!= openset.end();it++) { - if(mGScore[*it]>mGScore[dest]) + if(mGScore[*it] > mGScore[dest]) break; } - openset.insert(it,dest); + openset.insert(it, dest); } } - } + } // if in closedset, i.e. traversed this edge already, try the next edge } - } + // reconstruct path to return std::list path; while(mGraph[current].parent != -1) { @@ -266,6 +243,9 @@ namespace MWMechanics current = mGraph[current].parent; } + // TODO: Is this a bug? If path is empty the destination is inserted. + // Commented out pending further testing. +#if 0 if(path.empty()) { ESM::Pathgrid::Point pt = pathGrid->mPoints[goal]; @@ -273,10 +253,19 @@ namespace MWMechanics pt.mY += yCell; path.push_front(pt); } - +#endif return path; } + /* + * NOTE: This method may fail to find a path. The caller must check the result before using it. + * If there is no path the AI routies need to implement some other heuristics to reach the target. + * + * Updates mPath using aStarSearch(). + * mPathConstructed is set true if successful, false if not + * + * May update mGraph by calling buildPathgridGraph() if it isn't constructed yet. + */ void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts) { @@ -310,11 +299,10 @@ namespace MWMechanics { if(!mIsGraphConstructed) buildPathgridGraph(pathGrid); - mPath = aStarSearch(pathGrid,startNode,endNode,xCell,yCell);//findPath(startNode, endNode, mGraph); + mPath = aStarSearch(pathGrid,startNode,endNode,xCell,yCell); if(!mPath.empty()) { - mPath.push_back(endPoint); mIsPathConstructed = true; } } @@ -331,8 +319,8 @@ namespace MWMechanics float PathFinder::getZAngleToNext(float x, float y) const { - // This should never happen (programmers should have an if statement checking mIsPathConstructed that prevents this call - // if otherwise). + // This should never happen (programmers should have an if statement checking + // mIsPathConstructed that prevents this call if otherwise). if(mPath.empty()) return 0.; From 07fd801d94c14d24d0615da7263063d2401bdb63 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 29 Mar 2014 19:28:30 +1100 Subject: [PATCH 235/240] My previous analysis of the pathfinding issue was incorrect. It was in fact caused due to some of the pathgrid points being unreachable. Instead of returning an empty path in such a scenario, incorrect path + requested destination were being returned. There was also a defect where past cost was being used for selecting open points. There is still an unresolved issue where mGraph and mSCComp are being rebuilt unnecessarily. The check mCell != cell in buildPath() is being triggered frequently. Not sure why. --- apps/openmw/mwmechanics/aiwander.cpp | 26 +- apps/openmw/mwmechanics/aiwander.hpp | 2 + apps/openmw/mwmechanics/pathfinding.cpp | 393 +++++++++++++++++++----- apps/openmw/mwmechanics/pathfinding.hpp | 34 +- 4 files changed, 378 insertions(+), 77 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 26975ff1e4..a3286b8c05 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -127,14 +127,18 @@ namespace MWMechanics mYCell = mCellY * ESM::Land::REAL_SIZE; } + // convert npcPos to local (i.e. cell) co-ordinates Ogre::Vector3 npcPos(actor.getRefData().getPosition().pos); npcPos[0] = npcPos[0] - mXCell; npcPos[1] = npcPos[1] - mYCell; + // populate mAllowedNodes for this actor with pathgrid point indexes based on mDistance + // NOTE: mPoints and mAllowedNodes contain points in local co-ordinates for(unsigned int counter = 0; counter < mPathgrid->mPoints.size(); counter++) { - Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, mPathgrid->mPoints[counter].mY, - mPathgrid->mPoints[counter].mZ); + Ogre::Vector3 nodePos(mPathgrid->mPoints[counter].mX, + mPathgrid->mPoints[counter].mY, + mPathgrid->mPoints[counter].mZ); if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) mAllowedNodes.push_back(mPathgrid->mPoints[counter]); } @@ -145,8 +149,9 @@ namespace MWMechanics unsigned int index = 0; for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++) { - Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY, - mAllowedNodes[counterThree].mZ); + Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, + mAllowedNodes[counterThree].mY, + mAllowedNodes[counterThree].mZ); float tempDist = npcPos.squaredDistance(nodePos); if(tempDist < closestNode) index = counterThree; @@ -277,16 +282,24 @@ namespace MWMechanics dest.mY = destNodePos[1] + mYCell; dest.mZ = destNodePos[2]; + // actor position is already in world co-ordinates ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; start.mZ = pos.pos[2]; + // don't take shortcuts for wandering mPathFinder.buildPath(start, dest, actor.getCell(), false); if(mPathFinder.isPathConstructed()) { - // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): + // buildPath inserts dest in case it is not a pathgraph point index + // which is a duplicate for AiWander + //if(mPathFinder.getPathSize() > 1) + //mPathFinder.getPath().pop_back(); + + // Remove this node as an option and add back the previously used node + // (stops NPC from picking the same node): ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; mAllowedNodes.erase(mAllowedNodes.begin() + randNode); mAllowedNodes.push_back(mCurrentNode); @@ -377,7 +390,10 @@ namespace MWMechanics } else { + // normal walk forward actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + // turn towards the next point in mPath + // TODO: possibly no need to check every frame, maybe every 30 should be ok? zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 6de0b81813..48b67fb1cc 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -38,8 +38,10 @@ namespace MWMechanics float mY; float mZ; + // Cell location int mCellX; int mCellY; + // Cell location multiply by ESM::Land::REAL_SIZE to get the right scale float mXCell; float mYCell; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index c0b78f3c77..0eb7f07986 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -37,19 +37,75 @@ namespace return sqrt(x * x + y * y + z * z); } - int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z) + // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + // + // One of the smallest cost in Seyda Neen is between points 77 & 78: + // pt x y + // 77 = 8026, 4480 + // 78 = 7986, 4218 + // + // Euclidean distance is about 262 (ignoring z) and Manhattan distance is 300 + // (again ignoring z). Using a value of about 300 for D seems like a reasonable + // starting point for experiments. If in doubt, just use value 1. + // + // The distance between 3 & 4 are pretty small, too. + // 3 = 5435, 223 + // 4 = 5948, 193 + // + // Approx. 514 Euclidean distance and 533 Manhattan distance. + // + float manhattan(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b) + { + return 300 * (abs(a.mX - b.mX) + abs(a.mY - b.mY) + abs(a.mZ - b.mZ)); + } + + // Choose a heuristics - these may not be the best for directed graphs with + // non uniform edge costs. + // + // distance: + // - sqrt((curr.x - goal.x)^2 + (curr.y - goal.y)^2 + (curr.z - goal.z)^2) + // - slower but more accurate + // + // Manhattan: + // - |curr.x - goal.x| + |curr.y - goal.y| + |curr.z - goal.z| + // - faster but not the shortest path + float costAStar(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b) + { + //return distance(a, b); + return manhattan(a, b); + } + + // Slightly cheaper version for comparisons. + // Caller needs to be careful for very short distances (i.e. less than 1) + // or when accumuating the results i.e. (a + b)^2 != a^2 + b^2 + // + float distanceSquared(ESM::Pathgrid::Point point, Ogre::Vector3 pos) + { + return Ogre::Vector3(point.mX, point.mY, point.mZ).squaredDistance(pos); + } + + // Return the closest pathgrid point index from the specified position co + // -ordinates. NOTE: Does not check if there is a sensible way to get there + // (e.g. a cliff in front). + // + // NOTE: pos is expected to be in local co-ordinates, as is grid->mPoints + // + int getClosestPoint(const ESM::Pathgrid* grid, Ogre::Vector3 pos) { if(!grid || grid->mPoints.empty()) return -1; - float distanceBetween = distance(grid->mPoints[0], x, y, z); + float distanceBetween = distanceSquared(grid->mPoints[0], pos); int closestIndex = 0; + // TODO: if this full scan causes performance problems mapping pathgrid + // points to a quadtree may help for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) { - if(distance(grid->mPoints[counter], x, y, z) < distanceBetween) + float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos); + if(potentialDistBetween < distanceBetween) { - distanceBetween = distance(grid->mPoints[counter], x, y, z); + distanceBetween = potentialDistBetween; closestIndex = counter; } } @@ -57,6 +113,39 @@ namespace return closestIndex; } + // Uses mSCComp to choose a reachable end pathgrid point. start is assumed reachable. + std::pair getClosestReachablePoint(const ESM::Pathgrid* grid, + Ogre::Vector3 pos, int start, std::vector &sCComp) + { + // assume grid is fine + int startGroup = sCComp[start]; + + float distanceBetween = distanceSquared(grid->mPoints[0], pos); + int closestIndex = 0; + int closestReachableIndex = 0; + // TODO: if this full scan causes performance problems mapping pathgrid + // points to a quadtree may help + for(unsigned int counter = 1; counter < grid->mPoints.size(); counter++) + { + float potentialDistBetween = distanceSquared(grid->mPoints[counter], pos); + if(potentialDistBetween < distanceBetween) + { + // found a closer one + distanceBetween = potentialDistBetween; + closestIndex = counter; + if (sCComp[counter] == startGroup) + { + closestReachableIndex = counter; + } + } + } + if(start == closestReachableIndex) + closestReachableIndex = -1; // couldn't find anyting other than start + + return std::pair + (closestReachableIndex, closestReachableIndex == closestIndex); + } + } namespace MWMechanics @@ -76,13 +165,13 @@ namespace MWMechanics } /* - * NOTE: based on buildPath2(), please check git history if interested + * NOTE: Based on buildPath2(), please check git history if interested * - * Populate mGraph with the cost of each allowed edge (measured in distance ^2) - * Any existing data in mGraph is wiped clean first. The node's parent is - * set with initial value of -1. The parent values are populated by aStarSearch(). - * mGSore and mFScore are also resized. + * Populate mGraph with the cost of each allowed edge. * + * Any existing data in mGraph is wiped clean first. The node's parent + * is set with initial value of -1. The parent values are populated by + * aStarSearch() in order to reconstruct a path. * * mGraph[f].edges[n].destination = t * @@ -91,7 +180,7 @@ namespace MWMechanics * n = index of edges from point f * * - * Example: (note from p(0) to p(2) not allowed) + * Example: (note from p(0) to p(2) not allowed in this example) * * mGraph[0].edges[0].destination = 1 * .edges[1].destination = 3 @@ -130,25 +219,110 @@ namespace MWMechanics Node node; node.label = i; node.parent = -1; - mGraph[i] = node; // TODO: old code used push_back(node), check if any difference + mGraph[i] = node; } - // store the costs (measured in distance ^2) of each edge, in both directions + // store the costs of each edge for(unsigned int i = 0; i < pathGrid->mEdges.size(); i++) { Edge edge; - edge.cost = distance(pathGrid->mPoints[pathGrid->mEdges[i].mV0], - pathGrid->mPoints[pathGrid->mEdges[i].mV1]); + edge.cost = costAStar(pathGrid->mPoints[pathGrid->mEdges[i].mV0], + pathGrid->mPoints[pathGrid->mEdges[i].mV1]); // forward path of the edge edge.destination = pathGrid->mEdges[i].mV1; mGraph[pathGrid->mEdges[i].mV0].edges.push_back(edge); // reverse path of the edge - // NOTE: These are redundant, ESM already contains the required reverse paths + // NOTE: These are redundant, the ESM already contains the reverse paths. //edge.destination = pathGrid->mEdges[i].mV0; //mGraph[pathGrid->mEdges[i].mV1].edges.push_back(edge); } mIsGraphConstructed = true; } + // v is the pathgrid point index (some call them vertices) + void PathFinder::recursiveStrongConnect(int v) + { + mSCCPoint[v].first = mSCCIndex; // index + mSCCPoint[v].second = mSCCIndex; // lowlink + mSCCIndex++; + mSCCStack.push_back(v); + int w; + + for(int i = 0; i < mGraph[v].edges.size(); i++) + { + w = mGraph[v].edges[i].destination; + if(mSCCPoint[w].first == -1) // not visited + { + recursiveStrongConnect(w); // recurse + mSCCPoint[v].second = std::min(mSCCPoint[v].second, + mSCCPoint[w].second); + } + else + { + if(find(mSCCStack.begin(), mSCCStack.end(), w) != mSCCStack.end()) + mSCCPoint[v].second = std::min(mSCCPoint[v].second, + mSCCPoint[w].first); + } + } + + if(mSCCPoint[v].second == mSCCPoint[v].first) + { + // new component + do + { + w = mSCCStack.back(); + mSCCStack.pop_back(); + mSCComp[w] = mSCCId; + } + while(w != v); + + mSCCId++; + } + return; + } + + /* + * mSCComp contains the strongly connected component group id's. + * + * A cell can have disjointed pathgrid, e.g. Seyda Neen which has 3 + * + * mSCComp for Seyda Neen will have 3 different values. When selecting a + * random pathgrid point for AiWander, mSCComp can be checked for quickly + * finding whether the destination is reachable. + * + * Otherwise, buildPath will automatically select a closest reachable end + * pathgrid point (reachable from the closest start point). + * + * Using Tarjan's algorithm + * + * mGraph | graph G | + * mSCCPoint | V | derived from pathGrid->mPoints + * mGraph[v].edges | E (for v) | + * mSCCIndex | index | keep track of smallest unused index + * mSCCStack | S | + * pathGrid + * ->mEdges[v].mV1 | w | = mGraph[v].edges[i].destination + * + * FIXME: Some of these can be cleaned up by including them to struct + * Node used by mGraph + */ + void PathFinder::buildConnectedPoints(const ESM::Pathgrid* pathGrid) + { + mSCComp.clear(); + mSCComp.resize(pathGrid->mPoints.size(), 0); + mSCCId = 0; + + mSCCIndex = 0; + mSCCStack.clear(); + mSCCPoint.clear(); + mSCCPoint.resize(pathGrid->mPoints.size(), std::pair (-1, -1)); + + for(unsigned int v = 0; v < pathGrid->mPoints.size(); v++) + { + if(mSCCPoint[v].first == -1) // undefined (haven't visited) + recursiveStrongConnect(v); + } + } + void PathFinder::cleanUpAStar() { for(int i = 0; i < static_cast (mGraph.size()); i++) @@ -160,7 +334,8 @@ namespace MWMechanics } /* - * NOTE: based on buildPath2(), please check git history if interested + * NOTE: Based on buildPath2(), please check git history if interested + * Should consider a using 3rd party library version (e.g. boost) * * Find the shortest path to the target goal using a well known algorithm. * Uses mGraph which has pre-computed costs for allowed edges. It is assumed @@ -169,20 +344,27 @@ namespace MWMechanics * * Returns path (a list of pathgrid point indexes) which may be empty. * - * openset - point indexes to be traversed, lowest cost at the front - * closedset - point indexes already traversed + * Input params: + * start, goal - pathgrid point indexes (for this cell) + * xCell, yCell - values to add to convert path back to world scale * - * mGScore - past accumulated costs vector indexed by point index - * mFScore - future estimated costs vector indexed by point index - * these are resized by buildPathgridGraph() + * Variables: + * openset - point indexes to be traversed, lowest cost at the front + * closedset - point indexes already traversed * - * The heuristics used is distance^2 from current position to the final goal. + * Class variables: + * mGScore - past accumulated costs vector indexed by point index + * mFScore - future estimated costs vector indexed by point index + * these are resized by buildPathgridGraph() */ - std::list PathFinder::aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell, float yCell) + std::list PathFinder::aStarSearch(const ESM::Pathgrid* pathGrid, + int start, int goal, + float xCell, float yCell) { cleanUpAStar(); + // mGScore & mFScore keep costs for each pathgrid point in pathGrid->mPoints mGScore[start] = 0; - mFScore[start] = distance(pathGrid->mPoints[start],pathGrid->mPoints[goal]); + mFScore[start] = costAStar(pathGrid->mPoints[start], pathGrid->mPoints[goal]); std::list openset; std::list closedset; @@ -195,33 +377,36 @@ namespace MWMechanics current = openset.front(); // front has the lowest cost openset.pop_front(); - if(current == goal) break; + if(current == goal) + break; - closedset.push_back(current); + closedset.push_back(current); // remember we've been here - // check all edges for the "current" point index + // check all edges for the current point index for(int j = 0; j < static_cast (mGraph[current].edges.size()); j++) { - if(std::find(closedset.begin(),closedset.end(),mGraph[current].edges[j].destination) == closedset.end()) + if(std::find(closedset.begin(), closedset.end(), mGraph[current].edges[j].destination) == + closedset.end()) { // not in closedset - i.e. have not traversed this edge destination int dest = mGraph[current].edges[j].destination; float tentative_g = mGScore[current] + mGraph[current].edges[j].cost; - bool isInOpenSet = std::find(openset.begin(),openset.end(),dest) != openset.end(); + bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); if(!isInOpenSet - || tentative_g < mGScore[dest] ) + || tentative_g < mGScore[dest]) { mGraph[dest].parent = current; mGScore[dest] = tentative_g; - mFScore[dest] = tentative_g + distance(pathGrid->mPoints[dest],pathGrid->mPoints[goal]); + mFScore[dest] = tentative_g + + costAStar(pathGrid->mPoints[dest], pathGrid->mPoints[goal]); if(!isInOpenSet) { // add this edge to openset, lowest cost goes to the front - // TODO: if this causes performance problems a hash table may help (apparently) + // TODO: if this causes performance problems a hash table may help std::list::iterator it = openset.begin(); - for(it = openset.begin();it!= openset.end();it++) + for(it = openset.begin(); it!= openset.end(); it++) { - if(mGScore[*it] > mGScore[dest]) + if(mFScore[*it] > mFScore[dest]) break; } openset.insert(it, dest); @@ -231,11 +416,14 @@ namespace MWMechanics } } - // reconstruct path to return std::list path; + if(current != goal) + return path; // for some reason couldn't build a path + // e.g. start was not reachable (we assume it is) + + // reconstruct path to return, using world co-ordinates while(mGraph[current].parent != -1) { - //std::cout << "not empty" << xCell; ESM::Pathgrid::Point pt = pathGrid->mPoints[current]; pt.mX += xCell; pt.mY += yCell; @@ -243,7 +431,8 @@ namespace MWMechanics current = mGraph[current].parent; } - // TODO: Is this a bug? If path is empty the destination is inserted. + // TODO: Is this a bug? If path is empty the algorithm couldn't find a path. + // Simply using the destination as the path in this scenario seems strange. // Commented out pending further testing. #if 0 if(path.empty()) @@ -258,63 +447,127 @@ namespace MWMechanics } /* - * NOTE: This method may fail to find a path. The caller must check the result before using it. - * If there is no path the AI routies need to implement some other heuristics to reach the target. + * NOTE: This method may fail to find a path. The caller must check the + * result before using it. If there is no path the AI routies need to + * implement some other heuristics to reach the target. * - * Updates mPath using aStarSearch(). - * mPathConstructed is set true if successful, false if not + * NOTE: startPoint & endPoint are in world co-ordinates * - * May update mGraph by calling buildPathgridGraph() if it isn't constructed yet. + * Updates mPath using aStarSearch() or ray test (if shortcut allowed). + * mPath consists of pathgrid points, except the last element which is + * endPoint. This may be useful where the endPoint is not on a pathgrid + * point (e.g. combat). However, if the caller has already chosen a + * pathgrid point (e.g. wander) then it may be worth while to call + * pop_back() to remove the redundant entry. + * + * mPathConstructed is set true if successful, false if not + * + * May update mGraph by calling buildPathgridGraph() if it isn't + * constructed yet. At the same time mConnectedPoints is also updated. + * + * NOTE: co-ordinates must be converted prior to calling getClosestPoint() + * + * | + * | cell + * | +-----------+ + * | | | + * | | | + * | | @ | + * | i | j | + * |<--->|<---->| | + * | +-----------+ + * | k + * |<---------->| world + * +----------------------------- + * + * i = x value of cell itself (multiply by ESM::Land::REAL_SIZE to convert) + * j = @.x in local co-ordinates (i.e. within the cell) + * k = @.x in world co-ordinates */ - void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, + void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, + const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts) { mPath.clear(); - if(mCell != cell) mIsGraphConstructed = false; - mCell = cell; if(allowShortcuts) { - if(MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, - endPoint.mX, endPoint.mY, endPoint.mZ)) - allowShortcuts = false; + // if there's a ray cast hit, can't take a direct path + if(!MWBase::Environment::get().getWorld()->castRay(startPoint.mX, startPoint.mY, startPoint.mZ, + endPoint.mX, endPoint.mY, endPoint.mZ)) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + return; + } } - if(!allowShortcuts) + if(mCell != cell) { - const ESM::Pathgrid *pathGrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); - float xCell = 0; - float yCell = 0; + mIsGraphConstructed = false; // must be in a new cell, need a new mGraph and mSCComp + mCell = cell; + } - if (mCell->isExterior()) + const ESM::Pathgrid *pathGrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + float xCell = 0; + float yCell = 0; + + if (mCell->isExterior()) + { + xCell = mCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; + yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; + } + + // NOTE: It is possible that getClosestPoint returns a pathgrind point index + // that is unreachable in some situations. e.g. actor is standing + // outside an area enclosed by walls, but there is a pathgrid + // point right behind the wall that is closer than any pathgrid + // point outside the wall + // + // NOTE: getClosestPoint expects local co-ordinates + // + int startNode = getClosestPoint(pathGrid, + Ogre::Vector3(startPoint.mX - xCell, startPoint.mY - yCell, startPoint.mZ)); + + if(startNode != -1) // only check once, assume pathGrid won't change + { + if(!mIsGraphConstructed) { - xCell = mCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; - yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; + buildPathgridGraph(pathGrid); // pre-compute costs for use with aStarSearch + buildConnectedPoints(pathGrid); // must before calling getClosestReachablePoint } - int startNode = getClosestPoint(pathGrid, startPoint.mX - xCell, startPoint.mY - yCell,startPoint.mZ); - int endNode = getClosestPoint(pathGrid, endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ); + std::pair endNode = getClosestReachablePoint(pathGrid, + Ogre::Vector3(endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ), + startNode, mSCComp); - if(startNode != -1 && endNode != -1) + if(endNode.first != -1) { - if(!mIsGraphConstructed) buildPathgridGraph(pathGrid); - - mPath = aStarSearch(pathGrid,startNode,endNode,xCell,yCell); + mPath = aStarSearch(pathGrid, startNode, endNode.first, xCell, yCell); if(!mPath.empty()) { mIsPathConstructed = true; + // Add the destination (which may be different to the closest + // pathgrid point). However only add if endNode was the closest + // point to endPoint. + // + // This logic can fail in the opposite situate, e.g. endPoint may + // have been reachable but happened to be very close to an + // unreachable pathgrid point. + // + // The AI routines will have to deal with such situations. + if(endNode.second) + mPath.push_back(endPoint); } + else + mIsPathConstructed = false; } + else + mIsPathConstructed = false; } else - { - mPath.push_back(endPoint); - mIsPathConstructed = true; - } - - if(mPath.empty()) - mIsPathConstructed = false; + mIsPathConstructed = false; // this shouldn't really happen, but just in case } float PathFinder::getZAngleToNext(float x, float y) const @@ -332,6 +585,7 @@ namespace MWMechanics return Ogre::Radian(Ogre::Math::ACos(directionY / directionResult) * sgn(Ogre::Math::ASin(directionX / directionResult))).valueDegrees(); } + // Used by AiCombat, use Euclidean distance float PathFinder::getDistToNext(float x, float y, float z) { ESM::Pathgrid::Point nextPoint = *mPath.begin(); @@ -372,6 +626,7 @@ namespace MWMechanics return false; } + // used by AiCombat, see header for the rationale void PathFinder::syncStart(const std::list &path) { if (mPath.size() < 2) diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index ecaaef568f..ae849bff27 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -64,9 +64,10 @@ namespace MWMechanics return mPath; } - //When first point of newly created path is the nearest to actor point, then - //the cituation can occure when this point is undesirable (if the 2nd point of new path == the 1st point of old path) - //This functions deletes that point. + // When first point of newly created path is the nearest to actor point, + // then a situation can occure when this point is undesirable + // (if the 2nd point of new path == the 1st point of old path) + // This functions deletes that point. void syncStart(const std::list &path); void addPointToPath(ESM::Pathgrid::Point &point) @@ -74,6 +75,13 @@ namespace MWMechanics mPath.push_back(point); } + // While a public method is defined here, it is anticipated that + // mSCComp will only be used internally. + std::vector getSCComp() const + { + return mSCComp; + } + private: struct Edge @@ -101,6 +109,26 @@ namespace MWMechanics std::list mPath; bool mIsGraphConstructed; const MWWorld::CellStore* mCell; + + // contains an integer indicating the groups of connected pathgrid points + // (all connected points will have the same value) + // + // In Seyda Neen there are 3: + // + // 52, 53 and 54 are one set (enclosed yard) + // 48, 49, 50, 51, 84, 85, 86, 87, 88, 89, 90 are another (ship & office) + // all other pathgrid points are the third set + // + std::vector mSCComp; + // variables used to calculate mSCComp + int mSCCId; + int mSCCIndex; + std::list mSCCStack; + typedef std::pair VPair; // first is index, second is lowlink + std::vector mSCCPoint; + // methods used to calculate mSCComp + void recursiveStrongConnect(int v); + void buildConnectedPoints(const ESM::Pathgrid* pathGrid); }; } From ebb1813b9bb01ee3ae5f8c43e282ad6b93be3d6c Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 29 Mar 2014 20:17:04 +1100 Subject: [PATCH 236/240] Minor comment clarification. --- apps/openmw/mwmechanics/aiwander.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 48b67fb1cc..ca6e546edf 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -41,7 +41,7 @@ namespace MWMechanics // Cell location int mCellX; int mCellY; - // Cell location multiply by ESM::Land::REAL_SIZE to get the right scale + // Cell location multiplied by ESM::Land::REAL_SIZE float mXCell; float mYCell; From c7b969821fd3e7ec2e8883dc289038e828a514d3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 29 Mar 2014 11:11:43 +0100 Subject: [PATCH 237/240] silenced a warning --- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 0eb7f07986..86f9f9af23 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -247,7 +247,7 @@ namespace MWMechanics mSCCStack.push_back(v); int w; - for(int i = 0; i < mGraph[v].edges.size(); i++) + for(int i = 0; i < static_cast (mGraph[v].edges.size()); i++) { w = mGraph[v].edges[i].destination; if(mSCCPoint[w].first == -1) // not visited From 33aecc521848ec949d6d5c00e831b9eb6d1ed5a4 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 07:45:36 -0400 Subject: [PATCH 238/240] Revert "made mReceiver private" This reverts commit 3e8f7b3bae9a3e23ad6953fbe79c199da79213aa. --- apps/openmw/mwgui/dialogue.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 3681405208..242dae2995 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -36,6 +36,7 @@ namespace MWGui virtual void open(); // The receiver of the bribe + MWWorld::Ptr mReceiver; void setReceiver(MWWorld::Ptr receiver); private: @@ -50,9 +51,6 @@ namespace MWGui void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); - - // The receiver of the bribe - MWWorld::Ptr mReceiver; }; From ad9286a30f906c373d453fcaab70abbd7b7f289a Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 07:45:56 -0400 Subject: [PATCH 239/240] Revert "Revert "made mReceiver private"" This reverts commit 33aecc521848ec949d6d5c00e831b9eb6d1ed5a4. --- apps/openmw/mwgui/dialogue.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 242dae2995..3681405208 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -36,7 +36,6 @@ namespace MWGui virtual void open(); // The receiver of the bribe - MWWorld::Ptr mReceiver; void setReceiver(MWWorld::Ptr receiver); private: @@ -51,6 +50,9 @@ namespace MWGui void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); + + // The receiver of the bribe + MWWorld::Ptr mReceiver; }; From 6b28c06b2c6401d7a54a2df4e2f329b948acd7ed Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 07:47:41 -0400 Subject: [PATCH 240/240] Feature #1233 Bribe gold in inventory - Corrected 1000 drake bribe --- apps/openmw/mwgui/dialogue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 6cc580fb63..e64c80c90a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -76,7 +76,7 @@ namespace MWGui else /*if (sender == mBribe1000Button)*/ { player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, 1000, player); - mReceiver.getClass().getContainerStore(mReceiver).add(MWWorld::ContainerStore::sGoldId, 10000, mReceiver); + mReceiver.getClass().getContainerStore(mReceiver).add(MWWorld::ContainerStore::sGoldId, 1000, mReceiver); type = MWBase::MechanicsManager::PT_Bribe1000; }