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 3d45b85ce..846cff114 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 1b4af6d82..267c761e2 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 c314ce1fd..348178d81 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 3c1a4b3fa..fd487c87f 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 5448bc3c4..ba9684589 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 1f3c58521..ea146625a 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 3653587f8..b0e0c2f7b 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 bac258425..a58bfef05 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 cf5291fd3..ef6ead9d7 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 03052f327..41226ad79 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 77316fedf..012f71b72 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 4407363a6..2503181ae 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 fe395e566..30072a8cc 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 44bba90d0..820857137 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 2b61e109b..0cfcf826f 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 f147ae7b7..554ba22fa 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 0a82d67fb..0311c90b3 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 a0acfa4da..2140076c3 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 fd3fe4c0a..13e152e68 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 ba0e1d056..8c59247f4 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 965c9fc5d..cdfbb7b45 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 42c954afb..c3c3eb6e9 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 a4f219013..b90ea72a5 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 + }; + + private: + + const ESM::Cell *mCell; + + public: + + CellStore (const ESM::Cell *cell_); - 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 a7103b991..56d19c8be 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 c1cce84fc..ba816d6d5 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 167adf301..06141db54 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 b00ad15ca..c79d7ee4f 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 be6c0b338..4c97c594d 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 555b6d360..e7e34b8e9 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 3817d5e70..5954035fc 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 3638dc6c3..fa5c9c3c2 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 1f67e0262..b3c253919 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 e04333608..4ce45ffe8 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 b56c9c8c2..f5e1acfb4 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 7687f3555..0b0143abd 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 cc1578bdd..841919a9e 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 a33288025..4954efefb 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 3817d5e70..263e89e73 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 2a1a1407f..59c083bd8 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 3638dc6c3..fd384a32b 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 edf3bc6de..bc67c68b5 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 615a31b4d..04733a53e 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 e330d4775..782ccfd24 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 1f67e0262..4e578b180 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 265069dc4..f9b5b4d04 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 91f123eb7..b7207486b 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 33650e678..304876dbf 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 a23cd1077..4cc9c6234 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 cdfbb7b45..c3bc135a1 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 c3c3eb6e9..16f317f08 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 b90ea72a5..ef16c64c2 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 c3bc135a1..8ce7d4fe8 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 16f317f08..e95fd226b 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 ef16c64c2..66f5bbf8d 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 2140076c3..4d1c15f1e 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 e95fd226b..ceb27274e 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 66f5bbf8d..428890764 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 4c97c594d..e258d8503 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 ceb27274e..91bda79af 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.isEmpty()) - return ptr; - } + if (Ptr ptr = searchInContainerList (mContainers, id)) + return ptr; - { - Ptr ptr = searchInContainerList (mCreatures, id); + if (Ptr ptr = searchInContainerList (mCreatures, id)) + return ptr; - 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 67bfe4900..3920a3e79 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 1212619d0..4529ae279 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 | 10 ++-- 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, 294 insertions(+), 110 deletions(-) create mode 100644 apps/openmw/mwworld/cellreflist.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 84d116848..be583ea74 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 3d033838c..fd3978943 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 81ca0ce2b..516a4c510 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 adaf62a96..6df6db297 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 05ba0248b..2ac342a61 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 3a95f3c29..e00c3b720 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 bd7deca88..f8677dc20 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 cf8ea1176..b9284dc1a 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 846cff114..18ae7dd1b 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 98d963b22..884d567c5 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 924f40c28..03898093d 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 f3805b255..01eb770f7 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 f56d80883..2f11a4d12 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 267c761e2..51e160d26 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 3c3335d8b..2a48e62a9 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 e1970004c..a084e6453 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 3cf514dc5..74a4f88e7 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 68aecf28d..77df46514 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 88c14b791..c9c65a152 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 348178d81..89d73215d 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 fd487c87f..9417d2a4b 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 ba9684589..f44078256 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 66d93469c..314053799 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 994798b0b..0c5bd9afa 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 ea146625a..bdd7c8f3b 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 b0e0c2f7b..5be90bf2c 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 a58bfef05..b54f1d39c 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 ef6ead9d7..284937546 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -1,12 +1,14 @@ #include "aifollow.hpp" -#include + +#include + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" -#include "../mwworld/class.hpp" -#include "movement.hpp" -#include +#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 41226ad79..1fd719c60 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 012f71b72..9a95822f5 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 957e3122c..31cafa4dc 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 c2a26ced3..d19f32507 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 204264106..6aeb90dad 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 d3ea825cf..5f73e8acd 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 2503181ae..efa2bdf69 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 53681caf8..14abcd643 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 1b17f8305..48179d344 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 2e5eaecfd..edec45e15 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 30072a8cc..9745cdebe 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 74dc490ea..44402fe7b 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 cc239a650..354b1fd0b 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 7f4be9a68..de0457e57 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 820857137..2ddea7682 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 c0cb18010..564bb73ef 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 e2aa9a2b8..8ef584154 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 0cfcf826f..241f7e470 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 554ba22fa..772813c73 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 e721477ee..c97e5279a 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 0311c90b3..844be52d8 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 4d1c15f1e..903612258 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 fc21c5712..d489bfaf1 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 0c60e1c94..fa8441aa5 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 13e152e68..0720e798a 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 8c59247f4..2ca7d76b3 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 f5d7e2636..020bdb0ff 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 000000000..8240fbd2d --- /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 8ce7d4fe8..74df0f79d 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 7ee8a3f6c..0df55b586 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 428890764..c576a0609 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 acf429891..dd0c1b002 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 714ba47da..b4b275b6a 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 56d19c8be..1a16870da 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 ba816d6d5..6d551ecf1 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 4529ae279..b83069283 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 06141db54..248d452c1 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 665274831..af833f331 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 c79d7ee4f..5f38ee286 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 8bc104d25..16ab6321d 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 b3a1af288..8580d61ce 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 e3974f243..550151e43 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 0e6506514..ebc3b18e6 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 ab98d05ae..18a40d5d3 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 546d6538c..715784b7c 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 516a4c510..2939a5431 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 e9ac39f1d..3cd8237e7 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 faf29bc83..e15424c38 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 ddb2c16d6..72de620e4 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 795b66052..60ffec7b9 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 e58716f1c..e568bf869 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 e00c3b720..20c7d9a0e 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 d68db4e45..216f815cd 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 4209c1431..d376270cb 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 5f2065c3c..af79a9691 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 f8677dc20..4ac41350f 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 af0234cd5..e9b0c8f3c 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 8240fbd2d..264929bfb 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 91bda79af..8b0740d3b 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 c576a0609..5ff63c582 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 997e9e32c..844e2b18b 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 248d452c1..10afbc675 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 af833f331..16d4877a9 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 e258d8503..b51e224f8 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 | 301 +++++++++++++++--------------- components/esm/cellid.cpp | 13 +- components/esm/cellid.hpp | 3 + 4 files changed, 176 insertions(+), 156 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 8b0740d3b..f84b35b58 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 5ff63c582..6c18d1ce1 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); - } - - 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; - } - - bool operator!=(const CellStore &cell) { - return !(*this == cell); - } - - bool isExterior() const { - return mCell->isExterior(); - } - - 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. - }; + 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); + ///< 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 5bc8b7aef..3c6e23ffd 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 54dbdae78..40bc552e0 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 74df0f79d..acffe20f3 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 0df55b586..5209aa51a 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 f84b35b58..3f1ef8ab2 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 6c18d1ce1..4b7c0011b 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 555b6d360..e7e34b8e9 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 3817d5e70..5954035fc 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 3638dc6c3..fa5c9c3c2 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 1f67e0262..b3c253919 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 91c77702e..17a624f0f 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 5996eb079..8dbdd8770 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 5d5ac4c55..2b2f41754 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 (dataDirs); + + CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg"); - setupDataFiles(); + 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 930aa9d64..0f1c7a682 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 931d63312..212ed0836 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 33ae327a0..705adf2ad 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 6e327bb6e..88493fd76 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 355a6331e..deba47fbb 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 88493fd76..280a85fd4 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 deba47fbb..777398353 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 280a85fd4..4702b0f72 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 777398353..0b1817682 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 4702b0f72..4f7575024 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 0b1817682..d5042d97b 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 4f7575024..2cc9c93b5 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 d5042d97b..6b6a8486d 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 2cc9c93b5..ffe867ae5 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 6b6a8486d..1b78346f7 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 ffe867ae5..35a29c668 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 1b78346f7..37ef3cb94 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 35a29c668..b498fba06 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()) + { + mCamera->pitch ( + Ogre::Degree ((getFastFactor() * diff.y())/mMouseSensitivity)); + mUpdate = true; + } } - - if (diff.y()) + 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 37ef3cb94..58b122dee 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 b498fba06..d6f12b529 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 d6f12b529..c608df376 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 c608df376..07fe56f8f 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 58b122dee..55f62d661 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 705adf2ad..339a88519 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 a0fed908d..65e1614c1 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 4ce45ffe8..e04333608 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 f5e1acfb4..b56c9c8c2 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 0b0143abd..7687f3555 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 841919a9e..cc1578bdd 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 4954efefb..a33288025 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 263e89e73..5954035fc 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 59c083bd8..2a1a1407f 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 fd384a32b..fa5c9c3c2 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 bc67c68b5..edf3bc6de 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 04733a53e..615a31b4d 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 782ccfd24..e330d4775 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 4e578b180..b3c253919 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 6a49f2f5e..17a624f0f 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 4407363a6..8dbdd8770 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 f9b5b4d04..265069dc4 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 b7207486b..91f123eb7 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 304876dbf..33650e678 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 561931cde..601255763 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 4cc9c6234..a23cd1077 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 e7e34b8e9..555b6d360 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 5954035fc..3817d5e70 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 fa5c9c3c2..3638dc6c3 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 b3c253919..1f67e0262 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 17a624f0f..6a49f2f5e 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 8dbdd8770..4407363a6 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 601255763..561931cde 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 0a82d67fb..a4ce92347 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 481b99bad..f124abb99 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 6cd7244b8..4ef611dc8 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 6bcad1d08..1ba24039b 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 000000000..14ae7f0b7 --- /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 000000000..964ceeac6 --- /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 | 120 +++++++++------------- apps/opencs/view/render/scenewidget.hpp | 15 +-- apps/opencs/view/world/scenesubview.cpp | 12 ++- apps/opencs/view/world/scenesubview.hpp | 3 + 7 files changed, 161 insertions(+), 83 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 1ba24039b..bb4a9d329 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 000000000..5a3bc7b65 --- /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 000000000..815e0b117 --- /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 07fe56f8f..41868dfb5 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) - { - mCamera->move (getFastFactor() * mCamera->getDirection()); - mUpdate = true; - } - - if (!mKeyForward && mKeyBackward) - { - mCamera->move (getFastFactor() * -mCamera->getDirection()); - mUpdate = true; - } - - if (mKeyLeft && !mKeyRight) - { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (getFastFactor() * -direction); - mUpdate = true; - } - - if (!mKeyLeft && mKeyRight) + if (mNavigation) { - Ogre::Vector3 direction = mCamera->getDerivedRight(); - mCamera->move (getFastFactor() * direction); - mUpdate = true; + int horizontal = 0; + int vertical = 0; + + if (mKeyForward && !mKeyBackward) + vertical = 1; + else if (!mKeyForward && mKeyBackward) + vertical = -1; + + if (mKeyLeft && !mKeyRight) + horizontal = -1; + else if (!mKeyLeft && mKeyRight) + horizontal = 1; + + 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 55f62d661..247dbbb1a 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 339a88519..5b7b6a587 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 65e1614c1..f948ecefb 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 bb4a9d329..eba7a7acb 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 000000000..d470722bb --- /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 000000000..fa6fdad11 --- /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 5b7b6a587..fd327b4d1 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 f948ecefb..8e42540e1 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 964ceeac6..873519609 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 5a3bc7b65..b177c019b 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 815e0b117..d1e09d236 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 d470722bb..950e137a7 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 fa6fdad11..e30722f75 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 41868dfb5..f864e1528 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 247dbbb1a..4a18c4244 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 b177c019b..b892d3e3e 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 f864e1528..6f07c1b0d 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 e80bd954e..af8d49426 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 71fd1b523..06ee9fb4e 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 a23cd1077..26abdaecc 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 eba7a7acb..119280f13 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 000000000..c6e729e96 --- /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 000000000..7796eb9e7 --- /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 fd327b4d1..a23931950 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 8e42540e1..f944e17c3 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 10afbc675..508127837 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 8301ebfd3..1d1b3c960 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 533ca7f57..b055d34eb 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 09361a1c0..6160f2673 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 aa073f81d..127cf91e6 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 b055d34eb..38e2bff0d 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 13c15ec9b..a1f6bea71 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 127cf91e6..59781f869 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 38e2bff0d..1b271fcfc 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 182d1cdd6..d59f0c234 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 edf3bc6de..2c4651dd7 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 615a31b4d..995bc0095 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 e330d4775..30a60536a 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 b3c253919..910fec325 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 dd7bf3e42..55d043c8a 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 d342e88a4..9d97e36c7 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 408a89d92..442055d5f 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 8d53c4e53..d68b79ff0 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 bea307aa2..453a7da6a 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 74923867d..5a271de44 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 1b271fcfc..de3f476af 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 a1f6bea71..ee7380e2b 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 2c4651dd7..95433dbfc 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 995bc0095..3bf673dac 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 508127837..a6493ecf8 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 ae9ec8ac0..07b8ce289 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 26abdaecc..bb17a68e7 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 119280f13..72757556d 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 000000000..c88821a62 --- /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 000000000..1c122c935 --- /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 a23931950..516a5db80 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 be583ea74..eb502de38 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 21adc9f74..35762d2b3 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 07b8ce289..954e161a1 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 33bba07e1..3d70fdc6a 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 56d9601fc..8e4e9703f 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 694970e23..52682342f 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 28aa371cf..458cf2a19 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 21c3a1178..9efdeae54 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 1e52ff26a..249477551 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 a4a766226..de63b9906 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 7563ad015..9a3dd7342 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 8f521c8a6..d8d2cefbf 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 6f3ab3bce..017adf1e3 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 c1f167992..57842796f 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 6f51c767e..d3e6e7fea 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 897c8fe73..b6c0ebc70 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 32abb7799..028341ced 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 321bcf7c8..66f6fde97 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 91ae93b40..79e1cc2a5 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 7ee30ae7f..4eb533499 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 00af741b1..000000000 --- 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 928a34107..cf97afa7a 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 750441f6a..2ccf3bd0a 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 2ef014aaf..f84da5c5d 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 a5c629088..a05066313 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 000000000..9f0b8c09b --- /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 8e78d2216..31874af51 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 82ccc7c89..cb7660fed 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 ea299c096..32c7dd56a 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 d8cdab9ec..d7fb78492 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 4273f227d..49a561c0e 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 bf733b889..31bd51ab1 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 831b14057..938ef2e4d 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 31874af51..78b17909a 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 49a561c0e..e633c1a00 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 a6493ecf8..40004807b 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 cb7660fed..3969d4761 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 018dc082a..76ad1890f 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 3969d4761..44bf320e4 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 | 167 +++++++++++----------- components/terrain/quadtreenode.hpp | 16 +++ components/terrain/storage.hpp | 27 ++-- components/terrain/world.cpp | 69 +++++++++ components/terrain/world.hpp | 42 +++++- 12 files changed, 264 insertions(+), 124 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index cf97afa7a..68bf957c8 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 8597c762c..ec26b1872 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 f84da5c5d..b1acdb785 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 7bd00d6bf..6b99c0a0c 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 a05066313..139694ee5 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 d74c65ba6..a6477df28 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 9f0b8c09b..c14608e76 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 44bf320e4..721771ff3 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()); + mLoadState = LS_Loading; + mTerrain->queueLoad(this); + } - if (mSize == 1) - { - ensureLayerInfo(); - mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); - } - else + if (mLoadState == LS_Loaded) + { + // 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 (!mChunk->getVisible() && hasChildren()) { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + // Make sure child scene nodes are detached + mSceneNode->removeAllChildren(); + + // TODO: unload + //for (int i=0; i<4; ++i) + // mChildren[i]->unload(); } + + mChunk->setVisible(true); } + } - // 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 (wantToDisplay && mLoadState != LS_Loaded) + { + // 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()) { - // Make sure child scene nodes are detached - mSceneNode->removeAllChildren(); - - // 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); + for (int i=0; i<4; ++i) + if (mChildren[i]->hasChunk()) + mChildren[i]->update(cameraPos); } } - else + if (!wantToDisplay) { - // Wanted LOD is too detailed to be rendered in one chunk, - // so split it up by delegating to child nodes - if (hadChunk) + // We do not want to display this node - delegate to children + if (mChunk) + mChunk->setVisible(false); + 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) + 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 32c7dd56a..0bba1024c 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 d7fb78492..1b1968f1a 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 e633c1a00..87baeff84 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 31bd51ab1..d552bccb0 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 6f07c1b0d..3c389bec2 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 af8d49426..4c3cadb3b 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 92205c3e9..ada004612 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 868b58209..53a45db09 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 2d1d7431f..e91e5951d 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 51e160d26..1cc9610df 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 d729ee7fa..de96bcacd 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 8716c4dea..f941c699b 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 f44078256..1b71157a7 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 8918bfbe7..74587a626 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 6ba3b1fcd..e62fee6e2 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 280828652..013e3daf4 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 cd30cdf46..16e6ab017 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 241f7e470..ba39d10d5 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 7d41525b7..1e6119daa 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 772813c73..003f08300 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 d07aad31d..60760b7fb 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 246103471..67bc75e02 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 d590dbf4c..f22968e9d 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 68bf957c8..b67875405 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 e5db8346f..f52deedcc 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 9ebb0ab08..33e337649 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 39f7ccc85..90c08c299 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 adf20dc63..f3c0971e7 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 9e3105168..1fa5d8834 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 1a16870da..8ef5797ca 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 eb741fb10..6574f096b 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 43622cb9a..8bebe0589 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 43e8934c8..acab419b0 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 840cf4bb0..1b9a899a0 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 139694ee5..6f2b9ff73 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 721771ff3..1c8553967 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 87baeff84..4d4616511 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 65fb7f98b..5ef274b7e 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 f819043cf..2a14cc6b4 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 f215f4ab7..ad8e6d2b0 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 923b0b7e3..20b9296da 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 9c32924f1..cc5f572cf 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 c86697497..c816f2060 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 69375b74d..5aeb35c28 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 c487b24b0..b9b4cd9d9 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 c97e5279a..9c10ca84b 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 938ef2e4d..0a99c9528 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 000000000..f693c0e40 --- /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 000000000..f0aea9bfd --- /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 6f2b9ff73..cf6c99cf4 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 a6477df28..d198de264 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 c14608e76..1a081c969 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 1c8553967..913970606 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 0bba1024c..134bc2d34 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 4d4616511..69748a721 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 d552bccb0..354738744 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 cf6c99cf4..624809229 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 d198de264..9a7d20fb8 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 624809229..ed640be5f 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 9a7d20fb8..d037661ae 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 | 19 ++- 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 | 181 +++++++++++++--------- components/terrain/quadtreenode.hpp | 17 +- components/terrain/storage.hpp | 15 +- components/terrain/world.cpp | 80 ++++++---- components/terrain/world.hpp | 30 +++- 13 files changed, 292 insertions(+), 139 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b67875405..7c70b17f0 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::requestMap(MWWorld::CellStore* cell) +void RenderingManager::updateTerrain() { - // 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) + { + // 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) +{ 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 64ec029ce..115a94786 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 ec26b1872..2558c95c5 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 b1acdb785..b5e6012f3 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 40004807b..caae72c13 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 ed640be5f..0820dcc73 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 1a081c969..685937653 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 78b17909a..faa73a986 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 913970606..56cfc2a74 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()) + if (mChunk) { + // Are children already loaded? + bool childrenLoaded = true; for (int i=0; i<4; ++i) - if (mChildren[i]->hasChunk()) - 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; } - } - if (!wantToDisplay) - { - // We do not want to display this node - delegate to children - if (mChunk) - mChunk->setVisible(false); - if (hasChildren()) + else { + bool success = true; for (int i=0; i<4; ++i) - mChildren[i]->update(cameraPos); + 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) - { - ensureLayerInfo(); - mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); - } - else + if (mTerrain->areLayersLoaded()) { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial())); + if (mSize == 1) + { + mChunk->setMaterial(mMaterialGenerator->generate(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 134bc2d34..c57589487 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 1b1968f1a..d3770ea57 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 69748a721..844144d7c 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()); + + LayersResponseData* responseData = new LayersResponseData(); - responseData->time = timer.getMicroseconds(); + getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); + 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 354738744..26a6d034d 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 53a45db09..46c6aebdc 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 caae72c13..3d4413a35 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 0a99c9528..db4ecad0b 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 56cfc2a74..40a8baaf0 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 46c6aebdc..37e29591b 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 4a18c4244..c0537aa73 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 c88821a62..dcd152bb3 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 1c122c935..9d9452c17 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 516a5db80..88b84ace5 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 f944e17c3..21b3dc204 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 72757556d..621b9bf86 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 000000000..fa32e3959 --- /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 000000000..172e2477a --- /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 000000000..6ccb1b99b --- /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 000000000..7f9c895bc --- /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 88b84ace5..091e78ec4 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 6f07c1b0d..f71329c61 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 f71329c61..2c98b60cf 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 c0537aa73..ad68897ac 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 6ccb1b99b..812a3b165 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 7f9c895bc..17dc46918 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 091e78ec4..d04cc4c15 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 95433dbfc..4d469fd20 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 3bf673dac..3791bf4ac 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 4d469fd20..4bb9955e6 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 3791bf4ac..e8d5648d1 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 30a60536a..2d08d186e 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 812a3b165..c7edbe79b 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 9d9452c17..2eccca3bf 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 d04cc4c15..3601ae094 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 21b3dc204..ecf3fe4e4 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 cedb20de9..a3d723de3 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 64715f5b7..496730db5 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 74ce03cce..93ef7d210 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 b51e224f8..f154f3b24 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 | 133 +++++++++++++++------ apps/opencs/view/world/dialoguesubview.hpp | 50 ++++++++ 3 files changed, 146 insertions(+), 42 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6bcad1d08..a7b39feb8 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 a3d723de3..a411ca7f4 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 (model->flags (model->index (0, i)) & Qt::ItemIsEditable) + if (widget) { - 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 - } + layout->addWidget (widget, i, 1); + mWidgetMapper->addMapping (widget, i); } - 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; + if (model->flags (model->index (0, i)) & Qt::ItemIsEditable) + { - 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 496730db5..142d942eb 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 954e161a1..0f76e3688 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 5034d9753..b635f746e 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 a411ca7f4..37a2ab130 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 142d942eb..11cc918b1 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 93ef7d210..4a94cbfe6 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 | 239 +++++++++++++----- apps/openmw/mwworld/worldimp.hpp | 24 +- libs/openengine/bullet/physic.cpp | 2 + 16 files changed, 292 insertions(+), 124 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index fd3978943..5c2912a8f 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 ea586e5b6..caef521af 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 b5d83427b..30cefe2df 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 1fb22ce63..92be89f2f 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 d19f32507..04d909b49 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 9745cdebe..7f5a7fac5 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 9ae9c5878..294264951 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 60760b7fb..6169b6ddb 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 7c70b17f0..a0d06f068 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 43a0fedce..9a9175f19 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 3807a67b6..8b01bacdf 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 b4c572ba9..150f0bed2 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 8ef5797ca..1657cf267 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 f154f3b24..0ef50c582 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]; + + // 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; - MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), *actor.getCell(), pos); + CellStore* cell = actor.getCell(); + MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos); - ProjectileState state; + 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::Vector3 rot(ptr.getRefData().getPosition().rot); + Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation(); - // 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); + 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); + + 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 (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 036cafe2d..40fe4c96a 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 f124abb99..4484d9862 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 2939a5431..33c4390a0 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 20c7d9a0e..50e002113 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 6aeb90dad..cdc12e210 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 7f2415697..bc58227bf 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 6169b6ddb..8f8e65cd0 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 0ef50c582..62ef09ae4 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 40fe4c96a..b694e00f7 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 37a2ab130..65a3f437d 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 11cc918b1..06c849e39 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -27,22 +27,22 @@ 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 - QWidget* mEditor; + class refWrapper + { + public: + refWrapper(const QModelIndex& index); - CSMWorld::ColumnBase::Display mDisplay; + const QModelIndex& mIndex; + }; + QWidget* mEditor; + CSMWorld::ColumnBase::Display mDisplay; std::auto_ptr mIndexWrapper; public: 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 65a3f437d..3eaa9bd61 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 34bad20cc..f6363fe2e 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 e04333608..d990232f7 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 453a7da6a..97837e368 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 5a271de44..013741379 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 3eaa9bd61..a946a6f6f 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 a946a6f6f..e8b53f2cd 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 fc9b7ee3b..b882e20e6 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 e8b53f2cd..76c66160a 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 4a94cbfe6..2109f08a0 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 2ea09db61..5fa1d3bb1 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 40a8baaf0..4461e2b02 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 8293cbfa7..42922a5b3 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 30cefe2df..4bfd3f465 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 bd3f4954b..2eab03a34 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 8b01bacdf..05e928937 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 76c66160a..e8b53f2cd 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 2109f08a0..2fa2fc06b 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 4c3cadb3b..37821b990 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 5c15ddf6f..e0f51d0dc 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 db4ecad0b..f8a2969f9 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 000000000..d06b3b485 --- /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 000000000..8c3fb7bef --- /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 4461e2b02..21c1becb0 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 2b2f41754..9eb95fafa 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 0f1c7a682..ec417ba8e 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 | 44 +++++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e8b53f2cd..6307afabb 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; - - widget->setLayout (layout); + 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); + + 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 b882e20e6..7f40b966d 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 97af3b99c..fd35ef3ef 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 87f118cd7..0d04dda4e 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 6307afabb..6145c9dfa 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 7f40b966d..a6089a1b3 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 606f9278a..ac07f3d1e 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 fd35ef3ef..74aaeb6f0 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 0d04dda4e..23b9fcfac 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 6145c9dfa..c4e9b805c 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 a6089a1b3..858458b37 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 ac07f3d1e..8bd96a6f2 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 74aaeb6f0..7e8cddd1f 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 23b9fcfac..814f09d3a 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 d68b79ff0..d60dcae11 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 453a7da6a..56b16f5a1 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 5a271de44..7d812b083 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 1d1b3c960..a62acc02b 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 0c17da03b..34167cd85 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 3601ae094..66e026604 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 4bb9955e6..29bca2f6b 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 e8d5648d1..4231a4a43 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 c4e9b805c..6a37ce59c 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 7e8cddd1f..5da616ebb 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 5da616ebb..14d5c89a0 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 d990232f7..fe310d0aa 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 def225018..6311562a6 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 6a37ce59c..1f6c41148 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 858458b37..e5a766731 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 14d5c89a0..f93edab3e 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 814f09d3a..9b9d89535 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 1f6c41148..251c2755e 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 251c2755e..5c083f881 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 31d5d0318..7b790fd58 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 ad68897ac..05b06b287 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 dcd152bb3..9959c5a67 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 5c083f881..65db33cbf 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 06c849e39..33514d205 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 f93edab3e..3635ee1d4 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 9b9d89535..814f09d3a 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 e5a766731..40f447f9e 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 8bd96a6f2..e3ecc051d 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 2fa2fc06b..d4d9565a4 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 de3f476af..bc34c6118 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 3635ee1d4..766323d0b 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 65db33cbf..85cb94677 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 | 25 ++++++++-------------- apps/opencs/view/world/enumdelegate.hpp | 7 +++--- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 65db33cbf..85cb94677 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 40f447f9e..6c46232a8 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,25 +42,18 @@ CSVWorld::EnumDelegate::EnumDelegate (const std::vector } -QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, - const QModelIndex& index, CSMWorld::ColumnBase::Display display) const +QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { - if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).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; + 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) const + const QModelIndex& index, CSMWorld::ColumnBase::Display display) const { - if (!index.data(Qt::EditRole).isValid()) + if (!index.data(Qt::EditRole).isValid() && !index.data(Qt::DisplayRole).isValid()) return 0; QComboBox *comboBox = new QComboBox (parent); @@ -74,7 +67,7 @@ QWidget *CSVWorld::EnumDelegate::createEditor(QWidget *parent, const QStyleOptio 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 e3ecc051d..cd749a451 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 85cb94677..6a3e1d25d 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 766323d0b..85d8392ff 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 | 89 +++++++++++++--------- apps/opencs/view/world/dialoguesubview.hpp | 15 +++- 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6a3e1d25d..dd38e770f 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))) +{ + remake (id); +} +void CSVWorld::EditWidget::remake(const CSMWorld::UniversalId& id) { - CSMWorld::IdTable* model = dynamic_cast(document.getData().getTableModel (id)); - const QModelIndex indexToFocus(model->getModelIndex (id.getId(), 0)); - const int focusedRow = indexToFocus.row(); + const QModelIndex indexToFocus(mTable->getModelIndex (id.getId(), 0)); + + if (mMainWidget) + { + delete mMainWidget; + } + mMainWidget = new QWidget (this); - QScrollArea *scrollArea = new QScrollArea(this); - QWidget *widget = new QWidget (scrollArea); + if (mWidgetMapper) + { + delete mWidgetMapper; + } + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel(mTable); + mWidgetMapper->setItemDelegate(&mDispatcher); - QFrame* line = new QFrame(this); + 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 33514d205..40c658b9e 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 da1992474..2788c8f5c 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 6d52f26d5..a9453b2c8 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 4479a121f..2dc43f1b7 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 dd38e770f..e34d744d9 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 | 146 +++---------------- apps/openmw/mwrender/npcanimation.hpp | 27 +--- apps/openmw/mwrender/weaponanimation.cpp | 159 +++++++++++++++++++++ apps/openmw/mwrender/weaponanimation.hpp | 56 ++++++++ 7 files changed, 306 insertions(+), 156 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 eb502de38..5e108edaf 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 8ef584154..0eb883953 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 37826673d..d6cd8a517 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 8f8e65cd0..a09a58191 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::attachArrow() +void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) { - 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); + Ogre::Vector3 glowColor = getEnchantmentColor(ptr); + setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0, + !ptr.getClass().getEnchantment(ptr).empty(), &glowColor); - 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(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot)); + std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot)); +} - 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)); - } +void NpcAnimation::attachArrow() +{ + 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 725fde01d..8ec46facd 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 000000000..5f953bd83 --- /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 000000000..c09aa65d9 --- /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 e34d744d9..212031a01 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())); + + mMainLayout = new QVBoxLayout(mainWidget); - QVBoxLayout *mainLayout = 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 40c658b9e..0576c7aff 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 6c46232a8..377f479bf 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 2788c8f5c..4ad260fd9 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 6160f2673..dcd85945f 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 59781f869..85274a18d 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 212031a01..f8cb81a8f 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 a9453b2c8..48b515d65 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 f8cb81a8f..2a5dd93c6 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 2a5dd93c6..a8bd899c9 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 0576c7aff..b42b12555 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 a8bd899c9..97f116c06 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 97f116c06..bfb296a0e 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 b42b12555..62d95b622 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 bfb296a0e..c041dd1f4 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 4bb9955e6..f7f42eb5f 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 85d8392ff..cc9fac1cb 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 814f09d3a..a70af9dc8 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 c041dd1f4..a1c9de612 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 62d95b622..e133041b8 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 a1c9de612..65e9c5cb3 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 e133041b8..df985d5f8 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 f7f42eb5f..7f98801cb 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 4c3cadb3b..7d0626913 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 5c2912a8f..bb6f5741d 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 05e928937..d6309c1c9 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 62ef09ae4..992ecc960 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; + + 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); + } - // FIXME: should be set to 1, but the sound manager won't pause newly started sounds - mPlayIntro = 2; + 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); + // 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 b694e00f7..42f52cb61 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 9a95822f5..844eaf490 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 */ + } + + 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 a893e257e..6de0b8181 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 | 101 +++++++++++++-------- apps/opencs/view/world/dialoguesubview.hpp | 24 ++++- apps/opencs/view/world/util.cpp | 4 +- apps/opencs/view/world/util.hpp | 3 +- 6 files changed, 97 insertions(+), 42 deletions(-) diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp index b56c9c8c2..9dcecf3bc 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 7687f3555..44ac0f5f6 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 65e9c5cb3..50d19378d 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()) + if (newRow >= mTable->rowCount()) { - std::cout<<"test"<index(newRow, 0)); - - if (!newIndex.isValid()) + while (newRow < mTable->rowCount()) { - 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) - { - nextId(); - return; - } + if (!newIndex.isValid()) + { + return; + } - mEditWidget->remake(newRow); - setUniversalId(CSMWorld::UniversalId (static_cast (mTable->data (mTable->index (newRow, 2)).toInt()), + 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 df985d5f8..1c8aa5971 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 cc9fac1cb..7278f3471 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 a70af9dc8..7664f3eae 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 50d19378d..fdef57c41 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 7278f3471..a71937b08 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 fdef57c41..17b665d46 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 1c8aa5971..c2e7da338 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 844eaf490..a51092fea 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 9a9175f19..1efc79643 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 a51092fea..4da325abd 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 2dc43f1b7..e8cb23b77 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 a0d06f068..15d56b8a9 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 3dff1b7e4..499c1e191 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 a6ad43ce5..06a228a1f 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 04206fbc8..6d1ffd03c 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 50e002113..a1d2ef848 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 | 97 ++++++++++++++++++++------- apps/openmw/mwworld/worldimp.cpp | 2 +- 6 files changed, 122 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 33c4390a0..c66bda09f 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 6df6db297..c1bcb8739 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 2110086d3..39d48f95b 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 ad2cc3af4..739fd5942 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 1657cf267..1c6838939 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; - - waterlevel -= halfExtents.z * 0.5; + 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; + + /* + * 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 ec417ba8e..164398fb7 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 212ed0836..eded36394 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 000000000..160240b17 --- /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 000000000..01d40050f --- /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 000000000..0fa1d3b17 --- /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 000000000..e7a2a261e --- /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 74ce03cce..33a91330d 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 01d40050f..c8834775b 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 0fa1d3b17..587af561f 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 21c1becb0..a14fe1f84 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 17b665d46..f0a4ac131 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 7b790fd58..5a2d93385 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 160240b17..43d45f4cb 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 c8834775b..b3abd5587 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 f0a4ac131..2e77cf29e 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 5a2d93385..5eec702d3 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 5e108edaf..20011b0d9 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 043e85e83..000000000 --- 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 bb17a68e7..a054626dd 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 | 22 +++++++-------- 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, 153 insertions(+), 227 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 715784b7c..604b51990 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 c66bda09f..0f7ffdc48 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 caef521af..732038b2f 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 72de620e4..bd25b66b2 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 a1d2ef848..3fbd0d5b2 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 ddbd3f120..a6880ffcb 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 7e7ad5ec2..34ac8d9f4 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 481c98314..6b913f24a 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 6d1ffd03c..1645d8db0 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 d6d19d9e1..c9ec23cfa 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 f9a900eba..027c3201f 100644 --- a/apps/openmw/mwgui/itemview.cpp +++ b/apps/openmw/mwgui/itemview.cpp @@ -13,22 +13,18 @@ #include "itemmodel.hpp" -namespace +namespace MWGui { - 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) diff --git a/apps/openmw/mwgui/itemview.hpp b/apps/openmw/mwgui/itemview.hpp index 17f609f2b..74bc66ea0 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 8caea770e..2682323ce 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 09d3cf1a8..b9c0a60b3 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 48b515d65..722b329de 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 ff13ae1af..ba4fdb86a 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 6b261a799..19bd2e939 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 38a761931..4699cc445 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 bdd7c8f3b..8610cf4b2 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 b54f1d39c..f27fada39 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 1fd719c60..c62c4e970 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 4da325abd..2db875a01 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 5314b5919..3ecd40743 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 8771ef0ca..ecaaef568 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 d489bfaf1..66c8d4468 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 179e2bb0b..527c576cc 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 ab8901881..57fc2d470 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 c4f672dc7..80467f58a 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 c83697442..c595de5ae 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 d0d73379d..8276b45c7 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 8db4fa888..25b006fb3 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 d17f49caf..7961b8f41 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 bb0dffb87..c71aef95c 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 954e161a1..154f30161 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 f66dbf2c4..c5669fa70 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 ba4fdb86a..4c0faeac1 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 19bd2e939..b052739bd 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 4699cc445..53eed1ba1 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 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); + bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& 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 2682323ce..683fe9208 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 b9c0a60b3..819bda0fd 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 2e77cf29e..660bb305f 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 2b1e65ff0..fb3c1eb3e 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 660bb305f..8f602aa8d 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 c2e7da338..b7920a387 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 8f602aa8d..6bd8001c7 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 b7920a387..bf6e70e0d 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 601255763..28a30e907 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 6bd8001c7..1692cdf80 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 bf6e70e0d..af07d28f4 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 d4d9565a4..f647a9261 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 f647a9261..6c832e179 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 6c832e179..116f1997b 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 1692cdf80..9f86a8ce3 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 af07d28f4..8a96e3ec0 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 4ce18ce68..cd9138c35 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 9f86a8ce3..53e53e0e6 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 8a96e3ec0..5642f46a0 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 154f30161..fb3827588 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 5fa1d3bb1..16ddab584 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 d6309c1c9..c0860b784 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 5f38ee286..b74ca6393 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 fa2d9bd8e..3167a64c2 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 62bdd38ea..81afc394a 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 57842796f..4d5b36c74 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 53e53e0e6..5c718ad7f 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 5c718ad7f..4962acfce 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 fb3c1eb3e..2031e54cc 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 def225018..b1c61b76a 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 2f3911270..7410780e0 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 52e022e78..855e89cad 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 5f13ede52..154f30161 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 154f30161..5f13ede52 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 6311562a6..4c053bfd9 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 4962acfce..6be5da635 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 a71937b08..227c5c9c5 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 5f13ede52..154f30161 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 6be5da635..492fa920f 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 4c053bfd9..ccbeaf694 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 492fa920f..b29d6695b 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 b29d6695b..ae7c5923b 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 16ddab584..e9efe75e7 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 | 77 +++++++++++++-------------------- components/CMakeLists.txt | 2 +- components/esm/weatherstate.cpp | 59 +++++++++++++++++++++++++ components/esm/weatherstate.hpp | 27 ++++++++++++ 4 files changed, 118 insertions(+), 47 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 b74ca6393..335702c66 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); - - // reset other temporary state + // 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, 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(); - - // 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; + mThunderFlash = 0.0; + mThunderChance = 0.0; + mThunderChanceNeeded = 50.0; + + // 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 db4ecad0b..c4d13170d 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 000000000..48cf55a60 --- /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 000000000..545751225 --- /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 545751225..d0cd59c16 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 3167a64c2..cad3a4492 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 | 126 ++++++++++++++++++---- apps/opencs/view/render/previewwidget.hpp | 30 ++++-- apps/opencs/view/render/scenewidget.cpp | 5 + apps/opencs/view/render/scenewidget.hpp | 2 + 4 files changed, 135 insertions(+), 28 deletions(-) diff --git a/apps/opencs/view/render/previewwidget.cpp b/apps/opencs/view/render/previewwidget.cpp index 43d45f4cb..9b64cf6f4 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); + mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(); + mNode->setPosition (Ogre::Vector3 (0, 0, 0)); - int row = mData.getReferenceables().getIndex (id); + setModel(); - QVariant value = mData.getReferenceables().getData (row, column); + QAbstractItemModel *referenceables = + mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); - if (!value.isValid()) - return; + 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))); +} - std::string model = value.toString().toUtf8().constData(); +void CSVRender::PreviewWidget::setModel() +{ + if (mNode) + { + mObject.setNull(); - if (model.empty()) - return; + int column = mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); - mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode(); - mNode->setPosition (Ogre::Vector3 (0, 0, 0)); + int row = mData.getReferenceables().getIndex (mReferenceableId); + + QVariant value = mData.getReferenceables().getData (row, column); + + if (!value.isValid()) + return; + + std::string model = value.toString().toUtf8().constData(); - mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); + if (model.empty()) + return; + + mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model); + } } -void CSVRender::PreviewWidget::adjust (const std::string& id) +void CSVRender::PreviewWidget::adjust() { if (mNode) { - int row = mData.getReferences().getIndex (id); + 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(); + + 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) { - setup (referenceableId); - adjust (referenceId); + 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 b3abd5587..2b6517e71 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(); - void setup (const std::string& id); - ///< \param id ID of the referenceable to be viewed + void setModel(); - void adjust (const std::string& id); - ///< \param id ID of the reference to be viewed + 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 5eec702d3..6b2ca85b5 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 05b06b287..375c877d2 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 942780c32..87660a60b 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 79a09dcb4..d3d8f5fad 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 9dcecf3bc..ccd6fb496 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 7b62aafa3..d107b198c 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 dcd85945f..7fd005717 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 cc1578bdd..b163297f9 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 ae7c5923b..1627bc5f4 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 fccac75b4..b1528d525 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 cd9138c35..a2927c2f0 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 9dcecf3bc..f6256c263 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 44ac0f5f6..adcb147c1 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 ae7c5923b..cf932aba1 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) + { + 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; + 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 9b64cf6f4..94cb7ce6e 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 column = + mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model); - int row = mData.getReferenceables().getIndex (mReferenceableId); + 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 587af561f..769ff3ddc 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 94cb7ce6e..3e70fd999 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 2b6517e71..7a63d8fb1 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 769ff3ddc..df9c3276c 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 e7a2a261e..4ca25c3cb 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 3e70fd999..be7f66016 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 b892d3e3e..91f88634a 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 6b2ca85b5..5a3c14b49 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 3bd09bb50..abdc33103 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 04d909b49..2db3bacf0 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 43d45f4cb..1cd39c89b 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 d71704fd7..c15f63abe 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 8d48078b1..907204739 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 19e3d4882..a74eb838c 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 c15f63abe..0fbb26c84 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 907204739..008782130 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 a74eb838c..82371b056 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 5457d7625..98bd63ba1 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 fb3827588..392fdfc66 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 c99e2d0de..78adecd3e 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); + return; } - else - { - Settings::Manager::setBool("fullscreen", "Video", newState); - apply(); - } - } - else if (_sender == mVSyncButton) - { - 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 (getSettingType(_sender) == checkButtonType) { - 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); + if (getSettingValueType(scroller) == "Float") + { + float value = pos / float(scroller->getScrollRange()-1); + + 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(); } - 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); - - apply(); } void SettingsWindow::apply() diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 6b9ce414b..7a6c1a5ed 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 1fa5d8834..5368cbe68 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 61103963d..adf9f1557 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 4fb7097f8..5dfc05455 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 3c0c3ffaa..c2d902d69 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 5a3c14b49..c59c142fc 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 375c877d2..51d3464ed 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 c7edbe79b..fb74788cc 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 b2b534031..ee7887f3e 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 000000000..d57570d69 --- /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 000000000..a1da9f7e3 --- /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 c59c142fc..8a1db1da0 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 51d3464ed..5058fb860 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 2eccca3bf..7921c3560 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 df9c3276c..ac9776d22 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 66e026604..10e8b4071 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 28a30e907..7d2a5ca42 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 ee7887f3e..608f01d12 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 000000000..ab0257c0c --- /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 000000000..8638146e2 --- /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 8a1db1da0..77d9ea1e0 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 5058fb860..a1ecd733b 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 608f01d12..a6200062d 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 000000000..516bb3f40 --- /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 000000000..47d1d7ce8 --- /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 77d9ea1e0..f1371041c 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 a1ecd733b..96e7d7bb2 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 a6200062d..a7a694463 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 000000000..ab845b924 --- /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 000000000..bc01899cb --- /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 f1371041c..1aee421ed 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 96e7d7bb2..8df9cf347 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 2db3bacf0..93c789af1 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 4009744ef..5aea0210f 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 22dda0ce0..e4c480a8c 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 4c8f35edb..3164ca155 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 761caf586..5dd758377 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 8314d011a..a34c5476c 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 77ad98121..894cbe0a3 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 d6309c1c9..a0d482f0a 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 d6887f170..813865e25 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 9c7bf551d..b8615a6bc 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 894cbe0a3..e897c5a6b 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 4ad260fd9..655663eb6 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 722b329de..f38b2b751 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 f3c0971e7..7abc8140a 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 655663eb6..4e4599e06 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 4e4599e06..25821663d 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 f38b2b751..1850c473c 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 5dfc05455..6361476e3 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 e897c5a6b..6971358c1 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 c0860b784..c807c5e23 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 c807c5e23..5430caae5 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 0f7ffdc48..a224de71e 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 3fbd0d5b2..502ff6ef6 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 92ba9470d..41c3cd197 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 1a8999e6e..5c154d425 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 d61b96739..feed8d182 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 94e506fc4..20a9a5799 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 9d97e36c7..1fb3e1f1d 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 a224de71e..72b315484 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 502ff6ef6..020f3b3af 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 41c3cd197..0525a97ae 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 20011b0d9..19f3473f7 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 47e0d016f..a710ce59a 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 25821663d..0bc8b07d1 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 1850c473c..c571fda86 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 84ec6f7c5..8bd95e2cd 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 7d2a5ca42..cd533de3a 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 19f3473f7..511435108 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 a710ce59a..a118152ed 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 4fce19e33..e3bd428e2 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 bb6f5741d..f03a9197d 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 0bc8b07d1..00e124f6c 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 50d53abac..a1688d2e5 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 000000000..566c7cadb --- /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 000000000..16a71d367 --- /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 1b71157a7..ca8459fa1 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 dafb65e47..ab9770a41 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 4bfd3f465..840a3f71a 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 15d56b8a9..fa7b17a7c 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 115a94786..423a7078a 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 7abc8140a..80704ca7c 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 0e548e23e..47e252cc1 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 fa8441aa5..89a5ceec1 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 62bdd38ea..f884f69af 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 42f52cb61..f1e89bf6b 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 8bd95e2cd..6f7736994 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 a118152ed..f02432ba8 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 42922a5b3..d44da4974 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 37e29591b..b3f70a5ab 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 ca8459fa1..1e019aaa9 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 840a3f71a..e2d4f8cb2 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 2eab03a34..87fbda25c 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 6f7736994..e66f3fc01 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 f02432ba8..508b195e9 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 c816f2060..5f9578988 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 6971358c1..caa082646 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 a0d482f0a..7793cc960 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 6b99c0a0c..0fc2d547c 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 813865e25..b5e0810db 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 b8615a6bc..3e7cae775 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 72b315484..2af134a38 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 020f3b3af..f7b280a79 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 0525a97ae..93f53a11f 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 2af134a38..d347b2c04 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 c1bcb8739..04c010c83 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 f7b280a79..c06e31556 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 c54dd339a..fb45a2f1f 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 93f53a11f..3e15bcd78 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 39d48f95b..af8de1326 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 739fd5942..cee1b171d 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 d347b2c04..94238e6d5 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 c06e31556..d41f7002a 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 af8de1326..8c61e32f4 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 6b913f24a..6cc580fb6 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 befbd6eee..242dae299 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 242dae299..368140520 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 2db875a01..26975ff1e 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 3ecd40743..c0b78f3c7 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 26975ff1e..a3286b8c0 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 6de0b8181..48b67fb1c 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 c0b78f3c7..0eb7f0798 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. + * + * NOTE: startPoint & endPoint are in world co-ordinates * - * Updates mPath using aStarSearch(). - * mPathConstructed is set true if successful, false if not + * 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. * - * May update mGraph by calling buildPathgridGraph() if it isn't constructed yet. + * 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(mCell != cell) + { + mIsGraphConstructed = false; // must be in a new cell, need a new mGraph and mSCComp + mCell = cell; } - if(!allowShortcuts) + const ESM::Pathgrid *pathGrid = + MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + float xCell = 0; + float yCell = 0; + + if (mCell->isExterior()) { - const ESM::Pathgrid *pathGrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); - float xCell = 0; - float yCell = 0; + xCell = mCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; + yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; + } - if (mCell->isExterior()) + // 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 ecaaef568..ae849bff2 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 48b67fb1c..ca6e546ed 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 0eb7f0798..86f9f9af2 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 368140520..242dae299 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 242dae299..368140520 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 6cc580fb6..e64c80c90 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; }