From d8f24ac4998b0fb297a167e7b756089eb07400da Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 17 Feb 2014 15:49:49 +0100 Subject: [PATCH 001/157] bug fix --- apps/openmw/mwmechanics/aiactivate.cpp | 4 +++- apps/openmw/mwmechanics/aifollow.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 1f3c58521..2d5d11134 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -59,7 +59,9 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) } } - MWWorld::Ptr target = world->getPtr(mObjectId,false); + MWWorld::Ptr target = world->searchPtr(mObjectId,false); + if(target == MWWorld::Ptr()) return true; + ESM::Position targetPos = target.getRefData().getPosition(); bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index cf5291fd3..d3ee21abf 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -20,7 +20,9 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &ce bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); + + if(target == MWWorld::Ptr()) return false; mTimer = mTimer + duration; mStuckTimer = mStuckTimer + duration; From f4879dacd54bc7599e676a297297a40c58e4376e Mon Sep 17 00:00:00 2001 From: gus Date: Wed, 5 Mar 2014 11:24:39 +0100 Subject: [PATCH 002/157] add AIfollow to summoned creatures --- apps/openmw/mwmechanics/actors.cpp | 2 ++ apps/openmw/mwmechanics/aifollow.cpp | 40 +++++++++++++++++----------- apps/openmw/mwmechanics/aifollow.hpp | 2 ++ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 1fb22ce63..e28ce4b27 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -539,6 +539,8 @@ namespace MWMechanics ref.getPtr().getCellRef().mPos = ipos; // TODO: Add AI to follow player and fight for him + AiFollow package(ptr.getRefData().getHandle()); + MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package); // TODO: VFX_SummonStart, VFX_SummonEnd creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle())); diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index d3ee21abf..5e4559c0e 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -10,11 +10,16 @@ #include "steering.hpp" MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) { } MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0) +{ +} + +MWMechanics::AiFollow::AiFollow(const std::string &actorId) +: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0) { } @@ -30,22 +35,25 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) ESM::Position pos = actor.getRefData().getPosition(); - if(mTotalTime > mDuration && mDuration != 0) - return true; - - 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) + if(!mAlwaysFollow) { - if(actor.getCell()->isExterior()) - { - if(mCellId == "") - return true; - } - else + if(mTotalTime > mDuration && mDuration != 0) + return true; + + 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) { - if(mCellId == actor.getCell()->mCell->mName) - return true; + if(actor.getCell()->isExterior()) + { + if(mCellId == "") + return true; + } + else + { + if(mCellId == actor.getCell()->mCell->mName) + return true; + } } } diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 9d77b903d..48a8eb4c2 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -14,6 +14,7 @@ namespace MWMechanics public: AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId); virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? @@ -22,6 +23,7 @@ namespace MWMechanics std::string getFollowedActor(); private: + bool mAlwaysFollow; //this will make the actor always follow, thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). float mDuration; float mX; float mY; From d84319300a45caf5bd7c03e468ec557c6f3681c4 Mon Sep 17 00:00:00 2001 From: gus Date: Wed, 5 Mar 2014 11:27:16 +0100 Subject: [PATCH 003/157] fix --- apps/openmw/mwmechanics/aifollow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 5e4559c0e..e2a96fc87 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -27,7 +27,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); - if(target == MWWorld::Ptr()) return false; + if(target == MWWorld::Ptr()) return true; mTimer = mTimer + duration; mStuckTimer = mStuckTimer + duration; From 895748f18d2f5d66e0f0033f4a49ad7fd44857bb Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 28 Mar 2014 12:01:56 -0400 Subject: [PATCH 004/157] 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 005/157] 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 006/157] 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 007/157] 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 008/157] 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 009/157] 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 010/157] 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 011/157] 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 012/157] 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 013/157] 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 014/157] 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 015/157] 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 016/157] 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; } From 512ee1204ebf66795cfc64a7c070b80575d86157 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Sat, 29 Mar 2014 15:49:48 +0100 Subject: [PATCH 017/157] fixes a bug when resizing the window before a window listener is set --- libs/openengine/ogre/renderer.cpp | 17 ++++++++++++++++- libs/openengine/ogre/renderer.hpp | 9 ++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 5f9578988..caf62546e 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -157,6 +157,21 @@ void OgreRenderer::setFov(float fov) void OgreRenderer::windowResized(int x, int y) { - if (mWindowListener) + if (mWindowListener) { mWindowListener->windowResized(x,y); + } + else { + mWindowWidth = x; + mWindowHeight = y; + mOutstandingResize = true; + } +} + +void OgreRenderer::setWindowListener(WindowSizeListener* listener) +{ + mWindowListener = listener; + if (mOutstandingResize) { + windowResized(mWindowWidth, mWindowHeight); + mOutstandingResize = false; + } } diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 767e7cf99..ad88b1606 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -66,6 +66,10 @@ namespace OEngine WindowSizeListener* mWindowListener; + int mWindowWidth; + int mWindowHeight; + bool mOutstandingResize; + public: OgreRenderer() : mRoot(NULL) @@ -77,6 +81,9 @@ namespace OEngine , mOgreInit(NULL) , mFader(NULL) , mWindowListener(NULL) + , mWindowWidth(0) + , mWindowHeight(0) + , mOutstandingResize(false) { } @@ -133,7 +140,7 @@ namespace OEngine /// Viewport Ogre::Viewport *getViewport() { return mView; } - void setWindowListener(WindowSizeListener* listener) { mWindowListener = listener; } + void setWindowListener(WindowSizeListener* listener); void adjustViewport(); }; From d9ea7107b7b619fcd17ba5702c5f57377b8f25eb Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 29 Mar 2014 18:36:32 +0100 Subject: [PATCH 018/157] compile fix. --- apps/openmw/mwmechanics/aifollow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index f85d8889b..c3b36516c 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -3,6 +3,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" #include "movement.hpp" #include From 72df9e77c6263b2341eaf9869a398005431c1868 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Mar 2014 15:07:32 +0100 Subject: [PATCH 019/157] Don't show version text in the pause menu --- apps/openmw/mwgui/mainmenu.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 00e124f6c..5257baf22 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -174,6 +174,7 @@ namespace MWGui MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); showBackground(state == MWBase::StateManager::State_NoGame); + mVersionText->setVisible(state == MWBase::StateManager::State_NoGame); std::vector buttons; From 5eeed03f5b61645b5965586857418dff29f01ee7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 30 Mar 2014 00:12:31 +0100 Subject: [PATCH 020/157] Only exchange bribe gold if the bribe was accepted --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 19 +++++++++++++++++- apps/openmw/mwgui/dialogue.cpp | 20 ------------------- apps/openmw/mwgui/dialogue.hpp | 6 ------ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index c9e8ad955..88f1302bb 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -500,7 +500,24 @@ namespace MWDialogue mTemporaryDispositionChange = 100 - curDisp; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); + player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); + + if (success) + { + int gold=0; + if (type == MWBase::MechanicsManager::PT_Bribe10) + gold = 10; + else if (type == MWBase::MechanicsManager::PT_Bribe100) + gold = 100; + else if (type == MWBase::MechanicsManager::PT_Bribe1000) + gold = 1000; + + if (gold) + { + player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, gold, player); + mActor.getClass().getContainerStore(mActor).add(MWWorld::ContainerStore::sGoldId, gold, mActor); + } + } std::string text; diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e64c80c90..6c43f47b4 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -56,29 +56,16 @@ namespace MWGui void PersuasionDialog::onPersuade(MyGUI::Widget *sender) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWBase::MechanicsManager::PersuasionType type; if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire; else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate; else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt; 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, 1000, mReceiver); type = MWBase::MechanicsManager::PT_Bribe1000; - } MWBase::Environment::get().getDialogueManager()->persuade(type); @@ -100,12 +87,6 @@ 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) @@ -380,7 +361,6 @@ 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 368140520..befbd6eee 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -35,9 +35,6 @@ namespace MWGui virtual void open(); - // The receiver of the bribe - void setReceiver(MWWorld::Ptr receiver); - private: MyGUI::Button* mCancelButton; MyGUI::Button* mAdmireButton; @@ -50,9 +47,6 @@ namespace MWGui void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); - - // The receiver of the bribe - MWWorld::Ptr mReceiver; }; From 6c866deb1b0123c8f620448fa07cdfbb904910b8 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 21:23:34 -0400 Subject: [PATCH 021/157] Feature #1154 Not all NPCs get aggressive when one is attacked Partially implemented --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 65 ++++++++++++------- .../mwmechanics/mechanicsmanagerimp.hpp | 2 +- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 22dda0ce0..36b8df519 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -111,7 +111,7 @@ namespace MWBase * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ - virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4c8f35edb..2c51e0083 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -3,6 +3,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/cellstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -12,6 +13,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "aicombat.hpp" + #include #include "spellcasting.hpp" @@ -798,37 +801,55 @@ namespace MWMechanics return; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - - bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + + bool MechanicsManager::commitCrime(const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, OffenseType type, int arg) { - if (ptr.getRefData().getHandle() != "player") + if (offender.getRefData().getHandle() != "player") return false; + MWWorld::Ptr ptr; + bool reported=false; - for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) + MWWorld::CellStore* cell = victim.getCell(); + + // TODO: implement and check the distance of actors to victim using fAlarmRadius + // get all NPCs in victims cell + for (MWWorld::CellRefList::List::iterator it (cell->get().mList.begin()); it != cell->get().mList.end(); ++it) { - if (it->first != ptr && - MWBase::Environment::get().getWorld()->getLOS(ptr, it->first) && - awarenessCheck(ptr, it->first)) + MWWorld::Ptr ptr (&*it, cell); + + // offender can't be ally to themselves + if (ptr == offender) + continue; + + CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + + // curse at the thief + if (ptr == victim && type == OT_Theft) + MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); + + // TODO: Make guards persue unless other factors, such as bounty stops them + // If the actor is a guard + + // TODO: An actor reacts differently based on different values of AI_Alarm. + // Actor has witnessed a crime. Will he report it? + if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) { - // NPCs will always curse you when they notice you steal their items, even if they don't report the crime - if (it->first == victim && type == OT_Theft) - { - MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); - } + creatureStats.setAlarmed(true); + reported=true; + } + else + continue; - // Actor has witnessed a crime. Will he report it? - // (not sure, is > 0 correct?) - if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) - { - // TODO: stats.setAlarmed(true) on NPCs within earshot - // fAlarmRadius ? - reported=true; - break; - } + // TODO: An actor reacts differently based on different values of AI_Fight and AI_Flee. + // Actor has reported the crime, will the actor fight the offender? + if (creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified > 0) + { + creatureStats.getAiSequence().stack(AiCombat(offender)); + creatureStats.setHostile(true); + creatureStats.getAiSequence().execute(ptr, 0); } } - if (reported) reportCrime(ptr, victim, type, arg); return reported; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 761caf586..3327cf8b8 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -111,7 +111,7 @@ namespace MWMechanics * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ - virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, OffenseType type, int arg=0); virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); From 98fd381564309f4d1cd15423821b2f2e8d65a00c Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 21:25:20 -0400 Subject: [PATCH 022/157] Feature #1154 Not all NPCs get aggressive when one is attacked Compiling fix --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2c51e0083..534fc941b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -843,7 +843,7 @@ namespace MWMechanics // TODO: An actor reacts differently based on different values of AI_Fight and AI_Flee. // Actor has reported the crime, will the actor fight the offender? - if (creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified > 0) + if (creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified() > 0) { creatureStats.getAiSequence().stack(AiCombat(offender)); creatureStats.setHostile(true); From 8ce938c6f1537c63fb5a230a1188dbc3d42c8fdd Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 29 Mar 2014 22:26:53 -0400 Subject: [PATCH 023/157] Revert 6b28c06..98fd381 This rolls back to commit 6b28c06b2c6401d7a54a2df4e2f329b948acd7ed. --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 65 +++++++------------ .../mwmechanics/mechanicsmanagerimp.hpp | 2 +- 3 files changed, 24 insertions(+), 45 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 36b8df519..22dda0ce0 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -111,7 +111,7 @@ namespace MWBase * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ - virtual bool commitCrime (const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0) = 0; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 534fc941b..4c8f35edb 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -3,7 +3,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwworld/cellstore.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -13,8 +12,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "aicombat.hpp" - #include #include "spellcasting.hpp" @@ -801,55 +798,37 @@ namespace MWMechanics return; commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - - bool MechanicsManager::commitCrime(const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, OffenseType type, int arg) + + bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { - if (offender.getRefData().getHandle() != "player") + if (ptr.getRefData().getHandle() != "player") return false; - MWWorld::Ptr ptr; - bool reported=false; - MWWorld::CellStore* cell = victim.getCell(); - - // TODO: implement and check the distance of actors to victim using fAlarmRadius - // get all NPCs in victims cell - for (MWWorld::CellRefList::List::iterator it (cell->get().mList.begin()); it != cell->get().mList.end(); ++it) + for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) { - MWWorld::Ptr ptr (&*it, cell); - - // offender can't be ally to themselves - if (ptr == offender) - continue; - - CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); - - // curse at the thief - if (ptr == victim && type == OT_Theft) - MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); - - // TODO: Make guards persue unless other factors, such as bounty stops them - // If the actor is a guard - - // TODO: An actor reacts differently based on different values of AI_Alarm. - // Actor has witnessed a crime. Will he report it? - if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) + if (it->first != ptr && + MWBase::Environment::get().getWorld()->getLOS(ptr, it->first) && + awarenessCheck(ptr, it->first)) { - creatureStats.setAlarmed(true); - reported=true; - } - else - continue; + // NPCs will always curse you when they notice you steal their items, even if they don't report the crime + if (it->first == victim && type == OT_Theft) + { + MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); + } - // TODO: An actor reacts differently based on different values of AI_Fight and AI_Flee. - // Actor has reported the crime, will the actor fight the offender? - if (creatureStats.getAiSetting(CreatureStats::AI_Fight).getModified() > 0) - { - creatureStats.getAiSequence().stack(AiCombat(offender)); - creatureStats.setHostile(true); - creatureStats.getAiSequence().execute(ptr, 0); + // Actor has witnessed a crime. Will he report it? + // (not sure, is > 0 correct?) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) + { + // TODO: stats.setAlarmed(true) on NPCs within earshot + // fAlarmRadius ? + reported=true; + break; + } } } + if (reported) reportCrime(ptr, victim, type, arg); return reported; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 3327cf8b8..761caf586 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -111,7 +111,7 @@ namespace MWMechanics * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @return was the crime reported? */ - virtual bool commitCrime (const MWWorld::Ptr& offender, const MWWorld::Ptr& victim, + virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, OffenseType type, int arg=0); From d04bb3befb996d9e04a5a9646ab3033657bc029d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 30 Mar 2014 17:23:22 +1100 Subject: [PATCH 024/157] MSVC uses C version of locale. --- apps/openmw/mwgui/savegamedialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index caa082646..d17cd8c21 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -259,7 +259,7 @@ namespace MWGui timeinfo = localtime(&time); // Use system/environment locale settings for datetime formatting - std::setlocale(LC_TIME, ""); + setlocale(LC_TIME, ""); const int size=1024; char buffer[size]; From 50af9bc0d356aea7941e6e26ed8a4e47c66eaac6 Mon Sep 17 00:00:00 2001 From: megaton <9megaton6@gmail.com> Date: Sun, 30 Mar 2014 19:01:59 +0400 Subject: [PATCH 025/157] General perfomance optimizations. --- apps/openmw/mwmechanics/actors.cpp | 18 +++++++++------ apps/openmw/mwrender/localmap.cpp | 13 +++++++---- components/nifogre/controller.hpp | 34 ++++++++++++++++++---------- components/nifogre/ogrenifloader.cpp | 17 +++++++++----- 4 files changed, 53 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92be89f2f..270753718 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -196,9 +196,8 @@ namespace MWMechanics { disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr); } - bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr); - if( ( (fight == 100 ) + + if( (fight == 100 ) || (fight >= 95 && d <= 3000) || (fight >= 90 && d <= 2000) || (fight >= 80 && d <= 1000) @@ -206,12 +205,17 @@ namespace MWMechanics || (fight >= 70 && disp <= 35 && d <= 1000) || (fight >= 60 && disp <= 30 && d <= 1000) || (fight >= 50 && disp == 0) - || (fight >= 40 && disp <= 10 && d <= 500) ) - && LOS + || (fight >= 40 && disp <= 10 && d <= 500) ) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); - creatureStats.setHostile(true); + bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr); + + if (LOS) + { + creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); + creatureStats.setHostile(true); + } } } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 003f08300..0f6d782a6 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -390,8 +390,13 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaterni TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog"); if (!tex.isNull()) { + std::map >::iterator anIter; + // get its buffer - if (mBuffers.find(texName) == mBuffers.end()) return; + anIter = mBuffers.find(texName); + if (anIter == mBuffers.end()) return; + + std::vector& aBuffer = (*anIter).second; int i=0; for (int texV = 0; texV> 24); alpha = std::min( alpha, (uint8) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) ); - mBuffers[texName][i] = (uint32) (alpha << 24); + aBuffer[i] = (uint32) (alpha << 24); ++i; } } // copy to the texture - memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &mBuffers[texName][0], sFogOfWarResolution*sFogOfWarResolution*4); + memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &aBuffer[0], sFogOfWarResolution*sFogOfWarResolution*4); tex->getBuffer()->unlock(); } } diff --git a/components/nifogre/controller.hpp b/components/nifogre/controller.hpp index 6d7f6ab3f..317447d95 100644 --- a/components/nifogre/controller.hpp +++ b/components/nifogre/controller.hpp @@ -18,16 +18,21 @@ namespace NifOgre if(time <= keys.front().mTime) return keys.front().mValue; - Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) + const Nif::FloatKey* keyArray = keys.data(); + size_t size = keys.size(); + + for (size_t i = 1; i < size; ++i) { - if(iter->mTime < time) + const Nif::FloatKey* aKey = &keyArray[i]; + + if(aKey->mTime < time) continue; - Nif::FloatKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); + const Nif::FloatKey* aLastKey = &keyArray[i-1]; + float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } + return keys.back().mValue; } @@ -36,16 +41,21 @@ namespace NifOgre if(time <= keys.front().mTime) return keys.front().mValue; - Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) + const Nif::Vector3Key* keyArray = keys.data(); + size_t size = keys.size(); + + for (size_t i = 1; i < size; ++i) { - if(iter->mTime < time) + const Nif::Vector3Key* aKey = &keyArray[i]; + + if(aKey->mTime < time) continue; - Nif::Vector3KeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return last->mValue + ((iter->mValue - last->mValue)*a); + const Nif::Vector3Key* aLastKey = &keyArray[i-1]; + float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + return aLastKey->mValue + ((aKey->mValue - aLastKey->mValue) * a); } + return keys.back().mValue; } }; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index acab419b0..ce8244619 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -385,16 +385,21 @@ public: if(time <= keys.front().mTime) return keys.front().mValue; - Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1); - for(;iter != keys.end();iter++) + const Nif::QuaternionKey* keyArray = keys.data(); + size_t size = keys.size(); + + for (size_t i = 1; i < size; ++i) { - if(iter->mTime < time) + const Nif::QuaternionKey* aKey = &keyArray[i]; + + if(aKey->mTime < time) continue; - Nif::QuaternionKeyList::VecType::const_iterator last(iter-1); - float a = (time-last->mTime) / (iter->mTime-last->mTime); - return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue); + const Nif::QuaternionKey* aLastKey = &keyArray[i-1]; + float a = (time - aLastKey->mTime) / (aKey->mTime - aLastKey->mTime); + return Ogre::Quaternion::nlerp(a, aLastKey->mValue, aKey->mValue); } + return keys.back().mValue; } From 126513120395cbe172f9b6ac961ec6c64a0062b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 30 Mar 2014 20:07:43 +0200 Subject: [PATCH 026/157] Set the selected index after all items are added (workaround for MyGUI bug) --- apps/openmw/mwgui/savegamedialog.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index caa082646..52f6080d1 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -80,6 +80,8 @@ namespace MWGui mCharacterSelection->removeAllItems(); + int selectedIndex = MyGUI::ITEM_NONE; + for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) { if (it->begin()!=it->end()) @@ -109,11 +111,13 @@ namespace MWGui it->begin()->mPath.parent_path().filename().string()))) { mCurrentCharacter = &*it; - mCharacterSelection->setIndexSelected(mCharacterSelection->getItemCount()-1); + selectedIndex = mCharacterSelection->getItemCount()-1; } } } + mCharacterSelection->setIndexSelected(selectedIndex); + fillSaveList(); } From f5810b8e1c7416dc5d71cef2ec99cd8fdb70da65 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 30 Mar 2014 23:04:12 +0200 Subject: [PATCH 027/157] Consider aspect ratio for loading screen background More consistent with the main menu. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/backgroundimage.cpp | 63 ++++++++++++++++++++++++ apps/openmw/mwgui/backgroundimage.hpp | 37 ++++++++++++++ apps/openmw/mwgui/loadingscreen.cpp | 47 +++++++----------- apps/openmw/mwgui/loadingscreen.hpp | 9 ++-- apps/openmw/mwgui/mainmenu.cpp | 34 +++---------- apps/openmw/mwgui/mainmenu.hpp | 6 +-- apps/openmw/mwgui/windowmanagerimp.cpp | 2 + files/mygui/openmw_loading_screen.layout | 14 ++---- 9 files changed, 143 insertions(+), 71 deletions(-) create mode 100644 apps/openmw/mwgui/backgroundimage.cpp create mode 100644 apps/openmw/mwgui/backgroundimage.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 511435108..d1f7c45f3 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 videowidget + recharge mode videowidget backgroundimage ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/backgroundimage.cpp b/apps/openmw/mwgui/backgroundimage.cpp new file mode 100644 index 000000000..1e87c0ff1 --- /dev/null +++ b/apps/openmw/mwgui/backgroundimage.cpp @@ -0,0 +1,63 @@ +#include "backgroundimage.hpp" + +#include + +namespace MWGui +{ + +void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool correct) +{ + if (mChild) + { + MyGUI::Gui::getInstance().destroyWidget(mChild); + mChild = NULL; + } + if (correct) + { + setImageTexture("black.png"); + + if (fixedRatio) + mAspect = 4.0/3.0; + else + mAspect = 0; // TODO + + mChild = createWidgetReal("ImageBox", + MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); + mChild->setImageTexture(image); + + adjustSize(); + } + else + { + mAspect = 0; + setImageTexture(image); + } +} + +void BackgroundImage::adjustSize() +{ + if (mAspect == 0) + return; + + MyGUI::IntSize screenSize = getSize(); + + int leftPadding = std::max(0.0, (screenSize.width - screenSize.height * mAspect) / 2); + int topPadding = std::max(0.0, (screenSize.height - screenSize.width / mAspect) / 2); + + mChild->setCoord(leftPadding, topPadding, screenSize.width - leftPadding*2, screenSize.height - topPadding*2); +} + +void BackgroundImage::setSize (const MyGUI::IntSize& _value) +{ + MyGUI::Widget::setSize (_value); + adjustSize(); +} + +void BackgroundImage::setCoord (const MyGUI::IntCoord& _value) +{ + MyGUI::Widget::setCoord (_value); + adjustSize(); +} + + +} diff --git a/apps/openmw/mwgui/backgroundimage.hpp b/apps/openmw/mwgui/backgroundimage.hpp new file mode 100644 index 000000000..3d1a61eaf --- /dev/null +++ b/apps/openmw/mwgui/backgroundimage.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_MWGUI_BACKGROUNDIMAGE_H +#define OPENMW_MWGUI_BACKGROUNDIMAGE_H + +#include + +namespace MWGui +{ + + /** + * @brief A variant of MyGUI::ImageBox with aspect ratio correction using black bars + */ + class BackgroundImage : public MyGUI::ImageBox + { + MYGUI_RTTI_DERIVED(BackgroundImage) + + public: + BackgroundImage() : mChild(NULL), mAspect(0) {} + + /** + * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions + * @param correct Add black bars? + */ + void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool correct=true); + + virtual void setSize (const MyGUI::IntSize &_value); + virtual void setCoord (const MyGUI::IntCoord &_value); + + private: + MyGUI::ImageBox* mChild; + double mAspect; + + void adjustSize(); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index b3f70a5ab..7917c75f3 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -9,14 +9,14 @@ #include #include -#include - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/inputmanager.hpp" +#include "backgroundimage.hpp" + namespace MWGui { @@ -32,28 +32,13 @@ namespace MWGui { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); - getWidget(mBackgroundImage, "BackgroundImage"); mProgressBar->setScrollViewPage(1); - mBackgroundMaterial = Ogre::MaterialManager::getSingleton().create("BackgroundMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - mBackgroundMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mBackgroundMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mBackgroundMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(""); - - mRectangle = new Ogre::Rectangle2D(true); - mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mRectangle->setMaterial("BackgroundMaterial"); - // Render the background before everything else - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY-1); - // Use infinite AAB to always stay visible - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - mRectangle->setBoundingBox(aabInf); - // Attach background to the scene - Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(mRectangle); - mRectangle->setVisible(false); + mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Stretch, "Menu"); + + setVisible(false); } void LoadingScreen::setLabel(const std::string &label) @@ -63,18 +48,25 @@ namespace MWGui LoadingScreen::~LoadingScreen() { - delete mRectangle; + } + + void LoadingScreen::setVisible(bool visible) + { + WindowBase::setVisible(visible); + mBackgroundImage->setVisible(visible); } void LoadingScreen::onResChange(int w, int h) { setCoord(0,0,w,h); + + mBackgroundImage->setCoord(MyGUI::IntCoord(0,0,w,h)); } void LoadingScreen::loadingOn() { // Early-out if already on - if (mRectangle->getVisible()) + if (mMainWidget->getVisible()) return; // Temporarily turn off VSync, we want to do actual loading rather than waiting for the screen to sync. @@ -106,7 +98,7 @@ namespace MWGui 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()); + mBackgroundImage->setBackgroundImage(texture->getName(), false, false); } setVisible(true); @@ -149,9 +141,10 @@ namespace MWGui { std::string const & randomSplash = mResources.at (rand() % mResources.size()); - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); + Ogre::TextureManager::getSingleton ().load (randomSplash, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); - mBackgroundImage->setImageTexture (randomSplash); + // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 + mBackgroundImage->setBackgroundImage(randomSplash, true, true); } else std::cerr << "No loading screens found!" << std::endl; @@ -237,8 +230,6 @@ namespace MWGui mWindow->update(false); - mRectangle->setVisible(false); - // resume 3d rendering mSceneMgr->clearSpecialCaseRenderQueues(); mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index e91e5951d..55235173f 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -10,6 +10,8 @@ namespace MWGui { + class BackgroundImage; + class LoadingScreen : public WindowBase, public Loading::Listener { public: @@ -25,6 +27,8 @@ namespace MWGui virtual void setProgress (size_t value); virtual void increaseProgress (size_t increase); + virtual void setVisible(bool visible); + virtual void removeWallpaper(); LoadingScreen(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* rw); @@ -51,10 +55,7 @@ namespace MWGui MyGUI::TextBox* mLoadingText; MyGUI::ScrollBar* mProgressBar; - MyGUI::ImageBox* mBackgroundImage; - - Ogre::Rectangle2D* mRectangle; - Ogre::MaterialPtr mBackgroundMaterial; + BackgroundImage* mBackgroundImage; Ogre::StringVector mResources; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 5257baf22..b6e3915bb 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -14,6 +14,8 @@ #include "savegamedialog.hpp" #include "confirmationdialog.hpp" +#include "imagebutton.hpp" +#include "backgroundimage.hpp" namespace MWGui { @@ -132,34 +134,14 @@ namespace MWGui void MainMenu::showBackground(bool show) { - if (mBackground) - { - MyGUI::Gui::getInstance().destroyWidget(mBackground); - mBackground = NULL; - } - if (show) + if (show && !mBackground) { - 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"); - } + mBackground = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Stretch, "Menu"); + mBackground->setBackgroundImage("textures\\menu_morrowind.dds"); } + if (mBackground) + mBackground->setVisible(show); } void MainMenu::updateMenu() diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index c571fda86..c27442536 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -3,11 +3,11 @@ #include -#include "imagebutton.hpp" - namespace MWGui { + class ImageButton; + class BackgroundImage; class SaveGameDialog; class MainMenu : public OEngine::GUI::Layout @@ -29,7 +29,7 @@ namespace MWGui MyGUI::Widget* mButtonBox; MyGUI::TextBox* mVersionText; - MyGUI::ImageBox* mBackground; + BackgroundImage* mBackground; std::map mButtons; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1e019aaa9..db19070a6 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -61,6 +61,7 @@ #include "itemview.hpp" #include "fontloader.hpp" #include "videowidget.hpp" +#include "backgroundimage.hpp" namespace MWGui { @@ -160,6 +161,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(); diff --git a/files/mygui/openmw_loading_screen.layout b/files/mygui/openmw_loading_screen.layout index 5fd3440f9..19649cfd2 100644 --- a/files/mygui/openmw_loading_screen.layout +++ b/files/mygui/openmw_loading_screen.layout @@ -4,17 +4,13 @@ - + - - - - - - - - + + + + From db4975dab8908d7cff82551060e4da10430871dd Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 31 Mar 2014 21:52:20 +0200 Subject: [PATCH 028/157] Changed IRC notifications for Travis CI from normal messages to IRC notices. Signed-off-by: Lukasz Gromanowski --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5d0326a07..e09fa46dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,4 +41,4 @@ notifications: - "chat.freenode.net#openmw" on_success: change on_failure: always - + use_notice: true From 529d2436b50d0eecba4cd489e3767da02f11394c Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 31 Mar 2014 22:14:12 +0200 Subject: [PATCH 029/157] Temporary added broken CMakeLists.txt - Travis IRC notification test. Signed-off-by: Lukasz Gromanowski --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 392fdfc66..0b5b0f8d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ if (WIN32) endif() # We probably support older versions than this. -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 999.6) # source directory: libs From 5b5069535eff4df7d97eafe9912235dd35823bad Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 1 Apr 2014 10:04:14 +0200 Subject: [PATCH 030/157] keep track of active cells in PagedWorldspaceWidget and update SubView title accordingly --- .../view/render/pagedworldspacewidget.cpp | 37 ++++++++++++++++++- .../view/render/pagedworldspacewidget.hpp | 14 +++++++ apps/opencs/view/render/worldspacewidget.cpp | 2 + apps/opencs/view/render/worldspacewidget.hpp | 3 ++ apps/opencs/view/world/scenesubview.cpp | 30 ++++++++++++++- apps/opencs/view/world/scenesubview.hpp | 4 ++ 6 files changed, 87 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index fa32e3959..96d44543e 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -1,6 +1,39 @@ #include "pagedworldspacewidget.hpp" +#include + CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent) -: WorldspaceWidget (parent) -{} \ No newline at end of file +: WorldspaceWidget (parent), mMin (std::make_pair (0, 0)), mMax (std::make_pair (-1, -1)) +{} + +void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) +{ + if (!hint.empty()) + { + if (hint[0]=='c') + { + char ignore1, ignore2, ignore3; + std::pair cellIndex; + + std::istringstream stream (hint.c_str()); + if (stream >> ignore1 >> ignore2 >> ignore3 >> cellIndex.first >> cellIndex.second) + { + setCellIndex (cellIndex, cellIndex); + + /// \todo adjust camera position + } + } + + /// \todo implement 'r' type hints + } +} + +void CSVRender::PagedWorldspaceWidget::setCellIndex (const std::pair& min, + const std::pair& max) +{ + mMin = min; + mMax = max; + + emit cellIndexChanged (mMin, mMax); +} \ No newline at end of file diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 172e2477a..9a4b79c3a 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -9,9 +9,23 @@ namespace CSVRender { Q_OBJECT + std::pair mMin; + std::pair mMax; + public: PagedWorldspaceWidget (QWidget *parent); + ///< \note Sets the cell area selection to an invalid value to indicate that currently + /// no cells are displayed. The cells to be displayed will be specified later through + /// hint system. + + virtual void useViewHint (const std::string& hint); + + void setCellIndex (const std::pair& min, const std::pair& max); + + signals: + + void cellIndexChanged (const std::pair& min, const std::pair& max); }; } diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 9959c5a67..4d2442c89 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -26,6 +26,8 @@ void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) setNavigation (&mOrbit); } +void CSVRender::WorldspaceWidget::useViewHint (const std::string& hint) {} + void CSVRender::WorldspaceWidget::selectDefaultNavigationMode() { setNavigation (&m1st); diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 7921c3560..f7208d7a1 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -33,6 +33,9 @@ namespace CSVRender void selectDefaultNavigationMode(); + virtual void useViewHint (const std::string& hint); + ///< Default-implementation: ignored. + 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 10e8b4071..c075cb4d6 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -1,6 +1,8 @@ #include "scenesubview.hpp" +#include + #include #include #include @@ -35,7 +37,14 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48, this); if (id.getId()=="sys::default") - mScene = new CSVRender::PagedWorldspaceWidget (this); + { + CSVRender::PagedWorldspaceWidget *widget = new CSVRender::PagedWorldspaceWidget (this); + mScene = widget; + connect (widget, + SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), + this, + SLOT (cellIndexChanged (const std::pair&, const std::pair&))); + } else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); @@ -83,7 +92,26 @@ void CSVWorld::SceneSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } +void CSVWorld::SceneSubView::useHint (const std::string& hint) +{ + mScene->useViewHint (hint); +} + void CSVWorld::SceneSubView::closeRequest() { deleteLater(); +} + +void CSVWorld::SceneSubView::cellIndexChanged (const std::pair& min, + const std::pair& max) +{ + std::ostringstream stream; + stream << "Scene: " << getUniversalId().getId() << " (" << min.first << ", " << min.second; + + if (min!=max) + stream << " to " << max.first << ", " << max.second; + + stream << ")"; + + setWindowTitle (QString::fromUtf8 (stream.str().c_str())); } \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index ecf3fe4e4..ee5b7b41f 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -38,9 +38,13 @@ namespace CSVWorld virtual void setStatusBar (bool show); + virtual void useHint (const std::string& hint); + private slots: void closeRequest(); + + void cellIndexChanged (const std::pair& min, const std::pair& max); }; } From 78d322e6a5f3a5921776221c2f9ce2c6b878f4cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 1 Apr 2014 11:44:27 +0200 Subject: [PATCH 031/157] Revert "Temporary added broken CMakeLists.txt - Travis IRC notification test." This reverts commit 529d2436b50d0eecba4cd489e3767da02f11394c. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b5b0f8d5..392fdfc66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ if (WIN32) endif() # We probably support older versions than this. -cmake_minimum_required(VERSION 999.6) +cmake_minimum_required(VERSION 2.6) # source directory: libs From 6ee998ac38d9c387f9507831a9dd11d418e86d85 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 1 Apr 2014 18:44:55 +0200 Subject: [PATCH 032/157] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index cd533de3a..eb427a22b 100644 --- a/credits.txt +++ b/credits.txt @@ -52,6 +52,7 @@ Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) Mateusz Kołaczek (PL_kolek) +megaton Michael Hogan (Xethik) Michael Mc Donnell Michael Papageorgiou (werdanith) From 4037f3705e8855933a04a76b1bc717840ac99fd7 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Tue, 1 Apr 2014 14:15:55 -0400 Subject: [PATCH 033/157] Feature 1154 & 73: NPCs react to crime --- apps/openmw/mwclass/npc.cpp | 5 + apps/openmw/mwclass/npc.hpp | 2 + apps/openmw/mwmechanics/aiactivate.cpp | 9 +- apps/openmw/mwmechanics/aiactivate.hpp | 25 +-- apps/openmw/mwmechanics/aisequence.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 148 ++++++++++++------ apps/openmw/mwscript/aiextensions.cpp | 2 +- apps/openmw/mwworld/class.cpp | 5 + apps/openmw/mwworld/class.hpp | 2 + 9 files changed, 139 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index d41f7002a..4b84bd03e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1297,6 +1297,11 @@ namespace MWClass return ref->mBase->mNpdt12.mGold; } + bool Npc::isClass(const MWWorld::Ptr& ptr, const std::string &className) const + { + return ptr.get()->mBase->mClass == className; + } + 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 fb45a2f1f..596bf0e56 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -168,6 +168,8 @@ namespace MWClass ///< Write additional state from \a ptr into \a state. virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + + virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; }; } diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 8610cf4b2..b3fe40e1f 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -10,8 +10,9 @@ #include "steering.hpp" #include "movement.hpp" -MWMechanics::AiActivate::AiActivate(const std::string &objectId) - : mObjectId(objectId) +MWMechanics::AiActivate::AiActivate(const std::string &objectId, int arg) + : mObjectId(objectId), + mArg(arg) { } MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const @@ -25,6 +26,10 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->getCell(); + // Make guard chase player + //if (mArg == 1) + // actor.getClass().getNpcStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 7c94c2589..aa8725db2 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -1,25 +1,26 @@ #ifndef GAME_MWMECHANICS_AIACTIVATE_H #define GAME_MWMECHANICS_AIACTIVATE_H -#include "aipackage.hpp" -#include - -#include "pathfinding.hpp" - -namespace MWMechanics -{ +#include "aipackage.hpp" +#include + +#include "pathfinding.hpp" + +namespace MWMechanics +{ class AiActivate : public AiPackage { public: - AiActivate(const std::string &objectId); - virtual AiActivate *clone() const; - virtual bool execute (const MWWorld::Ptr& actor,float duration); - ///< \return Package completed? + AiActivate(const std::string &objectId, int arg); + virtual AiActivate *clone() const; + virtual bool execute (const MWWorld::Ptr& actor,float duration); + ///< \return Package completed? virtual int getTypeId() const; - private: + private: std::string mObjectId; + int mArg; PathFinder mPathFinder; int mCellX; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2110393fd..ba69b8098 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -161,7 +161,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else if (it->mType == ESM::AI_Activate) { ESM::AIActivate data = it->mActivate; - package = new MWMechanics::AiActivate(data.mName.toString()); + package = new MWMechanics::AiActivate(data.mName.toString(), 0); } else //if (it->mType == ESM::AI_Follow) { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4c8f35edb..b7ff10381 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -12,6 +12,9 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "aicombat.hpp" +#include "aiactivate.hpp" + #include #include "spellcasting.hpp" @@ -801,42 +804,121 @@ namespace MWMechanics bool MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { - if (ptr.getRefData().getHandle() != "player") + // NOTE: int arg can be from itemTaken() so DON'T modify it, since it is + // passed to reportCrime later on in this function. + + // Only player can commit crime and no victimless crimes + if (ptr.getRefData().getHandle() != "player" || victim.isEmpty()) return false; - bool reported=false; - for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) - { - if (it->first != ptr && - MWBase::Environment::get().getWorld()->getLOS(ptr, it->first) && - awarenessCheck(ptr, it->first)) + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + // What amount of alarm did this crime generate? + int alarm; + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + alarm = esmStore.get().find("iAlarmTresspass")->getInt(); + else if (type == OT_Pickpocket) + alarm = esmStore.get().find("iAlarmPickPocket")->getInt(); + else if (type == OT_Assault) + alarm = esmStore.get().find("iAlarmAttack")->getInt(); + else if (type == OT_Murder) + alarm = esmStore.get().find("iAlarmKilling")->getInt(); + else if (type == OT_Theft) + alarm = esmStore.get().find("iAlarmStealing")->getInt(); + + // Innocent until proven guilty + bool reported = false; + + // Find all the NPC's close enough, ie. within fAlarmRadius of the player + std::vector neighbors; + mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), + esmStore.get().find("fAlarmRadius")->getInt(), neighbors); + + // Did anyone see the crime? + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + { + if (*it == ptr) // Not the player + continue; + + CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); + + // Did the witness see the crime? + if ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) { - // NPCs will always curse you when they notice you steal their items, even if they don't report the crime - if (it->first == victim && type == OT_Theft) - { - MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); - } + // Say something! + // TODO: Add more messages + if (type == OT_Theft) + MWBase::Environment::get().getDialogueManager()->say(*it, "Thief"); - // Actor has witnessed a crime. Will he report it? - // (not sure, is > 0 correct?) - if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0) + // Will the witness report the crime? + if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { - // TODO: stats.setAlarmed(true) on NPCs within earshot - // fAlarmRadius ? - reported=true; - break; + creatureStats.setAlarmed(true); + reported = true; + reportCrime(ptr, victim, type, arg); + + // Is it a guard? Or will the witness fight? + if (it->getClass().isClass(*it, "Guard")) + { + // TODO: Persue player, concider bounty? + creatureStats.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); + creatureStats.getAiSequence().execute(*it,0); + } + else if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + { + creatureStats.getAiSequence().stack(AiCombat(ptr)); + creatureStats.setHostile(true); + creatureStats.getAiSequence().execute(*it,0); + } + else if (type == OT_Assault) + { + creatureStats.getAiSequence().stack(AiCombat(ptr)); + creatureStats.setHostile(true); + creatureStats.getAiSequence().execute(*it,0); + } + + // Tell everyone else + for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) + { + if (it == it1 || // Don't tell the witness or the player + ptr == *it1) + continue; + + // Is it a guard? Or will the witness fight? + CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); + if (it1->getClass().isClass(*it1, "Guard")) + { + // TODO: Persue player, concider bounty? + creatureStats1.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); + creatureStats1.getAiSequence().execute(*it1,0); + continue; + } + else if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + { + creatureStats1.getAiSequence().stack(AiCombat(ptr)); + creatureStats1.setHostile(true); + creatureStats1.getAiSequence().execute(*it1,0); + } + else if (type == OT_Assault) + { + creatureStats.getAiSequence().stack(AiCombat(ptr)); + creatureStats.setHostile(true); + creatureStats.getAiSequence().execute(*it,0); + } + } + + break; // Someone saw the crime and everyone has been told } } } - if (reported) - reportCrime(ptr, victim, type, arg); return reported; } void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); + // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) arg = store.find("iCrimeTresspass")->getInt(); @@ -849,32 +931,10 @@ namespace MWMechanics else if (type == OT_Theft) arg *= store.find("fCrimeStealing")->getFloat(); - // TODO: In some cases (type == Assault), if no NPCs are within earshot, the report will have no effect. - // however other crime types seem to be always produce a bounty. - MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + arg); - if (!victim.isEmpty()) - { - int fight = 0; - // Increase in fight rating for each type of crime - if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) - fight = store.find("iFightTrespass")->getFloat(); - else if (type == OT_Pickpocket) - fight = store.find("iFightPickpocket")->getInt(); - else if (type == OT_Assault) - fight = store.find("iFightAttack")->getInt(); - else if (type == OT_Murder) - fight = store.find("iFightKilling")->getInt(); - else if (type == OT_Theft) - fight = store.find("fFightStealing")->getFloat(); - // Not sure if this should be permanent? - fight = victim.getClass().getCreatureStats(victim).getAiSetting(CreatureStats::AI_Fight).getBase() + fight; - victim.getClass().getCreatureStats(victim).setAiSetting(CreatureStats::AI_Fight, fight); - } - // If committing a crime against a faction member, expell from the faction if (!victim.isEmpty() && victim.getClass().isNpc()) { @@ -886,8 +946,6 @@ namespace MWMechanics ptr.getClass().getNpcStats(ptr).expell(factionID); } } - - // TODO: make any guards in the area try to arrest the player } bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 8314d011a..898788bf1 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -47,7 +47,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i instance); virtual int getBaseGold(const MWWorld::Ptr& ptr) const; + + virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; }; } From 50dac98a2befb0722a043c5f29fd73f1eaec440e Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Tue, 1 Apr 2014 20:24:25 -0400 Subject: [PATCH 034/157] Feature 1154 & 73: Crime and NPC reactions --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwclass/npc.cpp | 6 ++ apps/openmw/mwmechanics/aiactivate.cpp | 6 +- apps/openmw/mwmechanics/creaturestats.cpp | 14 ++++ apps/openmw/mwmechanics/creaturestats.hpp | 6 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 69 ++++++++----------- apps/openmw/mwworld/worldimp.cpp | 16 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 8 files changed, 78 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bb6f5741d..170e6705a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -513,6 +513,8 @@ namespace MWBase virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; + + virtual void resetCrimes(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4b84bd03e..5a3ff10de 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -812,7 +812,13 @@ namespace MWClass return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing + + // player got activated by another NPC + if(ptr.getRefData().getHandle() == "player") + return boost::shared_ptr(new MWWorld::ActionTalk(actor)); + return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); + } MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index b3fe40e1f..472bca88f 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -9,6 +9,7 @@ #include "steering.hpp" #include "movement.hpp" +#include "creaturestats.hpp" MWMechanics::AiActivate::AiActivate(const std::string &objectId, int arg) : mObjectId(objectId), @@ -26,9 +27,8 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->getCell(); - // Make guard chase player - //if (mArg == 1) - // actor.getClass().getNpcStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + if (mArg == 1) // run to actor + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index feed8d182..cb1d8e275 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -17,6 +17,7 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mIsWerewolf(false), + mWitnesses(), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { @@ -496,4 +497,17 @@ namespace MWMechanics { return mGoldPool; } + + void CreatureStats::addPlayerWitnesses(std::vector witnesses) + { + mWitnesses.insert(mWitnesses.end(), witnesses.begin(), witnesses.end()); + } + std::vector CreatureStats::getPlayerWitnesses() const + { + return mWitnesses; + } + void CreatureStats::resetPlayerWitnesses() + { + mWitnesses.clear(); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 20a9a5799..527adf8c0 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -59,6 +59,8 @@ namespace MWMechanics int mGoldPool; // the pool of merchant gold not in inventory + std::vector mWitnesses; // the witnesses to players crimes + protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; @@ -233,6 +235,10 @@ namespace MWMechanics void setGoldPool(int pool); int getGoldPool() const; + + void addPlayerWitnesses(std::vector witnesses); + std::vector getPlayerWitnesses() const; + void resetPlayerWitnesses(); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index b7ff10381..4d1fca72d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -826,6 +826,11 @@ namespace MWMechanics else if (type == OT_Theft) alarm = esmStore.get().find("iAlarmStealing")->getInt(); + // what is the bounty cutoff? To high the guards will attack + float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * + float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * + esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); + // Innocent until proven guilty bool reported = false; @@ -842,7 +847,7 @@ namespace MWMechanics CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); - // Did the witness see the crime? + // Did a witness see the crime? if ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) { // Say something! @@ -853,65 +858,51 @@ namespace MWMechanics // Will the witness report the crime? if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { - creatureStats.setAlarmed(true); reported = true; - reportCrime(ptr, victim, type, arg); - - // Is it a guard? Or will the witness fight? - if (it->getClass().isClass(*it, "Guard")) - { - // TODO: Persue player, concider bounty? - creatureStats.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); - creatureStats.getAiSequence().execute(*it,0); - } - else if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) - { - creatureStats.getAiSequence().stack(AiCombat(ptr)); - creatureStats.setHostile(true); - creatureStats.getAiSequence().execute(*it,0); - } - else if (type == OT_Assault) - { - creatureStats.getAiSequence().stack(AiCombat(ptr)); - creatureStats.setHostile(true); - creatureStats.getAiSequence().execute(*it,0); - } + reportCrime(ptr, victim, type, arg); // Tell everyone else for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) { - if (it == it1 || // Don't tell the witness or the player - ptr == *it1) + if (*it1 == ptr) // Not the player continue; - // Is it a guard? Or will the witness fight? + // was the witness alarmed? CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); + if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + creatureStats1.setAlarmed(true); + + // was the witness a guard? if (it1->getClass().isClass(*it1, "Guard")) { - // TODO: Persue player, concider bounty? - creatureStats1.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); - creatureStats1.getAiSequence().execute(*it1,0); - continue; + // will the guard try to kill the player? + if (ptr.getClass().getNpcStats(ptr).getBounty() >= cutoff) + { + creatureStats1.getAiSequence().stack(AiCombat(ptr)); + creatureStats1.setHostile(true); + creatureStats1.getAiSequence().execute(*it,0); + } + else // will the guard persue the player? + { + creatureStats1.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); + creatureStats1.getAiSequence().execute(*it,0); + } } - else if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + // will the witness fight the player? + else if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || + type == OT_Assault) { creatureStats1.getAiSequence().stack(AiCombat(ptr)); creatureStats1.setHostile(true); creatureStats1.getAiSequence().execute(*it1,0); } - else if (type == OT_Assault) - { - creatureStats.getAiSequence().stack(AiCombat(ptr)); - creatureStats.setHostile(true); - creatureStats.getAiSequence().execute(*it,0); - } } - break; // Someone saw the crime and everyone has been told } } } - + if (reported) + ptr.getClass().getCreatureStats(ptr).addPlayerWitnesses(neighbors); return reported; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 81afc394a..3c53cabee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2762,12 +2762,28 @@ namespace MWWorld message += "\n" + skillMsg; } + resetCrimes(player); + std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); } } + void World::resetCrimes(const MWWorld::Ptr& ptr) + { + // Reset witnesses to the players crimes + std::vector neighbors = ptr.getClass().getCreatureStats(ptr).getPlayerWitnesses(); + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + { + // This to reset for each witness: + // TODO: More research is needed to complete this list + it->getClass().getCreatureStats(*it).setHostile(false); + it->getClass().getCreatureStats(*it).setAlarmed(false); + } + ptr.getClass().getCreatureStats(ptr).resetPlayerWitnesses(); + } + void World::spawnRandomCreature(const std::string &creatureList) { const ESM::CreatureLevList* list = getStore().get().find(creatureList); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 42f52cb61..06d49ad24 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -611,6 +611,7 @@ namespace MWWorld virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); + virtual void resetCrimes(const MWWorld::Ptr& ptr); }; } From 7c0b51fb7e6a3757c3fbe4047b88826512bc0897 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Wed, 2 Apr 2014 00:18:22 -0400 Subject: [PATCH 035/157] Ai pursue now controls guards pursuit of crimes Should extend AiActivate in the future --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/aiactivate.cpp | 9 +- apps/openmw/mwmechanics/aiactivate.hpp | 3 +- apps/openmw/mwmechanics/aipackage.hpp | 3 +- apps/openmw/mwmechanics/aipersue.cpp | 108 ++++++++++++++++++ apps/openmw/mwmechanics/aipersue.hpp | 29 +++++ apps/openmw/mwmechanics/aisequence.cpp | 11 +- apps/openmw/mwmechanics/aisequence.hpp | 3 + .../mwmechanics/mechanicsmanagerimp.cpp | 9 +- apps/openmw/mwscript/aiextensions.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 11 +- 11 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 apps/openmw/mwmechanics/aipersue.cpp create mode 100644 apps/openmw/mwmechanics/aipersue.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 20011b0d9..f7977dc59 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -67,7 +67,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects - drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow + drawstate spells activespells npcstats aipackage aisequence aipersue alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting disease pickpocket levelledlist combat steering ) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 472bca88f..8610cf4b2 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -9,11 +9,9 @@ #include "steering.hpp" #include "movement.hpp" -#include "creaturestats.hpp" -MWMechanics::AiActivate::AiActivate(const std::string &objectId, int arg) - : mObjectId(objectId), - mArg(arg) +MWMechanics::AiActivate::AiActivate(const std::string &objectId) + : mObjectId(objectId) { } MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const @@ -27,9 +25,6 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration) Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->getCell(); - if (mArg == 1) // run to actor - actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); - MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index aa8725db2..fd54869f6 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -12,7 +12,7 @@ namespace MWMechanics class AiActivate : public AiPackage { public: - AiActivate(const std::string &objectId, int arg); + AiActivate(const std::string &objectId); virtual AiActivate *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); ///< \return Package completed? @@ -20,7 +20,6 @@ namespace MWMechanics private: std::string mObjectId; - int mArg; PathFinder mPathFinder; int mCellX; diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 74c77bf97..8e015da15 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -19,7 +19,8 @@ namespace MWMechanics TypeIdEscort = 2, TypeIdFollow = 3, TypeIdActivate = 4, - TypeIdCombat = 5 + TypeIdCombat = 5, + TypeIdPersue = 6 }; virtual ~AiPackage(); diff --git a/apps/openmw/mwmechanics/aipersue.cpp b/apps/openmw/mwmechanics/aipersue.cpp new file mode 100644 index 000000000..21239860f --- /dev/null +++ b/apps/openmw/mwmechanics/aipersue.cpp @@ -0,0 +1,108 @@ +#include "aipersue.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" +#include "creaturestats.hpp" + +MWMechanics::AiPersue::AiPersue(const std::string &objectId) + : mObjectId(objectId) +{ +} +MWMechanics::AiPersue *MWMechanics::AiPersue::clone() const +{ + return new AiPersue(*this); +} +bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor,float duration) +{ + //TODO: Guards should not dialague with player after crime reset + + 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()->getCell(); + + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + + MWWorld::Ptr player = world->getPlayerPtr(); + if(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)) + { + movement.mPosition[1] = 0; + return false; + } + } + if(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)) + { + movement.mPosition[1] = 0; + return false; + } + } + + MWWorld::Ptr target = world->getPtr(mObjectId,false); + ESM::Position targetPos = target.getRefData().getPosition(); + + bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; + if(!mPathFinder.isPathConstructed() || cellChange) + { + mCellX = cell->mData.mX; + mCellY = cell->mData.mY; + + ESM::Pathgrid::Point dest; + dest.mX = targetPos.pos[0]; + dest.mY = targetPos.pos[1]; + dest.mZ = targetPos.pos[2]; + + ESM::Pathgrid::Point start; + start.mX = pos.pos[0]; + start.mY = pos.pos[1]; + start.mZ = pos.pos[2]; + + mPathFinder.buildPath(start, dest, actor.getCell(), true); + } + + if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+ + (pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+ + (pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200) + { + movement.mPosition[1] = 0; + MWWorld::Ptr target = world->getPtr(mObjectId,false); + MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); + return true; + } + + if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) + { + movement.mPosition[1] = 0; + MWWorld::Ptr target = world->getPtr(mObjectId,false); + MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); + return true; + } + + float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + zTurn(actor, Ogre::Degree(zAngle)); + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1; + movement.mPosition[1] = 1; + + return false; +} + +int MWMechanics::AiPersue::getTypeId() const +{ + return TypeIdPersue; +} diff --git a/apps/openmw/mwmechanics/aipersue.hpp b/apps/openmw/mwmechanics/aipersue.hpp new file mode 100644 index 000000000..3fd708ab3 --- /dev/null +++ b/apps/openmw/mwmechanics/aipersue.hpp @@ -0,0 +1,29 @@ +#ifndef GAME_MWMECHANICS_AIPERSUE_H +#define GAME_MWMECHANICS_AIPERSUE_H + +#include "aipackage.hpp" +#include + +#include "pathfinding.hpp" + +namespace MWMechanics +{ + + class AiPersue : public AiPackage + { + public: + AiPersue(const std::string &objectId); + virtual AiPersue *clone() const; + virtual bool execute (const MWWorld::Ptr& actor,float duration); + ///< \return Package completed? + virtual int getTypeId() const; + + private: + std::string mObjectId; + + PathFinder mPathFinder; + int mCellX; + int mCellY; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index ba69b8098..c67367a6c 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -73,6 +73,15 @@ void MWMechanics::AiSequence::stopCombat() } } +void MWMechanics::AiSequence::stopPersue() +{ + while (getTypeId() == AiPackage::TypeIdPersue) + { + delete *mPackages.begin(); + mPackages.erase (mPackages.begin()); + } +} + bool MWMechanics::AiSequence::isPackageDone() const { return mDone; @@ -161,7 +170,7 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else if (it->mType == ESM::AI_Activate) { ESM::AIActivate data = it->mActivate; - package = new MWMechanics::AiActivate(data.mName.toString(), 0); + package = new MWMechanics::AiActivate(data.mName.toString()); } else //if (it->mType == ESM::AI_Follow) { diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 62f48f981..07b7c898c 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -49,6 +49,9 @@ namespace MWMechanics void stopCombat(); ///< Removes all combat packages until first non-combat or stack empty. + + void stopPersue(); + ///< Removes all persue packages until first non-persue or stack empty. bool isPackageDone() const; ///< Has a package been completed during the last update? diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 4d1fca72d..6b9d6902e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -13,6 +13,7 @@ #include "../mwworld/player.hpp" #include "aicombat.hpp" +#include "aipersue.hpp" #include "aiactivate.hpp" #include @@ -838,8 +839,6 @@ namespace MWMechanics std::vector neighbors; mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), esmStore.get().find("fAlarmRadius")->getInt(), neighbors); - - // Did anyone see the crime? for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == ptr) // Not the player @@ -880,12 +879,12 @@ namespace MWMechanics { creatureStats1.getAiSequence().stack(AiCombat(ptr)); creatureStats1.setHostile(true); - creatureStats1.getAiSequence().execute(*it,0); + creatureStats1.getAiSequence().execute(*it1,0); } else // will the guard persue the player? { - creatureStats1.getAiSequence().stack(AiActivate(ptr.getClass().getId(ptr), 1)); - creatureStats1.getAiSequence().execute(*it,0); + creatureStats1.getAiSequence().stack(AiPersue(ptr.getClass().getId(ptr))); + creatureStats1.getAiSequence().execute(*it1,0); } } // will the witness fight the player? diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 898788bf1..8314d011a 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -47,7 +47,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i buttons; @@ -2776,10 +2780,15 @@ namespace MWWorld std::vector neighbors = ptr.getClass().getCreatureStats(ptr).getPlayerWitnesses(); for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - // This to reset for each witness: + // Reset states // TODO: More research is needed to complete this list it->getClass().getCreatureStats(*it).setHostile(false); it->getClass().getCreatureStats(*it).setAlarmed(false); + + // Stop guard persue + if(it->getClass().isClass(*it, "Guard")) + it->getClass().getCreatureStats(*it).getAiSequence().stopPersue(); + } ptr.getClass().getCreatureStats(ptr).resetPlayerWitnesses(); } From 6f1211dd8d8140585a20d24693645561fa4611cc Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Wed, 2 Apr 2014 12:23:38 -0400 Subject: [PATCH 036/157] Moved mWitnesses into Player. resetCrime for paying fine. --- apps/openmw/mwmechanics/creaturestats.cpp | 14 -------------- apps/openmw/mwmechanics/creaturestats.hpp | 6 ------ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 6 +++++- apps/openmw/mwworld/player.cpp | 13 +++++++++++++ apps/openmw/mwworld/player.hpp | 6 ++++++ apps/openmw/mwworld/worldimp.cpp | 4 ++-- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index cb1d8e275..feed8d182 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -17,7 +17,6 @@ namespace MWMechanics mAttacked (false), mHostile (false), mAttackingOrSpell(false), mIsWerewolf(false), - mWitnesses(), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) { @@ -497,17 +496,4 @@ namespace MWMechanics { return mGoldPool; } - - void CreatureStats::addPlayerWitnesses(std::vector witnesses) - { - mWitnesses.insert(mWitnesses.end(), witnesses.begin(), witnesses.end()); - } - std::vector CreatureStats::getPlayerWitnesses() const - { - return mWitnesses; - } - void CreatureStats::resetPlayerWitnesses() - { - mWitnesses.clear(); - } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 527adf8c0..20a9a5799 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -59,8 +59,6 @@ namespace MWMechanics int mGoldPool; // the pool of merchant gold not in inventory - std::vector mWitnesses; // the witnesses to players crimes - protected: bool mIsWerewolf; AttributeValue mWerewolfAttributes[8]; @@ -235,10 +233,6 @@ namespace MWMechanics void setGoldPool(int pool); int getGoldPool() const; - - void addPlayerWitnesses(std::vector witnesses); - std::vector getPlayerWitnesses() const; - void resetPlayerWitnesses(); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 6b9d6902e..8714e3eaf 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -901,7 +901,7 @@ namespace MWMechanics } } if (reported) - ptr.getClass().getCreatureStats(ptr).addPlayerWitnesses(neighbors); + MWBase::Environment::get().getWorld()->getPlayer().addPlayerWitnesses(neighbors); return reported; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index fa8441aa5..781a34368 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -810,9 +810,11 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime &runtime) { + MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - MWBase::Environment::get().getWorld()->confiscateStolenItems(player); + world->confiscateStolenItems(player); + world->resetCrimes(player); } }; @@ -821,8 +823,10 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime &runtime) { + MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); + world->resetCrimes(player); } }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 6d551ecf1..7db2afb3f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -274,4 +274,17 @@ namespace MWWorld return false; } + + void Player::addPlayerWitnesses(std::vector witnesses) + { + mWitnesses.insert(mWitnesses.end(), witnesses.begin(), witnesses.end()); + } + std::vector Player::getPlayerWitnesses() const + { + return mWitnesses; + } + void Player::resetPlayerWitnesses() + { + mWitnesses.clear(); + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 7eb023a2b..001d3b7a6 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -41,6 +41,8 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; bool mTeleported; + + std::vector mWitnesses; public: Player(const ESM::NPC *player, const MWBase::World& world); @@ -94,6 +96,10 @@ namespace MWWorld void write (ESM::ESMWriter& writer) const; bool readRecord (ESM::ESMReader& reader, int32_t type); + + void addPlayerWitnesses(std::vector witnesses); + std::vector getPlayerWitnesses() const; + void resetPlayerWitnesses(); }; } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 726176c07..848b57b54 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2777,7 +2777,7 @@ namespace MWWorld void World::resetCrimes(const MWWorld::Ptr& ptr) { // Reset witnesses to the players crimes - std::vector neighbors = ptr.getClass().getCreatureStats(ptr).getPlayerWitnesses(); + std::vector neighbors = mPlayer->getPlayerWitnesses(); for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { // Reset states @@ -2790,7 +2790,7 @@ namespace MWWorld it->getClass().getCreatureStats(*it).getAiSequence().stopPersue(); } - ptr.getClass().getCreatureStats(ptr).resetPlayerWitnesses(); + mPlayer->resetPlayerWitnesses(); } void World::spawnRandomCreature(const std::string &creatureList) From 510f2d10ac38f16c0fc0d9846a2ecdd2a9212df9 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Wed, 2 Apr 2014 14:20:33 -0400 Subject: [PATCH 037/157] Replaces broken code with todo --- apps/openmw/mwworld/worldimp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 848b57b54..954b8e126 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2763,8 +2763,7 @@ namespace MWWorld message += "\n" + skillMsg; } - // sleep the player, this doesn't work - //player.getClass().calculateRestoration(player, days, true); + // TODO: Sleep the player resetCrimes(player); From f597d3e88bc85208681b74d5c72579a6e81488d3 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 3 Apr 2014 07:46:26 +1100 Subject: [PATCH 038/157] Use duration rather than frame counts. Stops false detection of being "stuck" with high frame rates (e.g. indoors). --- apps/openmw/mwmechanics/aiwander.cpp | 43 +++++++++++++++++----------- apps/openmw/mwmechanics/aiwander.hpp | 3 +- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index a3286b8c0..e89d43ca4 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -18,9 +18,10 @@ namespace MWMechanics { // 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; + static const float DIST_SAME_SPOT = 1.8f; + static const float DURATION_SAME_SPOT = 1.0f; + static const float DURATION_TO_EVADE = 0.4f; AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) @@ -35,7 +36,8 @@ namespace MWMechanics , mPrevY(0) , mWalkState(State_Norm) , mStuckCount(0) - , mEvadeCount(0) + , mEvadeDuration(0) + , mStuckDuration(0) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -325,15 +327,21 @@ namespace MWMechanics } else { - /* 1 n + /* f t * State_Norm <---> State_CheckStuck --> State_Evade * ^ ^ | ^ | ^ | | * | | | | | | | | - * | +---+ +---+ +---+ | m - * | any < n < m | + * | +---+ +---+ +---+ | u + * | any < t < u | * +--------------------------------------------+ + * + * f = one frame + * t = how long before considered stuck + * u = how long to move sideways */ - bool samePosition = (abs(pos.pos[0] - mPrevX) < 1) && (abs(pos.pos[1] - mPrevY) < 1); + bool samePosition = (abs(pos.pos[0] - mPrevX) < DIST_SAME_SPOT) && + (abs(pos.pos[1] - mPrevY) < DIST_SAME_SPOT); + switch(mWalkState) { case State_Norm: @@ -349,30 +357,33 @@ namespace MWMechanics if(!samePosition) { mWalkState = State_Norm; - // to do this properly need yet another variable, simply don't clear for now - //mStuckCount = 0; + mStuckDuration = 0; break; } else { - // consider stuck only if position unchanges consecutively - if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0) + mStuckDuration += duration; + // consider stuck only if position unchanges for a period + if(mStuckDuration > DURATION_SAME_SPOT) + { mWalkState = State_Evade; - // NOTE: mStuckCount is purposely not cleared here + mStuckDuration = 0; + mStuckCount++; + } else - break; // still in the same state, but counter got incremented + break; // still in the same state, but duration added to timer } } /* FALL THROUGH */ case State_Evade: { - if(mEvadeCount++ < COUNT_EVADE) + mEvadeDuration += duration; + if(mEvadeDuration < DURATION_TO_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; + mEvadeDuration = 0; } } /* NO DEFAULT CASE */ diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index ca6e546ed..75621911e 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -58,7 +58,8 @@ namespace MWMechanics WalkState mWalkState; int mStuckCount; - int mEvadeCount; + float mStuckDuration; + float mEvadeDuration; bool mStoredAvailableNodes; bool mChooseAction; From 58b135a2be7d23f77192b2471702fd4ec960310f Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 3 Apr 2014 00:50:09 -0400 Subject: [PATCH 039/157] Crime is now checked every frame call --- apps/openmw/mwbase/world.hpp | 2 - apps/openmw/mwmechanics/actors.cpp | 53 +++++++++++++++++-- apps/openmw/mwmechanics/actors.hpp | 2 + apps/openmw/mwmechanics/aipersue.cpp | 4 +- apps/openmw/mwmechanics/creaturestats.cpp | 12 ++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 ++ .../mwmechanics/mechanicsmanagerimp.cpp | 47 +++------------- apps/openmw/mwscript/miscextensions.cpp | 5 -- apps/openmw/mwworld/player.cpp | 13 ----- apps/openmw/mwworld/player.hpp | 4 -- apps/openmw/mwworld/worldimp.cpp | 21 -------- apps/openmw/mwworld/worldimp.hpp | 1 - 12 files changed, 76 insertions(+), 93 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 170e6705a..bb6f5741d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -513,8 +513,6 @@ namespace MWBase virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; - - virtual void resetCrimes(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 92be89f2f..11469c1a9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -29,6 +29,9 @@ #include "aicombat.hpp" #include "aifollow.hpp" +#include "aipersue.hpp" + +#include "../mwbase/dialoguemanager.hpp" //------------------------ namespace { @@ -175,14 +178,16 @@ namespace MWMechanics adjustMagicEffects (ptr); if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats()) calculateDynamicStats (ptr); + calculateCreatureStatModifiers (ptr, duration); // AI if(MWBase::Environment::get().getMechanicsManager()->isAIActive()) { - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - //engage combat or not? + CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + //engage combat or not? if(ptr != player && !creatureStats.isHostile()) { ESM::Position playerpos = player.getRefData().getPosition(); @@ -214,7 +219,7 @@ namespace MWMechanics creatureStats.setHostile(true); } } - + updateCrimePersuit(ptr, duration); creatureStats.getAiSequence().execute (ptr,duration); } @@ -711,6 +716,48 @@ namespace MWMechanics } } + void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + int bounty = player.getClass().getNpcStats(player).getBounty(); + + // TODO: Move me! I shouldn't be here... + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * + float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * + esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); + + if (ptr != player) + { + CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + // Alarmed or not, I will kill you because you've commited heinous against the empire + if ((!creatureStats.isAlarmed() || creatureStats.isAlarmed()) && + ptr.getClass().isClass(ptr, "Guard") && bounty >= cutoff && !creatureStats.isHostile()) + creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); + else if (creatureStats.isAlarmed()) + { + MWBase::Environment::get().getDialogueManager()->say(ptr, "Thief"); + if(bounty == 0) + { + creatureStats.setAlarmed(false); + creatureStats.setHostile(false); + if (ptr.getClass().isClass(ptr, "Guard")) + creatureStats.getAiSequence().stopPersue(); + creatureStats.getAiSequence().stopCombat(); + + } + + if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) + creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); + else if (!creatureStats.isHostile()) + { + creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.setHostile(true); + } + } + } + } + Actors::Actors() {} Actors::~Actors() diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 4b18ac862..d61d74258 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -42,6 +42,8 @@ namespace MWMechanics void updateEquippedLight (const MWWorld::Ptr& ptr, float duration); + void updateCrimePersuit (const MWWorld::Ptr& ptr, float duration); + public: Actors(); diff --git a/apps/openmw/mwmechanics/aipersue.cpp b/apps/openmw/mwmechanics/aipersue.cpp index 21239860f..36e18946c 100644 --- a/apps/openmw/mwmechanics/aipersue.cpp +++ b/apps/openmw/mwmechanics/aipersue.cpp @@ -19,10 +19,8 @@ MWMechanics::AiPersue *MWMechanics::AiPersue::clone() const { return new AiPersue(*this); } -bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor,float duration) +bool MWMechanics::AiPersue::execute (const MWWorld::Ptr& actor, float duration) { - //TODO: Guards should not dialague with player after crime reset - MWBase::World *world = MWBase::Environment::get().getWorld(); ESM::Position pos = actor.getRefData().getPosition(); Movement &movement = actor.getClass().getMovementSettings(actor); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index feed8d182..a358917de 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,7 +14,7 @@ namespace MWMechanics CreatureStats::CreatureStats() : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), - mAttacked (false), mHostile (false), + mAttacked (false), mHostile (false), mAssaulted(false), mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), @@ -316,6 +316,16 @@ namespace MWMechanics mHostile = hostile; } + bool CreatureStats::isAssaulted() const + { + return mAssaulted; + } + + void CreatureStats::setAssaulted (bool assaulted) + { + mAssaulted = assaulted; + } + bool CreatureStats::getCreatureTargetted() const { std::string target; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 20a9a5799..7db895dab 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -39,6 +39,7 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; + bool mAssaulted; bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; @@ -186,6 +187,10 @@ namespace MWMechanics void setHostile (bool hostile); + bool isAssaulted() const; + + void setAssaulted (bool assaulted); + bool getCreatureTargetted() const; float getEvasion() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 8714e3eaf..2edb3d177 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1,5 +1,6 @@ #include "mechanicsmanagerimp.hpp" +#include "npcstats.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" @@ -12,10 +13,6 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" -#include "aicombat.hpp" -#include "aipersue.hpp" -#include "aiactivate.hpp" - #include #include "spellcasting.hpp" @@ -827,11 +824,6 @@ namespace MWMechanics else if (type == OT_Theft) alarm = esmStore.get().find("iAlarmStealing")->getInt(); - // what is the bounty cutoff? To high the guards will attack - float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * - float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * - esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); - // Innocent until proven guilty bool reported = false; @@ -846,8 +838,9 @@ namespace MWMechanics CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); - // Did a witness see the crime? - if ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) + // Was the crime seen or the victim assulted? + if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || + type == OT_Assault) { // Say something! // TODO: Add more messages @@ -858,7 +851,6 @@ namespace MWMechanics if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; - reportCrime(ptr, victim, type, arg); // Tell everyone else for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) @@ -870,38 +862,13 @@ namespace MWMechanics CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) creatureStats1.setAlarmed(true); - - // was the witness a guard? - if (it1->getClass().isClass(*it1, "Guard")) - { - // will the guard try to kill the player? - if (ptr.getClass().getNpcStats(ptr).getBounty() >= cutoff) - { - creatureStats1.getAiSequence().stack(AiCombat(ptr)); - creatureStats1.setHostile(true); - creatureStats1.getAiSequence().execute(*it1,0); - } - else // will the guard persue the player? - { - creatureStats1.getAiSequence().stack(AiPersue(ptr.getClass().getId(ptr))); - creatureStats1.getAiSequence().execute(*it1,0); - } - } - // will the witness fight the player? - else if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || - type == OT_Assault) - { - creatureStats1.getAiSequence().stack(AiCombat(ptr)); - creatureStats1.setHostile(true); - creatureStats1.getAiSequence().execute(*it1,0); - } } break; // Someone saw the crime and everyone has been told - } + } } } - if (reported) - MWBase::Environment::get().getWorld()->getPlayer().addPlayerWitnesses(neighbors); + if(reported) + reportCrime(ptr, victim, type, arg); return reported; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 781a34368..cd29e2c91 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -810,11 +810,8 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime &runtime) { - MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - world->confiscateStolenItems(player); - world->resetCrimes(player); } }; @@ -823,10 +820,8 @@ namespace MWScript public: virtual void execute(Interpreter::Runtime &runtime) { - MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - world->resetCrimes(player); } }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 7db2afb3f..6d551ecf1 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -274,17 +274,4 @@ namespace MWWorld return false; } - - void Player::addPlayerWitnesses(std::vector witnesses) - { - mWitnesses.insert(mWitnesses.end(), witnesses.begin(), witnesses.end()); - } - std::vector Player::getPlayerWitnesses() const - { - return mWitnesses; - } - void Player::resetPlayerWitnesses() - { - mWitnesses.clear(); - } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 001d3b7a6..b41e6fc9f 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -96,10 +96,6 @@ namespace MWWorld void write (ESM::ESMWriter& writer) const; bool readRecord (ESM::ESMReader& reader, int32_t type); - - void addPlayerWitnesses(std::vector witnesses); - std::vector getPlayerWitnesses() const; - void resetPlayerWitnesses(); }; } #endif diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 954b8e126..f3e404c35 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2765,33 +2765,12 @@ namespace MWWorld // TODO: Sleep the player - resetCrimes(player); - std::vector buttons; buttons.push_back("#{sOk}"); MWBase::Environment::get().getWindowManager()->messageBox(message, buttons); } } - void World::resetCrimes(const MWWorld::Ptr& ptr) - { - // Reset witnesses to the players crimes - std::vector neighbors = mPlayer->getPlayerWitnesses(); - for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) - { - // Reset states - // TODO: More research is needed to complete this list - it->getClass().getCreatureStats(*it).setHostile(false); - it->getClass().getCreatureStats(*it).setAlarmed(false); - - // Stop guard persue - if(it->getClass().isClass(*it, "Guard")) - it->getClass().getCreatureStats(*it).getAiSequence().stopPersue(); - - } - mPlayer->resetPlayerWitnesses(); - } - void World::spawnRandomCreature(const std::string &creatureList) { const ESM::CreatureLevList* list = getStore().get().find(creatureList); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 06d49ad24..42f52cb61 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -611,7 +611,6 @@ namespace MWWorld virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects, const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName); - virtual void resetCrimes(const MWWorld::Ptr& ptr); }; } From b1abef7a38a9e21b2fcffcf3473829dfbbb8149c Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 3 Apr 2014 01:07:56 -0400 Subject: [PATCH 040/157] Cleaned up code --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwscript/miscextensions.cpp | 1 + apps/openmw/mwworld/player.hpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2edb3d177..39cf63cd0 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -867,7 +867,7 @@ namespace MWMechanics } } } - if(reported) + if (reported) reportCrime(ptr, victim, type, arg); return reported; } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index cd29e2c91..a7f31c80b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -822,6 +822,7 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); + MWBase::Environment::get().getWorld()->confiscateStolenItems(player); } }; diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index b41e6fc9f..9d3fbbeec 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -41,8 +41,7 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; bool mTeleported; - - std::vector mWitnesses; + public: Player(const ESM::NPC *player, const MWBase::World& world); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f3e404c35..ac6f9d380 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -29,7 +29,6 @@ #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/levelledlist.hpp" #include "../mwmechanics/combat.hpp" -#include "../mwmechanics/actors.hpp" #include "../mwrender/sky.hpp" #include "../mwrender/animation.hpp" From 98f77714ce928de922ec6b83ccf667500f1d8e1e Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 3 Apr 2014 21:41:34 +1100 Subject: [PATCH 041/157] Per-cell pathgrid data and calculation moved off PathFinder. Now the edge cost calculations and strongly connected component searches are done only once per cell. Per-actor data and methods still remain with PathFinder. This version still has debugging statements and needs cleaning up. --- apps/openmw/mwmechanics/pathfinding.cpp | 726 +++++++++++++----------- apps/openmw/mwmechanics/pathfinding.hpp | 82 +-- apps/openmw/mwworld/cellstore.cpp | 30 + apps/openmw/mwworld/cellstore.hpp | 9 + 4 files changed, 494 insertions(+), 353 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 86f9f9af2..f56cfad5f 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -54,13 +54,13 @@ namespace // // Approx. 514 Euclidean distance and 533 Manhattan distance. // - float manhattan(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b) + float manhattan(const ESM::Pathgrid::Point a, const 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. + // Choose a heuristics - Note that 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) @@ -69,7 +69,7 @@ namespace // 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) + float costAStar(const ESM::Pathgrid::Point a, const ESM::Pathgrid::Point b) { //return distance(a, b); return manhattan(a, b); @@ -113,12 +113,13 @@ namespace return closestIndex; } - // Uses mSCComp to choose a reachable end pathgrid point. start is assumed reachable. + // Chooses a reachable end pathgrid point. start is assumed reachable. std::pair getClosestReachablePoint(const ESM::Pathgrid* grid, - Ogre::Vector3 pos, int start, std::vector &sCComp) + const MWWorld::CellStore *cell, + Ogre::Vector3 pos, int start) { - // assume grid is fine - int startGroup = sCComp[start]; + if(!grid || grid->mPoints.empty()) + return std::pair (-1, false); float distanceBetween = distanceSquared(grid->mPoints[0], pos); int closestIndex = 0; @@ -133,7 +134,7 @@ namespace // found a closer one distanceBetween = potentialDistBetween; closestIndex = counter; - if (sCComp[counter] == startGroup) + if (cell->isPointConnected(start, counter)) { closestReachableIndex = counter; } @@ -152,7 +153,7 @@ namespace MWMechanics { PathFinder::PathFinder() : mIsPathConstructed(false), - mIsGraphConstructed(false), + mPathgrid(NULL), mCell(NULL) { } @@ -164,293 +165,14 @@ namespace MWMechanics mIsPathConstructed = false; } - /* - * NOTE: Based on buildPath2(), please check git history if interested - * - * 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 - * - * 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 in this example) - * - * 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(); - // 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; - } - // store the costs of each edge - for(unsigned int i = 0; i < pathGrid->mEdges.size(); i++) - { - Edge edge; - 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, 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 < static_cast (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++) - { - mGraph[i].parent = -1; - mGScore[i] = -1; - mFScore[i] = -1; - } - } - - /* - * 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 - * 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. - * - * Input params: - * start, goal - pathgrid point indexes (for this cell) - * xCell, yCell - values to add to convert path back to world scale - * - * Variables: - * openset - point indexes to be traversed, lowest cost at the front - * closedset - point indexes already traversed - * - * 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) - { - cleanUpAStar(); - // mGScore & mFScore keep costs for each pathgrid point in pathGrid->mPoints - mGScore[start] = 0; - mFScore[start] = costAStar(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(); // front has the lowest cost - openset.pop_front(); - - if(current == goal) - break; - - closedset.push_back(current); // remember we've been here - - // 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()) - { - // 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(); - if(!isInOpenSet - || tentative_g < mGScore[dest]) - { - mGraph[dest].parent = current; - mGScore[dest] = tentative_g; - 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 - std::list::iterator it = openset.begin(); - for(it = openset.begin(); it!= openset.end(); it++) - { - if(mFScore[*it] > mFScore[dest]) - break; - } - openset.insert(it, dest); - } - } - } // if in closedset, i.e. traversed this edge already, try the next edge - } - } - - 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) - { - ESM::Pathgrid::Point pt = pathGrid->mPoints[current]; - pt.mX += xCell; - pt.mY += yCell; - path.push_front(pt); - current = mGraph[current].parent; - } - - // 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()) - { - ESM::Pathgrid::Point pt = pathGrid->mPoints[goal]; - pt.mX += xCell; - 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. * + * NOTE: It may be desirable to simply go directly to the endPoint if for + * example there are no pathgrids in this cell. + * * NOTE: startPoint & endPoint are in world co-ordinates * * Updates mPath using aStarSearch() or ray test (if shortcut allowed). @@ -462,9 +184,6 @@ namespace MWMechanics * * 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() * * | @@ -486,7 +205,8 @@ namespace MWMechanics */ void PathFinder::buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, - const MWWorld::CellStore* cell, bool allowShortcuts) + const MWWorld::CellStore* cell, + bool allowShortcuts) { mPath.clear(); @@ -502,48 +222,76 @@ namespace MWMechanics } } - if(mCell != cell) + if(mCell != cell || !mPathgrid) { - mIsGraphConstructed = false; // must be in a new cell, need a new mGraph and mSCComp mCell = cell; + + // Cache pathgrid as mPathgrid and update on cell changes. There + // might be a small gain in avoiding to search for it. + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + } + + // Refer to AiWander reseach topic on openmw forums for some background. + // Maybe there is no pathgrid for this cell. Just go to destination and let + // physics take care of any blockages. + if(!mPathgrid || mPathgrid->mPoints.empty()) + { +//#if 0 + std::cout << "no pathgrid " << + +"\"" +mCell->getCell()->mName+ "\"" + +", " +std::to_string(mCell->getCell()->mData.mX) + +", " +std::to_string(mCell->getCell()->mData.mY) + << std::endl; +//#endif + mPath.push_back(endPoint); + mIsPathConstructed = true; + return; } - const ESM::Pathgrid *pathGrid = - MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); + // NOTE: getClosestPoint expects local co-ordinates float xCell = 0; float yCell = 0; - if (mCell->isExterior()) { xCell = mCell->getCell()->mData.mX * ESM::Land::REAL_SIZE; yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; } + + + + + + + + + + // NOTE: It is possible that getClosestPoint returns a pathgrind point index // that is unreachable in some situations. e.g. actor is standing // outside an area enclosed by walls, but there is a pathgrid // point right behind the wall that is closer than any pathgrid // point outside the wall - // - // NOTE: getClosestPoint expects local co-ordinates - // - int startNode = getClosestPoint(pathGrid, - Ogre::Vector3(startPoint.mX - xCell, startPoint.mY - yCell, startPoint.mZ)); - - if(startNode != -1) // only check once, assume pathGrid won't change + int startNode = getClosestPoint(mPathgrid, + Ogre::Vector3(startPoint.mX - xCell, startPoint.mY - yCell, startPoint.mZ)); + // Some cells don't have any pathgrids at all + if(startNode != -1) { - if(!mIsGraphConstructed) - { - buildPathgridGraph(pathGrid); // pre-compute costs for use with aStarSearch - buildConnectedPoints(pathGrid); // must before calling getClosestReachablePoint - } - std::pair endNode = getClosestReachablePoint(pathGrid, + std::pair endNode = getClosestReachablePoint(mPathgrid, cell, Ogre::Vector3(endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ), - startNode, mSCComp); - + startNode); +//#if 0 + if(!mPathgrid) + std::cout << "no pathgrid " << + +"\"" +mCell->getCell()->mName+ "\"" + +", " +std::to_string(mCell->getCell()->mData.mX) + +", " +std::to_string(mCell->getCell()->mData.mY) + << std::endl; +//#endif + // this shouldn't really happen, but just in case if(endNode.first != -1) { - mPath = aStarSearch(pathGrid, startNode, endNode.first, xCell, yCell); + mPath = mCell->aStarSearch(startNode, endNode.first, mCell->isExterior()); if(!mPath.empty()) { @@ -561,13 +309,34 @@ namespace MWMechanics mPath.push_back(endPoint); } else + { mIsPathConstructed = false; + std::cout << "empty path error " << std::endl; + } + //mIsPathConstructed = false; } else + { mIsPathConstructed = false; + std::cout << "second point error " << std::endl; + } + //mIsPathConstructed = false; } else - mIsPathConstructed = false; // this shouldn't really happen, but just in case + { + // FIXME: shouldn't return endpoint if first point error? + mIsPathConstructed = false; + std::cout << "first point error " << std::endl; + } + +#if 0 + if(!mIsPathConstructed) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + } +#endif + return; } float PathFinder::getZAngleToNext(float x, float y) const @@ -645,5 +414,326 @@ namespace MWMechanics } + // TODO: Any multi threading concerns? + PathgridGraph::PathgridGraph() + : mCell(NULL) + , mIsGraphConstructed(false) + , mPathgrid(NULL) + , mGraph(0) + , mSCCId(0) + , mSCCIndex(0) + { + } + + /* + * mGraph is populated with the cost of each allowed edge. + * + * The data structure is based on the code in buildPath2() but modified. + * Please check git history if interested. + * + * mGraph[v].edges[i].index = w + * + * v = point index of location "from" + * i = index of edges from point v + * w = point index of location "to" + * + * + * Example: (notice from p(0) to p(2) is not allowed in this example) + * + * mGraph[0].edges[0].index = 1 + * .edges[1].index = 3 + * + * mGraph[1].edges[0].index = 0 + * .edges[1].index = 2 + * .edges[2].index = 3 + * + * mGraph[2].edges[0].index = 1 + * + * (etc, etc) + * + * + * low + * cost + * p(0) <---> p(1) <------------> p(2) + * ^ ^ + * | | + * | +-----> p(3) + * +----------------> + * high cost + */ + bool PathgridGraph::initPathgridGraph(const ESM::Cell* cell) + { + if(!cell) + { + std::cout << "init error " << std::endl; + return false; + } + mCell = cell; + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); + + if(!mPathgrid) + { + std::cout << "init error " << std::endl; + return false; + } + + mGraph.resize(mPathgrid->mPoints.size()); + for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) + { + ConnectedPoint neighbour; + neighbour.cost = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0], + mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]); + // forward path of the edge + neighbour.index = mPathgrid->mEdges[i].mV1; + mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour); + // reverse path of the edge + // NOTE: These are redundant, ESM already contains the required reverse paths + //neighbour.index = mPathgrid->mEdges[i].mV0; + //mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour); + } + buildConnectedPoints(); + mIsGraphConstructed = true; + //#if 0 + std::cout << "loading pathgrid " << + +"\""+ mPathgrid->mCell +"\"" + +", "+ std::to_string(mPathgrid->mData.mX) + +", "+ std::to_string(mPathgrid->mData.mY) + << std::endl; + //#endif + return true; + } + + // v is the pathgrid point index (some call them vertices) + void PathgridGraph::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 < static_cast (mGraph[v].edges.size()); i++) + { + w = mGraph[v].edges[i].index; + 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(); + mGraph[w].componentId = mSCCId; + } + while(w != v); + mSCCId++; + } + return; + } + + /* + * mGraph contains the strongly connected component group id's along + * with pre-calculated edge costs. + * + * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 + * + * mGraph for Seyda Neen will therefore have 3 different values. When + * selecting a random pathgrid point for AiWander, mGraph can be checked + * for quickly finding whether the destination is reachable. + * + * Otherwise, buildPath can 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 mPoints + * mGraph[v].edges | E (for v) | + * mSCCIndex | index | tracking smallest unused index + * mSCCStack | S | + * mGraph[v].edges[i].index | w | + * + */ + void PathgridGraph::buildConnectedPoints() + { + // both of these are set to zero in the constructor + //mSCCId = 0; // how many strongly connected components in this cell + //mSCCIndex = 0; + int pointsSize = mPathgrid->mPoints.size(); + mSCCPoint.resize(pointsSize, std::pair (-1, -1)); + mSCCStack.reserve(pointsSize); + + for(int v = 0; v < static_cast (pointsSize); v++) + { + if(mSCCPoint[v].first == -1) // undefined (haven't visited) + recursiveStrongConnect(v); + } + //#if 0 + std::cout << "components: " << std::to_string(mSCCId) + +", "+ mPathgrid->mCell + << std::endl; + //#endif + } + + bool PathgridGraph::isPointConnected(const int start, const int end) const + { + return (mGraph[start].componentId == mGraph[end].componentId); + } + + /* + * NOTE: Based on buildPath2(), please check git history if interested + * Should consider using a 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 + * that mGraph is already constructed. + * + * Should be possible to make this MT safe. + * + * Returns path (a list of pathgrid point indexes) which may be empty. + * + * Input params: + * start, goal - pathgrid point indexes (for this cell) + * isExterior - used to determine whether to convert to world co-ordinates + * + * Variables: + * openset - point indexes to be traversed, lowest cost at the front + * closedset - point indexes already traversed + * gScore - past accumulated costs vector indexed by point index + * fScore - future estimated costs vector indexed by point index + * + * TODO: An intersting exercise might be to cache the paths created for a + * start/goal pair. To cache the results the paths need to be in + * pathgrid points form (currently they are converted to world + * co-ordinates). Essentially trading speed w/ memory. + */ + std::list PathgridGraph::aStarSearch(const int start, + const int goal, + bool isExterior) const + { + std::list path; + if(!isPointConnected(start, goal)) + { + return path; // there is no path, return an empty path + } + + int graphSize = mGraph.size(); + std::vector gScore; + gScore.resize(graphSize, -1); + std::vector fScore; + fScore.resize(graphSize, -1); + std::vector graphParent; + graphParent.resize(graphSize, -1); + + // gScore & fScore keep costs for each pathgrid point in mPoints + gScore[start] = 0; + fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]); + + std::list openset; + std::list closedset; + openset.push_back(start); + + int current = -1; + + while(!openset.empty()) + { + current = openset.front(); // front has the lowest cost + openset.pop_front(); + + if(current == goal) + break; + + closedset.push_back(current); // remember we've been here + + // 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].index) == + closedset.end()) + { + // not in closedset - i.e. have not traversed this edge destination + int dest = mGraph[current].edges[j].index; + float tentative_g = gScore[current] + mGraph[current].edges[j].cost; + bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); + if(!isInOpenSet + || tentative_g < gScore[dest]) + { + graphParent[dest] = current; + gScore[dest] = tentative_g; + fScore[dest] = tentative_g + costAStar(mPathgrid->mPoints[dest], + mPathgrid->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 + std::list::iterator it = openset.begin(); + for(it = openset.begin(); it!= openset.end(); it++) + { + if(fScore[*it] > fScore[dest]) + break; + } + openset.insert(it, dest); + } + } + } // if in closedset, i.e. traversed this edge already, try the next edge + } + } + + if(current != goal) + return path; // for some reason couldn't build a path + + // reconstruct path to return, using world co-ordinates + float xCell = 0; + float yCell = 0; + if (isExterior) + { + xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE; + yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE; + } +//#if 0 + // for debugging only + int tmp = current; + if(tmp != goal) + { + std::cout << "aStarSearch: goal and result differ" << std::endl; + std::cout << "goal: " << std::to_string(goal) + +", result: "+ std::to_string(tmp) + << std::endl; + } + std::cout << "start: " << std::to_string(start) + +", goal: "+ std::to_string(goal) + +", result: "+ std::to_string(tmp) + << std::endl; +//#endif + + while(graphParent[current] != -1) + { + ESM::Pathgrid::Point pt = mPathgrid->mPoints[current]; +//#if 0 + // for debugging only + std::cout << " point: "+ std::to_string(current) + +", X: "+ std::to_string(pt.mX) + +", Y: "+ std::to_string(pt.mY) + << std::endl; +//#endif + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); + current = graphParent[current]; + } + return path; + } } diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index ae849bff2..fddb293d9 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -2,6 +2,7 @@ #define GAME_MWMECHANICS_PATHFINDING_H #include +#include #include #include @@ -34,8 +35,6 @@ namespace MWMechanics void clearPath(); - void buildPathgridGraph(const ESM::Pathgrid* pathGrid); - void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint, const MWWorld::CellStore* cell, bool allowShortcuts = true); @@ -75,60 +74,73 @@ 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 - { - int destination; - float cost; - }; - struct Node + bool mIsPathConstructed; + + std::list mPath; + + const ESM::Pathgrid *mPathgrid; + const MWWorld::CellStore* mCell; + }; + + class PathgridGraph + { + public: + PathgridGraph(); + + bool isGraphConstructed() const { - int label; - std::vector edges; - int parent;//used in pathfinding + return mIsGraphConstructed; }; - std::vector mGScore; - std::vector mFScore; + bool initPathgridGraph(const ESM::Cell *cell); - std::list aStarSearch(const ESM::Pathgrid* pathGrid,int start,int goal,float xCell = 0, float yCell = 0); - void cleanUpAStar(); + // returns true if end point is strongly connected (i.e. reachable + // from start point) both start and end are pathgrid point indexes + bool isPointConnected(const int start, const int end) const; - std::vector mGraph; - bool mIsPathConstructed; + // isOutside is used whether to convert path to world co-ordinates + std::list aStarSearch(const int start, const int end, + const bool isOutside) const; + private: + const ESM::Cell *mCell; + const ESM::Pathgrid *mPathgrid; - std::list mPath; - bool mIsGraphConstructed; - const MWWorld::CellStore* mCell; + struct ConnectedPoint // edge + { + int index; // pathgrid point index of neighbour + float cost; + }; + + struct Node // point + { + int componentId; + std::vector edges; // neighbours + }; - // contains an integer indicating the groups of connected pathgrid points - // (all connected points will have the same value) + // componentId is 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) + // 48, 49, 50, 51, 84, 85, 86, 87, 88, 89, 90 (ship & office) // all other pathgrid points are the third set // - std::vector mSCComp; - // variables used to calculate mSCComp + std::vector mGraph; + bool mIsGraphConstructed; + + // variables used to calculate connected components int mSCCId; int mSCCIndex; - std::list mSCCStack; + std::vector mSCCStack; typedef std::pair VPair; // first is index, second is lowlink std::vector mSCCPoint; - // methods used to calculate mSCComp + // methods used to calculate connected components void recursiveStrongConnect(int v); - void buildConnectedPoints(const ESM::Pathgrid* pathGrid); + void buildConnectedPoints(); }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 3f1ef8ab2..dd105a665 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -680,4 +680,34 @@ namespace MWWorld { return !(left==right); } + + bool CellStore::isPointConnected(const int start, const int end) const + { + if(!mPathgridGraph.isGraphConstructed()) + { + // Ugh... there must be a better way... + MWMechanics::PathgridGraph *p = const_cast (&mPathgridGraph); + + if(!p->initPathgridGraph(mCell)) + return false; + } + return mPathgridGraph.isPointConnected(start, end); + + } + + std::list CellStore::aStarSearch(const int start, const int end, + const bool isOutside) const + { + if(!mPathgridGraph.isGraphConstructed()) + { + MWMechanics::PathgridGraph *p = const_cast (&mPathgridGraph); + + if(!p->initPathgridGraph(mCell)) + { + std::list path; // empty + return path; + } + } + return mPathgridGraph.aStarSearch(start, end, isOutside); + } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 4b7c0011b..3cca993f2 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -8,6 +8,8 @@ #include "esmstore.hpp" #include "cellreflist.hpp" +#include "../mwmechanics/pathfinding.hpp" + namespace ESM { struct CellState; @@ -141,6 +143,11 @@ namespace MWWorld throw std::runtime_error ("Storage for this type not exist in cells"); } + bool isPointConnected(const int start, const int end) const; + + std::list aStarSearch(const int start, const int end, + const bool isOutside) const; + private: template @@ -166,6 +173,8 @@ namespace MWWorld ///< Make case-adjustments to \a ref and insert it into the respective container. /// /// Invalid \a ref objects are silently dropped. + + MWMechanics::PathgridGraph mPathgridGraph; }; template<> From 5d422fec8abd5f52adb1de423cd0effb3d4a6cb4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Apr 2014 13:00:19 +0200 Subject: [PATCH 042/157] fixed scene toolbar layout problems --- apps/opencs/view/world/scenetool.cpp | 1 + apps/opencs/view/world/scenetoolbar.cpp | 7 ++++++- apps/opencs/view/world/scenetoolbar.hpp | 3 +++ apps/opencs/view/world/scenetoolmode.cpp | 3 ++- apps/opencs/view/world/scenetoolmode.hpp | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/scenetool.cpp b/apps/opencs/view/world/scenetool.cpp index 320deb1ba..612b4c6d3 100644 --- a/apps/opencs/view/world/scenetool.cpp +++ b/apps/opencs/view/world/scenetool.cpp @@ -6,6 +6,7 @@ CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) { setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + setIconSize (QSize (parent->getIconSize(), parent->getIconSize())); setFixedSize (parent->getButtonSize(), parent->getButtonSize()); connect (this, SIGNAL (clicked()), this, SLOT (openRequest())); diff --git a/apps/opencs/view/world/scenetoolbar.cpp b/apps/opencs/view/world/scenetoolbar.cpp index 2972c5391..9eb02ce2f 100644 --- a/apps/opencs/view/world/scenetoolbar.cpp +++ b/apps/opencs/view/world/scenetoolbar.cpp @@ -6,7 +6,7 @@ #include "scenetool.hpp" CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) -: QWidget (parent), mButtonSize (buttonSize) +: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-8) { setFixedWidth (mButtonSize); @@ -26,4 +26,9 @@ void CSVWorld::SceneToolbar::addTool (SceneTool *tool) int CSVWorld::SceneToolbar::getButtonSize() const { return mButtonSize; +} + +int CSVWorld::SceneToolbar::getIconSize() const +{ + return mIconSize; } \ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolbar.hpp b/apps/opencs/view/world/scenetoolbar.hpp index f713ca3df..731806cc5 100644 --- a/apps/opencs/view/world/scenetoolbar.hpp +++ b/apps/opencs/view/world/scenetoolbar.hpp @@ -15,6 +15,7 @@ namespace CSVWorld QVBoxLayout *mLayout; int mButtonSize; + int mIconSize; public: @@ -23,6 +24,8 @@ namespace CSVWorld void addTool (SceneTool *tool); int getButtonSize() const; + + int getIconSize() const; }; } diff --git a/apps/opencs/view/world/scenetoolmode.cpp b/apps/opencs/view/world/scenetoolmode.cpp index 281d703b6..73b01ae3a 100644 --- a/apps/opencs/view/world/scenetoolmode.cpp +++ b/apps/opencs/view/world/scenetoolmode.cpp @@ -8,7 +8,7 @@ #include "scenetoolbar.hpp" CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent) -: SceneTool (parent), mButtonSize (parent->getButtonSize()) +: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()) { mPanel = new QFrame (this, Qt::Popup); @@ -29,6 +29,7 @@ void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::str { QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + button->setIconSize (QSize (mIconSize, mIconSize)); button->setFixedSize (mButtonSize, mButtonSize); mLayout->addWidget (button); diff --git a/apps/opencs/view/world/scenetoolmode.hpp b/apps/opencs/view/world/scenetoolmode.hpp index a8fe2b5a6..a156c0c95 100644 --- a/apps/opencs/view/world/scenetoolmode.hpp +++ b/apps/opencs/view/world/scenetoolmode.hpp @@ -20,6 +20,7 @@ namespace CSVWorld QHBoxLayout *mLayout; std::map mButtons; // widget, id int mButtonSize; + int mIconSize; public: From 325d0616bbe8e10d661fff03b9e64778865698b1 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 3 Apr 2014 22:17:45 +1100 Subject: [PATCH 043/157] Cleanup debug statements. --- apps/openmw/mwmechanics/pathfinding.cpp | 85 ++----------------------- 1 file changed, 6 insertions(+), 79 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index f56cfad5f..1ef05eda8 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -225,9 +225,6 @@ namespace MWMechanics if(mCell != cell || !mPathgrid) { mCell = cell; - - // Cache pathgrid as mPathgrid and update on cell changes. There - // might be a small gain in avoiding to search for it. mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*mCell->getCell()); } @@ -236,13 +233,6 @@ namespace MWMechanics // physics take care of any blockages. if(!mPathgrid || mPathgrid->mPoints.empty()) { -//#if 0 - std::cout << "no pathgrid " << - +"\"" +mCell->getCell()->mName+ "\"" - +", " +std::to_string(mCell->getCell()->mData.mX) - +", " +std::to_string(mCell->getCell()->mData.mY) - << std::endl; -//#endif mPath.push_back(endPoint); mIsPathConstructed = true; return; @@ -257,16 +247,6 @@ namespace MWMechanics yCell = mCell->getCell()->mData.mY * ESM::Land::REAL_SIZE; } - - - - - - - - - - // NOTE: It is possible that getClosestPoint returns a pathgrind point index // that is unreachable in some situations. e.g. actor is standing // outside an area enclosed by walls, but there is a pathgrid @@ -280,14 +260,7 @@ namespace MWMechanics std::pair endNode = getClosestReachablePoint(mPathgrid, cell, Ogre::Vector3(endPoint.mX - xCell, endPoint.mY - yCell, endPoint.mZ), startNode); -//#if 0 - if(!mPathgrid) - std::cout << "no pathgrid " << - +"\"" +mCell->getCell()->mName+ "\"" - +", " +std::to_string(mCell->getCell()->mData.mX) - +", " +std::to_string(mCell->getCell()->mData.mY) - << std::endl; -//#endif + // this shouldn't really happen, but just in case if(endNode.first != -1) { @@ -309,33 +282,14 @@ namespace MWMechanics mPath.push_back(endPoint); } else - { mIsPathConstructed = false; - std::cout << "empty path error " << std::endl; - } - //mIsPathConstructed = false; } else - { mIsPathConstructed = false; - std::cout << "second point error " << std::endl; - } - //mIsPathConstructed = false; } else - { - // FIXME: shouldn't return endpoint if first point error? mIsPathConstructed = false; - std::cout << "first point error " << std::endl; - } -#if 0 - if(!mIsPathConstructed) - { - mPath.push_back(endPoint); - mIsPathConstructed = true; - } -#endif return; } @@ -464,18 +418,13 @@ namespace MWMechanics bool PathgridGraph::initPathgridGraph(const ESM::Cell* cell) { if(!cell) - { - std::cout << "init error " << std::endl; return false; - } + mCell = cell; mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); if(!mPathgrid) - { - std::cout << "init error " << std::endl; return false; - } mGraph.resize(mPathgrid->mPoints.size()); for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) @@ -493,13 +442,13 @@ namespace MWMechanics } buildConnectedPoints(); mIsGraphConstructed = true; - //#if 0 +//#if 0 std::cout << "loading pathgrid " << +"\""+ mPathgrid->mCell +"\"" +", "+ std::to_string(mPathgrid->mData.mX) +", "+ std::to_string(mPathgrid->mData.mY) << std::endl; - //#endif +//#endif return true; } @@ -580,11 +529,11 @@ namespace MWMechanics if(mSCCPoint[v].first == -1) // undefined (haven't visited) recursiveStrongConnect(v); } - //#if 0 +//#if 0 std::cout << "components: " << std::to_string(mSCCId) +", "+ mPathgrid->mCell << std::endl; - //#endif +//#endif } bool PathgridGraph::isPointConnected(const int start, const int end) const @@ -702,32 +651,10 @@ namespace MWMechanics xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE; yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE; } -//#if 0 - // for debugging only - int tmp = current; - if(tmp != goal) - { - std::cout << "aStarSearch: goal and result differ" << std::endl; - std::cout << "goal: " << std::to_string(goal) - +", result: "+ std::to_string(tmp) - << std::endl; - } - std::cout << "start: " << std::to_string(start) - +", goal: "+ std::to_string(goal) - +", result: "+ std::to_string(tmp) - << std::endl; -//#endif while(graphParent[current] != -1) { ESM::Pathgrid::Point pt = mPathgrid->mPoints[current]; -//#if 0 - // for debugging only - std::cout << " point: "+ std::to_string(current) - +", X: "+ std::to_string(pt.mX) - +", Y: "+ std::to_string(pt.mY) - << std::endl; -//#endif pt.mX += xCell; pt.mY += yCell; path.push_front(pt); From 3a58da9ad78ade99a388923dafdfb29bc4110d8d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Apr 2014 13:30:22 +0200 Subject: [PATCH 044/157] size adjustment --- apps/opencs/view/world/scenetoolbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/world/scenetoolbar.cpp b/apps/opencs/view/world/scenetoolbar.cpp index 9eb02ce2f..d60240da7 100644 --- a/apps/opencs/view/world/scenetoolbar.cpp +++ b/apps/opencs/view/world/scenetoolbar.cpp @@ -6,7 +6,7 @@ #include "scenetool.hpp" CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) -: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-8) +: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6) { setFixedWidth (mButtonSize); From a8b2eb1fe9f16e51602c6cee8717082211555fbc Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 3 Apr 2014 22:49:22 +1100 Subject: [PATCH 045/157] Make Travis happy. --- apps/openmw/mwmechanics/pathfinding.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 1ef05eda8..9fcd335e4 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -442,13 +442,13 @@ namespace MWMechanics } buildConnectedPoints(); mIsGraphConstructed = true; -//#if 0 +#if 0 std::cout << "loading pathgrid " << +"\""+ mPathgrid->mCell +"\"" +", "+ std::to_string(mPathgrid->mData.mX) +", "+ std::to_string(mPathgrid->mData.mY) << std::endl; -//#endif +#endif return true; } @@ -529,11 +529,11 @@ namespace MWMechanics if(mSCCPoint[v].first == -1) // undefined (haven't visited) recursiveStrongConnect(v); } -//#if 0 +#if 0 std::cout << "components: " << std::to_string(mSCCId) +", "+ mPathgrid->mCell << std::endl; -//#endif +#endif } bool PathgridGraph::isPointConnected(const int start, const int end) const From baf30ba29295a8535f3af18961060e72d8ec9a09 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Apr 2014 14:44:48 +0200 Subject: [PATCH 046/157] added grid tool (does not work yet) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/scenesubview.cpp | 13 ++++ apps/opencs/view/world/scenetoolgrid.cpp | 75 ++++++++++++++++++++++++ apps/opencs/view/world/scenetoolgrid.hpp | 29 +++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/world/scenetoolgrid.cpp create mode 100644 apps/opencs/view/world/scenetoolgrid.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a7a694463..05cc93f89 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 dialoguesubview previewsubview + scenetoolmode infocreator scriptedit dialoguesubview previewsubview scenetoolgrid ) opencs_units (view/render diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index c075cb4d6..dedaf014b 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -18,6 +18,7 @@ #include "creator.hpp" #include "scenetoolbar.hpp" #include "scenetoolmode.hpp" +#include "scenetoolgrid.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id) @@ -36,6 +37,8 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48, this); + SceneToolGrid *gridTool = 0; + if (id.getId()=="sys::default") { CSVRender::PagedWorldspaceWidget *widget = new CSVRender::PagedWorldspaceWidget (this); @@ -44,6 +47,13 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), this, SLOT (cellIndexChanged (const std::pair&, const std::pair&))); + + gridTool = new SceneToolGrid (toolbar); + + connect (widget, + SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), + gridTool, + SLOT (cellIndexChanged (const std::pair&, const std::pair&))); } else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); @@ -54,6 +64,9 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); + if (gridTool) + toolbar->addTool (gridTool); + layout2->addWidget (toolbar, 0); layout2->addWidget (mScene, 1); diff --git a/apps/opencs/view/world/scenetoolgrid.cpp b/apps/opencs/view/world/scenetoolgrid.cpp new file mode 100644 index 000000000..0769be168 --- /dev/null +++ b/apps/opencs/view/world/scenetoolgrid.cpp @@ -0,0 +1,75 @@ + +#include "scenetoolgrid.hpp" + +#include + +#include +#include +#include + +#include "scenetoolbar.hpp" + +CSVWorld::SceneToolGrid::SceneToolGrid (SceneToolbar *parent) +: SceneTool (parent), mIconSize (parent->getIconSize()) +{ +} + +void CSVWorld::SceneToolGrid::showPanel (const QPoint& position) +{ + + +} + +void CSVWorld::SceneToolGrid::cellIndexChanged (const std::pair& min, + const std::pair& max) +{ + /// \todo make font size configurable + const int fontSize = 8; + + /// \todo replace with proper icon + QPixmap image (mIconSize, mIconSize); + image.fill (QColor (0, 0, 0, 0)); + + { + QPainter painter (&image); + painter.setPen (Qt::black); + QFont font (QApplication::font().family(), fontSize); + painter.setFont (font); + + QFontMetrics metrics (font); + + if (min==max) + { + // single cell + std::ostringstream stream; + stream << min.first << ", " << min.second; + + QString text = QString::fromUtf8 (stream.str().c_str()); + + painter.drawText (QPoint ((mIconSize-metrics.width (text))/2, mIconSize/2+fontSize/2), + text); + } + else + { + // range + { + std::ostringstream stream; + stream << min.first << ", " << min.second; + painter.drawText (QPoint (0, mIconSize), + QString::fromUtf8 (stream.str().c_str())); + } + + { + std::ostringstream stream; + stream << max.first << ", " << max.second; + + QString text = QString::fromUtf8 (stream.str().c_str()); + + painter.drawText (QPoint (mIconSize-metrics.width (text), fontSize), text); + } + } + } + + QIcon icon (image); + setIcon (icon); +} \ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolgrid.hpp b/apps/opencs/view/world/scenetoolgrid.hpp new file mode 100644 index 000000000..917df2a16 --- /dev/null +++ b/apps/opencs/view/world/scenetoolgrid.hpp @@ -0,0 +1,29 @@ +#ifndef CSV_WORLD_SCENETOOL_GRID_H +#define CSV_WORLD_SCENETOOL_GRID_H + +#include "scenetool.hpp" + +namespace CSVWorld +{ + class SceneToolbar; + + ///< \brief Cell grid selector tool + class SceneToolGrid : public SceneTool + { + Q_OBJECT + + int mIconSize; + + public: + + SceneToolGrid (SceneToolbar *parent); + + virtual void showPanel (const QPoint& position); + + public slots: + + void cellIndexChanged (const std::pair& min, const std::pair& max); + }; +} + +#endif From 0c957a3cde6c93830c587b3bb6e5a4687f6184be Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 3 Apr 2014 14:53:31 -0400 Subject: [PATCH 047/157] Added witnesses to the mix --- apps/openmw/mwmechanics/actors.cpp | 44 ++++++++++++------- apps/openmw/mwmechanics/creaturestats.cpp | 12 ++++- apps/openmw/mwmechanics/creaturestats.hpp | 5 +++ .../mwmechanics/mechanicsmanagerimp.cpp | 11 ++++- apps/openmw/mwworld/player.cpp | 13 +++++- apps/openmw/mwworld/player.hpp | 6 +++ 6 files changed, 70 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 11469c1a9..6d9f23aa2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -719,9 +719,9 @@ namespace MWMechanics void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - int bounty = player.getClass().getNpcStats(player).getBounty(); + CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); - // TODO: Move me! I shouldn't be here... + /// \todo Move me! I shouldn't be here... const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * @@ -729,30 +729,40 @@ namespace MWMechanics if (ptr != player) { - CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); - // Alarmed or not, I will kill you because you've commited heinous against the empire - if ((!creatureStats.isAlarmed() || creatureStats.isAlarmed()) && - ptr.getClass().isClass(ptr, "Guard") && bounty >= cutoff && !creatureStats.isHostile()) - creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); - else if (creatureStats.isAlarmed()) + // If I'm a guard and I'm not hostile + if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) + { + // Attack on sight if bounty is greater than the cutoff + if ( player.getClass().getNpcStats(player).getBounty() >= cutoff + && MWBase::Environment::get().getWorld()->getLOS(ptr, player) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) + { + creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.setHostile(true); + } + } + + // if I was a witness to a crime + if (creatureStats.getCrimeId() != -1) { - MWBase::Environment::get().getDialogueManager()->say(ptr, "Thief"); - if(bounty == 0) + if(player.getClass().getNpcStats(player).getBounty() == 0) { creatureStats.setAlarmed(false); creatureStats.setHostile(false); if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stopPersue(); creatureStats.getAiSequence().stopCombat(); - + creatureStats.setCrimeId(-1); } - - if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) - creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); - else if (!creatureStats.isHostile()) + else if (creatureStats.isAlarmed()) { - creatureStats.getAiSequence().stack(AiCombat(player)); - creatureStats.setHostile(true); + if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) + creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); + else if (!creatureStats.isHostile()) + { + creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.setHostile(true); + } } } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a358917de..17d36be79 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -15,7 +15,7 @@ namespace MWMechanics : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), mAttacked (false), mHostile (false), mAssaulted(false), - mAttackingOrSpell(false), + mAttackingOrSpell(false), mCrimeId(-1), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) @@ -326,6 +326,16 @@ namespace MWMechanics mAssaulted = assaulted; } + int CreatureStats::getCrimeId() const + { + return mCrimeId; + } + + void CreatureStats::setCrimeId (int id) + { + mCrimeId = id; + } + bool CreatureStats::getCreatureTargetted() const { std::string target; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7db895dab..67afd9f25 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,6 +40,7 @@ namespace MWMechanics bool mAttacked; bool mHostile; bool mAssaulted; + int mCrimeId; bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; @@ -191,6 +192,10 @@ namespace MWMechanics void setAssaulted (bool assaulted); + int getCrimeId() const; + + void setCrimeId (int id); + bool getCreatureTargetted() const; float getEvasion() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 39cf63cd0..54622c0b4 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -810,6 +810,7 @@ namespace MWMechanics return false; const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::Player player = MWBase::Environment::get().getWorld()->getPlayer(); // What amount of alarm did this crime generate? int alarm; @@ -858,13 +859,19 @@ namespace MWMechanics if (*it1 == ptr) // Not the player continue; - // was the witness alarmed? + // Will the witness be affected by the crime? CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + { creatureStats1.setAlarmed(true); + creatureStats1.setCrimeId(player.getWitnessTotal()); + player.addWitness(); + } } break; // Someone saw the crime and everyone has been told - } + } + else if (type == OT_Assault) + creatureStats.setAlarmed(true); } } if (reported) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 6d551ecf1..7c576960f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -28,8 +28,9 @@ namespace MWWorld : mCellStore(0), mLastKnownExteriorPosition(0,0,0), mAutoMove(false), - mForwardBackward (0), + mForwardBackward(0), mTeleported(false), + mWitnessTotal(0), mMarkedCell(NULL) { mPlayer.mBase = player; @@ -65,6 +66,16 @@ namespace MWWorld return mSign; } + void Player::addWitness() + { + mWitnessTotal++; + } + + int Player::getWitnessTotal() const + { + return mWitnessTotal; + } + void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 9d3fbbeec..71c231481 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -41,6 +41,8 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; bool mTeleported; + + int mWitnessTotal; public: @@ -65,6 +67,10 @@ namespace MWWorld void setBirthSign(const std::string &sign); + void addWitness(); + + int getWitnessTotal() const; + const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); From 040d4f8fc4495be3316c5ad47928c4eed6138f4a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 4 Apr 2014 06:13:47 +1100 Subject: [PATCH 048/157] Move PathgridGraph into separate files. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/pathfinding.cpp | 335 ----------------------- apps/openmw/mwmechanics/pathfinding.hpp | 60 ---- apps/openmw/mwmechanics/pathgrid.cpp | 348 ++++++++++++++++++++++++ apps/openmw/mwmechanics/pathgrid.hpp | 75 +++++ apps/openmw/mwworld/cellstore.cpp | 19 +- apps/openmw/mwworld/cellstore.hpp | 2 +- 7 files changed, 426 insertions(+), 415 deletions(-) create mode 100644 apps/openmw/mwmechanics/pathgrid.cpp create mode 100644 apps/openmw/mwmechanics/pathgrid.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index d1f7c45f3..ea4756ac4 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -68,7 +68,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting + aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting disease pickpocket levelledlist combat steering ) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 9fcd335e4..a4166d83b 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -1,7 +1,5 @@ #include "pathfinding.hpp" -#include - #include "OgreMath.h" #include "OgreVector3.h" @@ -37,44 +35,6 @@ namespace return sqrt(x * x + y * y + z * 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(const ESM::Pathgrid::Point a, const ESM::Pathgrid::Point b) - { - return 300 * (abs(a.mX - b.mX) + abs(a.mY - b.mY) + abs(a.mZ - b.mZ)); - } - - // Choose a heuristics - Note that 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(const ESM::Pathgrid::Point a, const 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 @@ -368,299 +328,4 @@ namespace MWMechanics } - // TODO: Any multi threading concerns? - PathgridGraph::PathgridGraph() - : mCell(NULL) - , mIsGraphConstructed(false) - , mPathgrid(NULL) - , mGraph(0) - , mSCCId(0) - , mSCCIndex(0) - { - } - - /* - * mGraph is populated with the cost of each allowed edge. - * - * The data structure is based on the code in buildPath2() but modified. - * Please check git history if interested. - * - * mGraph[v].edges[i].index = w - * - * v = point index of location "from" - * i = index of edges from point v - * w = point index of location "to" - * - * - * Example: (notice from p(0) to p(2) is not allowed in this example) - * - * mGraph[0].edges[0].index = 1 - * .edges[1].index = 3 - * - * mGraph[1].edges[0].index = 0 - * .edges[1].index = 2 - * .edges[2].index = 3 - * - * mGraph[2].edges[0].index = 1 - * - * (etc, etc) - * - * - * low - * cost - * p(0) <---> p(1) <------------> p(2) - * ^ ^ - * | | - * | +-----> p(3) - * +----------------> - * high cost - */ - bool PathgridGraph::initPathgridGraph(const ESM::Cell* cell) - { - if(!cell) - return false; - - mCell = cell; - mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); - - if(!mPathgrid) - return false; - - mGraph.resize(mPathgrid->mPoints.size()); - for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) - { - ConnectedPoint neighbour; - neighbour.cost = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0], - mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]); - // forward path of the edge - neighbour.index = mPathgrid->mEdges[i].mV1; - mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour); - // reverse path of the edge - // NOTE: These are redundant, ESM already contains the required reverse paths - //neighbour.index = mPathgrid->mEdges[i].mV0; - //mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour); - } - buildConnectedPoints(); - mIsGraphConstructed = true; -#if 0 - std::cout << "loading pathgrid " << - +"\""+ mPathgrid->mCell +"\"" - +", "+ std::to_string(mPathgrid->mData.mX) - +", "+ std::to_string(mPathgrid->mData.mY) - << std::endl; -#endif - return true; - } - - // v is the pathgrid point index (some call them vertices) - void PathgridGraph::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 < static_cast (mGraph[v].edges.size()); i++) - { - w = mGraph[v].edges[i].index; - 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(); - mGraph[w].componentId = mSCCId; - } - while(w != v); - mSCCId++; - } - return; - } - - /* - * mGraph contains the strongly connected component group id's along - * with pre-calculated edge costs. - * - * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 - * - * mGraph for Seyda Neen will therefore have 3 different values. When - * selecting a random pathgrid point for AiWander, mGraph can be checked - * for quickly finding whether the destination is reachable. - * - * Otherwise, buildPath can 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 mPoints - * mGraph[v].edges | E (for v) | - * mSCCIndex | index | tracking smallest unused index - * mSCCStack | S | - * mGraph[v].edges[i].index | w | - * - */ - void PathgridGraph::buildConnectedPoints() - { - // both of these are set to zero in the constructor - //mSCCId = 0; // how many strongly connected components in this cell - //mSCCIndex = 0; - int pointsSize = mPathgrid->mPoints.size(); - mSCCPoint.resize(pointsSize, std::pair (-1, -1)); - mSCCStack.reserve(pointsSize); - - for(int v = 0; v < static_cast (pointsSize); v++) - { - if(mSCCPoint[v].first == -1) // undefined (haven't visited) - recursiveStrongConnect(v); - } -#if 0 - std::cout << "components: " << std::to_string(mSCCId) - +", "+ mPathgrid->mCell - << std::endl; -#endif - } - - bool PathgridGraph::isPointConnected(const int start, const int end) const - { - return (mGraph[start].componentId == mGraph[end].componentId); - } - - /* - * NOTE: Based on buildPath2(), please check git history if interested - * Should consider using a 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 - * that mGraph is already constructed. - * - * Should be possible to make this MT safe. - * - * Returns path (a list of pathgrid point indexes) which may be empty. - * - * Input params: - * start, goal - pathgrid point indexes (for this cell) - * isExterior - used to determine whether to convert to world co-ordinates - * - * Variables: - * openset - point indexes to be traversed, lowest cost at the front - * closedset - point indexes already traversed - * gScore - past accumulated costs vector indexed by point index - * fScore - future estimated costs vector indexed by point index - * - * TODO: An intersting exercise might be to cache the paths created for a - * start/goal pair. To cache the results the paths need to be in - * pathgrid points form (currently they are converted to world - * co-ordinates). Essentially trading speed w/ memory. - */ - std::list PathgridGraph::aStarSearch(const int start, - const int goal, - bool isExterior) const - { - std::list path; - if(!isPointConnected(start, goal)) - { - return path; // there is no path, return an empty path - } - - int graphSize = mGraph.size(); - std::vector gScore; - gScore.resize(graphSize, -1); - std::vector fScore; - fScore.resize(graphSize, -1); - std::vector graphParent; - graphParent.resize(graphSize, -1); - - // gScore & fScore keep costs for each pathgrid point in mPoints - gScore[start] = 0; - fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]); - - std::list openset; - std::list closedset; - openset.push_back(start); - - int current = -1; - - while(!openset.empty()) - { - current = openset.front(); // front has the lowest cost - openset.pop_front(); - - if(current == goal) - break; - - closedset.push_back(current); // remember we've been here - - // 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].index) == - closedset.end()) - { - // not in closedset - i.e. have not traversed this edge destination - int dest = mGraph[current].edges[j].index; - float tentative_g = gScore[current] + mGraph[current].edges[j].cost; - bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); - if(!isInOpenSet - || tentative_g < gScore[dest]) - { - graphParent[dest] = current; - gScore[dest] = tentative_g; - fScore[dest] = tentative_g + costAStar(mPathgrid->mPoints[dest], - mPathgrid->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 - std::list::iterator it = openset.begin(); - for(it = openset.begin(); it!= openset.end(); it++) - { - if(fScore[*it] > fScore[dest]) - break; - } - openset.insert(it, dest); - } - } - } // if in closedset, i.e. traversed this edge already, try the next edge - } - } - - if(current != goal) - return path; // for some reason couldn't build a path - - // reconstruct path to return, using world co-ordinates - float xCell = 0; - float yCell = 0; - if (isExterior) - { - xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE; - yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE; - } - - while(graphParent[current] != -1) - { - ESM::Pathgrid::Point pt = mPathgrid->mPoints[current]; - pt.mX += xCell; - pt.mY += yCell; - path.push_front(pt); - current = graphParent[current]; - } - return path; - } } - diff --git a/apps/openmw/mwmechanics/pathfinding.hpp b/apps/openmw/mwmechanics/pathfinding.hpp index fddb293d9..eb093ad69 100644 --- a/apps/openmw/mwmechanics/pathfinding.hpp +++ b/apps/openmw/mwmechanics/pathfinding.hpp @@ -2,7 +2,6 @@ #define GAME_MWMECHANICS_PATHFINDING_H #include -#include #include #include @@ -83,65 +82,6 @@ namespace MWMechanics const ESM::Pathgrid *mPathgrid; const MWWorld::CellStore* mCell; }; - - class PathgridGraph - { - public: - PathgridGraph(); - - bool isGraphConstructed() const - { - return mIsGraphConstructed; - }; - - bool initPathgridGraph(const ESM::Cell *cell); - - // returns true if end point is strongly connected (i.e. reachable - // from start point) both start and end are pathgrid point indexes - bool isPointConnected(const int start, const int end) const; - - // isOutside is used whether to convert path to world co-ordinates - std::list aStarSearch(const int start, const int end, - const bool isOutside) const; - private: - - const ESM::Cell *mCell; - const ESM::Pathgrid *mPathgrid; - - struct ConnectedPoint // edge - { - int index; // pathgrid point index of neighbour - float cost; - }; - - struct Node // point - { - int componentId; - std::vector edges; // neighbours - }; - - // componentId is 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 (ship & office) - // all other pathgrid points are the third set - // - std::vector mGraph; - bool mIsGraphConstructed; - - // variables used to calculate connected components - int mSCCId; - int mSCCIndex; - std::vector mSCCStack; - typedef std::pair VPair; // first is index, second is lowlink - std::vector mSCCPoint; - // methods used to calculate connected components - void recursiveStrongConnect(int v); - void buildConnectedPoints(); - }; } #endif diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp new file mode 100644 index 000000000..5580e5d3f --- /dev/null +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -0,0 +1,348 @@ +#include "pathgrid.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/cellstore.hpp" + +namespace +{ + // 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(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b) + { + return 300 * (abs(a.mX - b.mX) + abs(a.mY - b.mY) + abs(a.mZ - b.mZ)); + } + + // Choose a heuristics - Note that 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(const ESM::Pathgrid::Point& a, const ESM::Pathgrid::Point& b) + { + //return distance(a, b); + return manhattan(a, b); + } +} + +namespace MWMechanics +{ + PathgridGraph::PathgridGraph() + : mCell(NULL) + , mIsGraphConstructed(false) + , mPathgrid(NULL) + , mGraph(0) + , mSCCId(0) + , mSCCIndex(0) + { + } + + /* + * mGraph is populated with the cost of each allowed edge. + * + * The data structure is based on the code in buildPath2() but modified. + * Please check git history if interested. + * + * mGraph[v].edges[i].index = w + * + * v = point index of location "from" + * i = index of edges from point v + * w = point index of location "to" + * + * + * Example: (notice from p(0) to p(2) is not allowed in this example) + * + * mGraph[0].edges[0].index = 1 + * .edges[1].index = 3 + * + * mGraph[1].edges[0].index = 0 + * .edges[1].index = 2 + * .edges[2].index = 3 + * + * mGraph[2].edges[0].index = 1 + * + * (etc, etc) + * + * + * low + * cost + * p(0) <---> p(1) <------------> p(2) + * ^ ^ + * | | + * | +-----> p(3) + * +----------------> + * high cost + */ + bool PathgridGraph::load(const ESM::Cell* cell) + { + if(!cell) + return false; + + mCell = cell; + mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); + + if(!mPathgrid) + return false; + + if(mIsGraphConstructed) + return true; + + mGraph.resize(mPathgrid->mPoints.size()); + for(int i = 0; i < static_cast (mPathgrid->mEdges.size()); i++) + { + ConnectedPoint neighbour; + neighbour.cost = costAStar(mPathgrid->mPoints[mPathgrid->mEdges[i].mV0], + mPathgrid->mPoints[mPathgrid->mEdges[i].mV1]); + // forward path of the edge + neighbour.index = mPathgrid->mEdges[i].mV1; + mGraph[mPathgrid->mEdges[i].mV0].edges.push_back(neighbour); + // reverse path of the edge + // NOTE: These are redundant, ESM already contains the required reverse paths + //neighbour.index = mPathgrid->mEdges[i].mV0; + //mGraph[mPathgrid->mEdges[i].mV1].edges.push_back(neighbour); + } + buildConnectedPoints(); + mIsGraphConstructed = true; +#if 0 + std::cout << "loading pathgrid " << + +"\""+ mPathgrid->mCell +"\"" + +", "+ std::to_string(mPathgrid->mData.mX) + +", "+ std::to_string(mPathgrid->mData.mY) + << std::endl; +#endif + return true; + } + + // v is the pathgrid point index (some call them vertices) + void PathgridGraph::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 < static_cast (mGraph[v].edges.size()); i++) + { + w = mGraph[v].edges[i].index; + 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(); + mGraph[w].componentId = mSCCId; + } + while(w != v); + mSCCId++; + } + return; + } + + /* + * mGraph contains the strongly connected component group id's along + * with pre-calculated edge costs. + * + * A cell can have disjointed pathgrids, e.g. Seyda Neen has 3 + * + * mGraph for Seyda Neen will therefore have 3 different values. When + * selecting a random pathgrid point for AiWander, mGraph can be checked + * for quickly finding whether the destination is reachable. + * + * Otherwise, buildPath can 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 mPoints + * mGraph[v].edges | E (for v) | + * mSCCIndex | index | tracking smallest unused index + * mSCCStack | S | + * mGraph[v].edges[i].index | w | + * + */ + void PathgridGraph::buildConnectedPoints() + { + // both of these are set to zero in the constructor + //mSCCId = 0; // how many strongly connected components in this cell + //mSCCIndex = 0; + int pointsSize = mPathgrid->mPoints.size(); + mSCCPoint.resize(pointsSize, std::pair (-1, -1)); + mSCCStack.reserve(pointsSize); + + for(int v = 0; v < static_cast (pointsSize); v++) + { + if(mSCCPoint[v].first == -1) // undefined (haven't visited) + recursiveStrongConnect(v); + } +#if 0 + std::cout << "components: " << std::to_string(mSCCId) + +", "+ mPathgrid->mCell + << std::endl; +#endif + } + + bool PathgridGraph::isPointConnected(const int start, const int end) const + { + return (mGraph[start].componentId == mGraph[end].componentId); + } + + /* + * NOTE: Based on buildPath2(), please check git history if interested + * Should consider using a 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 + * that mGraph is already constructed. + * + * Should be possible to make this MT safe. + * + * Returns path (a list of pathgrid point indexes) which may be empty. + * + * Input params: + * start, goal - pathgrid point indexes (for this cell) + * isExterior - used to determine whether to convert to world co-ordinates + * + * Variables: + * openset - point indexes to be traversed, lowest cost at the front + * closedset - point indexes already traversed + * gScore - past accumulated costs vector indexed by point index + * fScore - future estimated costs vector indexed by point index + * + * TODO: An intersting exercise might be to cache the paths created for a + * start/goal pair. To cache the results the paths need to be in + * pathgrid points form (currently they are converted to world + * co-ordinates). Essentially trading speed w/ memory. + */ + std::list PathgridGraph::aStarSearch(const int start, + const int goal, + bool isExterior) const + { + std::list path; + if(!isPointConnected(start, goal)) + { + return path; // there is no path, return an empty path + } + + int graphSize = mGraph.size(); + std::vector gScore; + gScore.resize(graphSize, -1); + std::vector fScore; + fScore.resize(graphSize, -1); + std::vector graphParent; + graphParent.resize(graphSize, -1); + + // gScore & fScore keep costs for each pathgrid point in mPoints + gScore[start] = 0; + fScore[start] = costAStar(mPathgrid->mPoints[start], mPathgrid->mPoints[goal]); + + std::list openset; + std::list closedset; + openset.push_back(start); + + int current = -1; + + while(!openset.empty()) + { + current = openset.front(); // front has the lowest cost + openset.pop_front(); + + if(current == goal) + break; + + closedset.push_back(current); // remember we've been here + + // 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].index) == + closedset.end()) + { + // not in closedset - i.e. have not traversed this edge destination + int dest = mGraph[current].edges[j].index; + float tentative_g = gScore[current] + mGraph[current].edges[j].cost; + bool isInOpenSet = std::find(openset.begin(), openset.end(), dest) != openset.end(); + if(!isInOpenSet + || tentative_g < gScore[dest]) + { + graphParent[dest] = current; + gScore[dest] = tentative_g; + fScore[dest] = tentative_g + costAStar(mPathgrid->mPoints[dest], + mPathgrid->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 + std::list::iterator it = openset.begin(); + for(it = openset.begin(); it!= openset.end(); it++) + { + if(fScore[*it] > fScore[dest]) + break; + } + openset.insert(it, dest); + } + } + } // if in closedset, i.e. traversed this edge already, try the next edge + } + } + + if(current != goal) + return path; // for some reason couldn't build a path + + // reconstruct path to return, using world co-ordinates + float xCell = 0; + float yCell = 0; + if (isExterior) + { + xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE; + yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE; + } + + while(graphParent[current] != -1) + { + ESM::Pathgrid::Point pt = mPathgrid->mPoints[current]; + pt.mX += xCell; + pt.mY += yCell; + path.push_front(pt); + current = graphParent[current]; + } + return path; + } +} + diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp new file mode 100644 index 000000000..9f3a997c6 --- /dev/null +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -0,0 +1,75 @@ +#ifndef GAME_MWMECHANICS_PATHGRID_H +#define GAME_MWMECHANICS_PATHGRID_H + +#include +#include + +namespace ESM +{ + class Cell; +} + +namespace MWWorld +{ + class CellStore; +} + +namespace MWMechanics +{ + class PathgridGraph + { + public: + PathgridGraph(); + + bool load(const ESM::Cell *cell); + + // returns true if end point is strongly connected (i.e. reachable + // from start point) both start and end are pathgrid point indexes + bool isPointConnected(const int start, const int end) const; + + // isOutside is used whether to convert path to world co-ordinates + std::list aStarSearch(const int start, + const int end, + const bool isOutside) const; + private: + + const ESM::Cell *mCell; + const ESM::Pathgrid *mPathgrid; + + struct ConnectedPoint // edge + { + int index; // pathgrid point index of neighbour + float cost; + }; + + struct Node // point + { + int componentId; + std::vector edges; // neighbours + }; + + // componentId is 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 (ship & office) + // all other pathgrid points are the third set + // + std::vector mGraph; + bool mIsGraphConstructed; + + // variables used to calculate connected components + int mSCCId; + int mSCCIndex; + std::vector mSCCStack; + typedef std::pair VPair; // first is index, second is lowlink + std::vector mSCCPoint; + // methods used to calculate connected components + void recursiveStrongConnect(int v); + void buildConnectedPoints(); + }; +} + +#endif diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index dd105a665..d39f22c3f 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -363,6 +363,7 @@ namespace MWWorld loadRefs (store, esm); mState = State_Loaded; + mPathgridGraph.load(mCell); } } @@ -683,14 +684,6 @@ namespace MWWorld bool CellStore::isPointConnected(const int start, const int end) const { - if(!mPathgridGraph.isGraphConstructed()) - { - // Ugh... there must be a better way... - MWMechanics::PathgridGraph *p = const_cast (&mPathgridGraph); - - if(!p->initPathgridGraph(mCell)) - return false; - } return mPathgridGraph.isPointConnected(start, end); } @@ -698,16 +691,6 @@ namespace MWWorld std::list CellStore::aStarSearch(const int start, const int end, const bool isOutside) const { - if(!mPathgridGraph.isGraphConstructed()) - { - MWMechanics::PathgridGraph *p = const_cast (&mPathgridGraph); - - if(!p->initPathgridGraph(mCell)) - { - std::list path; // empty - return path; - } - } return mPathgridGraph.aStarSearch(start, end, isOutside); } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 3cca993f2..c352779b1 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -8,7 +8,7 @@ #include "esmstore.hpp" #include "cellreflist.hpp" -#include "../mwmechanics/pathfinding.hpp" +#include "../mwmechanics/pathgrid.hpp" namespace ESM { From 5cf8e7e9334f60a10fb0ddabdc25c4a953151388 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 4 Apr 2014 06:16:26 +1100 Subject: [PATCH 049/157] Remove logging. --- apps/openmw/mwmechanics/pathgrid.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 5580e5d3f..f8b24fa1a 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -124,13 +124,6 @@ namespace MWMechanics } buildConnectedPoints(); mIsGraphConstructed = true; -#if 0 - std::cout << "loading pathgrid " << - +"\""+ mPathgrid->mCell +"\"" - +", "+ std::to_string(mPathgrid->mData.mX) - +", "+ std::to_string(mPathgrid->mData.mY) - << std::endl; -#endif return true; } @@ -211,11 +204,6 @@ namespace MWMechanics if(mSCCPoint[v].first == -1) // undefined (haven't visited) recursiveStrongConnect(v); } -#if 0 - std::cout << "components: " << std::to_string(mSCCId) - +", "+ mPathgrid->mCell - << std::endl; -#endif } bool PathgridGraph::isPointConnected(const int start, const int end) const From 70919ba60ae596bab066e34bfb860ca5a4aa539e Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 3 Apr 2014 16:13:14 -0400 Subject: [PATCH 050/157] Removed witnesses and minor changes --- apps/openmw/mwmechanics/actors.cpp | 19 +++++++++------ apps/openmw/mwmechanics/creaturestats.cpp | 24 ++----------------- apps/openmw/mwmechanics/creaturestats.hpp | 9 ------- .../mwmechanics/mechanicsmanagerimp.cpp | 11 ++------- apps/openmw/mwworld/player.cpp | 11 --------- apps/openmw/mwworld/player.hpp | 6 ----- 6 files changed, 16 insertions(+), 64 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6d9f23aa2..56f24a747 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -739,11 +739,12 @@ namespace MWMechanics { creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.setHostile(true); + creatureStats.setAlarmed(true); } } // if I was a witness to a crime - if (creatureStats.getCrimeId() != -1) + if (creatureStats.isAlarmed()) { if(player.getClass().getNpcStats(player).getBounty() == 0) { @@ -752,19 +753,23 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stopPersue(); creatureStats.getAiSequence().stopCombat(); - creatureStats.setCrimeId(-1); } - else if (creatureStats.isAlarmed()) + else if (!creatureStats.isHostile()) { - if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) + if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); - else if (!creatureStats.isHostile()) - { + else creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.setHostile(true); - } } } + + // if I didn't report a crime was I attacked? + else if (creatureStats.getAttacked() && !creatureStats.isHostile()) + { + creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.setHostile(true); + } } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 17d36be79..feed8d182 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -14,8 +14,8 @@ namespace MWMechanics CreatureStats::CreatureStats() : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), - mAttacked (false), mHostile (false), mAssaulted(false), - mAttackingOrSpell(false), mCrimeId(-1), + mAttacked (false), mHostile (false), + mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) @@ -316,26 +316,6 @@ namespace MWMechanics mHostile = hostile; } - bool CreatureStats::isAssaulted() const - { - return mAssaulted; - } - - void CreatureStats::setAssaulted (bool assaulted) - { - mAssaulted = assaulted; - } - - int CreatureStats::getCrimeId() const - { - return mCrimeId; - } - - void CreatureStats::setCrimeId (int id) - { - mCrimeId = id; - } - bool CreatureStats::getCreatureTargetted() const { std::string target; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 67afd9f25..97bcd719c 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,7 +40,6 @@ namespace MWMechanics bool mAttacked; bool mHostile; bool mAssaulted; - int mCrimeId; bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; @@ -188,14 +187,6 @@ namespace MWMechanics void setHostile (bool hostile); - bool isAssaulted() const; - - void setAssaulted (bool assaulted); - - int getCrimeId() const; - - void setCrimeId (int id); - bool getCreatureTargetted() const; float getEvasion() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 54622c0b4..9119b3ab6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -839,9 +839,8 @@ namespace MWMechanics CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); - // Was the crime seen or the victim assulted? - if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || - type == OT_Assault) + // Was the crime seen? + if ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) { // Say something! // TODO: Add more messages @@ -862,16 +861,10 @@ namespace MWMechanics // Will the witness be affected by the crime? CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) - { creatureStats1.setAlarmed(true); - creatureStats1.setCrimeId(player.getWitnessTotal()); - player.addWitness(); - } } break; // Someone saw the crime and everyone has been told } - else if (type == OT_Assault) - creatureStats.setAlarmed(true); } } if (reported) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 7c576960f..bfeca9653 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -30,7 +30,6 @@ namespace MWWorld mAutoMove(false), mForwardBackward(0), mTeleported(false), - mWitnessTotal(0), mMarkedCell(NULL) { mPlayer.mBase = player; @@ -66,16 +65,6 @@ namespace MWWorld return mSign; } - void Player::addWitness() - { - mWitnessTotal++; - } - - int Player::getWitnessTotal() const - { - return mWitnessTotal; - } - void Player::setDrawState (MWMechanics::DrawState_ state) { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 71c231481..9d3fbbeec 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -41,8 +41,6 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; bool mTeleported; - - int mWitnessTotal; public: @@ -67,10 +65,6 @@ namespace MWWorld void setBirthSign(const std::string &sign); - void addWitness(); - - int getWitnessTotal() const; - const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); From f59226265ad1b745d21747ce89b11de5a13b6d7d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 4 Apr 2014 18:10:06 +1100 Subject: [PATCH 051/157] Remove redundant parameter from aStarSearch. Also update some comments. --- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- apps/openmw/mwmechanics/pathgrid.cpp | 11 ++++++----- apps/openmw/mwmechanics/pathgrid.hpp | 4 ++-- apps/openmw/mwworld/cellstore.cpp | 6 ++---- apps/openmw/mwworld/cellstore.hpp | 3 +-- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index a4166d83b..730b8cb92 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -224,7 +224,7 @@ namespace MWMechanics // this shouldn't really happen, but just in case if(endNode.first != -1) { - mPath = mCell->aStarSearch(startNode, endNode.first, mCell->isExterior()); + mPath = mCell->aStarSearch(startNode, endNode.first); if(!mPath.empty()) { diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index f8b24fa1a..83ee86db5 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -55,6 +55,7 @@ namespace MWMechanics , mGraph(0) , mSCCId(0) , mSCCIndex(0) + , mIsExterior(0) { } @@ -100,6 +101,7 @@ namespace MWMechanics return false; mCell = cell; + mIsExterior = cell->isExterior(); mPathgrid = MWBase::Environment::get().getWorld()->getStore().get().search(*cell); if(!mPathgrid) @@ -221,11 +223,11 @@ namespace MWMechanics * * Should be possible to make this MT safe. * - * Returns path (a list of pathgrid point indexes) which may be empty. + * Returns path which may be empty. path contains pathgrid points in local + * cell co-ordinates (indoors) or world co-ordinates (external). * * Input params: * start, goal - pathgrid point indexes (for this cell) - * isExterior - used to determine whether to convert to world co-ordinates * * Variables: * openset - point indexes to be traversed, lowest cost at the front @@ -239,8 +241,7 @@ namespace MWMechanics * co-ordinates). Essentially trading speed w/ memory. */ std::list PathgridGraph::aStarSearch(const int start, - const int goal, - bool isExterior) const + const int goal) const { std::list path; if(!isPointConnected(start, goal)) @@ -316,7 +317,7 @@ namespace MWMechanics // reconstruct path to return, using world co-ordinates float xCell = 0; float yCell = 0; - if (isExterior) + if (mIsExterior) { xCell = mPathgrid->mData.mX * ESM::Land::REAL_SIZE; yCell = mPathgrid->mData.mY * ESM::Land::REAL_SIZE; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 9f3a997c6..372f6bd2d 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -29,12 +29,12 @@ namespace MWMechanics // isOutside is used whether to convert path to world co-ordinates std::list aStarSearch(const int start, - const int end, - const bool isOutside) const; + const int end) const; private: const ESM::Cell *mCell; const ESM::Pathgrid *mPathgrid; + bool mIsExterior; struct ConnectedPoint // edge { diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index d39f22c3f..6bc7657e4 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -685,12 +685,10 @@ namespace MWWorld bool CellStore::isPointConnected(const int start, const int end) const { return mPathgridGraph.isPointConnected(start, end); - } - std::list CellStore::aStarSearch(const int start, const int end, - const bool isOutside) const + std::list CellStore::aStarSearch(const int start, const int end) const { - return mPathgridGraph.aStarSearch(start, end, isOutside); + return mPathgridGraph.aStarSearch(start, end); } } diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index c352779b1..b970afe1b 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -145,8 +145,7 @@ namespace MWWorld bool isPointConnected(const int start, const int end) const; - std::list aStarSearch(const int start, const int end, - const bool isOutside) const; + std::list aStarSearch(const int start, const int end) const; private: From 28f7c42fb77536f84e9875e8e4bc5284d75f6aa3 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 4 Apr 2014 18:17:42 +1100 Subject: [PATCH 052/157] One more comment fix. --- apps/openmw/mwmechanics/pathgrid.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index 372f6bd2d..ac545efbc 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -27,7 +27,9 @@ namespace MWMechanics // from start point) both start and end are pathgrid point indexes bool isPointConnected(const int start, const int end) const; - // isOutside is used whether to convert path to world co-ordinates + // the input parameters are pathgrid point indexes + // the output list is in local (internal cells) or world (external + // cells) co-ordinates std::list aStarSearch(const int start, const int end) const; private: From df5cbe5dec98b1c14cd4aa8916d692b40c5b18f1 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Fri, 4 Apr 2014 08:10:35 -0400 Subject: [PATCH 053/157] Minor changes --- apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 3 ++- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 56f24a747..fcec5a3fb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -750,6 +750,7 @@ namespace MWMechanics { creatureStats.setAlarmed(false); creatureStats.setHostile(false); + creatureStats.setAttacked(false); if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stopPersue(); creatureStats.getAiSequence().stopCombat(); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index feed8d182..f81613ed1 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -435,8 +435,9 @@ namespace MWMechanics return getMovementFlag (Flag_Run) || getMovementFlag (Flag_ForceRun); case Stance_Sneak: return getMovementFlag (Flag_Sneak) || getMovementFlag (Flag_ForceSneak); + default: + return false; } - return false; // shut up, compiler } DrawState_ CreatureStats::getDrawState() const diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 9119b3ab6..8d546b598 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -840,7 +840,8 @@ namespace MWMechanics CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); // Was the crime seen? - if ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) + if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || + type == OT_Assault ) { // Say something! // TODO: Add more messages @@ -860,7 +861,8 @@ namespace MWMechanics // Will the witness be affected by the crime? CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); - if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || + type == OT_Assault) creatureStats1.setAlarmed(true); } break; // Someone saw the crime and everyone has been told From 0fe67b586a6c8156eb598887550b995dce8c7304 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 5 Apr 2014 13:16:13 +0200 Subject: [PATCH 054/157] increased scene toolbar button size --- apps/opencs/view/world/previewsubview.cpp | 2 +- apps/opencs/view/world/scenesubview.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/previewsubview.cpp b/apps/opencs/view/world/previewsubview.cpp index ac9776d22..e9a30e65d 100644 --- a/apps/opencs/view/world/previewsubview.cpp +++ b/apps/opencs/view/world/previewsubview.cpp @@ -28,7 +28,7 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo else mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), this); - SceneToolbar *toolbar = new SceneToolbar (48, this); + SceneToolbar *toolbar = new SceneToolbar (48+6, this); SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index dedaf014b..d62793cc9 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -35,7 +35,7 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout2->setContentsMargins (QMargins (0, 0, 0, 0)); - SceneToolbar *toolbar = new SceneToolbar (48, this); + SceneToolbar *toolbar = new SceneToolbar (48+6, this); SceneToolGrid *gridTool = 0; From 940c88d2ecd6b25a3919596eff3a60b7a7d2d0fe Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 5 Apr 2014 10:26:14 -0400 Subject: [PATCH 055/157] Cleaned up code, implemented crime ids There is a problem with my game freezing. ToggleAi stops my character --- apps/openmw/mwmechanics/actors.cpp | 57 +++++++++++-------- apps/openmw/mwmechanics/creaturestats.hpp | 4 -- .../mwmechanics/mechanicsmanagerimp.cpp | 35 ++++++------ apps/openmw/mwmechanics/npcstats.cpp | 11 ++++ apps/openmw/mwmechanics/npcstats.hpp | 6 +- apps/openmw/mwscript/miscextensions.cpp | 4 ++ apps/openmw/mwworld/player.cpp | 19 ++++++- apps/openmw/mwworld/player.hpp | 12 ++-- 8 files changed, 95 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index fcec5a3fb..ee30dae6d 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -31,8 +31,6 @@ #include "aifollow.hpp" #include "aipersue.hpp" -#include "../mwbase/dialoguemanager.hpp" //------------------------ - namespace { @@ -718,49 +716,58 @@ namespace MWMechanics void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); - - /// \todo Move me! I shouldn't be here... - const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * - float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * - esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); - - if (ptr != player) + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (ptr != playerPtr) { + // get stats of witness + CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr); + MWWorld::Player player = MWBase::Environment::get().getWorld()->getPlayer(); + // If I'm a guard and I'm not hostile if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) { + /// \todo Move me! I shouldn't be here... + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + float cutoff = float(esmStore.get().find("iCrimeThreshold")->getInt()) * + float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * + esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); // Attack on sight if bounty is greater than the cutoff - if ( player.getClass().getNpcStats(player).getBounty() >= cutoff - && MWBase::Environment::get().getWorld()->getLOS(ptr, player) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) + if ( playerPtr.getClass().getNpcStats(playerPtr).getBounty() >= cutoff + && MWBase::Environment::get().getWorld()->getLOS(ptr, playerPtr) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(playerPtr, ptr)) { - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(playerPtr)); creatureStats.setHostile(true); - creatureStats.setAlarmed(true); + npcStats.setCrimeId(player.getNewCrimeId()); } } // if I was a witness to a crime - if (creatureStats.isAlarmed()) + if (npcStats.getCrimeId() != -1) { - if(player.getClass().getNpcStats(player).getBounty() == 0) + // if you've payed for your crimes and I havent noticed + if(npcStats.getCrimeId() < player.getCrimeId() ) { - creatureStats.setAlarmed(false); - creatureStats.setHostile(false); - creatureStats.setAttacked(false); + // Calm witness down if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stopPersue(); creatureStats.getAiSequence().stopCombat(); + + // Reset factors to attack + // TODO: Not a complete list, disposition changes? + creatureStats.setHostile(false); + creatureStats.setAttacked(false); + + // Update witness crime id + npcStats.setCrimeId(-1); } else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); + creatureStats.getAiSequence().stack(AiPersue(playerPtr.getClass().getId(playerPtr))); else - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(playerPtr)); creatureStats.setHostile(true); } } @@ -768,7 +775,7 @@ namespace MWMechanics // if I didn't report a crime was I attacked? else if (creatureStats.getAttacked() && !creatureStats.isHostile()) { - creatureStats.getAiSequence().stack(AiCombat(player)); + creatureStats.getAiSequence().stack(AiCombat(playerPtr)); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 97bcd719c..5dc59e5ab 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -39,7 +39,6 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; - bool mAssaulted; bool mAttackingOrSpell; bool mKnockdown; bool mHitRecovery; @@ -176,15 +175,12 @@ namespace MWMechanics void talkedToPlayer(); bool isAlarmed() const; - void setAlarmed (bool alarmed); bool getAttacked() const; - void setAttacked (bool attacked); bool isHostile() const; - void setHostile (bool hostile); bool getCreatureTargetted() const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 8d546b598..a26520fc7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -828,42 +828,43 @@ namespace MWMechanics // Innocent until proven guilty bool reported = false; - // Find all the NPC's close enough, ie. within fAlarmRadius of the player + // Find all the NPCs within the alarm radius std::vector neighbors; mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), - esmStore.get().find("fAlarmRadius")->getInt(), neighbors); + esmStore.get().find("fAlarmRadius")->getInt(), neighbors); + + // Find an actor who witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - if (*it == ptr) // Not the player - continue; - - CreatureStats& creatureStats = MWWorld::Class::get(*it).getCreatureStats(*it); + if (*it == ptr) continue; // not the player // Was the crime seen? if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || type == OT_Assault ) { - // Say something! // TODO: Add more messages if (type == OT_Theft) - MWBase::Environment::get().getDialogueManager()->say(*it, "Thief"); + MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); + else if (type == OT_Assault) + MWBase::Environment::get().getDialogueManager()->say(*it, "attack"); // Will the witness report the crime? - if (creatureStats.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) + if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; + int id = player.getNewCrimeId(); - // Tell everyone else + // Tell everyone, including yourself for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) { - if (*it1 == ptr) // Not the player - continue; + if (*it1 == ptr) continue; // not the player - // Will the witness be affected by the crime? - CreatureStats& creatureStats1 = MWWorld::Class::get(*it1).getCreatureStats(*it1); - if (creatureStats1.getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || - type == OT_Assault) - creatureStats1.setAlarmed(true); + // Will other witnesses paticipate in crime + if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm + || type == OT_Assault ) + { + it1->getClass().getNpcStats(*it1).setCrimeId(id); + } } break; // Someone saw the crime and everyone has been told } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 74587a626..4f014102d 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -29,6 +29,7 @@ MWMechanics::NpcStats::NpcStats() , mLevelProgress(0) , mDisposition(0) , mReputation(0) +, mCrimeId(-1) , mWerewolfKills (0) , mProfit(0) , mTimeToStartDrowning(20.0) @@ -340,6 +341,16 @@ void MWMechanics::NpcStats::setReputation(int reputation) mReputation = reputation; } +int MWMechanics::NpcStats::getCrimeId() const +{ + return mCrimeId; +} + +void MWMechanics::NpcStats::setCrimeId(int id) +{ + mCrimeId = id; +} + bool MWMechanics::NpcStats::hasSkillsForRank (const std::string& factionId, int rank) const { if (rank<0 || rank>=10) diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index ad493be3c..0ae596a54 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -37,6 +37,7 @@ namespace MWMechanics std::set mExpelled; std::map mFactionReputation; int mReputation; + int mCrimeId; int mWerewolfKills; int mProfit; float mAttackStrength; @@ -63,13 +64,14 @@ namespace MWMechanics void modifyProfit(int diff); int getBaseDisposition() const; - void setBaseDisposition(int disposition); int getReputation() const; - void setReputation(int reputation); + int getCrimeId() const; + void setCrimeId(int id); + const SkillValue& getSkill (int index) const; SkillValue& getSkill (int index); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a7f31c80b..aa554adc3 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -21,6 +21,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/esmstore.hpp" @@ -802,6 +803,7 @@ namespace MWScript { MWBase::World* world = MWBase::Environment::get().getWorld(); world->goToJail(); + MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; @@ -812,6 +814,7 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); + MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; @@ -823,6 +826,7 @@ namespace MWScript MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); MWBase::Environment::get().getWorld()->confiscateStolenItems(player); + MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index bfeca9653..d39ec2c0f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -30,7 +30,9 @@ namespace MWWorld mAutoMove(false), mForwardBackward(0), mTeleported(false), - mMarkedCell(NULL) + mMarkedCell(NULL), + mCurrentCrimeId(-1), + mPayedCrimeId(-1) { mPlayer.mBase = player; mPlayer.mRef.mRefID = "player"; @@ -274,4 +276,19 @@ namespace MWWorld return false; } + + int Player::getNewCrimeId() + { + return mCurrentCrimeId++; + } + + void Player::recordCrimeId() + { + mPayedCrimeId = mCurrentCrimeId; + } + + int Player::getCrimeId() const + { + return mCurrentCrimeId; + } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 9d3fbbeec..7dbaaddb4 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -41,6 +41,9 @@ namespace MWWorld bool mAutoMove; int mForwardBackward; bool mTeleported; + + int mCurrentCrimeId; // the id assigned witnesses + int mPayedCrimeId; // the last id payed off (0 bounty) public: @@ -64,15 +67,12 @@ namespace MWWorld MWWorld::Ptr getPlayer(); void setBirthSign(const std::string &sign); - const std::string &getBirthSign() const; void setDrawState (MWMechanics::DrawState_ state); - - bool getAutoMove() const; - MWMechanics::DrawState_ getDrawState(); /// \todo constness + bool getAutoMove() const; void setAutoMove (bool enable); void setLeftRight (int value); @@ -95,6 +95,10 @@ namespace MWWorld void write (ESM::ESMWriter& writer) const; bool readRecord (ESM::ESMReader& reader, int32_t type); + + int getNewCrimeId(); // get new id for witnesses + void recordCrimeId(); // record the payed crime id when bounty is 0 + int getCrimeId() const; // get the last payed crime id }; } #endif From a274b48f2f8c136f03a43d2ba62c5771277f52ef Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 5 Apr 2014 22:45:40 -0400 Subject: [PATCH 056/157] States are saved. Crime is reacted to. Issues where some crime is ignored. Needs a lot more work --- apps/openmw/mwmechanics/actors.cpp | 23 +++++++++---------- .../mwmechanics/mechanicsmanagerimp.cpp | 14 +++++------ apps/openmw/mwworld/player.cpp | 4 ++-- components/esm/npcstats.cpp | 6 +++++ components/esm/npcstats.hpp | 1 + components/esm/player.cpp | 6 +++++ components/esm/player.hpp | 3 +++ 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ee30dae6d..ca37b152c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -716,13 +716,12 @@ namespace MWMechanics void Actors::updateCrimePersuit(const MWWorld::Ptr& ptr, float duration) { - MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); - if (ptr != playerPtr) + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + if (ptr != player && ptr.getClass().isNpc()) { // get stats of witness CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr); - MWWorld::Player player = MWBase::Environment::get().getWorld()->getPlayer(); // If I'm a guard and I'm not hostile if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile()) @@ -733,13 +732,13 @@ namespace MWMechanics float(esmStore.get().find("iCrimeThresholdMultiplier")->getInt()) * esmStore.get().find("fCrimeGoldDiscountMult")->getFloat(); // Attack on sight if bounty is greater than the cutoff - if ( playerPtr.getClass().getNpcStats(playerPtr).getBounty() >= cutoff - && MWBase::Environment::get().getWorld()->getLOS(ptr, playerPtr) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(playerPtr, ptr)) + if ( player.getClass().getNpcStats(player).getBounty() >= cutoff + && MWBase::Environment::get().getWorld()->getLOS(ptr, player) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiCombat(playerPtr)); + creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.setHostile(true); - npcStats.setCrimeId(player.getNewCrimeId()); + npcStats.setCrimeId( MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ); } } @@ -747,7 +746,7 @@ namespace MWMechanics if (npcStats.getCrimeId() != -1) { // if you've payed for your crimes and I havent noticed - if(npcStats.getCrimeId() < player.getCrimeId() ) + if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ) { // Calm witness down if (ptr.getClass().isClass(ptr, "Guard")) @@ -765,9 +764,9 @@ namespace MWMechanics else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPersue(playerPtr.getClass().getId(playerPtr))); + creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); else - creatureStats.getAiSequence().stack(AiCombat(playerPtr)); + creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.setHostile(true); } } @@ -775,7 +774,7 @@ namespace MWMechanics // if I didn't report a crime was I attacked? else if (creatureStats.getAttacked() && !creatureStats.isHostile()) { - creatureStats.getAiSequence().stack(AiCombat(playerPtr)); + creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.setHostile(true); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a26520fc7..d162e1037 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -810,7 +810,6 @@ namespace MWMechanics return false; const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - MWWorld::Player player = MWBase::Environment::get().getWorld()->getPlayer(); // What amount of alarm did this crime generate? int alarm; @@ -842,22 +841,23 @@ namespace MWMechanics if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || type == OT_Assault ) { - // TODO: Add more messages - if (type == OT_Theft) - MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); - else if (type == OT_Assault) - MWBase::Environment::get().getDialogueManager()->say(*it, "attack"); // Will the witness report the crime? if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; - int id = player.getNewCrimeId(); + int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); // Tell everyone, including yourself for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) { if (*it1 == ptr) continue; // not the player + + // TODO: Add more messages + if (type == OT_Theft) + MWBase::Environment::get().getDialogueManager()->say(*it1, "thief"); + else if (type == OT_Assault) + MWBase::Environment::get().getDialogueManager()->say(*it1, "attack"); // Will other witnesses paticipate in crime if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index d39ec2c0f..e8179b9f3 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -279,7 +279,7 @@ namespace MWWorld int Player::getNewCrimeId() { - return mCurrentCrimeId++; + return ++mCurrentCrimeId; } void Player::recordCrimeId() @@ -289,6 +289,6 @@ namespace MWWorld int Player::getCrimeId() const { - return mCurrentCrimeId; + return mPayedCrimeId; } } diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 531424ab2..80238ad68 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -67,6 +67,9 @@ void ESM::NpcStats::load (ESMReader &esm) mLevelHealthBonus = 0; esm.getHNOT (mLevelHealthBonus, "LVLH"); + + mCrimeId = -1; + esm.getHNOT (mCrimeId, "CRID"); } void ESM::NpcStats::save (ESMWriter &esm) const @@ -130,4 +133,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const if (mLevelHealthBonus) esm.writeHNT ("LVLH", mLevelHealthBonus); + + if (mCrimeId != -1) + esm.writeHNT ("CRID", mCrimeId); } \ No newline at end of file diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index b3f70db25..504cd0163 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -45,6 +45,7 @@ namespace ESM float mTimeToStartDrowning; float mLastDrowningHit; float mLevelHealthBonus; + int mCrimeId; void load (ESMReader &esm); void save (ESMWriter &esm) const; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index d5ddc74d0..e41cc535e 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -25,6 +25,9 @@ void ESM::Player::load (ESMReader &esm) esm.getHNOT (mAutoMove, "AMOV"); mBirthsign = esm.getHNString ("SIGN"); + + esm.getHNT (mCurrentCrimeId, "CURD"); + esm.getHNT (mPayedCrimeId, "PAYD"); } void ESM::Player::save (ESMWriter &esm) const @@ -45,4 +48,7 @@ void ESM::Player::save (ESMWriter &esm) const esm.writeHNT ("AMOV", mAutoMove); esm.writeHNString ("SIGN", mBirthsign); + + esm.writeHNT ("CURD", mCurrentCrimeId); + esm.writeHNT ("PAYD", mPayedCrimeId); } \ No newline at end of file diff --git a/components/esm/player.hpp b/components/esm/player.hpp index 0d70ee090..377c8547a 100644 --- a/components/esm/player.hpp +++ b/components/esm/player.hpp @@ -24,6 +24,9 @@ namespace ESM CellId mMarkedCell; unsigned char mAutoMove; std::string mBirthsign; + + int mCurrentCrimeId; + int mPayedCrimeId; void load (ESMReader &esm); void save (ESMWriter &esm) const; From ce7aa963717122a1de7d055d06be41e1365ab1d7 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 6 Apr 2014 22:21:28 +0200 Subject: [PATCH 057/157] Fix visual glitch happening when closing inventory while sneaking (bug #1255) --- apps/openmw/mwrender/renderingmanager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fa7b17a7c..97283d065 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -364,10 +364,9 @@ void RenderingManager::update (float duration, bool paused) static const int i1stPersonSneakDelta = MWBase::Environment::get().getWorld()->getStore().get() .find("i1stPersonSneakDelta")->getInt(); - if(isSneaking && !(isSwimming || isInAir)) + if(!paused && isSneaking && !(isSwimming || isInAir)) mCamera->setSneakOffset(i1stPersonSneakDelta); - mOcclusionQuery->update(duration); mRendering.update(duration); From 03b3487f1bbb3114e00999e18cc329bbaf961af8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Apr 2014 09:56:36 +0200 Subject: [PATCH 058/157] minor cleanup --- apps/openmw/mwmechanics/pathgrid.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index 83ee86db5..ec647c1cb 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -250,12 +250,9 @@ namespace MWMechanics } int graphSize = mGraph.size(); - std::vector gScore; - gScore.resize(graphSize, -1); - std::vector fScore; - fScore.resize(graphSize, -1); - std::vector graphParent; - graphParent.resize(graphSize, -1); + std::vector gScore (graphSize, -1); + std::vector fScore (graphSize, -1); + std::vector graphParent (graphSize, -1); // gScore & fScore keep costs for each pathgrid point in mPoints gScore[start] = 0; From 324b2743d447bb3d6f2a18dae780ea3783857070 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Apr 2014 10:21:26 +0200 Subject: [PATCH 059/157] removed grid button (discarding the first attempt at a cell selector) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/scenesubview.cpp | 13 ---- apps/opencs/view/world/scenetoolgrid.cpp | 75 ------------------------ apps/opencs/view/world/scenetoolgrid.hpp | 29 --------- 4 files changed, 1 insertion(+), 118 deletions(-) delete mode 100644 apps/opencs/view/world/scenetoolgrid.cpp delete mode 100644 apps/opencs/view/world/scenetoolgrid.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 05cc93f89..a7a694463 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 dialoguesubview previewsubview scenetoolgrid + scenetoolmode infocreator scriptedit dialoguesubview previewsubview ) opencs_units (view/render diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index d62793cc9..4ebaa9352 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -18,7 +18,6 @@ #include "creator.hpp" #include "scenetoolbar.hpp" #include "scenetoolmode.hpp" -#include "scenetoolgrid.hpp" CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id) @@ -37,8 +36,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolbar *toolbar = new SceneToolbar (48+6, this); - SceneToolGrid *gridTool = 0; - if (id.getId()=="sys::default") { CSVRender::PagedWorldspaceWidget *widget = new CSVRender::PagedWorldspaceWidget (this); @@ -47,13 +44,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), this, SLOT (cellIndexChanged (const std::pair&, const std::pair&))); - - gridTool = new SceneToolGrid (toolbar); - - connect (widget, - SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), - gridTool, - SLOT (cellIndexChanged (const std::pair&, const std::pair&))); } else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); @@ -64,9 +54,6 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); toolbar->addTool (lightingTool); - if (gridTool) - toolbar->addTool (gridTool); - layout2->addWidget (toolbar, 0); layout2->addWidget (mScene, 1); diff --git a/apps/opencs/view/world/scenetoolgrid.cpp b/apps/opencs/view/world/scenetoolgrid.cpp deleted file mode 100644 index 0769be168..000000000 --- a/apps/opencs/view/world/scenetoolgrid.cpp +++ /dev/null @@ -1,75 +0,0 @@ - -#include "scenetoolgrid.hpp" - -#include - -#include -#include -#include - -#include "scenetoolbar.hpp" - -CSVWorld::SceneToolGrid::SceneToolGrid (SceneToolbar *parent) -: SceneTool (parent), mIconSize (parent->getIconSize()) -{ -} - -void CSVWorld::SceneToolGrid::showPanel (const QPoint& position) -{ - - -} - -void CSVWorld::SceneToolGrid::cellIndexChanged (const std::pair& min, - const std::pair& max) -{ - /// \todo make font size configurable - const int fontSize = 8; - - /// \todo replace with proper icon - QPixmap image (mIconSize, mIconSize); - image.fill (QColor (0, 0, 0, 0)); - - { - QPainter painter (&image); - painter.setPen (Qt::black); - QFont font (QApplication::font().family(), fontSize); - painter.setFont (font); - - QFontMetrics metrics (font); - - if (min==max) - { - // single cell - std::ostringstream stream; - stream << min.first << ", " << min.second; - - QString text = QString::fromUtf8 (stream.str().c_str()); - - painter.drawText (QPoint ((mIconSize-metrics.width (text))/2, mIconSize/2+fontSize/2), - text); - } - else - { - // range - { - std::ostringstream stream; - stream << min.first << ", " << min.second; - painter.drawText (QPoint (0, mIconSize), - QString::fromUtf8 (stream.str().c_str())); - } - - { - std::ostringstream stream; - stream << max.first << ", " << max.second; - - QString text = QString::fromUtf8 (stream.str().c_str()); - - painter.drawText (QPoint (mIconSize-metrics.width (text), fontSize), text); - } - } - } - - QIcon icon (image); - setIcon (icon); -} \ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolgrid.hpp b/apps/opencs/view/world/scenetoolgrid.hpp deleted file mode 100644 index 917df2a16..000000000 --- a/apps/opencs/view/world/scenetoolgrid.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CSV_WORLD_SCENETOOL_GRID_H -#define CSV_WORLD_SCENETOOL_GRID_H - -#include "scenetool.hpp" - -namespace CSVWorld -{ - class SceneToolbar; - - ///< \brief Cell grid selector tool - class SceneToolGrid : public SceneTool - { - Q_OBJECT - - int mIconSize; - - public: - - SceneToolGrid (SceneToolbar *parent); - - virtual void showPanel (const QPoint& position); - - public slots: - - void cellIndexChanged (const std::pair& min, const std::pair& max); - }; -} - -#endif From 67965ec10cc211469308c443363ea28e25fdf304 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Apr 2014 13:42:00 +0200 Subject: [PATCH 060/157] added CellCoordinates and CellSelection classes --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/cellcoordinates.cpp | 60 +++++++++++++++ apps/opencs/model/world/cellcoordinates.hpp | 42 +++++++++++ apps/opencs/model/world/cellselection.cpp | 83 +++++++++++++++++++++ apps/opencs/model/world/cellselection.hpp | 57 ++++++++++++++ 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/world/cellcoordinates.cpp create mode 100644 apps/opencs/model/world/cellcoordinates.hpp create mode 100644 apps/opencs/model/world/cellselection.cpp create mode 100644 apps/opencs/model/world/cellselection.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a7a694463..d9e271498 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -24,7 +24,7 @@ opencs_units (model/world opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection - refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection ) opencs_hdrs_noqt (model/world diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp new file mode 100644 index 000000000..b1c8441e6 --- /dev/null +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -0,0 +1,60 @@ + +#include "cellcoordinates.hpp" + +#include +#include + +CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {} + +CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} + +int CSMWorld::CellCoordinates::getX() const +{ + return mX; +} + +int CSMWorld::CellCoordinates::getY() const +{ + return mY; +} + +CSMWorld::CellCoordinates CSMWorld::CellCoordinates::move (int x, int y) const +{ + return CellCoordinates (mX + x, mY + y); +} + +std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) const +{ + // we ignore the worldspace for now, since there is only one (will change in 1.1) + std::ostringstream stream; + + stream << "#" << mX << " " << mY; + + return stream.str(); +} + +bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right) +{ + return left.getX()==right.getX() && left.getY()==right.getY(); +} + +bool CSMWorld::operator!= (const CellCoordinates& left, const CellCoordinates& right) +{ + return !(left==right); +} + +bool CSMWorld::operator< (const CellCoordinates& left, const CellCoordinates& right) +{ + if (left.getX()right.getX()) + return false; + + return left.getY() +#include + +#include + +namespace CSMWorld +{ + class CellCoordinates + { + int mX; + int mY; + + public: + + CellCoordinates(); + + CellCoordinates (int x, int y); + + int getX() const; + + int getY() const; + + CellCoordinates move (int x, int y) const; + ///< Return a copy of *this, moved by the given offset. + + std::string getId (const std::string& worldspace) const; + ///< Return the ID for the cell at these coordinates. + }; + + bool operator== (const CellCoordinates& left, const CellCoordinates& right); + bool operator!= (const CellCoordinates& left, const CellCoordinates& right); + bool operator< (const CellCoordinates& left, const CellCoordinates& right); + + std::ostream& operator<< (std::ostream& stream, const CellCoordinates& coordiantes); +} + +Q_DECLARE_METATYPE (CSMWorld::CellCoordinates) + +#endif diff --git a/apps/opencs/model/world/cellselection.cpp b/apps/opencs/model/world/cellselection.cpp new file mode 100644 index 000000000..73b5196f1 --- /dev/null +++ b/apps/opencs/model/world/cellselection.cpp @@ -0,0 +1,83 @@ + +#include "cellselection.hpp" + +#include +#include +#include + +CSMWorld::CellSelection::Iterator CSMWorld::CellSelection::begin() const +{ + return mCells.begin(); +} + +CSMWorld::CellSelection::Iterator CSMWorld::CellSelection::end() const +{ + return mCells.end(); +} + +bool CSMWorld::CellSelection::add (const CellCoordinates& coordinates) +{ + return mCells.insert (coordinates).second; +} + +void CSMWorld::CellSelection::remove (const CellCoordinates& coordinates) +{ + mCells.erase (coordinates); +} + +bool CSMWorld::CellSelection::has (const CellCoordinates& coordinates) const +{ + return mCells.find (coordinates)!=end(); +} + +int CSMWorld::CellSelection::getSize() const +{ + return mCells.size(); +} + +CSMWorld::CellCoordinates CSMWorld::CellSelection::getCentre() const +{ + if (mCells.empty()) + throw std::logic_error ("call of getCentre on empty cell selection"); + + double x = 0; + double y = 0; + + for (Iterator iter = begin(); iter!=end(); ++iter) + { + x += iter->getX(); + y += iter->getY(); + } + + x /= mCells.size(); + y /= mCells.size(); + + Iterator closest = begin(); + double distance = std::numeric_limits::max(); + + for (Iterator iter (begin()); iter!=end(); ++iter) + { + double deltaX = x - iter->getX(); + double deltaY = y - iter->getY(); + + double delta = std::sqrt (deltaX * deltaX + deltaY * deltaY); + + if (deltamove (x, y)); + + mCells.swap (moved); +} diff --git a/apps/opencs/model/world/cellselection.hpp b/apps/opencs/model/world/cellselection.hpp new file mode 100644 index 000000000..042416a33 --- /dev/null +++ b/apps/opencs/model/world/cellselection.hpp @@ -0,0 +1,57 @@ +#ifndef CSM_WOLRD_CELLSELECTION_H +#define CSM_WOLRD_CELLSELECTION_H + +#include + +#include + +#include "cellcoordinates.hpp" + +namespace CSMWorld +{ + /// \brief Selection of cells in a paged worldspace + /// + /// \note The CellSelection does not specify the worldspace it applies to. + class CellSelection + { + public: + + typedef std::set Container; + typedef Container::const_iterator Iterator; + + private: + + Container mCells; + + public: + + Iterator begin() const; + + Iterator end() const; + + bool add (const CellCoordinates& coordinates); + ///< Ignored if the cell specified by \a coordinates is already part of the selection. + /// + /// \return Was a cell added to the collection? + + void remove (const CellCoordinates& coordinates); + ///< ignored if the cell specified by \a coordinates is not part of the selection. + + bool has (const CellCoordinates& coordinates) const; + ///< \return Is the cell specified by \a coordinates part of the selection? + + int getSize() const; + ///< Return number of cells. + + CellCoordinates getCentre() const; + ///< Return the selected cell that is closest to the geometric centre of the selection. + /// + /// \attention This function must not be called on selections that are empty. + + void move (int x, int y); + }; +} + +Q_DECLARE_METATYPE (CSMWorld::CellSelection) + +#endif From 0d352cb8832863445b7ad464610f8351c2588323 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Apr 2014 14:16:02 +0200 Subject: [PATCH 061/157] replaced rectangular cell selection with a CellSelection object --- .../view/render/pagedworldspacewidget.cpp | 21 ++++++------ .../view/render/pagedworldspacewidget.hpp | 9 +++--- apps/opencs/view/world/scenesubview.cpp | 32 +++++++++++++------ apps/opencs/view/world/scenesubview.hpp | 7 +++- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 96d44543e..ab02d63ee 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -4,36 +4,37 @@ #include CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent) -: WorldspaceWidget (parent), mMin (std::make_pair (0, 0)), mMax (std::make_pair (-1, -1)) +: WorldspaceWidget (parent) {} void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) { if (!hint.empty()) { + CSMWorld::CellSelection selection; + if (hint[0]=='c') { char ignore1, ignore2, ignore3; - std::pair cellIndex; + int x, y; std::istringstream stream (hint.c_str()); - if (stream >> ignore1 >> ignore2 >> ignore3 >> cellIndex.first >> cellIndex.second) + if (stream >> ignore1 >> ignore2 >> ignore3 >> x >> y) { - setCellIndex (cellIndex, cellIndex); + selection.add (CSMWorld::CellCoordinates (x, y)); /// \todo adjust camera position } } /// \todo implement 'r' type hints + + setCellSelection (selection); } } -void CSVRender::PagedWorldspaceWidget::setCellIndex (const std::pair& min, - const std::pair& max) +void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection) { - mMin = min; - mMax = max; - - emit cellIndexChanged (mMin, mMax); + mSelection = selection; + emit cellSelectionChanged (mSelection); } \ No newline at end of file diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 9a4b79c3a..f6ff32dc1 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -1,6 +1,8 @@ #ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H +#include "../../model/world/cellselection.hpp" + #include "worldspacewidget.hpp" namespace CSVRender @@ -9,8 +11,7 @@ namespace CSVRender { Q_OBJECT - std::pair mMin; - std::pair mMax; + CSMWorld::CellSelection mSelection; public: @@ -21,11 +22,11 @@ namespace CSVRender virtual void useViewHint (const std::string& hint); - void setCellIndex (const std::pair& min, const std::pair& max); + void setCellSelection (const CSMWorld::CellSelection& selection); signals: - void cellIndexChanged (const std::pair& min, const std::pair& max); + void cellSelectionChanged (const CSMWorld::CellSelection& selection); }; } diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 4ebaa9352..0bb11ce8c 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,6 +9,8 @@ #include "../../model/doc/document.hpp" +#include "../../model/world/cellselection.hpp" + #include "../filter/filterbox.hpp" #include "../render/pagedworldspacewidget.hpp" @@ -40,10 +42,8 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D { CSVRender::PagedWorldspaceWidget *widget = new CSVRender::PagedWorldspaceWidget (this); mScene = widget; - connect (widget, - SIGNAL (cellIndexChanged (const std::pair&, const std::pair&)), - this, - SLOT (cellIndexChanged (const std::pair&, const std::pair&))); + connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)), + this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&))); } else mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); @@ -102,16 +102,28 @@ void CSVWorld::SceneSubView::closeRequest() deleteLater(); } -void CSVWorld::SceneSubView::cellIndexChanged (const std::pair& min, - const std::pair& max) +void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection) { + int size = selection.getSize(); + std::ostringstream stream; - stream << "Scene: " << getUniversalId().getId() << " (" << min.first << ", " << min.second; + stream << "Scene: " << getUniversalId().getId(); - if (min!=max) - stream << " to " << max.first << ", " << max.second; + if (size==0) + stream << " (empty)"; + else if (size==1) + { + stream << " (" << *selection.begin() << ")"; + } + else + { + stream << " (" << selection.getCentre() << " and " << size-1 << " more "; - stream << ")"; + if (size>1) + stream << "cells around it)"; + else + stream << "cell around it)"; + } setWindowTitle (QString::fromUtf8 (stream.str().c_str())); } \ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp index ee5b7b41f..0b15ea541 100644 --- a/apps/opencs/view/world/scenesubview.hpp +++ b/apps/opencs/view/world/scenesubview.hpp @@ -5,6 +5,11 @@ class QModelIndex; +namespace CSMWorld +{ + class CellSelection; +} + namespace CSMDoc { class Document; @@ -44,7 +49,7 @@ namespace CSVWorld void closeRequest(); - void cellIndexChanged (const std::pair& min, const std::pair& max); + void cellSelectionChanged (const CSMWorld::CellSelection& selection); }; } From e0550ba336e7d0bd0b884ceb3facbc51b61d51c9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Apr 2014 15:23:14 +0200 Subject: [PATCH 062/157] allow multiple cell coordinates in c-type hint for scene subviews --- .../view/render/pagedworldspacewidget.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index ab02d63ee..0f23dfe7b 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -15,19 +15,26 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) if (hint[0]=='c') { - char ignore1, ignore2, ignore3; - int x, y; + // syntax: c:#x1 y1; #x2 y2 (number of coordinate pairs can be 0 or larger) + char ignore; std::istringstream stream (hint.c_str()); - if (stream >> ignore1 >> ignore2 >> ignore3 >> x >> y) + if (stream >> ignore) { - selection.add (CSMWorld::CellCoordinates (x, y)); + char ignore1; // : or ; + char ignore2; // # + int x, y; + + while (stream >> ignore1 >> ignore2 >> x >> y) + selection.add (CSMWorld::CellCoordinates (x, y)); /// \todo adjust camera position } } - - /// \todo implement 'r' type hints + else if (hint[0]=='r') + { + /// \todo implement 'r' type hints + } setCellSelection (selection); } From 09bd0324c92b7c6125e291d1bbaf6cba601db2e3 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 8 Apr 2014 20:19:09 +0200 Subject: [PATCH 063/157] Fail properly when a content file is not found --- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 594a9f7f4..f808856c5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2071,6 +2071,12 @@ namespace MWWorld { contentLoader.load(col.getPath(*it), idx); } + else + { + std::stringstream msg; + msg << "Failed loading " << *it << ": the content file does not exist"; + throw std::runtime_error(msg.str()); + } } } From 0516d95253a1baecff5136f78a93124c1f778b90 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Apr 2014 22:12:09 +0200 Subject: [PATCH 064/157] added context menu with selection functions to region map --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/regionmap.cpp | 11 ++ apps/opencs/model/world/regionmap.hpp | 5 + apps/opencs/view/world/regionmap.cpp | 142 ++++++++++++++++++++ apps/opencs/view/world/regionmap.hpp | 45 +++++++ apps/opencs/view/world/regionmapsubview.cpp | 19 +-- apps/opencs/view/world/regionmapsubview.hpp | 8 +- 7 files changed, 214 insertions(+), 18 deletions(-) create mode 100644 apps/opencs/view/world/regionmap.cpp create mode 100644 apps/opencs/view/world/regionmap.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d9e271498..cbe90b1d3 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 dialoguesubview previewsubview + scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap ) opencs_units (view/render diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 697c5f450..544d5cbc5 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -406,6 +406,17 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QString::fromUtf8 (stream.str().c_str()); } + if (role==Role_Region) + { + CellIndex cellIndex = getIndex (index); + + std::map::const_iterator cell = + mMap.find (cellIndex); + + if (cell!=mMap.end() && !cell->second.mRegion.empty()) + return QString::fromUtf8 (Misc::StringUtils::lowerCase (cell->second.mRegion).c_str()); + } + return QVariant(); } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 7fb89f20a..699166a72 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -25,6 +25,11 @@ namespace CSMWorld typedef std::pair CellIndex; + enum Role + { + Role_Region = Qt::UserRole + }; + private: struct CellDescription diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp new file mode 100644 index 000000000..9502c423f --- /dev/null +++ b/apps/opencs/view/world/regionmap.cpp @@ -0,0 +1,142 @@ + +#include "regionmap.hpp" + +#include +#include + +#include +#include +#include + +#include "../../model/world/regionmap.hpp" + +void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) +{ + QMenu menu (this); + + if (getUnselectedCells().size()>0) + menu.addAction (mSelectAllAction); + + if (selectionModel()->selectedIndexes().size()>0) + menu.addAction (mClearSelectionAction); + + if (getMissingRegionCells().size()>0) + menu.addAction (mSelectRegionsAction); + + menu.exec (event->globalPos()); +} + +QModelIndexList CSVWorld::RegionMap::getUnselectedCells() const +{ + const QAbstractItemModel *model = QTableView::model(); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + std::sort (selected.begin(), selected.end()); + + QModelIndexList all; + + for (int y=0; yindex (y, x); + if (model->data (index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern)) + all.push_back (index); + } + + std::sort (all.begin(), all.end()); + + QModelIndexList list; + + std::set_difference (all.begin(), all.end(), selected.begin(), selected.end(), + std::back_inserter (list)); + + return list; +} + +QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const +{ + const QAbstractItemModel *model = QTableView::model(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + std::set regions; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string region = + model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); + + if (!region.empty()) + regions.insert (region); + } + + QModelIndexList list; + + QModelIndexList unselected = getUnselectedCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + { + std::string region = + model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); + + if (!region.empty() && regions.find (region)!=regions.end()) + list.push_back (*iter); + } + + return list; +} + +CSVWorld::RegionMap::RegionMap (QAbstractItemModel *model, QWidget *parent) +: QTableView (parent) +{ + verticalHeader()->hide(); + horizontalHeader()->hide(); + + setSelectionMode (QAbstractItemView::ExtendedSelection); + + setModel (model); + + resizeColumnsToContents(); + resizeRowsToContents(); + + mSelectAllAction = new QAction (tr ("Select All"), this); + connect (mSelectAllAction, SIGNAL (triggered()), this, SLOT (selectAll())); + addAction (mSelectAllAction); + + mClearSelectionAction = new QAction (tr ("Clear Selection"), this); + connect (mClearSelectionAction, SIGNAL (triggered()), this, SLOT (clearSelection())); + addAction (mClearSelectionAction); + + mSelectRegionsAction = new QAction (tr ("Select Regions"), this); + connect (mSelectRegionsAction, SIGNAL (triggered()), this, SLOT (selectRegions())); + addAction (mSelectRegionsAction); +} + +void CSVWorld::RegionMap::setEditLock (bool locked) +{ + +} + +void CSVWorld::RegionMap::selectAll() +{ + QModelIndexList unselected = getUnselectedCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + selectionModel()->select (*iter, QItemSelectionModel::Select); +} + +void CSVWorld::RegionMap::clearSelection() +{ + selectionModel()->clearSelection(); +} + +void CSVWorld::RegionMap::selectRegions() +{ + QModelIndexList unselected = getMissingRegionCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + selectionModel()->select (*iter, QItemSelectionModel::Select); +} \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp new file mode 100644 index 000000000..e30267b03 --- /dev/null +++ b/apps/opencs/view/world/regionmap.hpp @@ -0,0 +1,45 @@ +#ifndef CSV_WORLD_REGIONMAP_H +#define CSV_WORLD_REGIONMAP_H + +#include + +class QAction; +class QAbstractItemModel; + +namespace CSVWorld +{ + class RegionMap : public QTableView + { + Q_OBJECT + + QAction *mSelectAllAction; + QAction *mClearSelectionAction; + QAction *mSelectRegionsAction; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + QModelIndexList getUnselectedCells() const; + ///< Note non-existent cells are not listed. + + QModelIndexList getMissingRegionCells() const; + ///< Unselected cells within all regions that have at least one selected cell. + + public: + + RegionMap (QAbstractItemModel *model, QWidget *parent = 0); + + void setEditLock (bool locked); + + private slots: + + void selectAll(); + + void clearSelection(); + + void selectRegions(); + }; +} + +#endif diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp index b82c1afb5..e170ee309 100644 --- a/apps/opencs/view/world/regionmapsubview.cpp +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -1,29 +1,18 @@ #include "regionmapsubview.hpp" -#include -#include +#include "regionmap.hpp" CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document) : CSVDoc::SubView (universalId) { - mTable = new QTableView (this); + mRegionMap = new RegionMap (document.getData().getTableModel (universalId), this); - mTable->verticalHeader()->hide(); - mTable->horizontalHeader()->hide(); - - mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); - - mTable->setModel (document.getData().getTableModel (universalId)); - - mTable->resizeColumnsToContents(); - mTable->resizeRowsToContents(); - - setWidget (mTable); + setWidget (mRegionMap); } void CSVWorld::RegionMapSubView::setEditLock (bool locked) { - + mRegionMap->setEditLock (locked); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp index 1655107af..f329c7f3b 100644 --- a/apps/opencs/view/world/regionmapsubview.hpp +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -class QTableView; +class QAction; namespace CSMDoc { @@ -12,9 +12,13 @@ namespace CSMDoc namespace CSVWorld { + class RegionMap; + class RegionMapSubView : public CSVDoc::SubView { - QTableView *mTable; + Q_OBJECT + + RegionMap *mRegionMap; public: From d0ea23431c2417dd86df9aa650a0978ee8ca7749 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 11 Apr 2014 10:06:16 +0200 Subject: [PATCH 065/157] replaced CellIndex typedef with new CellCoordinates class --- apps/opencs/model/world/regionmap.cpp | 149 ++++++++++---------------- apps/opencs/model/world/regionmap.hpp | 19 ++-- 2 files changed, 67 insertions(+), 101 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 544d5cbc5..7f233a433 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -1,6 +1,7 @@ #include "regionmap.hpp" +#include #include #include @@ -25,17 +26,16 @@ CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) mName = cell2.mName; } -CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return CellIndex (index.column()+mMin.first, - (mMax.second-mMin.second - index.row()-1)+mMin.second); + return mMin.move (index.column(), mMax.getY()-mMin.getY() - index.row()-1); } -QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const +QModelIndex CSMWorld::RegionMap::getIndex (const CellCoordinates& index) const { // I hate you, Qt API naming scheme! - return QAbstractTableModel::index (mMax.second-mMin.second - (index.second-mMin.second)-1, - index.first-mMin.first); + return QAbstractTableModel::index (mMax.getY()-mMin.getY() - (index.getY()-mMin.getY())-1, + index.getX()-mMin.getX()); } void CSMWorld::RegionMap::buildRegions() @@ -70,21 +70,21 @@ void CSMWorld::RegionMap::buildMap() { CellDescription description (cell); - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index (cell2.mData.mX, cell2.mData.mY); mMap.insert (std::make_pair (index, description)); } } - std::pair mapSize = getSize(); + std::pair mapSize = getSize(); mMin = mapSize.first; mMax = mapSize.second; } -void CSMWorld::RegionMap::addCell (const CellIndex& index, const CellDescription& description) +void CSMWorld::RegionMap::addCell (const CellCoordinates& index, const CellDescription& description) { - std::map::iterator cell = mMap.find (index); + std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { @@ -114,7 +114,7 @@ void CSMWorld::RegionMap::addCells (int start, int end) if (cell2.isExterior()) { - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index (cell2.mData.mX, cell2.mData.mY); CellDescription description (cell); @@ -123,9 +123,9 @@ void CSMWorld::RegionMap::addCells (int start, int end) } } -void CSMWorld::RegionMap::removeCell (const CellIndex& index) +void CSMWorld::RegionMap::removeCell (const CellCoordinates& index) { - std::map::iterator cell = mMap.find (index); + std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { @@ -160,7 +160,7 @@ void CSMWorld::RegionMap::updateRegions (const std::vector& regions std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); std::sort (regions2.begin(), regions2.end()); - for (std::map::const_iterator iter (mMap.begin()); + for (std::map::const_iterator iter (mMap.begin()); iter!=mMap.end(); ++iter) { if (!iter->second.mRegion.empty() && @@ -176,90 +176,57 @@ void CSMWorld::RegionMap::updateRegions (const std::vector& regions void CSMWorld::RegionMap::updateSize() { - std::pair size = getSize(); + std::pair size = getSize(); + if (int diff = size.first.getX() - mMin.getX()) { - int diff = size.first.first - mMin.first; - - if (diff<0) - { - beginInsertColumns (QModelIndex(), 0, -diff-1); - mMin.first = size.first.first; - endInsertColumns(); - } - else if (diff>0) - { - beginRemoveColumns (QModelIndex(), 0, diff-1); - mMin.first = size.first.first; - endRemoveColumns(); - } + beginInsertColumns (QModelIndex(), 0, std::abs (diff)-1); + mMin = CellCoordinates (size.first.getX(), mMin.getY()); + endInsertColumns(); } + if (int diff = size.first.getY() - mMin.getY()) { - int diff = size.first.second - mMin.second; - - if (diff<0) - { - beginInsertRows (QModelIndex(), 0, -diff-1); - mMin.second = size.first.second; - endInsertRows(); - } - else if (diff>0) - { - beginRemoveRows (QModelIndex(), 0, diff-1); - mMin.second = size.first.second; - endRemoveRows(); - } + beginInsertRows (QModelIndex(), 0, std::abs (diff)-1); + mMin = CellCoordinates (mMin.getX(), size.first.getY()); + endInsertRows(); } + if (int diff = size.second.getX() - mMax.getX()) { - int diff = size.second.first - mMax.first; + int columns = columnCount(); if (diff>0) - { - int columns = columnCount(); beginInsertColumns (QModelIndex(), columns, columns+diff-1); - mMax.first = size.second.first; - endInsertColumns(); - } - else if (diff<0) - { - int columns = columnCount(); + else beginRemoveColumns (QModelIndex(), columns+diff, columns-1); - mMax.first = size.second.first; - endRemoveColumns(); - } + + mMax = CellCoordinates (size.second.getX(), mMax.getY()); + endInsertColumns(); } + if (int diff = size.second.getY() - mMax.getY()) { - int diff = size.second.second - mMax.second; + int rows = rowCount(); if (diff>0) - { - int rows = rowCount(); beginInsertRows (QModelIndex(), rows, rows+diff-1); - mMax.second = size.second.second; - endInsertRows(); - } - else if (diff<0) - { - int rows = rowCount(); + else beginRemoveRows (QModelIndex(), rows+diff, rows-1); - mMax.second = size.second.second; - endRemoveRows(); - } + + mMax = CellCoordinates (mMax.getX(), size.second.getY()); + endInsertRows(); } } -std::pair CSMWorld::RegionMap::getSize() - const +std::pair CSMWorld::RegionMap::getSize() const { const IdCollection& cells = mData.getCells(); int size = cells.getSize(); - CellIndex min (0, 0); - CellIndex max (0, 0); + CellCoordinates min (0, 0); + CellCoordinates max (0, 0); for (int i=0; i CSMWor if (cell2.isExterior()) { - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index (cell2.mData.mX, cell2.mData.mY); if (min==max) { min = index; - max = std::make_pair (min.first+1, min.second+1); + max = min.move (1, 1); } else { - if (index.first=max.first) - max.first = index.first + 1; - - if (index.second=max.second) - max.second = index.second + 1; + if (index.getX()=max.getX()) + max = CellCoordinates (index.getX()+1, max.getY()); + + if (index.getY()=max.getY()) + max = CellCoordinates (max.getX(), index.getY() + 1); } } } @@ -323,7 +290,7 @@ int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const if (parent.isValid()) return 0; - return mMax.second-mMin.second; + return mMax.getY()-mMin.getY(); } int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const @@ -331,7 +298,7 @@ int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const if (parent.isValid()) return 0; - return mMax.first-mMin.first; + return mMax.getX()-mMin.getX(); } QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const @@ -343,7 +310,7 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const { /// \todo GUI class in non-GUI code. Needs to be addressed eventually. - std::map::const_iterator cell = + std::map::const_iterator cell = mMap.find (getIndex (index)); if (cell!=mMap.end()) @@ -370,13 +337,13 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const if (role==Qt::ToolTipRole) { - CellIndex cellIndex = getIndex (index); + CellCoordinates cellIndex = getIndex (index); std::ostringstream stream; - stream << cellIndex.first << ", " << cellIndex.second; + stream << cellIndex; - std::map::const_iterator cell = + std::map::const_iterator cell = mMap.find (cellIndex); if (cell!=mMap.end()) @@ -408,9 +375,9 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const if (role==Role_Region) { - CellIndex cellIndex = getIndex (index); + CellCoordinates cellIndex = getIndex (index); - std::map::const_iterator cell = + std::map::const_iterator cell = mMap.find (cellIndex); if (cell!=mMap.end() && !cell->second.mRegion.empty()) @@ -503,7 +470,7 @@ void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int if (cell2.isExterior()) { - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index (cell2.mData.mX, cell2.mData.mY); removeCell (index); } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 699166a72..29cc8c512 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -9,6 +9,7 @@ #include "record.hpp" #include "cell.hpp" +#include "cellcoordinates.hpp" namespace CSMWorld { @@ -23,8 +24,6 @@ namespace CSMWorld public: - typedef std::pair CellIndex; - enum Role { Role_Region = Qt::UserRole @@ -44,27 +43,27 @@ namespace CSMWorld }; Data& mData; - std::map mMap; - CellIndex mMin; ///< inclusive - CellIndex mMax; ///< exclusive + std::map mMap; + CellCoordinates mMin; ///< inclusive + CellCoordinates mMax; ///< exclusive std::map mColours; ///< region ID, colour (RGBA) - CellIndex getIndex (const QModelIndex& index) const; + CellCoordinates getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) - QModelIndex getIndex (const CellIndex& index) const; + QModelIndex getIndex (const CellCoordinates& index) const; void buildRegions(); void buildMap(); - void addCell (const CellIndex& index, const CellDescription& description); + void addCell (const CellCoordinates& index, const CellDescription& description); ///< May be called on a cell that is already in the map (in which case an update is // performed) void addCells (int start, int end); - void removeCell (const CellIndex& index); + void removeCell (const CellCoordinates& index); ///< May be called on a cell that is not in the map (in which case the call is ignored) void addRegion (const std::string& region, unsigned int colour); @@ -83,7 +82,7 @@ namespace CSMWorld void updateSize(); - std::pair getSize() const; + std::pair getSize() const; public: From d2a41167d03a46808ab71542d0ab8c0d5c4388d9 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Apr 2014 21:24:00 +1000 Subject: [PATCH 066/157] Allow flying and swimming creatures to step inclines. Should have listen to Chris in the first place, see https://forum.openmw.org/viewtopic.php?f=6&t=2075 --- apps/openmw/mwworld/physicssystem.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index c2d902d69..7d531d6d3 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -308,11 +308,17 @@ namespace MWWorld break; } + Ogre::Vector3 oldPosition = newPosition; // 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 + // NOTE: stepMove modifies newPosition if successful + if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) + { + // don't let slaughterfish move out of water after stepMove + if(ptr.getClass().canSwim(ptr) && newPosition.z > (waterlevel - halfExtents.z * 0.5)) + newPosition = oldPosition; + else // Only on the ground if there's gravity + isOnGround = !(newPosition.z < waterlevel || isFlying); + } else { // Can't move this way, try to find another spot along the plane From 2f63eb7ca46f869fc4493488db1f9b465665970d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 12 Apr 2014 20:07:09 +0200 Subject: [PATCH 067/157] added missing edit lock for record reordering --- apps/opencs/view/world/table.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index a2927c2f0..712b8f556 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -358,6 +358,9 @@ void CSVWorld::Table::cloneRecord() void CSVWorld::Table::moveUpRecord() { + if (mEditLock) + return; + QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) @@ -387,6 +390,9 @@ void CSVWorld::Table::moveUpRecord() void CSVWorld::Table::moveDownRecord() { + if (mEditLock) + return; + QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) From 0bef75487366b786c3db147181ea009f8649b86b Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 13 Apr 2014 08:46:02 +1000 Subject: [PATCH 068/157] Fix jumping animation glitches caused by minor vertical movements. Should resolve Bug #1271. --- apps/openmw/mwmechanics/character.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 93c789af1..b7d99adec 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1090,7 +1090,13 @@ void CharacterController::update(float duration) if (inwater || flying) cls.getCreatureStats(mPtr).land(); - if(!onground && !flying && !inwater) + // FIXME: The check for vec.z is a hack, but onground is not a reliable + // indicator of whether the actor is on the ground (defaults to false, which + // means this code block will always execute at least once for most, and + // collisions can move z position slightly off zero). A very small value of + // 0.1 is used here, but maybe something larger like 10 should be used. + // Should resolve Bug#1271. + if(!onground && !flying && !inwater && vec.z > 0.1f) { // In the air (either getting up —ascending part of jump— or falling). From e9be6d3f42fa89ef52ca3393e46db66d833e9b0d Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 13 Apr 2014 11:34:59 +1000 Subject: [PATCH 069/157] Fix falling animation where vec.z is set to zero. --- apps/openmw/mwmechanics/character.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b7d99adec..d34812417 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1090,13 +1090,14 @@ void CharacterController::update(float duration) if (inwater || flying) cls.getCreatureStats(mPtr).land(); - // FIXME: The check for vec.z is a hack, but onground is not a reliable - // indicator of whether the actor is on the ground (defaults to false, which - // means this code block will always execute at least once for most, and - // collisions can move z position slightly off zero). A very small value of - // 0.1 is used here, but maybe something larger like 10 should be used. - // Should resolve Bug#1271. - if(!onground && !flying && !inwater && vec.z > 0.1f) + if(!onground && !flying && !inwater + // FIXME: The check for vec.z is a hack, but onground is not a reliable + // indicator of whether the actor is on the ground (defaults to false, which + // means this code block will always execute at least once for most actors, + // and collisions can move z position slightly off zero). A very small value + // of 0.1 is used here, but something larger like 10 may be more suitable. + // Should resolve Bug#1271. + && (mJumpState == JumpState_Falling || vec.z > 0.1f)) { // In the air (either getting up —ascending part of jump— or falling). From 3e6e325e5b44e199b84ffa417765480d717962bc Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 13 Apr 2014 14:53:36 +1000 Subject: [PATCH 070/157] Instead of hacking character.cpp, provide a more reliable check for world->isOnGround(mPtr). --- apps/openmw/mwmechanics/character.cpp | 9 +-------- apps/openmw/mwworld/worldimp.cpp | 29 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d34812417..93c789af1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1090,14 +1090,7 @@ void CharacterController::update(float duration) if (inwater || flying) cls.getCreatureStats(mPtr).land(); - if(!onground && !flying && !inwater - // FIXME: The check for vec.z is a hack, but onground is not a reliable - // indicator of whether the actor is on the ground (defaults to false, which - // means this code block will always execute at least once for most actors, - // and collisions can move z position slightly off zero). A very small value - // of 0.1 is used here, but something larger like 10 may be more suitable. - // Should resolve Bug#1271. - && (mJumpState == JumpState_Falling || vec.z > 0.1f)) + if(!onground && !flying && !inwater) { // In the air (either getting up —ascending part of jump— or falling). diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f808856c5..83299e126 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -9,6 +9,7 @@ #include +#include #include #include @@ -1711,11 +1712,37 @@ namespace MWWorld return pos.z < cell->getWaterLevel(); } + // physactor->getOnGround() is not a reliable indicator of whether the actor + // is on the ground (defaults to false, which means code blocks such as + // CharacterController::update() may falsely detect "falling"). + // + // Also, collisions can move z position slightly off zero, giving a false + // indication. In order to reduce false detection of jumping, small distance + // below the actor is detected and ignored. A value of 1.5 is used here, but + // something larger may be more suitable. This change should resolve Bug#1271. + // + // FIXME: Collision detection each time may have a performance impact. + // There might be better places to update PhysicActor::mOnGround. bool World::isOnGround(const MWWorld::Ptr &ptr) const { RefData &refdata = ptr.getRefData(); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); - return physactor && physactor->getOnGround(); + if(physactor) + { + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + OEngine::Physic::ActorTracer tracer; + // a small distance above collision object is considered "on ground" + tracer.findGround(physactor->getCollisionBody(), + pos, + pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down + mPhysEngine); + if(tracer.mFraction < 1.0f) // collision, must be close to something below + return true; // TODO: should update physactor + else + return physactor->getOnGround(); + } + else + return false; } bool World::vanityRotateCamera(float * rot) From 966ed468701799429663edc2d7bb1abc3a4618ae Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 13 Apr 2014 18:34:08 +1000 Subject: [PATCH 071/157] Better performance but less tolerant of collision induced glitches. Also had to use const_cast to cache on ground status. --- apps/openmw/mwworld/worldimp.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 83299e126..d56ca1f16 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1721,13 +1721,18 @@ namespace MWWorld // below the actor is detected and ignored. A value of 1.5 is used here, but // something larger may be more suitable. This change should resolve Bug#1271. // - // FIXME: Collision detection each time may have a performance impact. - // There might be better places to update PhysicActor::mOnGround. + // TODO: There might be better places to update PhysicActor::mOnGround. bool World::isOnGround(const MWWorld::Ptr &ptr) const { RefData &refdata = ptr.getRefData(); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); - if(physactor) + + if(!physactor) + return false; + + if(physactor->getOnGround()) + return true; + else { Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); OEngine::Physic::ActorTracer tracer; @@ -1737,12 +1742,13 @@ namespace MWWorld pos - Ogre::Vector3(0, 0, 1.5f), // trace a small amount down mPhysEngine); if(tracer.mFraction < 1.0f) // collision, must be close to something below - return true; // TODO: should update physactor + { + const_cast (physactor)->setOnGround(true); + return true; + } else - return physactor->getOnGround(); + return false; } - else - return false; } bool World::vanityRotateCamera(float * rot) From 300eb6f444f75900a30937f029cf2a046909f2cf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 13:23:50 +0200 Subject: [PATCH 072/157] make FNAM sub-record optional for all record types --- components/esm/loadacti.cpp | 4 ++-- components/esm/loadarmo.cpp | 4 ++-- components/esm/loadbody.cpp | 4 ++-- components/esm/loadbsgn.cpp | 4 ++-- components/esm/loadclas.cpp | 4 ++-- components/esm/loadfact.cpp | 4 ++-- components/esm/loadingr.cpp | 4 ++-- components/esm/loadlock.cpp | 4 ++-- components/esm/loadprob.cpp | 4 ++-- components/esm/loadrace.cpp | 4 ++-- components/esm/loadregn.cpp | 4 ++-- components/esm/loadrepa.cpp | 4 ++-- components/esm/loadsoun.cpp | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index 6ba0df0b3..8efea3302 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -11,13 +11,13 @@ namespace ESM void Activator::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); mScript = esm.getHNOString("SCRI"); } void Activator::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); } diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index ec8ff4f20..5bf38c840 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -34,7 +34,7 @@ unsigned int Armor::sRecordId = REC_ARMO; void Armor::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); mScript = esm.getHNOString("SCRI"); esm.getHNT(mData, "AODT", 24); mIcon = esm.getHNOString("ITEX"); @@ -45,7 +45,7 @@ void Armor::load(ESMReader &esm) void Armor::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("SCRI", mScript); esm.writeHNT("AODT", mData, 24); esm.writeHNOCString("ITEX", mIcon); diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 4015e6c91..c45f8d252 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -12,13 +12,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mRace = esm.getHNString("FNAM"); + mRace = esm.getHNOString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mRace); + esm.writeHNOCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index 55e1e7f65..db1a72a36 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -10,7 +10,7 @@ namespace ESM void BirthSign::load(ESMReader &esm) { - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); mTexture = esm.getHNOString("TNAM"); mDescription = esm.getHNOString("DESC"); @@ -19,7 +19,7 @@ void BirthSign::load(ESMReader &esm) void BirthSign::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("TNAM", mTexture); esm.writeHNOCString("DESC", mDescription); diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index 33489eec4..ec339bd15 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -41,7 +41,7 @@ const char *Class::sGmstSpecializationIds[3] = { void Class::load(ESMReader &esm) { - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "CLDT", 60); if (mData.mIsPlayable > 1) @@ -51,7 +51,7 @@ void Class::load(ESMReader &esm) } void Class::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); esm.writeHNOString("DESC", mDescription); } diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index 61fa90263..84be21938 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -28,7 +28,7 @@ namespace ESM void Faction::load(ESMReader &esm) { - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); // Read rank names. These are optional. int i = 0; @@ -52,7 +52,7 @@ void Faction::load(ESMReader &esm) } void Faction::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); for (int i = 0; i < 10; i++) { diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 0e0243362..5c98cb8b9 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -11,7 +11,7 @@ namespace ESM void Ingredient::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "IRDT", 56); mScript = esm.getHNOString("SCRI"); mIcon = esm.getHNOString("ITEX"); @@ -42,7 +42,7 @@ void Ingredient::load(ESMReader &esm) void Ingredient::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("IRDT", mData, 56); esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("ITEX", mIcon); diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp index 9ffce78a7..42677a22b 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -11,7 +11,7 @@ namespace ESM void Lockpick::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "LKDT", 16); @@ -22,7 +22,7 @@ void Lockpick::load(ESMReader &esm) void Lockpick::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("LKDT", mData, 16); esm.writeHNOString("SCRI", mScript); diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp index caa3d7e0e..b736bb64b 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -11,7 +11,7 @@ namespace ESM void Probe::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "PBDT", 16); @@ -22,7 +22,7 @@ void Probe::load(ESMReader &esm) void Probe::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("PBDT", mData, 16); esm.writeHNOString("SCRI", mScript); diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index e50e43a74..17f2e0267 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -20,14 +20,14 @@ namespace ESM void Race::load(ESMReader &esm) { - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "RADT", 140); mPowers.load(esm); mDescription = esm.getHNOString("DESC"); } void Race::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("RADT", mData, 140); mPowers.save(esm); esm.writeHNOString("DESC", mDescription); diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index fa4271e26..da03e009f 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -10,7 +10,7 @@ namespace ESM void Region::load(ESMReader &esm) { - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); if (esm.getVer() == VER_12) esm.getHNExact(&mData, sizeof(mData) - 2, "WEAT"); @@ -32,7 +32,7 @@ void Region::load(ESMReader &esm) } void Region::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); if (esm.getVersion() == VER_12) esm.writeHNT("WEAT", mData, sizeof(mData) - 2); diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index a7132828d..4e6cd7794 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -11,7 +11,7 @@ namespace ESM void Repair::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mName = esm.getHNOString("FNAM"); esm.getHNT(mData, "RIDT", 16); @@ -22,7 +22,7 @@ void Repair::load(ESMReader &esm) void Repair::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNOCString("FNAM", mName); esm.writeHNT("RIDT", mData, 16); esm.writeHNOString("SCRI", mScript); diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 49c9eb54e..28e4d7d9c 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -10,7 +10,7 @@ namespace ESM void Sound::load(ESMReader &esm) { - mSound = esm.getHNString("FNAM"); + mSound = esm.getHNOString("FNAM"); esm.getHNT(mData, "DATA", 3); /* cout << "vol=" << (int)data.volume @@ -21,7 +21,7 @@ void Sound::load(ESMReader &esm) } void Sound::save(ESMWriter &esm) const { - esm.writeHNCString("FNAM", mSound); + esm.writeHNOCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); } From decd4270d9aa1b31729f4aa0c195d7b04c768f14 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 13:59:27 +0200 Subject: [PATCH 073/157] added CellId role to RegionMap model --- apps/opencs/model/world/regionmap.cpp | 15 +++++++++++---- apps/opencs/model/world/regionmap.hpp | 5 ++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 7f233a433..fc4638432 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -384,15 +384,22 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QString::fromUtf8 (Misc::StringUtils::lowerCase (cell->second.mRegion).c_str()); } + if (role==Role_CellId) + { + CellCoordinates cellIndex = getIndex (index); + + std::ostringstream stream; + stream << "#" << cellIndex.getX() << " " << cellIndex.getY(); + + return QString::fromUtf8 (stream.str().c_str()); + } + return QVariant(); } Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const { - if (mMap.find (getIndex (index))!=mMap.end()) - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; - - return 0; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 29cc8c512..5b82ac217 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -26,7 +26,8 @@ namespace CSMWorld enum Role { - Role_Region = Qt::UserRole + Role_Region = Qt::UserRole, + Role_CellId = Qt::UserRole+1 }; private: @@ -93,6 +94,8 @@ namespace CSMWorld virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; + ///< \note Calling this function with role==Role_CellId may return the ID of a cell + /// that does not exist. virtual Qt::ItemFlags flags (const QModelIndex& index) const; From 19b31c4146efb4b5a0cbbb2d4d1714ab86d50ac2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 14:16:59 +0200 Subject: [PATCH 074/157] always use the cell ID instead of the exterior coordinate fields from the original cell struct --- apps/opencs/model/world/cell.hpp | 3 +++ apps/opencs/model/world/regionmap.cpp | 24 ++++++++++++++++-------- apps/opencs/model/world/regionmap.hpp | 2 ++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/cell.hpp b/apps/opencs/model/world/cell.hpp index 89854312a..e6f3c8c35 100644 --- a/apps/opencs/model/world/cell.hpp +++ b/apps/opencs/model/world/cell.hpp @@ -9,6 +9,9 @@ namespace CSMWorld { /// \brief Wrapper for Cell record + /// + /// \attention The mData.mX and mData.mY fields of the ESM::Cell struct are not used. + /// Exterior cell coordinates are encoded in the cell ID. struct Cell : public ESM::Cell { std::string mId; diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index fc4638432..5f030bb52 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -38,6 +38,18 @@ QModelIndex CSMWorld::RegionMap::getIndex (const CellCoordinates& index) const index.getX()-mMin.getX()); } +CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const Cell& cell) const +{ + std::istringstream stream (cell.mId); + + char ignore; + int x = 0; + int y = 0; + stream >> ignore >> x >> y; + + return CellCoordinates (x, y); +} + void CSMWorld::RegionMap::buildRegions() { const IdCollection& regions = mData.getRegions(); @@ -70,7 +82,7 @@ void CSMWorld::RegionMap::buildMap() { CellDescription description (cell); - CellCoordinates index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); mMap.insert (std::make_pair (index, description)); } @@ -114,7 +126,7 @@ void CSMWorld::RegionMap::addCells (int start, int end) if (cell2.isExterior()) { - CellCoordinates index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); CellDescription description (cell); @@ -236,7 +248,7 @@ std::pair CSMWorld::Region if (cell2.isExterior()) { - CellCoordinates index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); if (min==max) { @@ -476,11 +488,7 @@ void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int const Cell& cell2 = cell.get(); if (cell2.isExterior()) - { - CellCoordinates index (cell2.mData.mX, cell2.mData.mY); - - removeCell (index); - } + removeCell (getIndex (cell2)); } } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 5b82ac217..7d7685e89 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -54,6 +54,8 @@ namespace CSMWorld QModelIndex getIndex (const CellCoordinates& index) const; + CellCoordinates getIndex (const Cell& cell) const; + void buildRegions(); void buildMap(); From fc4195a88fc682cb47b893523635aa4efc4b4347 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 14:17:18 +0200 Subject: [PATCH 075/157] added create cell menu item to regionmap --- apps/opencs/view/world/regionmap.cpp | 84 +++++++++++++++++++-- apps/opencs/view/world/regionmap.hpp | 25 +++++- apps/opencs/view/world/regionmapsubview.cpp | 2 +- 3 files changed, 102 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 9502c423f..1e44ff056 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -3,12 +3,19 @@ #include #include +#include #include #include #include +#include "../../model/doc/document.hpp" + #include "../../model/world/regionmap.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/commands.hpp" void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) { @@ -23,12 +30,28 @@ void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) if (getMissingRegionCells().size()>0) menu.addAction (mSelectRegionsAction); + int selectedNonExistentCells = getSelectedCells (false, true).size(); + + if (selectedNonExistentCells>0) + { + if (selectedNonExistentCells==1) + mCreateCellsAction->setText ("Create one cell"); + else + { + std::ostringstream stream; + stream << "Create " << selectedNonExistentCells << " cells"; + mCreateCellsAction->setText (QString::fromUtf8 (stream.str().c_str())); + } + + menu.addAction (mCreateCellsAction); + } + menu.exec (event->globalPos()); } QModelIndexList CSVWorld::RegionMap::getUnselectedCells() const { - const QAbstractItemModel *model = QTableView::model(); + const QAbstractItemModel *model = QTableView::model(); int rows = model->rowCount(); int columns = model->columnCount(); @@ -56,6 +79,25 @@ QModelIndexList CSVWorld::RegionMap::getUnselectedCells() const return list; } +QModelIndexList CSVWorld::RegionMap::getSelectedCells (bool existent, bool nonExistent) const +{ + const QAbstractItemModel *model = QTableView::model(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + QModelIndexList list; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + bool exists = model->data (*iter, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern); + + if ((exists && existent) || (!exists && nonExistent)) + list.push_back (*iter); + } + + return list; +} + QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const { const QAbstractItemModel *model = QTableView::model(); @@ -89,15 +131,16 @@ QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const return list; } -CSVWorld::RegionMap::RegionMap (QAbstractItemModel *model, QWidget *parent) -: QTableView (parent) +CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, + CSMDoc::Document& document, QWidget *parent) +: QTableView (parent), mEditLock (false), mDocument (document) { verticalHeader()->hide(); horizontalHeader()->hide(); setSelectionMode (QAbstractItemView::ExtendedSelection); - setModel (model); + setModel (document.getData().getTableModel (universalId)); resizeColumnsToContents(); resizeRowsToContents(); @@ -113,11 +156,15 @@ CSVWorld::RegionMap::RegionMap (QAbstractItemModel *model, QWidget *parent) mSelectRegionsAction = new QAction (tr ("Select Regions"), this); connect (mSelectRegionsAction, SIGNAL (triggered()), this, SLOT (selectRegions())); addAction (mSelectRegionsAction); + + mCreateCellsAction = new QAction (tr ("Create Cells Action"), this); + connect (mCreateCellsAction, SIGNAL (triggered()), this, SLOT (createCells())); + addAction (mCreateCellsAction); } void CSVWorld::RegionMap::setEditLock (bool locked) { - + mEditLock = locked; } void CSVWorld::RegionMap::selectAll() @@ -139,4 +186,31 @@ void CSVWorld::RegionMap::selectRegions() for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) selectionModel()->select (*iter, QItemSelectionModel::Select); +} + +void CSVWorld::RegionMap::createCells() +{ + if (mEditLock) + return; + + QModelIndexList selected = getSelectedCells (false, true); + + QAbstractItemModel *regionModel = model(); + + CSMWorld::IdTable *cellsModel = &dynamic_cast (* + mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + if (selected.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Create cells")); + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + mDocument.getUndoStack().push (new CSMWorld::CreateCommand (*cellsModel, cellId)); + } + + if (selected.size()>1) + mDocument.getUndoStack().endMacro(); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp index e30267b03..5570cc585 100644 --- a/apps/opencs/view/world/regionmap.hpp +++ b/apps/opencs/view/world/regionmap.hpp @@ -4,7 +4,16 @@ #include class QAction; -class QAbstractItemModel; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class UniversalId; +} namespace CSVWorld { @@ -15,20 +24,28 @@ namespace CSVWorld QAction *mSelectAllAction; QAction *mClearSelectionAction; QAction *mSelectRegionsAction; + QAction *mCreateCellsAction; + bool mEditLock; + CSMDoc::Document& mDocument; private: void contextMenuEvent (QContextMenuEvent *event); QModelIndexList getUnselectedCells() const; - ///< Note non-existent cells are not listed. + ///< \note Non-existent cells are not listed. + + QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; + ///< \param existant Include existant cells. + /// \param nonExistant Include non-existant cells. QModelIndexList getMissingRegionCells() const; ///< Unselected cells within all regions that have at least one selected cell. public: - RegionMap (QAbstractItemModel *model, QWidget *parent = 0); + RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, + QWidget *parent = 0); void setEditLock (bool locked); @@ -39,6 +56,8 @@ namespace CSVWorld void clearSelection(); void selectRegions(); + + void createCells(); }; } diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp index e170ee309..a966c419f 100644 --- a/apps/opencs/view/world/regionmapsubview.cpp +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -7,7 +7,7 @@ CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document) : CSVDoc::SubView (universalId) { - mRegionMap = new RegionMap (document.getData().getTableModel (universalId), this); + mRegionMap = new RegionMap (universalId, document, this); setWidget (mRegionMap); } From 1892550833e23bb064dfbeb679a74f5947be094c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 15:32:49 +0200 Subject: [PATCH 076/157] added set/unset region actions to region map --- apps/opencs/view/world/regionmap.cpp | 66 ++++++++++++++++++++++++++++ apps/opencs/view/world/regionmap.hpp | 10 +++++ 2 files changed, 76 insertions(+) diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 1e44ff056..7e0a30242 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -16,6 +16,7 @@ #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) { @@ -46,6 +47,17 @@ void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) menu.addAction (mCreateCellsAction); } + if (getSelectedCells().size()>0) + { + if (!mRegionId.empty()) + { + mSetRegionAction->setText (QString::fromUtf8 (("Set region to " + mRegionId).c_str())); + menu.addAction (mSetRegionAction); + } + + menu.addAction (mUnsetRegionAction); + } + menu.exec (event->globalPos()); } @@ -131,6 +143,36 @@ QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const return list; } +void CSVWorld::RegionMap::setRegion (const std::string& regionId) +{ + QModelIndexList selected = getSelectedCells(); + + QAbstractItemModel *regionModel = model(); + + CSMWorld::IdTable *cellsModel = &dynamic_cast (* + mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + QString regionId2 = QString::fromUtf8 (regionId.c_str()); + + if (selected.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Set Region")); + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + QModelIndex index = cellsModel->getModelIndex (cellId, + cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)); + + mDocument.getUndoStack().push ( + new CSMWorld::ModifyCommand (*cellsModel, index, regionId2)); + } + + if (selected.size()>1) + mDocument.getUndoStack().endMacro(); +} + CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, QWidget *parent) : QTableView (parent), mEditLock (false), mDocument (document) @@ -160,6 +202,14 @@ CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, mCreateCellsAction = new QAction (tr ("Create Cells Action"), this); connect (mCreateCellsAction, SIGNAL (triggered()), this, SLOT (createCells())); addAction (mCreateCellsAction); + + mSetRegionAction = new QAction (tr ("Set Region"), this); + connect (mSetRegionAction, SIGNAL (triggered()), this, SLOT (setRegion())); + addAction (mSetRegionAction); + + mUnsetRegionAction = new QAction (tr ("Unset Region"), this); + connect (mUnsetRegionAction, SIGNAL (triggered()), this, SLOT (unsetRegion())); + addAction (mUnsetRegionAction); } void CSVWorld::RegionMap::setEditLock (bool locked) @@ -213,4 +263,20 @@ void CSVWorld::RegionMap::createCells() if (selected.size()>1) mDocument.getUndoStack().endMacro(); +} + +void CSVWorld::RegionMap::setRegion() +{ + if (mEditLock) + return; + + setRegion (mRegionId); +} + +void CSVWorld::RegionMap::unsetRegion() +{ + if (mEditLock) + return; + + setRegion (""); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp index 5570cc585..b93a13eb8 100644 --- a/apps/opencs/view/world/regionmap.hpp +++ b/apps/opencs/view/world/regionmap.hpp @@ -25,8 +25,11 @@ namespace CSVWorld QAction *mClearSelectionAction; QAction *mSelectRegionsAction; QAction *mCreateCellsAction; + QAction *mSetRegionAction; + QAction *mUnsetRegionAction; bool mEditLock; CSMDoc::Document& mDocument; + std::string mRegionId; private: @@ -42,6 +45,9 @@ namespace CSVWorld QModelIndexList getMissingRegionCells() const; ///< Unselected cells within all regions that have at least one selected cell. + void setRegion (const std::string& regionId); + ///< Set region Id of selected cells. + public: RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, @@ -58,6 +64,10 @@ namespace CSVWorld void selectRegions(); void createCells(); + + void setRegion(); + + void unsetRegion(); }; } From 2eca9e72fd154774757e111968bc53df8ed19d9a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 15:46:31 +0200 Subject: [PATCH 077/157] added view action to region map --- apps/opencs/view/world/regionmap.cpp | 41 ++++++++++++++++++--- apps/opencs/view/world/regionmap.hpp | 7 ++++ apps/opencs/view/world/regionmapsubview.cpp | 9 +++++ apps/opencs/view/world/regionmapsubview.hpp | 4 ++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 7e0a30242..5b15cc9a8 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -36,7 +36,7 @@ void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) if (selectedNonExistentCells>0) { if (selectedNonExistentCells==1) - mCreateCellsAction->setText ("Create one cell"); + mCreateCellsAction->setText ("Create one Cell"); else { std::ostringstream stream; @@ -51,13 +51,16 @@ void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) { if (!mRegionId.empty()) { - mSetRegionAction->setText (QString::fromUtf8 (("Set region to " + mRegionId).c_str())); + mSetRegionAction->setText (QString::fromUtf8 (("Set Region to " + mRegionId).c_str())); menu.addAction (mSetRegionAction); } menu.addAction (mUnsetRegionAction); } + if (selectionModel()->selectedIndexes().size()>0) + menu.addAction (mViewAction); + menu.exec (event->globalPos()); } @@ -210,6 +213,10 @@ CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, mUnsetRegionAction = new QAction (tr ("Unset Region"), this); connect (mUnsetRegionAction, SIGNAL (triggered()), this, SLOT (unsetRegion())); addAction (mUnsetRegionAction); + + mViewAction = new QAction (tr ("View Cells"), this); + connect (mViewAction, SIGNAL (triggered()), this, SLOT (view())); + addAction (mViewAction); } void CSVWorld::RegionMap::setEditLock (bool locked) @@ -245,8 +252,6 @@ void CSVWorld::RegionMap::createCells() QModelIndexList selected = getSelectedCells (false, true); - QAbstractItemModel *regionModel = model(); - CSMWorld::IdTable *cellsModel = &dynamic_cast (* mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); @@ -255,7 +260,7 @@ void CSVWorld::RegionMap::createCells() for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) { - std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId). + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). toString().toUtf8().constData(); mDocument.getUndoStack().push (new CSMWorld::CreateCommand (*cellsModel, cellId)); @@ -279,4 +284,30 @@ void CSVWorld::RegionMap::unsetRegion() return; setRegion (""); +} + +void CSVWorld::RegionMap::view() +{ + std::ostringstream hint; + hint << "c:"; + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + bool first = true; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + if (first) + first = false; + else + hint << "; "; + + hint << cellId; + } + + emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "sys::default"), + hint.str()); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp index b93a13eb8..12951b459 100644 --- a/apps/opencs/view/world/regionmap.hpp +++ b/apps/opencs/view/world/regionmap.hpp @@ -27,6 +27,7 @@ namespace CSVWorld QAction *mCreateCellsAction; QAction *mSetRegionAction; QAction *mUnsetRegionAction; + QAction *mViewAction; bool mEditLock; CSMDoc::Document& mDocument; std::string mRegionId; @@ -55,6 +56,10 @@ namespace CSVWorld void setEditLock (bool locked); + signals: + + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); + private slots: void selectAll(); @@ -68,6 +73,8 @@ namespace CSVWorld void setRegion(); void unsetRegion(); + + void view(); }; } diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp index a966c419f..a7675a4a6 100644 --- a/apps/opencs/view/world/regionmapsubview.cpp +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -10,9 +10,18 @@ CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, mRegionMap = new RegionMap (universalId, document, this); setWidget (mRegionMap); + + connect (mRegionMap, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), + this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&))); } void CSVWorld::RegionMapSubView::setEditLock (bool locked) { mRegionMap->setEditLock (locked); +} + +void CSVWorld::RegionMapSubView::editRequest (const CSMWorld::UniversalId& id, + const std::string& hint) +{ + focusId (id, hint); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp index f329c7f3b..524727901 100644 --- a/apps/opencs/view/world/regionmapsubview.hpp +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -25,6 +25,10 @@ namespace CSVWorld RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); virtual void setEditLock (bool locked); + + private slots: + + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); }; } From 097c063b8a28cb6483c153507bff63fbf00e5e2c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 16:40:16 +0200 Subject: [PATCH 078/157] added 'view in table' action to region map --- apps/opencs/view/world/regionmap.cpp | 33 ++++++++++++++++++++++++++++ apps/opencs/view/world/regionmap.hpp | 3 +++ 2 files changed, 36 insertions(+) diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp index 5b15cc9a8..738de89ae 100644 --- a/apps/opencs/view/world/regionmap.cpp +++ b/apps/opencs/view/world/regionmap.cpp @@ -56,6 +56,8 @@ void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) } menu.addAction (mUnsetRegionAction); + + menu.addAction (mViewInTableAction); } if (selectionModel()->selectedIndexes().size()>0) @@ -217,6 +219,10 @@ CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, mViewAction = new QAction (tr ("View Cells"), this); connect (mViewAction, SIGNAL (triggered()), this, SLOT (view())); addAction (mViewAction); + + mViewInTableAction = new QAction (tr ("View Cells in Table"), this); + connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable())); + addAction (mViewInTableAction); } void CSVWorld::RegionMap::setEditLock (bool locked) @@ -310,4 +316,31 @@ void CSVWorld::RegionMap::view() emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "sys::default"), hint.str()); +} + +void CSVWorld::RegionMap::viewInTable() +{ + std::ostringstream hint; + hint << "f:!or("; + + QModelIndexList selected = getSelectedCells(); + + bool first = true; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + if (first) + first = false; + else + hint << ","; + + hint << "string(ID,\"" << cellId << "\")"; + } + + hint << ")"; + + emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str()); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp index 12951b459..c3757fe45 100644 --- a/apps/opencs/view/world/regionmap.hpp +++ b/apps/opencs/view/world/regionmap.hpp @@ -28,6 +28,7 @@ namespace CSVWorld QAction *mSetRegionAction; QAction *mUnsetRegionAction; QAction *mViewAction; + QAction *mViewInTableAction; bool mEditLock; CSMDoc::Document& mDocument; std::string mRegionId; @@ -75,6 +76,8 @@ namespace CSVWorld void unsetRegion(); void view(); + + void viewInTable(); }; } From d188e68227d2b6bd430bc49d6da6b00bb3634dd5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Apr 2014 16:40:41 +0200 Subject: [PATCH 079/157] added f-type hint to TableSubView --- apps/opencs/view/filter/filterbox.cpp | 15 +++++++++----- apps/opencs/view/filter/filterbox.hpp | 8 ++++++- apps/opencs/view/filter/recordfilterbox.cpp | 15 +++++++++----- apps/opencs/view/filter/recordfilterbox.hpp | 6 ++++++ apps/opencs/view/world/tablesubview.cpp | 23 ++++++++++++++------- apps/opencs/view/world/tablesubview.hpp | 8 +++++++ 6 files changed, 57 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index a33288025..0089143e9 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -15,23 +15,28 @@ CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) layout->setContentsMargins (0, 0, 0, 0); - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + mRecordFilterBox = new RecordFilterBox (data, this); - layout->addWidget (recordFilterBox); + layout->addWidget (mRecordFilterBox); setLayout (layout); - connect (recordFilterBox, + connect (mRecordFilterBox, 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))); + mRecordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + connect(this, SIGNAL(useFilterRequest(const std::string&)), mRecordFilterBox, SIGNAL(useFilterRequest(const std::string&))); setAcceptDrops(true); } +void CSVFilter::FilterBox::setRecordFilter (const std::string& filter) +{ + mRecordFilterBox->setFilter (filter); +} + void CSVFilter::FilterBox::dropEvent (QDropEvent* event) { std::vector data = dynamic_cast (event->mimeData())->getData(); diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 5954035fc..a8aa31953 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -16,10 +16,14 @@ namespace CSMWorld namespace CSVFilter { + class RecordFilterBox; + class FilterBox : public QWidget { Q_OBJECT + RecordFilterBox *mRecordFilterBox; + void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); @@ -30,11 +34,13 @@ namespace CSVFilter FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + void setRecordFilter (const std::string& filter); + signals: void recordFilterChanged (boost::shared_ptr filter); void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, + void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); void useFilterRequest(const std::string& idOfFilter); }; diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 2a1a1407f..f15b1e17a 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -15,18 +15,23 @@ CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *pare layout->addWidget (new QLabel ("Record Filter", this)); - EditWidget *editWidget = new EditWidget (data, this); + mEdit = new EditWidget (data, this); - layout->addWidget (editWidget); + layout->addWidget (mEdit); setLayout (layout); connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + mEdit, 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))); + mEdit, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); + connect(this, SIGNAL(useFilterRequest(const std::string&)), mEdit, SLOT(useFilterRequest(const std::string&))); } + +void CSVFilter::RecordFilterBox::setFilter (const std::string& filter) +{ + mEdit->setText (QString::fromUtf8 (filter.c_str())); +} \ No newline at end of file diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fa5c9c3c2..8e01310a3 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -17,14 +17,20 @@ namespace CSMWorld namespace CSVFilter { + class EditWidget; + class RecordFilterBox : public QWidget { Q_OBJECT + EditWidget *mEdit; + public: RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + void setFilter (const std::string& filter); + signals: void filterChanged (boost::shared_ptr filter); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 2d08d186e..7f7b1477e 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -26,9 +26,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->insertWidget (0, mTable = new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2); - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + mFilterBox = new CSVFilter::FilterBox (document.getData(), this); - layout->insertWidget (0, filterBox); + layout->insertWidget (0, mFilterBox); QWidget *widget = new QWidget; @@ -48,7 +48,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mTable->selectionSizeUpdate(); mTable->viewport()->installEventFilter(this); mBottom->installEventFilter(this); - filterBox->installEventFilter(this); + mFilterBox->installEventFilter(this); if (mBottom->canCreateAndDelete()) { @@ -63,17 +63,17 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); - connect (filterBox, + connect (mFilterBox, SIGNAL (recordFilterChanged (boost::shared_ptr)), mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + connect(mFilterBox, 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(useFilterRequest(const std::string&)), mFilterBox, SIGNAL(useFilterRequest(const std::string&))); connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + mFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -97,6 +97,15 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } +void CSVWorld::TableSubView::useHint (const std::string& hint) +{ + if (hint.empty()) + return; + + if (hint[0]=='f' && hint.size()>=2) + mFilterBox->setRecordFilter (hint.substr (2)); +} + void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { emit cloneRequest(toClone.getId(), toClone.getType()); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 910fec325..3f82a7592 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -17,6 +17,11 @@ namespace CSMDoc class Document; } +namespace CSVFilter +{ + class FilterBox; +} + namespace CSVWorld { class Table; @@ -29,6 +34,7 @@ namespace CSVWorld Table *mTable; TableBottomBox *mBottom; + CSVFilter::FilterBox *mFilterBox; public: @@ -41,6 +47,8 @@ namespace CSVWorld virtual void setStatusBar (bool show); + virtual void useHint (const std::string& hint); + protected: bool eventFilter(QObject* object, QEvent *event); From 89be1069a78e858f431112d6b0bc8423a6da6aa3 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 14 Apr 2014 18:31:46 +1000 Subject: [PATCH 080/157] Bug #1260: show thief.dds image for a custom class level up menu --- apps/openmw/mwgui/levelupdialog.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index 2f11a4d12..cd72f5677 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -131,7 +131,14 @@ namespace MWGui const ESM::Class *cls = world->getStore().get().find(playerData->mClass); - mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); + // Vanilla uses thief.dds for custom classes. A player with a custom class + // doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as + // "$dynamic0". This check should resolve bug #1260. + if(world->getStore().get().isDynamic(cls->mId)) + mClassImage->setImageTexture ("textures\\levelup\\thief.dds"); + else + mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); + int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); From e2fab228f9956fb68e850dfbbe6d579d174184d3 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Mon, 14 Apr 2014 18:11:04 -0400 Subject: [PATCH 081/157] Save state is handled correctly now. --- apps/openmw/mwmechanics/npcstats.cpp | 3 +++ apps/openmw/mwworld/player.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 4f014102d..e11e3b0c4 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -452,6 +452,8 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf); } + state.mCrimeId = mCrimeId; + state.mBounty = mBounty; for (std::set::const_iterator iter (mExpelled.begin()); @@ -504,6 +506,7 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf); } + mCrimeId = state.mCrimeId; mBounty = state.mBounty; mReputation = state.mReputation; mWerewolfKills = state.mWerewolfKills; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index e8179b9f3..a4a4e9568 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -196,6 +196,9 @@ namespace MWWorld mPlayer.save (player.mObject); player.mCellId = mCellStore->getCell()->getCellId(); + player.mCurrentCrimeId = mCurrentCrimeId; + player.mPayedCrimeId = mPayedCrimeId; + player.mBirthsign = mSign; player.mLastKnownExteriorPosition[0] = mLastKnownExteriorPosition.x; @@ -241,6 +244,9 @@ namespace MWWorld !world.getStore().get().search (player.mBirthsign)) throw std::runtime_error ("invalid player state record (birthsign)"); + mCurrentCrimeId = player.mCurrentCrimeId; + mPayedCrimeId = player.mPayedCrimeId; + mSign = player.mBirthsign; mLastKnownExteriorPosition.x = player.mLastKnownExteriorPosition[0]; From 1fc030653fb9c3b25dc96ee5ac202a5eb5778d4c Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 15 Apr 2014 22:30:41 +1000 Subject: [PATCH 082/157] Avoid hard coding "thief.dds" string. --- apps/openmw/mwgui/levelupdialog.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index cd72f5677..5a3fc2855 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -134,12 +134,20 @@ namespace MWGui // Vanilla uses thief.dds for custom classes. A player with a custom class // doesn't have mId set, see mwworld/esmstore.hpp where it is initialised as // "$dynamic0". This check should resolve bug #1260. + // Choosing Stealth specialization and Speed/Agility as attributes. if(world->getStore().get().isDynamic(cls->mId)) - mClassImage->setImageTexture ("textures\\levelup\\thief.dds"); + { + MWWorld::SharedIterator it = world->getStore().get().begin(); + for(; it != world->getStore().get().end(); it++) + { + if(it->mData.mIsPlayable && it->mData.mSpecialization == 2 && it->mData.mAttribute[0] == 4 && it->mData.mAttribute[1] == 3) + break; + } + mClassImage->setImageTexture ("textures\\levelup\\" + it->mId + ".dds"); + } else mClassImage->setImageTexture ("textures\\levelup\\" + cls->mId + ".dds"); - int level = creatureStats.getLevel ()+1; mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); From fc1837e2edfe2c56057ea61c8d263a0dd0577bd5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 15 Apr 2014 19:26:43 +0200 Subject: [PATCH 083/157] fixed bug: regions can be dragged ps fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu --- apps/opencs/model/world/columnimp.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 89fb586aa..6976b454d 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -785,7 +785,7 @@ namespace CSMWorld template struct RegionColumn : public Column { - RegionColumn() : Column (Columns::ColumnId_Region, ColumnBase::Display_String) {} + RegionColumn() : Column (Columns::ColumnId_Region, ColumnBase::Display_Region) {} virtual QVariant get (const Record& record) const { From 8fba71101c014a873f7ec412d8e590ca8413ef22 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Tue, 15 Apr 2014 20:39:19 +0200 Subject: [PATCH 084/157] removed signals --- apps/opencs/view/filter/editwidget.cpp | 9 +-------- apps/opencs/view/filter/editwidget.hpp | 6 +++--- apps/opencs/view/filter/filterbox.cpp | 10 ++++++---- apps/opencs/view/filter/filterbox.hpp | 20 ++++++++++---------- apps/opencs/view/filter/recordfilterbox.cpp | 14 ++++++++------ apps/opencs/view/filter/recordfilterbox.hpp | 8 +++++--- apps/opencs/view/world/tablesubview.cpp | 9 ++------- apps/opencs/view/world/tablesubview.hpp | 3 --- 8 files changed, 35 insertions(+), 44 deletions(-) diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index b163297f9..68e99e0de 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -193,11 +193,4 @@ std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std:: } return ss.str(); -} - -void CSVFilter::EditWidget::useFilterRequest (const std::string& idOfFilter) -{ - clear(); - insert(QString::fromUtf8(idOfFilter.c_str())); -} - +} \ No newline at end of file diff --git a/apps/opencs/view/filter/editwidget.hpp b/apps/opencs/view/filter/editwidget.hpp index e7e34b8e9..a0f9f8919 100644 --- a/apps/opencs/view/filter/editwidget.hpp +++ b/apps/opencs/view/filter/editwidget.hpp @@ -30,6 +30,9 @@ namespace CSVFilter EditWidget (CSMWorld::Data& data, QWidget *parent = 0); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + signals: void filterChanged (boost::shared_ptr filter); @@ -47,10 +50,7 @@ namespace CSVFilter void filterRowsInserted (const QModelIndex& parent, int start, int end); - void createFilterRequest(std::vector > >& filterSource, - Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); }; } diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index 0089143e9..e588770b1 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -25,10 +25,6 @@ CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (recordFilterChanged (boost::shared_ptr))); - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - mRecordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), mRecordFilterBox, SIGNAL(useFilterRequest(const std::string&))); setAcceptDrops(true); } @@ -53,3 +49,9 @@ void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) { event->accept(); } + +void CSVFilter::FilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + mRecordFilterBox->createFilterRequest(filterSource, action); +} \ No newline at end of file diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index a8aa31953..c765164e7 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -24,25 +24,25 @@ namespace CSVFilter RecordFilterBox *mRecordFilterBox; - void dragEnterEvent (QDragEnterEvent* event); + public: + FilterBox (CSMWorld::Data& data, QWidget *parent = 0); - void dropEvent (QDropEvent* event); + void setRecordFilter (const std::string& filter); - void dragMoveEvent(QDragMoveEvent *event); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); - public: - FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + private: + void dragEnterEvent (QDragEnterEvent* event); - void setRecordFilter (const std::string& filter); + void dropEvent (QDropEvent* event); - signals: + void dragMoveEvent(QDragMoveEvent *event); + 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); }; } diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index f15b1e17a..ec5647618 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -24,14 +24,16 @@ CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *pare connect ( mEdit, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (filterChanged (boost::shared_ptr))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - mEdit, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), mEdit, SLOT(useFilterRequest(const std::string&))); } void CSVFilter::RecordFilterBox::setFilter (const std::string& filter) { + mEdit->clear(); mEdit->setText (QString::fromUtf8 (filter.c_str())); -} \ No newline at end of file +} + +void CSVFilter::RecordFilterBox::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + mEdit->createFilterRequest(filterSource, action); +} diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 8e01310a3..f4d17510b 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -31,12 +31,14 @@ namespace CSVFilter void setFilter (const std::string& filter); - signals: + void useFilterRequest(const std::string& idOfFilter); - void filterChanged (boost::shared_ptr filter); void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); - void useFilterRequest(const std::string& idOfFilter); + + signals: + + void filterChanged (boost::shared_ptr filter); }; } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 7f7b1477e..a5a7e8252 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -69,11 +69,6 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect(mFilterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - - connect(this, SIGNAL(useFilterRequest(const std::string&)), mFilterBox, SIGNAL(useFilterRequest(const std::string&))); - - connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - mFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -122,7 +117,7 @@ void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::Univers filterSource.push_back(pair); } - emit createFilterRequest(filterSource, action); + mFilterBox->createFilterRequest(filterSource, action); } bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) @@ -134,7 +129,7 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); if (handled) { - emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); } return handled; } diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 3f82a7592..b344ba1ad 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -55,9 +55,6 @@ namespace CSVWorld 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: From a7cece3d30d3ea43521119f72813313a580d0ef1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Apr 2014 22:34:15 +0200 Subject: [PATCH 085/157] do not generate modfiy commands on edits to change a cell to a value equal its original value before the edit --- apps/opencs/view/world/util.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 227c5c9c5..b2a32b551 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -108,7 +108,11 @@ void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemM { NastyTableModelHack hack (*model); QStyledItemDelegate::setModelData (editor, &hack, index); - mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); + + QVariant new_ = hack.getData(); + + if (model->data (index)!=new_) + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, new_)); } CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) From 1ceeeb4a224753c6df6fae64c3bf2f6d9929fa74 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 13 Apr 2014 07:35:26 +1000 Subject: [PATCH 086/157] Fix movement glitches for actors with low speeds (e.g. a rat). Was caused by false detection of being stuck. --- apps/openmw/mwmechanics/aiwander.cpp | 30 ++++++++++++++++++---------- apps/openmw/mwmechanics/aiwander.hpp | 3 ++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index e89d43ca4..c50506c75 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -35,6 +35,7 @@ namespace MWMechanics , mPrevX(0) , mPrevY(0) , mWalkState(State_Norm) + , mDistSameSpot(0) , mStuckCount(0) , mEvadeDuration(0) , mStuckDuration(0) @@ -71,7 +72,6 @@ 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); @@ -113,7 +113,9 @@ namespace MWMechanics 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 there is no path this actor doesn't go anywhere. See: + // https://forum.openmw.org/viewtopic.php?t=1556 + // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833 if(!mPathgrid) mDistance = 0; else if(mPathgrid->mPoints.empty()) @@ -166,7 +168,7 @@ namespace MWMechanics } } - // TODO: Does this actor stay in one spot forever while in AiWander? + // Actor becomes stationary - see above URL's for previous research if(mAllowedNodes.empty()) mDistance = 0; @@ -277,8 +279,10 @@ namespace MWMechanics { assert(mAllowedNodes.size()); unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); + // NOTE: destNodePos initially constructed with local (i.e. cell) co-ordinates Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); + // convert dest to use world co-ordinates ESM::Pathgrid::Point dest; dest.mX = destNodePos[0] + mXCell; dest.mY = destNodePos[1] + mYCell; @@ -338,9 +342,15 @@ namespace MWMechanics * f = one frame * t = how long before considered stuck * u = how long to move sideways + * + * DIST_SAME_SPOT is calibrated for movement speed of around 150. + * A rat has walking speed of around 30, so we need to adjust for + * that. */ - bool samePosition = (abs(pos.pos[0] - mPrevX) < DIST_SAME_SPOT) && - (abs(pos.pos[1] - mPrevY) < DIST_SAME_SPOT); + if(!mDistSameSpot) + mDistSameSpot = DIST_SAME_SPOT * (actor.getClass().getSpeed(actor) / 150); + bool samePosition = (abs(pos.pos[0] - mPrevX) < mDistSameSpot) && + (abs(pos.pos[1] - mPrevY) < mDistSameSpot); switch(mWalkState) { @@ -364,14 +374,14 @@ namespace MWMechanics { mStuckDuration += duration; // consider stuck only if position unchanges for a period - if(mStuckDuration > DURATION_SAME_SPOT) + if(mStuckDuration < DURATION_SAME_SPOT) + break; // still checking, note duration added to timer + else { - mWalkState = State_Evade; mStuckDuration = 0; mStuckCount++; + mWalkState = State_Evade; } - else - break; // still in the same state, but duration added to timer } } /* FALL THROUGH */ @@ -395,7 +405,7 @@ namespace MWMechanics // diagonal should have same animation as walk forward actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 0.01f; + actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; // change the angle a bit, too zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 75621911e..dd718a677 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -58,8 +58,9 @@ namespace MWMechanics WalkState mWalkState; int mStuckCount; - float mStuckDuration; + float mStuckDuration; // accumulate time here while in same spot float mEvadeDuration; + float mDistSameSpot; // take account of actor's speed bool mStoredAvailableNodes; bool mChooseAction; From d3be725ee72f157afe14524e7955b885b46a4499 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Apr 2014 09:03:36 +1000 Subject: [PATCH 087/157] Actors are moved on if idling near a closed interior door. Unreachable pathgrid points due to a closed door are removed from the allowed set of points. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwmechanics/aiwander.cpp | 415 ++++++++++++++++----------- apps/openmw/mwmechanics/aiwander.hpp | 39 ++- apps/openmw/mwmechanics/obstacle.cpp | 176 ++++++++++++ apps/openmw/mwmechanics/obstacle.hpp | 52 ++++ 5 files changed, 487 insertions(+), 197 deletions(-) create mode 100644 apps/openmw/mwmechanics/obstacle.cpp create mode 100644 apps/openmw/mwmechanics/obstacle.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 9fabb2080..e83ae2d8d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence aipersue alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting - disease pickpocket levelledlist combat steering + disease pickpocket levelledlist combat steering obstacle ) add_openmw_dir (mwstate diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c50506c75..029be2fb8 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -17,11 +17,8 @@ namespace MWMechanics { - // NOTE: determined empirically but probably need further tweaking - static const int COUNT_BEFORE_RESET = 200; - static const float DIST_SAME_SPOT = 1.8f; - static const float DURATION_SAME_SPOT = 1.0f; - static const float DURATION_TO_EVADE = 0.4f; + static const int COUNT_BEFORE_RESET = 200; // TODO: maybe no longer needed + static const float DOOR_CHECK_INTERVAL = 1.5f; AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat) @@ -29,16 +26,10 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) , mXCell(0) , mYCell(0) - , mX(0) - , mY(0) - , mZ(0) - , mPrevX(0) - , mPrevY(0) - , mWalkState(State_Norm) - , mDistSameSpot(0) - , mStuckCount(0) - , mEvadeDuration(0) - , mStuckDuration(0) + , mCell(NULL) + , mStuckCount(0) // TODO: maybe no longer needed + , mDoorCheckDuration(0) + , mTrimCurrentNode(false) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -56,7 +47,7 @@ namespace MWMechanics mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; - mPathgrid = NULL; + //mPathgrid = NULL; mIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); @@ -72,10 +63,71 @@ namespace MWMechanics return new AiWander(*this); } + /* + * AiWander high level states (0.29.0). Not entirely accurate in some cases + * e.g. non-NPC actors do not greet and some creatures may be moving even in + * the IdleNow state. + * + * [select node, + * build path] + * +---------->MoveNow----------->Walking + * | | + * [allowed | | + * nodes] | [hello if near] | + * start--->ChooseAction----->IdleNow | + * ^ ^ | | + * | | | | + * | +-----------+ | + * | | + * +----------------------------------+ + * + * + * New high level states. Not exactly as per vanilla (e.g. door stuff) + * but the differences are required because our physics does not work like + * vanilla and therefore have to compensate/work around. Note also many of + * the actions now have reaction times. + * + * [select node, [if stuck evade + * build path] or remove nodes if near door] + * +---------->MoveNow<---------->Walking + * | ^ | | + * | |(near door) | | + * [allowed | | | | + * nodes] | [hello if near] | | + * start--->ChooseAction----->IdleNow | | + * ^ ^ | ^ | | + * | | | | (stuck near | | + * | +-----------+ +---------------+ | + * | player) | + * +----------------------------------+ + * + * TODO: non-time critical operations should be run once every 250ms or so. + * + * TODO: It would be great if door opening/closing can be detected and pathgrid + * links dynamically updated. Currently (0.29.0) AiWander allows destination + * beyond closed doors which sometimes makes the actors stuck at the door and + * impossible for the player to open the door. + * + * For now detect being stuck at the door and simply delete the nodes from the + * allowed set. The issue is when the door opens the allowed set is not + * re-calculated. Normally this would not be an issue since hostile actors will + * enter combat (i.e. no longer wandering) + * + * FIXME: Sometimes allowed nodes that shouldn't be deleted are deleted. + */ bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { - actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); - actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); + bool cellChange = mCell && (actor.getCell() != mCell); + if(!mCell || cellChange) + { + mCell = actor.getCell(); + mStoredAvailableNodes = false; // prob. not needed since mDistance = 0 + } + const ESM::Cell *cell = mCell->getCell(); + + MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); + cStats.setDrawState(DrawState_Nothing); + cStats.setMovementFlag(CreatureStats::Flag_Run, false); MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { @@ -105,65 +157,76 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); - // Once off initialization to discover & store allowed node points for this actor. + // Initialization to discover & store allowed node points for this actor. if(!mStoredAvailableNodes) { - mPathgrid = world->getStore().get().search(*actor.getCell()->getCell()); + // infrequently used, therefore no benefit in caching it as a member + const ESM::Pathgrid * + pathgrid = world->getStore().get().search(*cell); - mCellX = actor.getCell()->getCell()->mData.mX; - mCellY = actor.getCell()->getCell()->mData.mY; + // cache the current cell location + mCellX = cell->mData.mX; + mCellY = cell->mData.mY; // If there is no path this actor doesn't go anywhere. See: // https://forum.openmw.org/viewtopic.php?t=1556 // http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833 - if(!mPathgrid) - mDistance = 0; - else if(mPathgrid->mPoints.empty()) + if(!pathgrid || pathgrid->mPoints.empty()) mDistance = 0; - if(mDistance) // A distance value is initially passed into the constructor. + // A distance value passed into the constructor indicates how far the + // actor can wander from the spawn position. AiWander assumes that + // pathgrid points are available, and uses them to randomly select wander + // destinations within the allowed set of pathgrid points (nodes). + if(mDistance) { mXCell = 0; mYCell = 0; - if(actor.getCell()->getCell()->isExterior()) + if(cell->isExterior()) { mXCell = mCellX * ESM::Land::REAL_SIZE; 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++) + // convert actorPos to local (i.e. cell) co-ordinates + Ogre::Vector3 actorPos(pos.pos); + actorPos[0] = actorPos[0] - mXCell; + actorPos[1] = actorPos[1] - mYCell; + + // mAllowedNodes for this actor with pathgrid point indexes + // based on mDistance + // NOTE: mPoints and mAllowedNodes are in local co-ordinates + float closestNodeDist = -1; + unsigned int closestIndex = 0; + unsigned int indexAllowedNodes = 0; + for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { - 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]); + float sqrDist = actorPos.squaredDistance(Ogre::Vector3( + pathgrid->mPoints[counter].mX, + pathgrid->mPoints[counter].mY, + pathgrid->mPoints[counter].mZ)); + if(sqrDist <= (mDistance * mDistance)) + { + mAllowedNodes.push_back(pathgrid->mPoints[counter]); + // keep track of the closest node + if(closestNodeDist == -1 || sqrDist < closestNodeDist) + { + closestNodeDist = sqrDist; + closestIndex = indexAllowedNodes; + } + indexAllowedNodes++; + } } if(!mAllowedNodes.empty()) { - Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); - float closestNode = npcPos.squaredDistance(firstNodePos); - 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); - float tempDist = npcPos.squaredDistance(nodePos); - if(tempDist < closestNode) - index = counterThree; - } - mCurrentNode = mAllowedNodes[index]; - mAllowedNodes.erase(mAllowedNodes.begin() + index); - - mStoredAvailableNodes = true; // set only if successful in finding allowed nodes + // Start with the closest node and remove it from the allowed set + // so that it does not get selected again. The removed node will + // later be put in the back of the queue, unless it gets removed + // due to inaccessibility (e.g. a closed door) + mCurrentNode = mAllowedNodes[closestIndex]; + mAllowedNodes.erase(mAllowedNodes.begin() + closestIndex); + // set only if successful in finding allowed nodes + mStoredAvailableNodes = true; } } } @@ -173,10 +236,10 @@ 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()->getCell()->mData.mX || mCellY != actor.getCell()->getCell()->mData.mY)) + if(mDistance && cellChange) mDistance = 0; - if(mChooseAction) // Initially set true by the constructor. + if(mChooseAction) { mPlayedIdle = 0; unsigned short idleRoll = 0; @@ -207,7 +270,7 @@ namespace MWMechanics mIdleNow = true; // Play idle voiced dialogue entries randomly - int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); if (hello > 0) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -216,19 +279,38 @@ namespace MWMechanics MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // Don't bother if the player is out of hearing range - if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(actor.getRefData().getPosition().pos)) < 1500) + if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(pos.pos)) < 1500) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } } + // Check if an idle actor is too close to a door - if so start walking + mDoorCheckDuration += duration; + if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL) + { + mDoorCheckDuration = 0; // restart timer + if(mDistance && // actor is not intended to be stationary + mIdleNow && // but is in idle + !mWalking && // FIXME: some actors are idle while walking + proximityToDoor(actor)) // NOTE: checks interior cells only + { + mIdleNow = false; + mMoveNow = true; + mTrimCurrentNode = false; // just in case +//#if 0 + std::cout << "idle door \""+actor.getClass().getName(actor)+"\" "<< std::endl; +//#endif + } + } + // Allow interrupting a walking actor to trigger a greeting - if(mIdleNow || (mWalking && (mWalkState != State_Norm))) + if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState())) { // Play a random voice greeting if the player gets too close const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = hello; int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); helloDistance *= iGreetDistanceMultiplier; @@ -242,7 +324,7 @@ namespace MWMechanics stopWalking(actor); mMoveNow = false; mWalking = false; - mWalkState = State_Norm; + mObstacleCheck.clear(); } if (!mSaidGreeting) @@ -275,12 +357,15 @@ namespace MWMechanics if(mMoveNow && mDistance) { + // Construct a new path if there isn't one if(!mPathFinder.isPathConstructed()) { assert(mAllowedNodes.size()); unsigned int randNode = (int)(rand() / ((double)RAND_MAX + 1) * mAllowedNodes.size()); - // NOTE: destNodePos initially constructed with local (i.e. cell) co-ordinates - Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, mAllowedNodes[randNode].mY, mAllowedNodes[randNode].mZ); + // NOTE: initially constructed with local (i.e. cell) co-ordinates + Ogre::Vector3 destNodePos(mAllowedNodes[randNode].mX, + mAllowedNodes[randNode].mY, + mAllowedNodes[randNode].mZ); // convert dest to use world co-ordinates ESM::Pathgrid::Point dest; @@ -299,16 +384,27 @@ namespace MWMechanics if(mPathFinder.isPathConstructed()) { - // buildPath inserts dest in case it is not a pathgraph point index - // which is a duplicate for AiWander + // buildPath inserts dest in case it is not a pathgraph point + // index which is a duplicate for AiWander. However below code + // does not work since getPath() returns a copy of path not a + // reference //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): + // 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); + // check if mCurrentNode was taken out of mAllowedNodes + if(mTrimCurrentNode && mAllowedNodes.size() > 1) + { + mTrimCurrentNode = false; +#if 0 + std::cout << "deleted "<< std::to_string(mCurrentNode.mX) + +", "+std::to_string(mCurrentNode.mY) << std::endl; +#endif + } + else + mAllowedNodes.push_back(mCurrentNode); mCurrentNode = temp; mMoveNow = false; @@ -320,124 +416,97 @@ namespace MWMechanics } } - if(mWalking) + // Are we there yet? + if(mWalking && + mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) { - if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2])) - { - stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; - } - else + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } + else if(mWalking) // have not yet reached the destination + { + // turn towards the next point in mPath + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + + // Returns true if evasive action needs to be taken + if(mObstacleCheck.check(actor, duration)) { - /* f t - * State_Norm <---> State_CheckStuck --> State_Evade - * ^ ^ | ^ | ^ | | - * | | | | | | | | - * | +---+ +---+ +---+ | u - * | any < t < u | - * +--------------------------------------------+ - * - * f = one frame - * t = how long before considered stuck - * u = how long to move sideways - * - * DIST_SAME_SPOT is calibrated for movement speed of around 150. - * A rat has walking speed of around 30, so we need to adjust for - * that. - */ - if(!mDistSameSpot) - mDistSameSpot = DIST_SAME_SPOT * (actor.getClass().getSpeed(actor) / 150); - bool samePosition = (abs(pos.pos[0] - mPrevX) < mDistSameSpot) && - (abs(pos.pos[1] - mPrevY) < mDistSameSpot); - - switch(mWalkState) + // first check if we're walking into a door + if(proximityToDoor(actor)) // NOTE: checks interior cells only { - case State_Norm: - { - if(!samePosition) - break; - else - mWalkState = State_CheckStuck; - } - /* FALL THROUGH */ - case State_CheckStuck: - { - if(!samePosition) - { - mWalkState = State_Norm; - mStuckDuration = 0; - break; - } - else - { - mStuckDuration += duration; - // consider stuck only if position unchanges for a period - if(mStuckDuration < DURATION_SAME_SPOT) - break; // still checking, note duration added to timer - else - { - mStuckDuration = 0; - mStuckCount++; - mWalkState = State_Evade; - } - } - } - /* FALL THROUGH */ - case State_Evade: - { - mEvadeDuration += duration; - if(mEvadeDuration < DURATION_TO_EVADE) - break; - else - { - mWalkState = State_Norm; // tried to evade, assume all is ok and start again - mEvadeDuration = 0; - } - } - /* NO DEFAULT CASE */ + // remove allowed points then select another random destination + mTrimCurrentNode = true; + trimAllowedNodes(mAllowedNodes, mPathFinder); + mObstacleCheck.clear(); + mPathFinder.clearPath(); + mWalking = false; + mMoveNow = true; } - - if(mWalkState == State_Evade) + else // probably walking into another NPC { - //std::cout << "Stuck \""<= COUNT_BEFORE_RESET) // something has gone wrong, reset + { + //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; + mObstacleCheck.clear(); - if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset - { - //std::cout << "Reset \""<& nodes, + const PathFinder& pathfinder) + { +//#if 0 + std::cout << "allowed size "<< std::to_string(nodes.size()) << std::endl; +//#endif + // TODO: how to add these back in once the door opens? + std::list paths = pathfinder.getPath(); + while(paths.size() >= 2) + { + ESM::Pathgrid::Point pt = paths.back(); +#if 0 + std::cout << "looking for "<< + "pt "+std::to_string(pt.mX)+", "+std::to_string(pt.mY) + < #include "pathfinding.hpp" +#include "obstacle.hpp" #include "../mwworld/timestamp.hpp" @@ -26,7 +27,7 @@ namespace MWMechanics void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); - int mDistance; + int mDistance; // how far the actor can wander from the spawn point int mDuration; int mTimeOfDay; std::vector mIdle; @@ -34,35 +35,18 @@ namespace MWMechanics bool mSaidGreeting; - float mX; - float mY; - float mZ; - - // Cell location + // Cached current cell location int mCellX; int mCellY; // Cell location multiplied by ESM::Land::REAL_SIZE float mXCell; float mYCell; - // for checking if we're stuck (but don't check Z axis) - float mPrevX; - float mPrevY; - - enum WalkState - { - State_Norm, - State_CheckStuck, - State_Evade - }; - WalkState mWalkState; - - int mStuckCount; - float mStuckDuration; // accumulate time here while in same spot - float mEvadeDuration; - float mDistSameSpot; // take account of actor's speed + const MWWorld::CellStore* mCell; // for detecting cell change + // if false triggers calculating allowed nodes based on mDistance bool mStoredAvailableNodes; + // AiWander states bool mChooseAction; bool mIdleNow; bool mMoveNow; @@ -73,12 +57,21 @@ namespace MWMechanics MWWorld::TimeStamp mStartTime; + // allowed pathgrid nodes based on mDistance from the spawn point std::vector mAllowedNodes; ESM::Pathgrid::Point mCurrentNode; + bool mTrimCurrentNode; + void trimAllowedNodes(std::vector& nodes, + const PathFinder& pathfinder); PathFinder mPathFinder; - const ESM::Pathgrid *mPathgrid; + //const ESM::Pathgrid *mPathgrid; + + ObstacleCheck mObstacleCheck; + float mDoorCheckDuration; + int mStuckCount; + //float mReaction; }; } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp new file mode 100644 index 000000000..00f97ae01 --- /dev/null +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -0,0 +1,176 @@ +#include "obstacle.hpp" + +#include + +#include "../mwbase/world.hpp" +#include "../mwworld/class.hpp" +#include "../mwworld/cellstore.hpp" + +namespace MWMechanics +{ + // NOTE: determined empirically but probably need further tweaking + static const float DIST_SAME_SPOT = 1.8f; + static const float DURATION_SAME_SPOT = 1.0f; + static const float DURATION_TO_EVADE = 0.4f; + + // Proximity check function for interior doors. Given that most interior cells + // do not have many doors performance shouldn't be too much of an issue. + // + // Limitation: there can be false detections, and does not test whether the + // actor is facing the door. + bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr, bool closed) + { + MWWorld::CellStore *cell = actor.getCell(); + + if(cell->getCell()->isExterior()) + return false; // check interior cells only + + // Check all the doors in this cell + MWWorld::CellRefList& doors = cell->get(); + MWWorld::CellRefList::List& refList = doors.mList; + MWWorld::CellRefList::List::iterator it = refList.begin(); + Ogre::Vector3 pos(actor.getRefData().getPosition().pos); + + // TODO: How to check whether the actor is facing a door? Below code is for + // the player, perhaps it can be adapted. + //MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); + //if(!ptr.isEmpty()) + //std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl; + + // TODO: The in-game observation of rot[2] value seems to be the + // opposite of the code in World::activateDoor() ::confused:: + for (; it != refList.end(); ++it) + { + MWWorld::LiveCellRef& ref = *it; + if(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && + ref.mData.getLocalRotation().rot[2] == (closed ? 0 : 1)) + { +//#if 0 + std::cout << "\""+actor.getClass().getName(actor)+"\" " + <<"next to door "+ref.mRef.mRefID + //+", enabled? "+std::to_string(ref.mData.isEnabled()) + +", dist "+std::to_string(sqrt(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)))) + << std::endl; +//#endif + return true; // found, stop searching + } + } + return false; // none found + } + + ObstacleCheck::ObstacleCheck(): + mPrevX(0) // to see if the moved since last time + , mPrevY(0) + , mDistSameSpot(-1) // avoid calculating it each time + , mWalkState(State_Norm) + , mStuckDuration(0) + , mEvadeDuration(0) + { + } + + void ObstacleCheck::clear() + { + mWalkState = State_Norm; + mStuckDuration = 0; + mEvadeDuration = 0; + } + + bool ObstacleCheck::isNormalState() const + { + return mWalkState == State_Norm; + } + + /* + * input - actor, duration (time since last check) + * output - true if evasive action needs to be taken + * + * Walking state transitions (player greeting check not shown): + * + * MoveNow <------------------------------------+ + * | d| + * | | + * +-> State_Norm <---> State_CheckStuck --> State_Evade + * ^ ^ | f ^ | t ^ | | + * | | | | | | | | + * | +---+ +---+ +---+ | u + * | any < t < u | + * +--------------------------------------------+ + * + * f = one reaction time + * d = proximity to a closed door + * t = how long before considered stuck + * u = how long to move sideways + * + * DIST_SAME_SPOT is calibrated for movement speed of around 150. + * A rat has walking speed of around 30, so we need to adjust for + * that. + */ + bool ObstacleCheck::check(const MWWorld::Ptr& actor, float duration) + { + const MWWorld::Class& cls = actor.getClass(); + ESM::Position pos = actor.getRefData().getPosition(); + + if(mDistSameSpot == -1) + mDistSameSpot = DIST_SAME_SPOT * (cls.getSpeed(actor) / 150); + + bool samePosition = (abs(pos.pos[0] - mPrevX) < mDistSameSpot) && + (abs(pos.pos[1] - mPrevY) < mDistSameSpot); + // update position + mPrevX = pos.pos[0]; + mPrevY = pos.pos[1]; + + switch(mWalkState) + { + case State_Norm: + { + if(!samePosition) + break; + else + mWalkState = State_CheckStuck; + } + /* FALL THROUGH */ + case State_CheckStuck: + { + if(!samePosition) + { + mWalkState = State_Norm; + mStuckDuration = 0; + break; + } + else + { + mStuckDuration += duration; + // consider stuck only if position unchanges for a period + if(mStuckDuration < DURATION_SAME_SPOT) + break; // still checking, note duration added to timer + else + { + mStuckDuration = 0; + mWalkState = State_Evade; + } + } + } + /* FALL THROUGH */ + case State_Evade: + { + mEvadeDuration += duration; + if(mEvadeDuration < DURATION_TO_EVADE) + return true; + else + { +//#if 0 + std::cout << "evade \""+actor.getClass().getName(actor)+"\" " + //<<"dist spot "+std::to_string(mDistSameSpot) + //+", speed "+std::to_string(cls.getSpeed(actor)) + << std::endl; +//#endif + // tried to evade, assume all is ok and start again + mWalkState = State_Norm; + mEvadeDuration = 0; + } + } + /* NO DEFAULT CASE */ + } + return false; // no obstacles to evade (yet) + } +} diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp new file mode 100644 index 000000000..920e2e794 --- /dev/null +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -0,0 +1,52 @@ +#ifndef OPENMW_MECHANICS_OBSTACLE_H + +namespace MWWorld +{ + class Ptr; +} + +namespace MWMechanics +{ + // NOTE: determined empirically based on in-game behaviour + static const float MIN_DIST_TO_DOOR_SQUARED = 128*128; + + // tests actor's proximity to a closed door by default + bool proximityToDoor(const MWWorld::Ptr& actor, + float minSqr = MIN_DIST_TO_DOOR_SQUARED, + bool closed = true); + + class ObstacleCheck + { + public: + ObstacleCheck(); + + // Clear the timers and set the state machine to default + void clear(); + + bool isNormalState() const; + + // Returns true if there is an obstacle and an evasive action + // should be taken + bool check(const MWWorld::Ptr& actor, float duration); + + private: + + // for checking if we're stuck (ignoring Z axis) + float mPrevX; + float mPrevY; + + enum WalkState + { + State_Norm, + State_CheckStuck, + State_Evade + }; + WalkState mWalkState; + + float mStuckDuration; // accumulate time here while in same spot + float mEvadeDuration; + float mDistSameSpot; // take account of actor's speed + }; +} + +#endif From aad13d315c4a19d8dc55be4ca4f7f1483373ee18 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Apr 2014 14:25:08 +1000 Subject: [PATCH 088/157] Fixed issue where allowed nodes were being erased. PathFinder was returning an empty path if the closest pathgrid point to the start was also the closest pathgrid point to the goal. Still need to clean up and remove logging statements. --- apps/openmw/mwmechanics/aiwander.cpp | 101 ++++++++++++++---------- apps/openmw/mwmechanics/pathfinding.cpp | 42 +++++++++- apps/openmw/mwmechanics/pathgrid.cpp | 14 ++++ 3 files changed, 115 insertions(+), 42 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 029be2fb8..c25821b4e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -112,11 +112,13 @@ namespace MWMechanics * allowed set. The issue is when the door opens the allowed set is not * re-calculated. Normally this would not be an issue since hostile actors will * enter combat (i.e. no longer wandering) - * - * FIXME: Sometimes allowed nodes that shouldn't be deleted are deleted. */ bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { + MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); + if(cStats.isDead() || cStats.getHealth().getCurrent() <= 0) + return true; // Don't bother with dead actors + bool cellChange = mCell && (actor.getCell() != mCell); if(!mCell || cellChange) { @@ -125,7 +127,6 @@ namespace MWMechanics } const ESM::Cell *cell = mCell->getCell(); - MWMechanics::CreatureStats& cStats = actor.getClass().getCreatureStats(actor); cStats.setDrawState(DrawState_Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -188,45 +189,56 @@ namespace MWMechanics mYCell = mCellY * ESM::Land::REAL_SIZE; } - // convert actorPos to local (i.e. cell) co-ordinates - Ogre::Vector3 actorPos(pos.pos); - actorPos[0] = actorPos[0] - mXCell; - actorPos[1] = actorPos[1] - mYCell; - - // mAllowedNodes for this actor with pathgrid point indexes - // based on mDistance + // FIXME: There might be a bug here. The allowed node points are + // based on the actor's current position rather than the actor's + // spawn point. As a result the allowed nodes for wander can change + // between saves, for example. + // + // convert npcPos to local (i.e. cell) co-ordinates + Ogre::Vector3 npcPos(pos.pos); + npcPos[0] = npcPos[0] - mXCell; + npcPos[1] = npcPos[1] - mYCell; + + // mAllowedNodes for this actor with pathgrid point indexes based on mDistance // NOTE: mPoints and mAllowedNodes are in local co-ordinates - float closestNodeDist = -1; - unsigned int closestIndex = 0; - unsigned int indexAllowedNodes = 0; for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++) { - float sqrDist = actorPos.squaredDistance(Ogre::Vector3( - pathgrid->mPoints[counter].mX, - pathgrid->mPoints[counter].mY, - pathgrid->mPoints[counter].mZ)); - if(sqrDist <= (mDistance * mDistance)) - { + Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY, + pathgrid->mPoints[counter].mZ); + if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance) mAllowedNodes.push_back(pathgrid->mPoints[counter]); - // keep track of the closest node - if(closestNodeDist == -1 || sqrDist < closestNodeDist) - { - closestNodeDist = sqrDist; - closestIndex = indexAllowedNodes; - } - indexAllowedNodes++; - } } if(!mAllowedNodes.empty()) { - // Start with the closest node and remove it from the allowed set - // so that it does not get selected again. The removed node will - // later be put in the back of the queue, unless it gets removed - // due to inaccessibility (e.g. a closed door) - mCurrentNode = mAllowedNodes[closestIndex]; - mAllowedNodes.erase(mAllowedNodes.begin() + closestIndex); - // set only if successful in finding allowed nodes - mStoredAvailableNodes = true; + Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ); + float closestNode = npcPos.squaredDistance(firstNodePos); + 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); + float tempDist = npcPos.squaredDistance(nodePos); + if(tempDist < closestNode) + index = counterThree; + } +#if 0 + if(actor.getClass().getName(actor) == "Rat") + { + std::cout << "rat allowed "<< std::to_string(mAllowedNodes.size()) + +" mDist "+std::to_string(mDistance) + +" pos "+std::to_string(static_cast(npcPos[0])) + +", "+std::to_string(static_cast(npcPos[1])) + < 1) { mTrimCurrentNode = false; -#if 0 +//#if 0 std::cout << "deleted "<< std::to_string(mCurrentNode.mX) +", "+std::to_string(mCurrentNode.mY) << std::endl; -#endif +//#endif +//#if 0 + std::cout << "allowed size "<< + std::to_string(mAllowedNodes.size()) << std::endl; +//#endif } else mAllowedNodes.push_back(mCurrentNode); @@ -412,7 +428,15 @@ namespace MWMechanics } // Choose a different node and delete this one from possible nodes because it is uncreachable: else + { mAllowedNodes.erase(mAllowedNodes.begin() + randNode); +//#if 0 + //std::cout << "actor \""<< actor.getClass().getName(actor) << "\"" << std::endl; + if(actor.getClass().getName(actor) == "Rat") + std::cout << "erase no path "<< std::to_string(mAllowedNodes[randNode].mX) + +", "+std::to_string(mAllowedNodes[randNode].mY) << std::endl; +//#endif + } } } @@ -477,9 +501,6 @@ namespace MWMechanics void AiWander::trimAllowedNodes(std::vector& nodes, const PathFinder& pathfinder) { -//#if 0 - std::cout << "allowed size "<< std::to_string(nodes.size()) << std::endl; -//#endif // TODO: how to add these back in once the door opens? std::list paths = pathfinder.getPath(); while(paths.size() >= 2) diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 730b8cb92..3a67a9b72 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -100,8 +100,11 @@ namespace } } } - if(start == closestReachableIndex) - closestReachableIndex = -1; // couldn't find anyting other than start + // AiWander has logic that depends on whether a path was created, deleting + // allowed nodes if not. Hence a path needs to be created even if the start + // and the end points are the same. + //if(start == closestReachableIndex) + //closestReachableIndex = -1; // couldn't find anyting other than start return std::pair (closestReachableIndex, closestReachableIndex == closestIndex); @@ -224,6 +227,18 @@ namespace MWMechanics // this shouldn't really happen, but just in case if(endNode.first != -1) { + // AiWander has logic that depends on whether a path was created, + // deleting allowed nodes if not. Hence a path needs to be created + // even if the start and the end points are the same. + // NOTE: aStarSearch will return an empty path if the start and end + // nodes are the same + if(startNode == endNode.first) + { + mPath.push_back(endPoint); + mIsPathConstructed = true; + return; + } + mPath = mCell->aStarSearch(startNode, endNode.first); if(!mPath.empty()) @@ -243,9 +258,32 @@ namespace MWMechanics } else mIsPathConstructed = false; +#if 0 + { + mIsPathConstructed = false; + std::cout << "no path "<< + std::to_string(startPoint.mX-xCell) + +", "+std::to_string(startPoint.mY-yCell) + +", "+std::to_string(startNode) + +", "+std::to_string(endPoint.mX-xCell) + +", "+std::to_string(endPoint.mY-yCell) + +", "+std::to_string(endNode.first)<mName == "Gnisis, Arvs-Drelen") + for(unsigned int v = 0; v < mPathgrid->mPoints.size(); v++) + { + std::cout << "SCC \"X:" << + std::to_string(mPathgrid->mPoints[v].mX) + +", Y:"+std::to_string(mPathgrid->mPoints[v].mY) + +", Num:"+std::to_string(mSCCId) + +", Point:"+std::to_string(v) + +", Group:"+std::to_string(mGraph[v].componentId) + <<"\""< Date: Fri, 18 Apr 2014 14:35:27 +1000 Subject: [PATCH 089/157] Cleaned up logging statements. --- apps/openmw/mwmechanics/aiwander.cpp | 45 ------------------------- apps/openmw/mwmechanics/pathfinding.cpp | 23 ------------- apps/openmw/mwmechanics/pathgrid.cpp | 14 -------- 3 files changed, 82 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c25821b4e..276d1fac6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -221,21 +221,7 @@ namespace MWMechanics if(tempDist < closestNode) index = counterThree; } -#if 0 - if(actor.getClass().getName(actor) == "Rat") - { - std::cout << "rat allowed "<< std::to_string(mAllowedNodes.size()) - +" mDist "+std::to_string(mDistance) - +" pos "+std::to_string(static_cast(npcPos[0])) - +", "+std::to_string(static_cast(npcPos[1])) - < 1) - { mTrimCurrentNode = false; -//#if 0 - std::cout << "deleted "<< std::to_string(mCurrentNode.mX) - +", "+std::to_string(mCurrentNode.mY) << std::endl; -//#endif -//#if 0 - std::cout << "allowed size "<< - std::to_string(mAllowedNodes.size()) << std::endl; -//#endif - } else mAllowedNodes.push_back(mCurrentNode); mCurrentNode = temp; @@ -428,15 +401,7 @@ namespace MWMechanics } // Choose a different node and delete this one from possible nodes because it is uncreachable: else - { mAllowedNodes.erase(mAllowedNodes.begin() + randNode); -//#if 0 - //std::cout << "actor \""<< actor.getClass().getName(actor) << "\"" << std::endl; - if(actor.getClass().getName(actor) == "Rat") - std::cout << "erase no path "<< std::to_string(mAllowedNodes[randNode].mX) - +", "+std::to_string(mAllowedNodes[randNode].mY) << std::endl; -//#endif - } } } @@ -506,11 +471,6 @@ namespace MWMechanics while(paths.size() >= 2) { ESM::Pathgrid::Point pt = paths.back(); -#if 0 - std::cout << "looking for "<< - "pt "+std::to_string(pt.mX)+", "+std::to_string(pt.mY) - <mName == "Gnisis, Arvs-Drelen") - for(unsigned int v = 0; v < mPathgrid->mPoints.size(); v++) - { - std::cout << "SCC \"X:" << - std::to_string(mPathgrid->mPoints[v].mX) - +", Y:"+std::to_string(mPathgrid->mPoints[v].mY) - +", Num:"+std::to_string(mSCCId) - +", Point:"+std::to_string(v) - +", Group:"+std::to_string(mGraph[v].componentId) - <<"\""< Date: Fri, 18 Apr 2014 15:19:22 +1000 Subject: [PATCH 090/157] More cleaning up. --- apps/openmw/mwmechanics/aiwander.cpp | 16 ++++++++-------- apps/openmw/mwmechanics/aiwander.hpp | 3 --- apps/openmw/mwmechanics/obstacle.cpp | 15 --------------- apps/openmw/mwmechanics/pathfinding.cpp | 2 +- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 276d1fac6..260aa72cf 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -47,7 +47,6 @@ namespace MWMechanics mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; - //mPathgrid = NULL; mIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); @@ -104,14 +103,15 @@ namespace MWMechanics * TODO: non-time critical operations should be run once every 250ms or so. * * TODO: It would be great if door opening/closing can be detected and pathgrid - * links dynamically updated. Currently (0.29.0) AiWander allows destination - * beyond closed doors which sometimes makes the actors stuck at the door and - * impossible for the player to open the door. + * links dynamically updated. Currently (0.29.0) AiWander allows choosing a + * destination beyond closed doors which sometimes makes the actors stuck at the + * door and impossible for the player to open the door. * * For now detect being stuck at the door and simply delete the nodes from the * allowed set. The issue is when the door opens the allowed set is not - * re-calculated. Normally this would not be an issue since hostile actors will - * enter combat (i.e. no longer wandering) + * re-calculated. However this would not be an issue in most cases since hostile + * actors will enter combat (i.e. no longer wandering) and different pathfinding + * will kick in. */ bool AiWander::execute (const MWWorld::Ptr& actor,float duration) { @@ -473,8 +473,8 @@ namespace MWMechanics ESM::Pathgrid::Point pt = paths.back(); for(int j = 0; j < nodes.size(); j++) { - // NOTE: doesn't hadle a door with the same X/Y - // coordinates but with a different Z + // FIXME: doesn't hadle a door with the same X/Y + // co-ordinates but with a different Z if(nodes[j].mX == pt.mX && nodes[j].mY == pt.mY) { nodes.erase(nodes.begin() + j); diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8fe35e53d..59c21de89 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -65,13 +65,10 @@ namespace MWMechanics const PathFinder& pathfinder); PathFinder mPathFinder; - //const ESM::Pathgrid *mPathgrid; ObstacleCheck mObstacleCheck; float mDoorCheckDuration; int mStuckCount; - - //float mReaction; }; } diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 00f97ae01..9a748c052 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -44,16 +44,7 @@ namespace MWMechanics MWWorld::LiveCellRef& ref = *it; if(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && ref.mData.getLocalRotation().rot[2] == (closed ? 0 : 1)) - { -//#if 0 - std::cout << "\""+actor.getClass().getName(actor)+"\" " - <<"next to door "+ref.mRef.mRefID - //+", enabled? "+std::to_string(ref.mData.isEnabled()) - +", dist "+std::to_string(sqrt(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)))) - << std::endl; -//#endif return true; // found, stop searching - } } return false; // none found } @@ -158,12 +149,6 @@ namespace MWMechanics return true; else { -//#if 0 - std::cout << "evade \""+actor.getClass().getName(actor)+"\" " - //<<"dist spot "+std::to_string(mDistSameSpot) - //+", speed "+std::to_string(cls.getSpeed(actor)) - << std::endl; -//#endif // tried to evade, assume all is ok and start again mWalkState = State_Norm; mEvadeDuration = 0; diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 139553556..11d436700 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -231,7 +231,7 @@ namespace MWMechanics // deleting allowed nodes if not. Hence a path needs to be created // even if the start and the end points are the same. // NOTE: aStarSearch will return an empty path if the start and end - // nodes are the same + // nodes are the same if(startNode == endNode.first) { mPath.push_back(endPoint); From a0fc514df78d605b9b4db6d89eb8fb0f9115ebb0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Apr 2014 15:45:39 +1000 Subject: [PATCH 091/157] Suppress compiler warning. --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 260aa72cf..988da4eb4 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -471,7 +471,7 @@ namespace MWMechanics while(paths.size() >= 2) { ESM::Pathgrid::Point pt = paths.back(); - for(int j = 0; j < nodes.size(); j++) + for(unsigned int j = 0; j < nodes.size(); j++) { // FIXME: doesn't hadle a door with the same X/Y // co-ordinates but with a different Z From 7437647f70fad054793b0bf649a4b1484cf0d0c9 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Apr 2014 16:45:31 +1000 Subject: [PATCH 092/157] Forgot to add a guard :-( --- apps/openmw/mwmechanics/obstacle.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwmechanics/obstacle.hpp b/apps/openmw/mwmechanics/obstacle.hpp index 920e2e794..12030b2be 100644 --- a/apps/openmw/mwmechanics/obstacle.hpp +++ b/apps/openmw/mwmechanics/obstacle.hpp @@ -1,4 +1,5 @@ #ifndef OPENMW_MECHANICS_OBSTACLE_H +#define OPENMW_MECHANICS_OBSTACLE_H namespace MWWorld { From 4625adfb7fdc85d5f83d9f1adf8c0ad00762daa6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Apr 2014 11:43:37 +0200 Subject: [PATCH 093/157] Fix typo in a condition. --- apps/openmw/mwmechanics/spellcasting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 7f5a7fac5..6ea3f5472 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -714,7 +714,7 @@ namespace MWMechanics effect.mDuration = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) { - if (!magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) + if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) magnitude = int((0.05 * y) / (0.1 * magicEffect->mData.mBaseCost)); else magnitude = int(y / (0.1 * magicEffect->mData.mBaseCost)); From d3d0b7362f1d4b0c1945116e88e041b2e8b92ad4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Apr 2014 12:34:12 +0200 Subject: [PATCH 094/157] Fix older savegame compatibility regarding new crime feature. --- components/esm/player.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/esm/player.cpp b/components/esm/player.cpp index e41cc535e..7a2c03b48 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -26,8 +26,10 @@ void ESM::Player::load (ESMReader &esm) mBirthsign = esm.getHNString ("SIGN"); - esm.getHNT (mCurrentCrimeId, "CURD"); - esm.getHNT (mPayedCrimeId, "PAYD"); + mCurrentCrimeId = 0; + esm.getHNOT (mCurrentCrimeId, "CURD"); + mPayedCrimeId = 0; + esm.getHNOT (mPayedCrimeId, "PAYD"); } void ESM::Player::save (ESMWriter &esm) const @@ -51,4 +53,4 @@ void ESM::Player::save (ESMWriter &esm) const esm.writeHNT ("CURD", mCurrentCrimeId); esm.writeHNT ("PAYD", mPayedCrimeId); -} \ No newline at end of file +} From b3916e77448eb65dd54434113cf5f55491edc83a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Apr 2014 13:44:09 +0200 Subject: [PATCH 095/157] Crime: mark witnesses as alarmed. Fixes guard dialogue to properly detect if the player turned himself in. --- apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f7478e22c..7b83ff1f1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -763,6 +763,7 @@ namespace MWMechanics // TODO: Not a complete list, disposition changes? creatureStats.setHostile(false); creatureStats.setAttacked(false); + creatureStats.setAlarmed(false); // Update witness crime id npcStats.setCrimeId(-1); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 3a26ea7dc..a44d1a2ba 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -866,6 +866,9 @@ namespace MWMechanics { it1->getClass().getNpcStats(*it1).setCrimeId(id); } + + // Mark as Alarmed for dialogue + it1->getClass().getCreatureStats(*it1).setAlarmed(true); } break; // Someone saw the crime and everyone has been told } From a3dffd5d57d272e95ad6875e8c3fccb2195db7d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 18 Apr 2014 14:13:29 +0200 Subject: [PATCH 096/157] Add some missing SDL to MyGUI key translations. Most importantly to allow numpad enter as alternative to return key. --- extern/sdl4ogre/sdlinputwrapper.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index d48e43c01..c3ea2fd74 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -443,5 +443,10 @@ namespace SFO mKeyMap.insert( KeyMap::value_type(SDLK_PAGEDOWN, OIS::KC_PGDOWN) ); mKeyMap.insert( KeyMap::value_type(SDLK_INSERT, OIS::KC_INSERT) ); mKeyMap.insert( KeyMap::value_type(SDLK_DELETE, OIS::KC_DELETE) ); + mKeyMap.insert( KeyMap::value_type(SDLK_KP_ENTER, OIS::KC_NUMPADENTER) ); + mKeyMap.insert( KeyMap::value_type(SDLK_RCTRL, OIS::KC_RCONTROL) ); + mKeyMap.insert( KeyMap::value_type(SDLK_LGUI, OIS::KC_LWIN) ); + mKeyMap.insert( KeyMap::value_type(SDLK_RGUI, OIS::KC_RWIN) ); + mKeyMap.insert( KeyMap::value_type(SDLK_APPLICATION, OIS::KC_APPS) ); } } From 479a94b35df7f825a50ae2ede32a7c291d1840b2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Apr 2014 08:16:56 +1000 Subject: [PATCH 097/157] Backing off closed doors working, needs cleanup and tweaking. --- apps/openmw/mwmechanics/aicombat.cpp | 209 ++++++++++++++++++++++++++- apps/openmw/mwmechanics/aicombat.hpp | 10 ++ apps/openmw/mwmechanics/aiwander.cpp | 7 +- apps/openmw/mwmechanics/obstacle.cpp | 9 +- apps/openmw/mwmechanics/pathgrid.cpp | 6 +- 5 files changed, 231 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index fd2fa61ba..e94899359 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -35,6 +35,9 @@ namespace namespace MWMechanics { + static const float DOOR_CHECK_INTERVAL = 1.5f; + // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp + AiCombat::AiCombat(const MWWorld::Ptr& actor) : mTarget(actor), mTimerAttack(0), @@ -44,12 +47,104 @@ namespace MWMechanics mReadyToAttack(false), mStrike(false), mCombatMove(false), + mBackOffDoor(false), mRotate(false), mMovement(), + mDoorIter(actor.getCell()->get().mList.end()), + mDoors(actor.getCell()->get().mList), + mDoorCheckDuration(0), mTargetAngle(0) { } + /* + * The 'pursuit' part of AiCombat now has some memory to allow backtracking. + * The intention is to allow actors to detect being 'stuck' somewhere, whether + * in a river or facing a door, to go back to a known 'good' position. + * + * Each actor goes through these states of movement once alerted and an AiCombat + * package is queued (FIXME: below is a DRAFT proposal only): + * + * - Maybe remember the starting position so that the actor can return if + * lose sight of the target? Last Known Target Location Tracking - keep track + * of target positions every sec, paired with actor's location at the time + * + * - Get to the target if far away (need path finding), how close depends on + * the current best (selected?) weapon. As the actor moves, some breadcrumb + * is left (e.g. every second store a position in a FIFO) so that the actor + * can back track if necessary. + * + * - If the actor gets stuck (need a way of detecting this) then decide what + * to do next. For example, if next to a door then maybe back track one or + * two positions and check LOS of the target every x frames (reaction time?). + * Or maybe back track then look for a new path. + * + * - Once in weapon range, may need a strategy to get to the target for a + * strike (maybe there are others nearby attacking the same target). + * + * + * Current AiCombat movement states (as of 0.29.0), ignoring the details of the + * attack states such as CombatMove, Strike and ReadyToAttack: + * + * +-----(within strike range)----->attack--(beyond strike range)-->follow + * | | ^ | | + * | | | | | + * pursue<----(beyond follow range)-----+ +----(within strike range)---+ | + * ^ | + * | | + * +--------------------------(beyond follow range)--------------------+ + * + * + * Below diagram is high level only, FIXME: code detail may be different: + * + * +-----------(same)-------------->attack---------(same)---------->follow + * | |^^ | | + * | ||| | | + * | +--(same)-----------------+|+----------(same)------------+ | + * | | | in range | + * | | too far | | + * | | +---------------got hit or LOS<---+ | + * | <----+ | | | + * pursue<---------+ door | | + * ^^ <--> maybe stuck, check --------------> back up and wait | + * || | ^ | | ^ | | + * || | | | stuck | | | waited | + * || +---+ | +---+ | too long | + * || backtrack | | | + * |+----------------------+ go back? <-----------+ | + * | | + * +----------------------------(same)---------------------------------+ + * + * FIXME: + * + * The new scheme is way too complicated, should really be implemented as a + * proper state machine. + * + * TODO: + * + * Use the Observer Pattern to co-ordinate attacks, provide intelligence on + * whether the target was hit, etc. + * + * TODO: + * + * Auto-generate large cubes or squares as a poor-man's navmesh? Many + * external cells do not have any pathgrids, and certainly none for flying + * or swimming. + * + * + * Called from: (check Doxygen as this comment could be out of date) + * + * OMW::Engine::go() | + * Ogre::Root::renderOneFrame() | + * Ogre::Root::... | ... detail omitted + * Ogre::Root::fireFrameRenderingQueued() | + * OMW::Engine::frameRenderingQueued() | virtual Ogre::FrameListener + * MWMechanics::MechanicsManager::update() | + * MWMechanics::Actors::update() | + * MWMechanics::Actors::updateActor() | + * MWMechanics::AiSequence::execute() | from priority queue mPackages + * MWMechanics::AiCombat::execute() | virtual MWMechanics::AiPackage + */ bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { //General description @@ -168,6 +263,43 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); + /* + * Some notes on meanings of variables: + * + * rangeMelee: + * + * - Distance where attack using the actor's weapon is possible + * - longer for ranged weapons (obviously?) vs. melee weapons + * - Once within this distance mFollowTarget is triggered + * (TODO: check whether the follow logic still works for ranged + * weapons, since rangeCloseup is set to zero) + * - TODO: The variable name is confusing. It was ok when AiCombat only + * had melee weapons but now that ranged weapons are supported that is + * no longer the case. It should really be renamed to something + * like rangeStrike - alternatively, keep this name for melee + * weapons and use a different variable for tracking ranged weapon + * distance (rangeRanged maybe?) + * + * rangeCloseup: + * + * - Applies to melee weapons or hand to hand only (or creatures without + * weapons) + * - Distance a little further away from the actor's weapon strike + * i.e. rangeCloseup > rangeMelee for melee weapons + * (the variable names make this simple concept counter-intuitive, + * something like rangeMelee > rangeStrike may be better) + * - Once the target gets beyond this distance mFollowTarget is cleared + * and a path to the target needs to be found + * - TODO: Possibly rename this variable to rangeMelee or even rangeFollow + * + * mFollowTarget: + * + * - Once triggered, the actor follows the target with LOS shortcut + * (the shortcut really only applies to cells where pathgrids are + * available, since the default path without pathgrids is direct to + * target even if LOS is not achieved) + * + */ float rangeMelee; float rangeCloseUp; bool distantCombat = false; @@ -189,6 +321,7 @@ namespace MWMechanics Ogre::Vector3 vDir = vDest - vStart; float distBetween = vDir.length(); + // (within strike dist) || (not quite strike dist while following) if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) { //Melee and Close-up combat @@ -198,12 +331,13 @@ namespace MWMechanics mRotate = true; //bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + // (not quite strike dist while following) if (mFollowTarget && distBetween > rangeMelee) { //Close-up combat: just run up on target mMovement.mPosition[1] = 1; } - else + else // (within strike dist) { //Melee: stop running and attack mMovement.mPosition[1] = 0; @@ -240,7 +374,7 @@ namespace MWMechanics } else { - //target is at far distance: build path to target OR follow target (if previously actor had reached it once) + //target is at far distance: build path to target mFollowTarget = false; buildNewPath(actor); //may fail to build a path, check before use @@ -261,6 +395,55 @@ namespace MWMechanics mMovement.mPosition[1] = 1; mReadyToAttack = false; + + // remember that this section gets updated every tReaction, which is + // currently hard coded at 250ms or 1/4 second + if(mObstacleCheck.check(actor, tReaction)) // true if evasive action needed + { + std::cout<<"found obstacle"<getCell()->isExterior()) + { + // Check all the doors in this cell + mDoors = cell->get().mList; // update list member + mDoorIter = mDoors.begin(); + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + for (; mDoorIter != mDoors.end(); ++mDoorIter) + { + MWWorld::LiveCellRef& ref = *mDoorIter; + if(actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED && + ref.mData.getLocalRotation().rot[2] == 0) + { + std::cout<<"closed door id \""< rangeMelee) @@ -293,6 +476,28 @@ namespace MWMechanics } } + MWWorld::LiveCellRef& ref = *mDoorIter; + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + if(mBackOffDoor && + actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < 1.5*1.5*MIN_DIST_TO_DOOR_SQUARED) + { + mMovement.mPosition[1] = -0.2; // back off, but slowly + if(mDoorIter != mDoors.end() && ref.mData.getLocalRotation().rot[2] >= 1) // open + { + mDoorIter = mDoors.end(); + mBackOffDoor = false; + std::cout<<"open door id \""<::List::iterator mDoorIter; + MWWorld::CellRefList::List& mDoors; + void buildNewPath(const MWWorld::Ptr& actor); }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 988da4eb4..5d165488c 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -83,8 +83,7 @@ namespace MWMechanics * * New high level states. Not exactly as per vanilla (e.g. door stuff) * but the differences are required because our physics does not work like - * vanilla and therefore have to compensate/work around. Note also many of - * the actions now have reaction times. + * vanilla and therefore have to compensate/work around. * * [select node, [if stuck evade * build path] or remove nodes if near door] @@ -467,6 +466,10 @@ namespace MWMechanics const PathFinder& pathfinder) { // TODO: how to add these back in once the door opens? + // Idea: keep a list of detected closed doors (see aicombat.cpp) + // Every now and then check whether one of the doors is opened. (maybe + // at the end of playing idle?) If the door is opened then re-calculate + // allowed nodes starting from the spawn point. std::list paths = pathfinder.getPath(); while(paths.size() >= 2) { diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 9a748c052..6694de096 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -42,9 +42,12 @@ namespace MWMechanics for (; it != refList.end(); ++it) { MWWorld::LiveCellRef& ref = *it; - if(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && - ref.mData.getLocalRotation().rot[2] == (closed ? 0 : 1)) - return true; // found, stop searching + if(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) + if(closed && ref.mData.getLocalRotation().rot[2] == 0 || + !closed && ref.mData.getLocalRotation().rot[2] >= 1) + { + return true; // found, stop searching + } } return false; // none found } diff --git a/apps/openmw/mwmechanics/pathgrid.cpp b/apps/openmw/mwmechanics/pathgrid.cpp index ec647c1cb..cb9f051e3 100644 --- a/apps/openmw/mwmechanics/pathgrid.cpp +++ b/apps/openmw/mwmechanics/pathgrid.cpp @@ -197,11 +197,11 @@ namespace MWMechanics // both of these are set to zero in the constructor //mSCCId = 0; // how many strongly connected components in this cell //mSCCIndex = 0; - int pointsSize = mPathgrid->mPoints.size(); + int pointsSize = static_cast (mPathgrid->mPoints.size()); mSCCPoint.resize(pointsSize, std::pair (-1, -1)); mSCCStack.reserve(pointsSize); - for(int v = 0; v < static_cast (pointsSize); v++) + for(int v = 0; v < pointsSize; v++) { if(mSCCPoint[v].first == -1) // undefined (haven't visited) recursiveStrongConnect(v); @@ -249,7 +249,7 @@ namespace MWMechanics return path; // there is no path, return an empty path } - int graphSize = mGraph.size(); + int graphSize = static_cast (mGraph.size()); std::vector gScore (graphSize, -1); std::vector fScore (graphSize, -1); std::vector graphParent (graphSize, -1); From 3dfd08cf2de36dd081c25a75b1c637e9ede987ea Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 08:31:02 +1000 Subject: [PATCH 098/157] Cleanup and little tweaking. --- apps/openmw/mwmechanics/aicombat.cpp | 235 ++++++++++++--------------- apps/openmw/mwmechanics/aicombat.hpp | 5 +- apps/openmw/mwmechanics/pathgrid.hpp | 2 + apps/openmw/mwworld/cellstore.hpp | 2 +- 4 files changed, 107 insertions(+), 137 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index e94899359..335bc8702 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -35,7 +35,7 @@ namespace namespace MWMechanics { - static const float DOOR_CHECK_INTERVAL = 1.5f; + static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : @@ -50,70 +50,49 @@ namespace MWMechanics mBackOffDoor(false), mRotate(false), mMovement(), + mCell(NULL), mDoorIter(actor.getCell()->get().mList.end()), - mDoors(actor.getCell()->get().mList), + mDoors(actor.getCell()->get()), mDoorCheckDuration(0), mTargetAngle(0) { } /* - * The 'pursuit' part of AiCombat now has some memory to allow backtracking. - * The intention is to allow actors to detect being 'stuck' somewhere, whether - * in a river or facing a door, to go back to a known 'good' position. - * - * Each actor goes through these states of movement once alerted and an AiCombat - * package is queued (FIXME: below is a DRAFT proposal only): - * - * - Maybe remember the starting position so that the actor can return if - * lose sight of the target? Last Known Target Location Tracking - keep track - * of target positions every sec, paired with actor's location at the time - * - * - Get to the target if far away (need path finding), how close depends on - * the current best (selected?) weapon. As the actor moves, some breadcrumb - * is left (e.g. every second store a position in a FIFO) so that the actor - * can back track if necessary. - * - * - If the actor gets stuck (need a way of detecting this) then decide what - * to do next. For example, if next to a door then maybe back track one or - * two positions and check LOS of the target every x frames (reaction time?). - * Or maybe back track then look for a new path. - * - * - Once in weapon range, may need a strategy to get to the target for a - * strike (maybe there are others nearby attacking the same target). - * - * * Current AiCombat movement states (as of 0.29.0), ignoring the details of the * attack states such as CombatMove, Strike and ReadyToAttack: * - * +-----(within strike range)----->attack--(beyond strike range)-->follow - * | | ^ | | - * | | | | | - * pursue<----(beyond follow range)-----+ +----(within strike range)---+ | - * ^ | - * | | - * +--------------------------(beyond follow range)--------------------+ + * +----(within strike range)----->attack--(beyond strike range)-->follow + * | | ^ | | + * | | | | | + * pursue<---(beyond follow range)-----+ +----(within strike range)---+ | + * ^ | + * | | + * +-------------------------(beyond follow range)--------------------+ * * - * Below diagram is high level only, FIXME: code detail may be different: + * Below diagram is high level only, the code detail is a little different + * (but including those detail will just complicate the diagram w/o adding much) * - * +-----------(same)-------------->attack---------(same)---------->follow - * | |^^ | | - * | ||| | | - * | +--(same)-----------------+|+----------(same)------------+ | - * | | | in range | - * | | too far | | - * | | +---------------got hit or LOS<---+ | - * | <----+ | | | - * pursue<---------+ door | | - * ^^ <--> maybe stuck, check --------------> back up and wait | - * || | ^ | | ^ | | - * || | | | stuck | | | waited | - * || +---+ | +---+ | too long | - * || backtrack | | | - * |+----------------------+ go back? <-----------+ | - * | | - * +----------------------------(same)---------------------------------+ + * +----------(same)-------------->attack---------(same)---------->follow + * | |^^ ||| + * | ||| ||| + * | +--(same)-----------------+|+----------(same)------------+|| + * | | | || + * | | | (in range) || + * | <---+ (too far) | || + * pursue<-------------------------[door open]<-----+ || + * ^^^ | || + * ||| | || + * ||+----------evade-----+ | || + * || | [closed door] | || + * |+----> maybe stuck, check --------------> back up, check door || + * | ^ | ^ | ^ || + * | | | | | | || + * | | +---+ +---+ || + * | +-------------------------------------------------------+| + * | | + * +---------------------------(same)---------------------------------+ * * FIXME: * @@ -124,26 +103,6 @@ namespace MWMechanics * * Use the Observer Pattern to co-ordinate attacks, provide intelligence on * whether the target was hit, etc. - * - * TODO: - * - * Auto-generate large cubes or squares as a poor-man's navmesh? Many - * external cells do not have any pathgrids, and certainly none for flying - * or swimming. - * - * - * Called from: (check Doxygen as this comment could be out of date) - * - * OMW::Engine::go() | - * Ogre::Root::renderOneFrame() | - * Ogre::Root::... | ... detail omitted - * Ogre::Root::fireFrameRenderingQueued() | - * OMW::Engine::frameRenderingQueued() | virtual Ogre::FrameListener - * MWMechanics::MechanicsManager::update() | - * MWMechanics::Actors::update() | - * MWMechanics::Actors::updateActor() | - * MWMechanics::AiSequence::execute() | from priority queue mPackages - * MWMechanics::AiCombat::execute() | virtual MWMechanics::AiPackage */ bool AiCombat::execute (const MWWorld::Ptr& actor,float duration) { @@ -175,7 +134,6 @@ namespace MWMechanics mRotate = false; } - mTimerAttack -= duration; actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); @@ -190,6 +148,12 @@ namespace MWMechanics mTimerReact = 0; + bool cellChange = mCell && (actor.getCell() != mCell); + if(!mCell || cellChange) + { + mCell = actor.getCell(); + } + //actual attacking logic //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f float attackPeriod = 1.0f; @@ -298,7 +262,6 @@ namespace MWMechanics * (the shortcut really only applies to cells where pathgrids are * available, since the default path without pathgrids is direct to * target even if LOS is not achieved) - * */ float rangeMelee; float rangeCloseUp; @@ -395,55 +358,6 @@ namespace MWMechanics mMovement.mPosition[1] = 1; mReadyToAttack = false; - - // remember that this section gets updated every tReaction, which is - // currently hard coded at 250ms or 1/4 second - if(mObstacleCheck.check(actor, tReaction)) // true if evasive action needed - { - std::cout<<"found obstacle"<getCell()->isExterior()) - { - // Check all the doors in this cell - mDoors = cell->get().mList; // update list member - mDoorIter = mDoors.begin(); - Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); - for (; mDoorIter != mDoors.end(); ++mDoorIter) - { - MWWorld::LiveCellRef& ref = *mDoorIter; - if(actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED && - ref.mData.getLocalRotation().rot[2] == 0) - { - std::cout<<"closed door id \""< rangeMelee) @@ -476,26 +390,77 @@ namespace MWMechanics } } + // NOTE: This section gets updated every tReaction, which is currently hard + // coded at 250ms or 1/4 second + // + // TODO: Add a parameter to vary DURATION_SAME_SPOT? + if((distBetween > rangeMelee || mFollowTarget) && + mObstacleCheck.check(actor, tReaction)) // check if evasive action needed + { + // first check if we're walking into a door + mDoorCheckDuration += 1.0f; // add time taken for obstacle check + MWWorld::CellStore *cell = actor.getCell(); + if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior()) + { + mDoorCheckDuration = 0; + // Check all the doors in this cell + mDoors = cell->get(); // update + mDoorIter = mDoors.mList.begin(); + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + for (; mDoorIter != mDoors.mList.end(); ++mDoorIter) + { + MWWorld::LiveCellRef& ref = *mDoorIter; + float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility + if(actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && + ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening + { + //std::cout<<"closed door id \""<& ref = *mDoorIter; Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility + // TODO: add reaction to checking open doors if(mBackOffDoor && - actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < 1.5*1.5*MIN_DIST_TO_DOOR_SQUARED) + actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) { mMovement.mPosition[1] = -0.2; // back off, but slowly - if(mDoorIter != mDoors.end() && ref.mData.getLocalRotation().rot[2] >= 1) // open - { - mDoorIter = mDoors.end(); - mBackOffDoor = false; - std::cout<<"open door id \""<= 1) + { + mDoorIter = mDoors.mList.end(); + mBackOffDoor = false; + //std::cout<<"open door id \""<::List::iterator mDoorIter; - MWWorld::CellRefList::List& mDoors; + MWWorld::CellRefList& mDoors; void buildNewPath(const MWWorld::Ptr& actor); }; diff --git a/apps/openmw/mwmechanics/pathgrid.hpp b/apps/openmw/mwmechanics/pathgrid.hpp index ac545efbc..5d01dca00 100644 --- a/apps/openmw/mwmechanics/pathgrid.hpp +++ b/apps/openmw/mwmechanics/pathgrid.hpp @@ -30,6 +30,8 @@ namespace MWMechanics // the input parameters are pathgrid point indexes // the output list is in local (internal cells) or world (external // cells) co-ordinates + // + // NOTE: if start equals end an empty path is returned std::list aStarSearch(const int start, const int end) const; private: diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index b970afe1b..88b49ed1c 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -8,7 +8,7 @@ #include "esmstore.hpp" #include "cellreflist.hpp" -#include "../mwmechanics/pathgrid.hpp" +#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld namespace ESM { From 6733a1541efb66ec561c9e619969cb07e912f5d0 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 19 Apr 2014 19:03:31 -0400 Subject: [PATCH 099/157] Fixes to Crime System Victimless crimes were removed because victims may not exist in the case of stealing items. Is class now uses case insensitive comparison. Fixed issue with confiscation of stolen items. --- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 4 ++-- apps/openmw/mwscript/miscextensions.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5a3ff10de..e43bfe40d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1305,7 +1305,7 @@ namespace MWClass bool Npc::isClass(const MWWorld::Ptr& ptr, const std::string &className) const { - return ptr.get()->mBase->mClass == className; + return Misc::StringUtils::ciEqual(ptr.get()->mBase->mClass, className); } const ESM::GameSetting *Npc::fMinWalkSpeed; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d162e1037..147f6d03f 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -805,8 +805,8 @@ namespace MWMechanics // NOTE: int arg can be from itemTaken() so DON'T modify it, since it is // passed to reportCrime later on in this function. - // Only player can commit crime and no victimless crimes - if (ptr.getRefData().getHandle() != "player" || victim.isEmpty()) + // Only player can commit crime + if (ptr.getRefData().getHandle() != "player") return false; const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index aa554adc3..d8ba3ea22 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -814,6 +814,7 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); + MWBase::Environment::get().getWorld()->confiscateStolenItems(player); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; @@ -825,7 +826,6 @@ namespace MWScript { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(0); - MWBase::Environment::get().getWorld()->confiscateStolenItems(player); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; From 3484df0743670e68a70c688922fd55885e1bcb13 Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Sat, 19 Apr 2014 19:42:49 -0400 Subject: [PATCH 100/157] Fixed garage value found in beta's static analysis Source: https://forum.openmw.org/viewtopic.php?f=6&t=2059#p23844 --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 147f6d03f..84dc337c9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -823,6 +823,8 @@ namespace MWMechanics alarm = esmStore.get().find("iAlarmKilling")->getInt(); else if (type == OT_Theft) alarm = esmStore.get().find("iAlarmStealing")->getInt(); + else + return false; // Innocent until proven guilty bool reported = false; From 9bd31b62365a17341ddae54d2ca8af2d2e300125 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 10:06:03 +1000 Subject: [PATCH 101/157] Added reaction time and moved game setting variables init to the constructor. --- apps/openmw/mwmechanics/aiwander.cpp | 173 +++++++++++++++------------ apps/openmw/mwmechanics/aiwander.hpp | 5 + 2 files changed, 101 insertions(+), 77 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 5d165488c..12d86a5dc 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -30,6 +30,10 @@ namespace MWMechanics , mStuckCount(0) // TODO: maybe no longer needed , mDoorCheckDuration(0) , mTrimCurrentNode(false) + , mReaction(0) + , mGreetDistanceMultiplier(0) + , mGreetDistanceReset(0) + , mChance(0) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -47,8 +51,15 @@ namespace MWMechanics mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mPlayedIdle = 0; + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); mIdleChanceMultiplier = - MWBase::Environment::get().getWorld()->getStore().get().find("fIdleChanceMultiplier")->getFloat(); + store.get().find("fIdleChanceMultiplier")->getFloat(); + + mGreetDistanceMultiplier = + store.get().find("iGreetDistanceMultiplier")->getInt(); + mGreetDistanceReset = + store.get().find("fGreetDistanceReset")->getFloat(); + mChance = store.get().find("fVoiceIdleOdds")->getFloat(); mStoredAvailableNodes = false; mChooseAction = true; @@ -99,7 +110,7 @@ namespace MWMechanics * | player) | * +----------------------------------+ * - * TODO: non-time critical operations should be run once every 250ms or so. + * NOTE: non-time critical operations are run once every 250ms or so. * * TODO: It would be great if door opening/closing can be detected and pathgrid * links dynamically updated. Currently (0.29.0) AiWander allows choosing a @@ -128,6 +139,83 @@ namespace MWMechanics cStats.setDrawState(DrawState_Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); + + ESM::Position pos = actor.getRefData().getPosition(); + + // Check if an idle actor is too close to a door - if so start walking + mDoorCheckDuration += duration; + if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL) + { + mDoorCheckDuration = 0; // restart timer + if(mDistance && // actor is not intended to be stationary + mIdleNow && // but is in idle + !mWalking && // FIXME: some actors are idle while walking + proximityToDoor(actor)) // NOTE: checks interior cells only + { + mIdleNow = false; + mMoveNow = true; + mTrimCurrentNode = false; // just in case + } + } + + if(mWalking) // have not yet reached the destination + { + // turn towards the next point in mPath + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + + // Returns true if evasive action needs to be taken + if(mObstacleCheck.check(actor, duration)) + { + // first check if we're walking into a door + if(proximityToDoor(actor)) // NOTE: checks interior cells only + { + // remove allowed points then select another random destination + mTrimCurrentNode = true; + trimAllowedNodes(mAllowedNodes, mPathFinder); + mObstacleCheck.clear(); + mPathFinder.clearPath(); + mWalking = false; + mMoveNow = true; + } + else // probably walking into another NPC + { + // TODO: diagonal should have same animation as walk forward + // but doesn't seem to do that? + actor.getClass().getMovementSettings(actor).mPosition[0] = 1; + actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; + // change the angle a bit, too + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); + } + mStuckCount++; // TODO: maybe no longer needed + } +//#if 0 + // TODO: maybe no longer needed + if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset + { + //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; + mObstacleCheck.clear(); + + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mChooseAction = true; + } +//#endif + } + + + zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); + + mReaction += duration; + if(mReaction > 0.25f) // FIXME: hard coded constant + { + mReaction = 0; + return false; + } + + // NOTE: everything below get updated every mReaction + MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) { @@ -155,8 +243,6 @@ namespace MWMechanics } } - ESM::Position pos = actor.getRefData().getPosition(); - // Initialization to discover & store allowed node points for this actor. if(!mStoredAvailableNodes) { @@ -270,50 +356,29 @@ namespace MWMechanics int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); if (hello > 0) { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - float chance = store.get().find("fVoiceIdleOdds")->getFloat(); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // Don't bother if the player is out of hearing range - if (roll < chance && Ogre::Vector3(player.getRefData().getPosition().pos).distance(Ogre::Vector3(pos.pos)) < 1500) + if (roll < mChance && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } } - // Check if an idle actor is too close to a door - if so start walking - mDoorCheckDuration += duration; - if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL) - { - mDoorCheckDuration = 0; // restart timer - if(mDistance && // actor is not intended to be stationary - mIdleNow && // but is in idle - !mWalking && // FIXME: some actors are idle while walking - proximityToDoor(actor)) // NOTE: checks interior cells only - { - mIdleNow = false; - mMoveNow = true; - mTrimCurrentNode = false; // just in case - } - } - // Allow interrupting a walking actor to trigger a greeting if(mIdleNow || (mWalking && !mObstacleCheck.isNormalState())) { // Play a random voice greeting if the player gets too close - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); float helloDistance = hello; - int iGreetDistanceMultiplier = store.get().find("iGreetDistanceMultiplier")->getInt(); - helloDistance *= iGreetDistanceMultiplier; + helloDistance *= mGreetDistanceMultiplier; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).distance( + float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance( Ogre::Vector3(actor.getRefData().getPosition().pos)); - if(mWalking && playerDist <= helloDistance) + if(mWalking && playerDist <= helloDistance*helloDistance) { stopWalking(actor); mMoveNow = false; @@ -324,7 +389,7 @@ namespace MWMechanics if (!mSaidGreeting) { // TODO: check if actor is aware / has line of sight - if (playerDist <= helloDistance + if (playerDist <= helloDistance*helloDistance // Only play a greeting if the player is not moving && Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0) { @@ -335,8 +400,7 @@ namespace MWMechanics } else { - float fGreetDistanceReset = store.get().find("fGreetDistanceReset")->getFloat(); - if (playerDist >= fGreetDistanceReset * iGreetDistanceMultiplier) + if (playerDist >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier) mSaidGreeting = false; } @@ -413,51 +477,6 @@ namespace MWMechanics mWalking = false; mChooseAction = true; } - else if(mWalking) // have not yet reached the destination - { - // turn towards the next point in mPath - zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); - actor.getClass().getMovementSettings(actor).mPosition[1] = 1; - - // Returns true if evasive action needs to be taken - if(mObstacleCheck.check(actor, duration)) - { - // first check if we're walking into a door - if(proximityToDoor(actor)) // NOTE: checks interior cells only - { - // remove allowed points then select another random destination - mTrimCurrentNode = true; - trimAllowedNodes(mAllowedNodes, mPathFinder); - mObstacleCheck.clear(); - mPathFinder.clearPath(); - mWalking = false; - mMoveNow = true; - } - else // probably walking into another NPC - { - // TODO: diagonal should have same animation as walk forward - // but doesn't seem to do that? - actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; - // change the angle a bit, too - zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); - } - mStuckCount++; // TODO: maybe no longer needed - } -//#if 0 - // TODO: maybe no longer needed - if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset - { - //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; - mObstacleCheck.clear(); - - stopWalking(actor); - mMoveNow = false; - mWalking = false; - mChooseAction = true; - } -//#endif - } return false; // AiWander package not yet completed } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 59c21de89..a39862135 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -34,6 +34,9 @@ namespace MWMechanics bool mRepeat; bool mSaidGreeting; + int mGreetDistanceMultiplier; + float mGreetDistanceReset; + float mChance; // Cached current cell location int mCellX; @@ -69,6 +72,8 @@ namespace MWMechanics ObstacleCheck mObstacleCheck; float mDoorCheckDuration; int mStuckCount; + + float mReaction; // update some actions infrequently }; } From 3d26702f5e9d8086cba0c4c775dc975b85b94649 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 10:36:01 +1000 Subject: [PATCH 102/157] Make idle actors face the player. Turning animation is not smooth. --- apps/openmw/mwmechanics/aiwander.cpp | 31 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 12d86a5dc..167f61959 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -204,9 +204,6 @@ namespace MWMechanics //#endif } - - zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); - mReaction += duration; if(mReaction > 0.25f) // FIXME: hard coded constant { @@ -375,32 +372,40 @@ namespace MWMechanics helloDistance *= mGreetDistanceMultiplier; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - float playerDist = Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance( - Ogre::Vector3(actor.getRefData().getPosition().pos)); + Ogre::Vector3 playerPos(player.getRefData().getPosition().pos); + Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos); + float playerDistSqr = playerPos.squaredDistance(actorPos); - if(mWalking && playerDist <= helloDistance*helloDistance) + if(playerDistSqr <= helloDistance*helloDistance) { - stopWalking(actor); - mMoveNow = false; - mWalking = false; - mObstacleCheck.clear(); + if(mWalking) + { + stopWalking(actor); + mMoveNow = false; + mWalking = false; + mObstacleCheck.clear(); + } + Ogre::Vector3 dir = playerPos - actorPos; + float length = dir.length(); + + // FIXME: horrible hack, and does not turn smoothly + zTurn(actor, Ogre::Degree(Ogre::Radian(Ogre::Math::ACos(dir.y / length) * ((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees())); } if (!mSaidGreeting) { // TODO: check if actor is aware / has line of sight - if (playerDist <= helloDistance*helloDistance + if (playerDistSqr <= helloDistance*helloDistance // Only play a greeting if the player is not moving && Ogre::Vector3(player.getClass().getMovementSettings(player).mPosition).squaredLength() == 0) { mSaidGreeting = true; MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); - // TODO: turn to face player and interrupt the idle animation? } } else { - if (playerDist >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier) + if (playerDistSqr >= mGreetDistanceReset*mGreetDistanceReset * mGreetDistanceMultiplier*mGreetDistanceMultiplier) mSaidGreeting = false; } From 000afa48b73cad62b3e762998b3ef2042467af74 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 11:59:47 +1000 Subject: [PATCH 103/157] An attempt at making turning animation smoother. Copied some code from AiCombat for rotation. --- apps/openmw/mwmechanics/aiwander.cpp | 34 ++++++++++++++++++++++++---- apps/openmw/mwmechanics/aiwander.hpp | 5 ++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 167f61959..548eb60f6 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -34,6 +34,9 @@ namespace MWMechanics , mGreetDistanceMultiplier(0) , mGreetDistanceReset(0) , mChance(0) + , mRotate(false) + , mTargetAngle(0) + , mOriginalAngle(0) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -204,6 +207,12 @@ namespace MWMechanics //#endif } + if (mRotate) + { + if (zTurn(actor, Ogre::Degree(mTargetAngle))) + mRotate = false; + } + mReaction += duration; if(mReaction > 0.25f) // FIXME: hard coded constant { @@ -385,11 +394,28 @@ namespace MWMechanics mWalking = false; mObstacleCheck.clear(); } - Ogre::Vector3 dir = playerPos - actorPos; - float length = dir.length(); - // FIXME: horrible hack, and does not turn smoothly - zTurn(actor, Ogre::Degree(Ogre::Radian(Ogre::Math::ACos(dir.y / length) * ((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees())); + if(!mRotate) + { + Ogre::Vector3 dir = playerPos - actorPos; + float length = dir.length(); + + // FIXME: horrible hack + float faceAngle = Ogre::Radian(Ogre::Math::ACos(dir.y / length) * + ((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees(); + // an attempt at reducing the turning animation glitch + // TODO: doesn't seem to work very well + if(abs(faceAngle) > 10) + { + mTargetAngle = faceAngle; + mRotate = true; + } + } + } + else if(!mDistance) // FIXME: stationary actors go back to their normal position + { + //mTargetAngle = mOriginalAngle; + //mRotate = true; } if (!mSaidGreeting) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index a39862135..2ed1af3be 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -73,6 +73,11 @@ namespace MWMechanics float mDoorCheckDuration; int mStuckCount; + // the z rotation angle (degrees) we want to reach + // used every frame when mRotate is true + float mTargetAngle; + float mOriginalAngle; + bool mRotate; float mReaction; // update some actions infrequently }; } From 2b544d550b0844e928b66a69baf5f4117636c4e4 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 14:27:18 +1000 Subject: [PATCH 104/157] Fixed the rotation animation glitch. --- apps/openmw/mwmechanics/aiwander.cpp | 15 ++++++--------- apps/openmw/mwmechanics/steering.cpp | 3 +-- apps/openmw/mwmechanics/steering.hpp | 3 ++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 548eb60f6..988879379 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -209,7 +209,10 @@ namespace MWMechanics if (mRotate) { - if (zTurn(actor, Ogre::Degree(mTargetAngle))) + // Reduce the turning animation glitch by using a *HUGE* value of + // epsilon... TODO: a proper fix might be in either the physics or the + // animation subsystem + if (zTurn(actor, Ogre::Degree(mTargetAngle), Ogre::Degree(12))) mRotate = false; } @@ -220,7 +223,7 @@ namespace MWMechanics return false; } - // NOTE: everything below get updated every mReaction + // NOTE: everything below get updated every 0.25 seconds MWBase::World *world = MWBase::Environment::get().getWorld(); if(mDuration) @@ -404,7 +407,6 @@ namespace MWMechanics float faceAngle = Ogre::Radian(Ogre::Math::ACos(dir.y / length) * ((Ogre::Math::ASin(dir.x / length).valueRadians()>0)?1.0:-1.0)).valueDegrees(); // an attempt at reducing the turning animation glitch - // TODO: doesn't seem to work very well if(abs(faceAngle) > 10) { mTargetAngle = faceAngle; @@ -412,11 +414,6 @@ namespace MWMechanics } } } - else if(!mDistance) // FIXME: stationary actors go back to their normal position - { - //mTargetAngle = mOriginalAngle; - //mRotate = true; - } if (!mSaidGreeting) { @@ -436,7 +433,7 @@ namespace MWMechanics } // Check if idle animation finished - if(!checkIdle(actor, mPlayedIdle)) + if(!checkIdle(actor, mPlayedIdle) && !mRotate) { mPlayedIdle = 0; mIdleNow = false; diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp index d911fd81b..054107f73 100644 --- a/apps/openmw/mwmechanics/steering.cpp +++ b/apps/openmw/mwmechanics/steering.cpp @@ -10,7 +10,7 @@ namespace MWMechanics { -bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle) +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, Ogre::Degree epsilon) { Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]); Ogre::Radian diff (targetAngle - currentAngle); @@ -27,7 +27,6 @@ bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle) // The turning animation actually moves you slightly, so the angle will be wrong again. // Use epsilon to prevent jerkiness. - const Ogre::Degree epsilon (0.5); if (absDiff < epsilon) return true; diff --git a/apps/openmw/mwmechanics/steering.hpp b/apps/openmw/mwmechanics/steering.hpp index 504dc3ac3..4042b5412 100644 --- a/apps/openmw/mwmechanics/steering.hpp +++ b/apps/openmw/mwmechanics/steering.hpp @@ -12,7 +12,8 @@ namespace MWMechanics /// configure rotation settings for an actor to reach this target angle (eventually) /// @return have we reached the target angle? -bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle); +bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, + Ogre::Degree epsilon = Ogre::Degree(0.5)); } From 95ef874205ee741360563e9b2c6828a90fce0d4d Mon Sep 17 00:00:00 2001 From: Thomas Luppi Date: Sun, 20 Apr 2014 00:34:58 -0400 Subject: [PATCH 105/157] Gets default keybindings from scancode, not keyvalue. Gives keyboard set-up independant default keybindings; useful for international keyboards or dvorak users --- apps/openmw/mwinput/inputmanagerimp.cpp | 61 +++++++++++++------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e2d4f8cb2..616d8ae91 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -811,36 +811,37 @@ namespace MWInput // across different versions of OpenMW (in the case where another input action is added) std::map defaultKeyBindings; - defaultKeyBindings[A_Activate] = SDLK_SPACE; - defaultKeyBindings[A_MoveBackward] = SDLK_s; - defaultKeyBindings[A_MoveForward] = SDLK_w; - defaultKeyBindings[A_MoveLeft] = SDLK_a; - defaultKeyBindings[A_MoveRight] = SDLK_d; - defaultKeyBindings[A_ToggleWeapon] = SDLK_f; - defaultKeyBindings[A_ToggleSpell] = SDLK_r; - defaultKeyBindings[A_QuickKeysMenu] = SDLK_F1; - defaultKeyBindings[A_Console] = SDLK_F2; - defaultKeyBindings[A_Run] = SDLK_LSHIFT; - defaultKeyBindings[A_Sneak] = SDLK_LCTRL; - defaultKeyBindings[A_AutoMove] = SDLK_q; - defaultKeyBindings[A_Jump] = SDLK_e; - defaultKeyBindings[A_Journal] = SDLK_j; - defaultKeyBindings[A_Rest] = SDLK_t; - defaultKeyBindings[A_GameMenu] = SDLK_ESCAPE; - defaultKeyBindings[A_TogglePOV] = SDLK_TAB; - defaultKeyBindings[A_QuickKey1] = SDLK_1; - defaultKeyBindings[A_QuickKey2] = SDLK_2; - defaultKeyBindings[A_QuickKey3] = SDLK_3; - defaultKeyBindings[A_QuickKey4] = SDLK_4; - defaultKeyBindings[A_QuickKey5] = SDLK_5; - defaultKeyBindings[A_QuickKey6] = SDLK_6; - defaultKeyBindings[A_QuickKey7] = SDLK_7; - defaultKeyBindings[A_QuickKey8] = SDLK_8; - defaultKeyBindings[A_QuickKey9] = SDLK_9; - defaultKeyBindings[A_QuickKey10] = SDLK_0; - defaultKeyBindings[A_Screenshot] = SDLK_F12; - defaultKeyBindings[A_ToggleHUD] = SDLK_F11; - defaultKeyBindings[A_AlwaysRun] = SDLK_y; + //Gets the Keyvalue from the Scancode; gives the button in the same place reguardless of keyboard format + defaultKeyBindings[A_Activate] = SDL_GetKeyFromScancode(SDL_SCANCODE_SPACE); + defaultKeyBindings[A_MoveBackward] = SDL_GetKeyFromScancode(SDL_SCANCODE_S); + defaultKeyBindings[A_MoveForward] = SDL_GetKeyFromScancode(SDL_SCANCODE_W); + defaultKeyBindings[A_MoveLeft] = SDL_GetKeyFromScancode(SDL_SCANCODE_A); + defaultKeyBindings[A_MoveRight] = SDL_GetKeyFromScancode(SDL_SCANCODE_D); + defaultKeyBindings[A_ToggleWeapon] = SDL_GetKeyFromScancode(SDL_SCANCODE_F); + defaultKeyBindings[A_ToggleSpell] = SDL_GetKeyFromScancode(SDL_SCANCODE_R); + defaultKeyBindings[A_QuickKeysMenu] = SDL_GetKeyFromScancode(SDL_SCANCODE_F1); + defaultKeyBindings[A_Console] = SDL_GetKeyFromScancode(SDL_SCANCODE_F2); + defaultKeyBindings[A_Run] = SDL_GetKeyFromScancode(SDL_SCANCODE_LSHIFT); + defaultKeyBindings[A_Sneak] = SDL_GetKeyFromScancode(SDL_SCANCODE_LCTRL); + defaultKeyBindings[A_AutoMove] = SDL_GetKeyFromScancode(SDL_SCANCODE_Q); + defaultKeyBindings[A_Jump] = SDL_GetKeyFromScancode(SDL_SCANCODE_E); + defaultKeyBindings[A_Journal] = SDL_GetKeyFromScancode(SDL_SCANCODE_J); + defaultKeyBindings[A_Rest] = SDL_GetKeyFromScancode(SDL_SCANCODE_T); + defaultKeyBindings[A_GameMenu] = SDL_GetKeyFromScancode(SDL_SCANCODE_ESCAPE); + defaultKeyBindings[A_TogglePOV] = SDL_GetKeyFromScancode(SDL_SCANCODE_TAB); + defaultKeyBindings[A_QuickKey1] = SDL_GetKeyFromScancode(SDL_SCANCODE_1); + defaultKeyBindings[A_QuickKey2] = SDL_GetKeyFromScancode(SDL_SCANCODE_2); + defaultKeyBindings[A_QuickKey3] = SDL_GetKeyFromScancode(SDL_SCANCODE_3); + defaultKeyBindings[A_QuickKey4] = SDL_GetKeyFromScancode(SDL_SCANCODE_4); + defaultKeyBindings[A_QuickKey5] = SDL_GetKeyFromScancode(SDL_SCANCODE_5); + defaultKeyBindings[A_QuickKey6] = SDL_GetKeyFromScancode(SDL_SCANCODE_6); + defaultKeyBindings[A_QuickKey7] = SDL_GetKeyFromScancode(SDL_SCANCODE_7); + defaultKeyBindings[A_QuickKey8] = SDL_GetKeyFromScancode(SDL_SCANCODE_8); + defaultKeyBindings[A_QuickKey9] = SDL_GetKeyFromScancode(SDL_SCANCODE_9); + defaultKeyBindings[A_QuickKey10] = SDL_GetKeyFromScancode(SDL_SCANCODE_0); + defaultKeyBindings[A_Screenshot] = SDL_GetKeyFromScancode(SDL_SCANCODE_F12); + defaultKeyBindings[A_ToggleHUD] = SDL_GetKeyFromScancode(SDL_SCANCODE_F11); + defaultKeyBindings[A_AlwaysRun] = SDL_GetKeyFromScancode(SDL_SCANCODE_y); std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; From d6f1f129eb0df850ecb302dcc264b8f5b71724d9 Mon Sep 17 00:00:00 2001 From: Thomas Luppi Date: Sun, 20 Apr 2014 00:39:50 -0400 Subject: [PATCH 106/157] Forgot to capitalize something. whooooops dat y do --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 616d8ae91..1346b9e95 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -841,7 +841,7 @@ namespace MWInput defaultKeyBindings[A_QuickKey10] = SDL_GetKeyFromScancode(SDL_SCANCODE_0); defaultKeyBindings[A_Screenshot] = SDL_GetKeyFromScancode(SDL_SCANCODE_F12); defaultKeyBindings[A_ToggleHUD] = SDL_GetKeyFromScancode(SDL_SCANCODE_F11); - defaultKeyBindings[A_AlwaysRun] = SDL_GetKeyFromScancode(SDL_SCANCODE_y); + defaultKeyBindings[A_AlwaysRun] = SDL_GetKeyFromScancode(SDL_SCANCODE_Y); std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; From e17fab891da936e82c0bf77108bf146b800749cd Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 16:49:57 +1000 Subject: [PATCH 107/157] Suppress travis warning and one more tweak. --- apps/openmw/mwmechanics/aiwander.cpp | 2 +- apps/openmw/mwmechanics/obstacle.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 988879379..d552e045d 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -153,7 +153,7 @@ namespace MWMechanics if(mDistance && // actor is not intended to be stationary mIdleNow && // but is in idle !mWalking && // FIXME: some actors are idle while walking - proximityToDoor(actor)) // NOTE: checks interior cells only + proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6*1.6)) // NOTE: checks interior cells only { mIdleNow = false; mMoveNow = true; diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 6694de096..181560935 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -43,8 +43,8 @@ namespace MWMechanics { MWWorld::LiveCellRef& ref = *it; if(pos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) - if(closed && ref.mData.getLocalRotation().rot[2] == 0 || - !closed && ref.mData.getLocalRotation().rot[2] >= 1) + if((closed && ref.mData.getLocalRotation().rot[2] == 0) || + (!closed && ref.mData.getLocalRotation().rot[2] >= 1)) { return true; // found, stop searching } From 6a1435c49e7ed6a83bb92801ed6c7303872eb78a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 20 Apr 2014 17:59:08 +1000 Subject: [PATCH 108/157] Remove rotation check that was freezing NPC's. --- apps/openmw/mwmechanics/aiwander.cpp | 3 +-- apps/openmw/mwmechanics/aiwander.hpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index d552e045d..ad94be0eb 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -36,7 +36,6 @@ namespace MWMechanics , mChance(0) , mRotate(false) , mTargetAngle(0) - , mOriginalAngle(0) , mSaidGreeting(false) { for(unsigned short counter = 0; counter < mIdle.size(); counter++) @@ -433,7 +432,7 @@ namespace MWMechanics } // Check if idle animation finished - if(!checkIdle(actor, mPlayedIdle) && !mRotate) + if(!checkIdle(actor, mPlayedIdle)) { mPlayedIdle = 0; mIdleNow = false; diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 2ed1af3be..fe14abeb6 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -76,7 +76,6 @@ namespace MWMechanics // the z rotation angle (degrees) we want to reach // used every frame when mRotate is true float mTargetAngle; - float mOriginalAngle; bool mRotate; float mReaction; // update some actions infrequently }; From 86bd2f48dc08da0878264a57b0204db72304e111 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 20 Apr 2014 17:28:10 +0200 Subject: [PATCH 109/157] removed an unused function --- apps/opencs/model/world/refcollection.cpp | 11 ----------- apps/opencs/model/world/refcollection.hpp | 5 ----- 2 files changed, 16 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 9ee59bd1e..4fdd97909 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -38,14 +38,3 @@ std::string CSMWorld::RefCollection::getNewId() stream << "ref#" << mNextId++; return stream.str(); } - -void CSMWorld::RefCollection::cloneRecord(const std::string& origin, - const std::string& destination, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType) -{ - Record clone(getRecord(origin)); - clone.mState = CSMWorld::RecordBase::State_ModifiedOnly; - clone.get().mId = destination; - insertRecord(clone, getAppendIndex(destination, type), type); -} \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index dcfd2036c..173efba05 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -26,11 +26,6 @@ namespace CSMWorld ///< Load a sequence of references. std::string getNewId(); - - void cloneRecord(const std::string& origin, - const std::string& destination, - const CSMWorld::UniversalId::Type type, - const CSMWorld::UniversalId::ArgumentType argumentType); }; } From 79f32546e194d435e877f29b6216ea165be1218c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Apr 2014 19:28:25 +0200 Subject: [PATCH 110/157] Dead link fix --- cmake/FindLIBUNSHIELD.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FindLIBUNSHIELD.cmake b/cmake/FindLIBUNSHIELD.cmake index 4f4e98a1c..f0fa4cc82 100644 --- a/cmake/FindLIBUNSHIELD.cmake +++ b/cmake/FindLIBUNSHIELD.cmake @@ -4,7 +4,7 @@ # LIBUNSHIELD_FOUND, if false, do not try to link to LibUnshield # LIBUNSHIELD_INCLUDE_DIR, where to find the headers # -# Created by Tom Mason (wheybags) for OpenMW (http://openmw.com), based on FindMPG123.cmake +# Created by Tom Mason (wheybags) for OpenMW (http://openmw.org), based on FindMPG123.cmake # # Ripped off from other sources. In fact, this file is so generic (I # just did a search and replace on another file) that I wonder why the From 262e9596996bb29d4e000ded8235682988818613 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Apr 2014 19:28:39 +0200 Subject: [PATCH 111/157] Remove unused slice_array --- components/CMakeLists.txt | 2 +- components/misc/slice_array.hpp | 82 --------------------------------- 2 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 components/misc/slice_array.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2b06babe7..38be5b11a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -49,7 +49,7 @@ add_component_dir (esm ) add_component_dir (misc - slice_array stringops + utf8stream stringops ) add_component_dir (files diff --git a/components/misc/slice_array.hpp b/components/misc/slice_array.hpp deleted file mode 100644 index cd58e7bd6..000000000 --- a/components/misc/slice_array.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (slice_array.h) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -#ifndef MISC_SLICE_ARRAY_H -#define MISC_SLICE_ARRAY_H - -// A simple array implementation containing a pointer and a -// length. Used for holding slices into a data buffer. -#include -#include - -namespace Misc -{ - -template -struct SliceArray -{ - const T* ptr; - size_t length; - - /// Initialize to zero length - SliceArray() : ptr(0), length(0) {} - - /// Initialize from pointer + length - SliceArray(const T* _ptr, size_t _length) - : ptr(_ptr), length(_length) {} - - /// Initialize from null-terminated string - SliceArray(const char* str) - { - ptr = str; - length = strlen(str); - } - - bool operator==(SliceArray &t) - { - return - length == t.length && - (memcmp(ptr,t.ptr, length*sizeof(T)) == 0); - } - - /// Only use this for stings - bool operator==(const char* str) - { - return - str[length] == 0 && - (strncmp(ptr, str, length) == 0); - } - - /** This allocates a copy of the data. Only use this for debugging - and error messages. */ - std::string toString() - { return std::string(ptr,length); } -}; - -typedef SliceArray SString; -typedef SliceArray IntArray; -typedef SliceArray FloatArray; - -} - -#endif From 2cb9f38a45596859f2f3dc7fe1db51cf2b78bf36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 20 Apr 2014 19:34:58 +0200 Subject: [PATCH 112/157] Changed crime IDs for legacy savegames. Not an issue, but it was inconsistent. --- components/esm/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 7a2c03b48..70f795afe 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -26,9 +26,9 @@ void ESM::Player::load (ESMReader &esm) mBirthsign = esm.getHNString ("SIGN"); - mCurrentCrimeId = 0; + mCurrentCrimeId = -1; esm.getHNOT (mCurrentCrimeId, "CURD"); - mPayedCrimeId = 0; + mPayedCrimeId = -1; esm.getHNOT (mPayedCrimeId, "PAYD"); } From 6929e541ddd21b211faa2b3817d0a763f0f31777 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Apr 2014 05:30:14 +0200 Subject: [PATCH 113/157] Fix texture name issue with Vality's Bitter Coast Mod --- components/nifogre/material.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/nifogre/material.cpp b/components/nifogre/material.cpp index 4dae1a93d..3a87e1d52 100644 --- a/components/nifogre/material.cpp +++ b/components/nifogre/material.cpp @@ -67,6 +67,10 @@ std::string NIFMaterialLoader::findTextureName(const std::string &filename) std::string texname = filename; Misc::StringUtils::toLower(texname); + // Apparently, leading separators are allowed + while (texname.size() && (texname[0] == '/' || texname[0] == '\\')) + texname.erase(0, 1); + if(texname.compare(0, sizeof(path)-1, path) != 0 && texname.compare(0, sizeof(path2)-1, path2) != 0) texname = path + texname; From 9998c2783eb4813137c61451085615f7ba8f70bc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 21 Apr 2014 17:37:06 +0200 Subject: [PATCH 114/157] Fix travis Why again do we need a unit test for something that was never used? --- .../components/misc/test_slicearray.cpp | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 apps/openmw_test_suite/components/misc/test_slicearray.cpp diff --git a/apps/openmw_test_suite/components/misc/test_slicearray.cpp b/apps/openmw_test_suite/components/misc/test_slicearray.cpp deleted file mode 100644 index ab63e56c4..000000000 --- a/apps/openmw_test_suite/components/misc/test_slicearray.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include "components/misc/slice_array.hpp" - -struct SliceArrayTest : public ::testing::Test -{ - protected: - virtual void SetUp() - { - } - - virtual void TearDown() - { - } -}; - -TEST_F(SliceArrayTest, hello_string) -{ - Misc::SString s("hello"); - ASSERT_EQ(sizeof("hello") - 1, s.length); - ASSERT_FALSE(s=="hel"); - ASSERT_FALSE(s=="hell"); - ASSERT_TRUE(s=="hello"); -} - -TEST_F(SliceArrayTest, othello_string_with_offset_2_and_size_4) -{ - Misc::SString s("othello" + 2, 4); - ASSERT_EQ(sizeof("hell") - 1, s.length); - ASSERT_FALSE(s=="hel"); - ASSERT_TRUE(s=="hell"); - ASSERT_FALSE(s=="hello"); -} - From 1ab51306c3dfafdc5007dc8f15a96d9daf103118 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Apr 2014 17:47:20 +0200 Subject: [PATCH 115/157] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index eb427a22b..499c55eeb 100644 --- a/credits.txt +++ b/credits.txt @@ -70,6 +70,7 @@ Sebastian Wick (swick) Sergey Shambir sir_herrbatka Sylvain Thesnieres (Garvek) +Thomas Luppi (Digmaster) Tom Mason (wheybags) Torben Leif Carrington (TorbenC) From d3148555fad4ed85bb9e592003804e4f185f9b14 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Apr 2014 22:16:14 -0500 Subject: [PATCH 116/157] Delete old framework files --- .../model/settings/settingcontainer.cpp | 82 ------- .../model/settings/settingcontainer.hpp | 47 ---- apps/opencs/model/settings/settingsitem.cpp | 104 --------- apps/opencs/model/settings/settingsitem.hpp | 67 ------ apps/opencs/view/settings/abstractblock.cpp | 112 --------- apps/opencs/view/settings/abstractblock.hpp | 82 ------- apps/opencs/view/settings/abstractpage.cpp | 44 ---- apps/opencs/view/settings/abstractpage.hpp | 70 ------ apps/opencs/view/settings/abstractwidget.cpp | 78 ------- apps/opencs/view/settings/abstractwidget.hpp | 69 ------ apps/opencs/view/settings/blankpage.cpp | 50 ---- apps/opencs/view/settings/blankpage.hpp | 28 --- apps/opencs/view/settings/customblock.cpp | 121 ---------- apps/opencs/view/settings/customblock.hpp | 47 ---- .../view/settings/datadisplayformatpage.cpp | 57 ----- .../view/settings/datadisplayformatpage.hpp | 33 --- apps/opencs/view/settings/editorpage.cpp | 53 ----- apps/opencs/view/settings/editorpage.hpp | 33 --- apps/opencs/view/settings/groupblock.cpp | 108 --------- apps/opencs/view/settings/groupblock.hpp | 43 ---- apps/opencs/view/settings/groupbox.cpp | 56 ----- apps/opencs/view/settings/groupbox.hpp | 28 --- apps/opencs/view/settings/itemblock.cpp | 115 ---------- apps/opencs/view/settings/itemblock.hpp | 48 ---- apps/opencs/view/settings/proxyblock.cpp | 152 ------------- apps/opencs/view/settings/proxyblock.hpp | 52 ----- apps/opencs/view/settings/settingwidget.cpp | 1 - apps/opencs/view/settings/settingwidget.hpp | 214 ------------------ apps/opencs/view/settings/toggleblock.cpp | 80 ------- apps/opencs/view/settings/toggleblock.hpp | 29 --- .../view/settings/usersettingsdialog.cpp | 119 ---------- .../view/settings/usersettingsdialog.hpp | 71 ------ apps/opencs/view/settings/windowpage.cpp | 144 ------------ apps/opencs/view/settings/windowpage.hpp | 34 --- 34 files changed, 2471 deletions(-) delete mode 100644 apps/opencs/model/settings/settingcontainer.cpp delete mode 100644 apps/opencs/model/settings/settingcontainer.hpp delete mode 100644 apps/opencs/model/settings/settingsitem.cpp delete mode 100644 apps/opencs/model/settings/settingsitem.hpp delete mode 100644 apps/opencs/view/settings/abstractblock.cpp delete mode 100644 apps/opencs/view/settings/abstractblock.hpp delete mode 100644 apps/opencs/view/settings/abstractpage.cpp delete mode 100644 apps/opencs/view/settings/abstractpage.hpp delete mode 100644 apps/opencs/view/settings/abstractwidget.cpp delete mode 100644 apps/opencs/view/settings/abstractwidget.hpp delete mode 100644 apps/opencs/view/settings/blankpage.cpp delete mode 100644 apps/opencs/view/settings/blankpage.hpp delete mode 100644 apps/opencs/view/settings/customblock.cpp delete mode 100644 apps/opencs/view/settings/customblock.hpp delete mode 100755 apps/opencs/view/settings/datadisplayformatpage.cpp delete mode 100755 apps/opencs/view/settings/datadisplayformatpage.hpp delete mode 100644 apps/opencs/view/settings/editorpage.cpp delete mode 100644 apps/opencs/view/settings/editorpage.hpp delete mode 100644 apps/opencs/view/settings/groupblock.cpp delete mode 100644 apps/opencs/view/settings/groupblock.hpp delete mode 100644 apps/opencs/view/settings/groupbox.cpp delete mode 100644 apps/opencs/view/settings/groupbox.hpp delete mode 100644 apps/opencs/view/settings/itemblock.cpp delete mode 100644 apps/opencs/view/settings/itemblock.hpp delete mode 100644 apps/opencs/view/settings/proxyblock.cpp delete mode 100644 apps/opencs/view/settings/proxyblock.hpp delete mode 100644 apps/opencs/view/settings/settingwidget.cpp delete mode 100644 apps/opencs/view/settings/settingwidget.hpp delete mode 100644 apps/opencs/view/settings/toggleblock.cpp delete mode 100644 apps/opencs/view/settings/toggleblock.hpp delete mode 100644 apps/opencs/view/settings/usersettingsdialog.cpp delete mode 100644 apps/opencs/view/settings/usersettingsdialog.hpp delete mode 100644 apps/opencs/view/settings/windowpage.cpp delete mode 100644 apps/opencs/view/settings/windowpage.hpp diff --git a/apps/opencs/model/settings/settingcontainer.cpp b/apps/opencs/model/settings/settingcontainer.cpp deleted file mode 100644 index a75a84ec5..000000000 --- a/apps/opencs/model/settings/settingcontainer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "settingcontainer.hpp" - -#include - -CSMSettings::SettingContainer::SettingContainer(QObject *parent) : - QObject(parent), mValue (0), mValues (0) -{ -} - -CSMSettings::SettingContainer::SettingContainer(const QString &value, QObject *parent) : - QObject(parent), mValue (new QString (value)), mValues (0) -{ -} - -void CSMSettings::SettingContainer::insert (const QString &value) -{ - if (mValue) - { - mValues = new QStringList; - mValues->push_back (*mValue); - mValues->push_back (value); - - delete mValue; - mValue = 0; - } - else - { - delete mValue; - mValue = new QString (value); - } - -} - -void CSMSettings::SettingContainer::update (const QString &value, int index) -{ - if (isEmpty()) - mValue = new QString(value); - - else if (mValue) - *mValue = value; - - else if (mValues) - mValues->replace(index, value); -} - -QString CSMSettings::SettingContainer::getValue (int index) const -{ - QString retVal(""); - - //if mValue is valid, it's a single-value property. - //ignore the index and return the value - if (mValue) - retVal = *mValue; - - //otherwise, if it's a multivalued property - //return the appropriate value at the index - else if (mValues) - { - if (index == -1) - retVal = mValues->at(0); - - else if (index < mValues->size()) - retVal = mValues->at(index); - } - - return retVal; -} - -int CSMSettings::SettingContainer::count () const -{ - int retVal = 0; - - if (!isEmpty()) - { - if (mValues) - retVal = mValues->size(); - else - retVal = 1; - } - - return retVal; -} diff --git a/apps/opencs/model/settings/settingcontainer.hpp b/apps/opencs/model/settings/settingcontainer.hpp deleted file mode 100644 index 5af298a57..000000000 --- a/apps/opencs/model/settings/settingcontainer.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef SETTINGCONTAINER_HPP -#define SETTINGCONTAINER_HPP - -#include - -class QStringList; - -namespace CSMSettings -{ - class SettingContainer : public QObject - { - Q_OBJECT - - QString *mValue; - QStringList *mValues; - - public: - - explicit SettingContainer (QObject *parent = 0); - explicit SettingContainer (const QString &value, QObject *parent = 0); - - /// add a value to the container - /// multiple values supported - void insert (const QString &value); - - /// update an existing value - /// index specifies multiple values - void update (const QString &value, int index = 0); - - /// return value at specified index - QString getValue (int index = -1) const; - - /// retrieve list of all values - inline QStringList *getValues() const { return mValues; } - - /// return size of list - int count() const; - - /// test for empty container - /// useful for default-constructed containers returned by QMap when invalid key is passed - inline bool isEmpty() const { return (!mValue && !mValues); } - - inline bool isMultiValue() const { return (mValues); } - }; -} - -#endif // SETTINGCONTAINER_HPP diff --git a/apps/opencs/model/settings/settingsitem.cpp b/apps/opencs/model/settings/settingsitem.cpp deleted file mode 100644 index 5d897448a..000000000 --- a/apps/opencs/model/settings/settingsitem.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "settingsitem.hpp" - -#include - -bool CSMSettings::SettingsItem::updateItem (const QStringList *values) -{ - QStringList::ConstIterator it = values->begin(); - - //if the item is not multivalued, - //save the last value passed in the container - if (!mIsMultiValue) - { - it = values->end(); - it--; - } - - bool isValid = true; - QString value (""); - - for (; it != values->end(); ++it) - { - value = *it; - isValid = validate(value); - - //skip only the invalid values - if (!isValid) - continue; - - insert(value); - } - - return isValid; -} - -bool CSMSettings::SettingsItem::updateItem (const QString &value) -{ - //takes a value or a SettingsContainer and updates itself accordingly - //after validating the data against it's own definition - - QString newValue = value; - - if (!validate (newValue)) - newValue = mDefaultValue; - - bool success = (getValue() != newValue); - - if (success) - { - if (mIsMultiValue) - insert (newValue); - else - update (newValue); - } - return success; -} - -bool CSMSettings::SettingsItem::updateItem(int valueListIndex) -{ - bool success = false; - - if (mValueList) - { - if (mValueList->size() > valueListIndex) - success = updateItem (mValueList->at(valueListIndex)); - } - return success; -} - -bool CSMSettings::SettingsItem::validate (const QString &value) -{ - //if there is no value list or value pair, there is no validation to do - bool isValid = !(!mValueList->isEmpty() || mValuePair); - - if (!isValid && !mValueList->isEmpty()) - { - for (QStringList::Iterator it = mValueList->begin(); it != mValueList->end(); ++it) - // foreach (QString listItem, *mValueList) - { - isValid = (value == *it); - - if (isValid) - break; - } - } - else if (!isValid && mValuePair) - { - int numVal = value.toInt(); - - isValid = (numVal > mValuePair->left.toInt() && numVal < mValuePair->right.toInt()); - } - - return isValid; -} - -void CSMSettings::SettingsItem::setDefaultValue (const QString &value) -{ - mDefaultValue = value; - update (value); -} - -QString CSMSettings::SettingsItem::getDefaultValue() const -{ - return mDefaultValue; -} diff --git a/apps/opencs/model/settings/settingsitem.hpp b/apps/opencs/model/settings/settingsitem.hpp deleted file mode 100644 index 87a85e8e4..000000000 --- a/apps/opencs/model/settings/settingsitem.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef SETTINGSITEM_HPP -#define SETTINGSITEM_HPP - -#include -#include "support.hpp" -#include "settingcontainer.hpp" - -namespace CSMSettings -{ - /// Represents a setting including metadata - /// (valid values, ranges, defaults, and multivalue status - class SettingsItem : public SettingContainer - { - QStringPair *mValuePair; - QStringList *mValueList; - bool mIsMultiValue; - QString mDefaultValue; - - public: - explicit SettingsItem(QString name, bool isMultiValue, - const QString& defaultValue, QObject *parent = 0) - : SettingContainer(defaultValue, parent), - mIsMultiValue (isMultiValue), mValueList (0), - mValuePair (0), mDefaultValue (defaultValue) - { - QObject::setObjectName(name); - } - - /// updateItem overloads for updating setting value - /// provided a list of values (multi-valued), - /// a specific value - /// or an index value corresponding to the mValueList - bool updateItem (const QStringList *values); - bool updateItem (const QString &value); - bool updateItem (int valueListIndex); - - /// retrieve list of valid values for setting - inline QStringList *getValueList() { return mValueList; } - - /// write list of valid values for setting - inline void setValueList (QStringList *valueList) { mValueList = valueList; } - - /// valuePair used for spin boxes (max / min) - inline QStringPair *getValuePair() { return mValuePair; } - - /// set value range (spinbox / integer use) - inline void setValuePair (QStringPair valuePair) - { - delete mValuePair; - mValuePair = new QStringPair(valuePair); - } - - inline bool isMultivalue () { return mIsMultiValue; } - - void setDefaultValue (const QString &value); - QString getDefaultValue () const; - - private: - - /// Verifies that the supplied value is one of the following: - /// 1. Within the limits of the value pair (min / max) - /// 2. One of the values indicated in the value list - bool validate (const QString &value); - }; -} -#endif // SETTINGSITEM_HPP - diff --git a/apps/opencs/view/settings/abstractblock.cpp b/apps/opencs/view/settings/abstractblock.cpp deleted file mode 100644 index 65825ce8b..000000000 --- a/apps/opencs/view/settings/abstractblock.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "abstractblock.hpp" - -CSVSettings::AbstractBlock::AbstractBlock(QWidget* parent) - : QObject (parent), mBox ( new GroupBox (parent) ), mWidgetParent (parent) -{} - -CSVSettings::AbstractBlock::AbstractBlock(bool isVisible, QWidget* parent) - : QObject (parent), mBox ( new GroupBox (isVisible, parent)), mWidgetParent (parent) -{} - -QLayout *CSVSettings::AbstractBlock::createLayout (Orientation direction, - bool isZeroMargin, QWidget* parent) -{ - QLayout *layout = 0; - - if (direction == Orient_Vertical) - layout = new QVBoxLayout (parent); - else - layout = new QHBoxLayout (parent); - - if (isZeroMargin) - layout->setContentsMargins(0, 0, 0, 0); - - return layout; -} - -QGroupBox *CSVSettings::AbstractBlock::getGroupBox() -{ - return mBox; -} - -CSVSettings::AbstractWidget *CSVSettings::AbstractBlock::buildWidget (const QString& widgetName, WidgetDef &def, - QLayout *layout, bool isConnected) const -{ - AbstractWidget *widg = 0; - - switch (def.type) - { - - case Widget_RadioButton: - widg = new SettingWidget (def, layout, mBox); - break; - - case Widget_SpinBox: - widg = new SettingWidget (def, layout, mBox); - break; - - case Widget_CheckBox: - widg = new SettingWidget (def, layout, mBox); - break; - - case Widget_LineEdit: - widg = new SettingWidget (def, layout, mBox); - break; - - case Widget_ListBox: - widg = new SettingWidget (def, layout, mBox); - break; - - case Widget_ComboBox: - widg = new SettingWidget (def, layout, mBox); - break; - - default: - break; - }; - - if (!mBox->layout()) - mBox->setLayout(widg->getLayout()); - - widg->widget()->setObjectName(widgetName); - - if (isConnected) - connect (widg, SIGNAL (signalUpdateItem (const QString &)), this, SLOT (slotUpdate (const QString &))); - connect (this, SIGNAL (signalUpdateWidget (const QString &)), widg, SLOT (slotUpdateWidget (const QString &) )); - - return widg; -} - -void CSVSettings::AbstractBlock::setVisible (bool isVisible) -{ - mBox->setBorderVisibility (isVisible); -} - -bool CSVSettings::AbstractBlock::isVisible () const -{ - return mBox->borderVisibile(); -} - -QWidget *CSVSettings::AbstractBlock::getParent() const -{ - return mWidgetParent; -} - -void CSVSettings::AbstractBlock::slotUpdate (const QString &value) -{ - slotUpdateSetting (objectName(), value); -} - -void CSVSettings::AbstractBlock::slotSetEnabled(bool value) -{ - mBox->setEnabled(value); -} - -void CSVSettings::AbstractBlock::slotUpdateSetting (const QString &settingName, const QString &settingValue) -{ - bool doEmit = true; - updateBySignal (settingName, settingValue, doEmit); - - if (doEmit) - emit signalUpdateSetting (settingName, settingValue); -} diff --git a/apps/opencs/view/settings/abstractblock.hpp b/apps/opencs/view/settings/abstractblock.hpp deleted file mode 100644 index 361339fe2..000000000 --- a/apps/opencs/view/settings/abstractblock.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ABSTRACTBLOCK_HPP -#define ABSTRACTBLOCK_HPP - -#include -#include - -#include "settingwidget.hpp" -#include "../../model/settings/settingsitem.hpp" -#include "groupbox.hpp" - -namespace CSVSettings -{ - - /// Abstract base class for all blocks - class AbstractBlock : public QObject - { - Q_OBJECT - - protected: - - typedef QMap SettingsItemMap; - GroupBox *mBox; - QWidget *mWidgetParent; - - public: - - explicit AbstractBlock (QWidget *parent = 0); - explicit AbstractBlock (bool isVisible, QWidget *parent = 0); - - QGroupBox *getGroupBox(); - void setVisible (bool isVisible); - bool isVisible() const; - - virtual CSMSettings::SettingList *getSettings() = 0; - - /// update settings found in the passed map and are encapsulated by the block - virtual bool updateSettings (const CSMSettings::SettingMap &settings) = 0; - - /// update callback function called from update slot - /// used for updating application-level settings in the editor - virtual bool updateBySignal (const QString &name, const QString &value, bool &doEmit) - { return false; } - - protected: - - /// Creates the layout for the block's QGroupBox - QLayout *createLayout (Orientation direction, bool isZeroMargin, QWidget* parent = 0); - - /// Creates widgets that exist as direct children of the block - AbstractWidget *buildWidget (const QString &widgetName, WidgetDef &wDef, - QLayout *layout = 0, bool isConnected = true) const; - - QWidget *getParent() const; - - public slots: - - /// enables / disables block-level widgets based on signals from other widgets - /// used in ToggleBlock - void slotSetEnabled (bool value); - - /// receives updates to applicaion-level settings in the Editor - void slotUpdateSetting (const QString &settingName, const QString &settingValue); - - private slots: - - /// receives updates to a setting in the block pushed from the application level - void slotUpdate (const QString &value); - - signals: - - /// signal to UserSettings instance - void signalUpdateSetting (const QString &propertyName, const QString &propertyValue); - - /// signal to widget for updating widget value - void signalUpdateWidget (const QString & value); - - /// ProxyBlock use only. - /// Name and value correspond to settings for which the block is a proxy. - void signalUpdateProxySetting (const QString &propertyName, const QString &propertyValue); - }; -} -#endif // ABSTRACTBLOCK_HPP diff --git a/apps/opencs/view/settings/abstractpage.cpp b/apps/opencs/view/settings/abstractpage.cpp deleted file mode 100644 index e6c605275..000000000 --- a/apps/opencs/view/settings/abstractpage.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "abstractpage.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -CSVSettings::AbstractPage::AbstractPage(QWidget *parent): - QWidget(parent) -{ - QGridLayout *pageLayout = new QGridLayout(this); - setLayout (pageLayout); -} - -CSVSettings::AbstractPage::AbstractPage(const QString &pageName, QWidget *parent): - QWidget(parent) -{ - QWidget::setObjectName (pageName); - - QGridLayout *pageLayout = new QGridLayout(this); - setLayout (pageLayout); -} - -CSVSettings::AbstractPage::~AbstractPage() -{ -} - -CSMSettings::SettingList *CSVSettings::AbstractPage::getSettings() -{ - CSMSettings::SettingList *settings = new CSMSettings::SettingList(); - - foreach (AbstractBlock *block, mAbstractBlocks) - { - CSMSettings::SettingList *groupSettings = block->getSettings(); - settings->append (*groupSettings); - } - - return settings; -} diff --git a/apps/opencs/view/settings/abstractpage.hpp b/apps/opencs/view/settings/abstractpage.hpp deleted file mode 100644 index 77ef4524f..000000000 --- a/apps/opencs/view/settings/abstractpage.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef ABSTRACTPAGE_HPP -#define ABSTRACTPAGE_HPP - -#include -#include -#include - -#include "abstractblock.hpp" - -class SettingMap; -class SettingList; - -namespace CSVSettings { - - typedef QList AbstractBlockList; - - /// Abstract base class for all setting pages in the dialog - - /// \todo Scripted implementation of settings should eliminate the need - /// \todo derive page classes. - /// \todo AbstractPage should be replaced with a general page construction class. - class AbstractPage: public QWidget - { - - protected: - - AbstractBlockList mAbstractBlocks; - - public: - - AbstractPage(QWidget *parent = 0); - AbstractPage (const QString &pageName, QWidget* parent = 0); - - ~AbstractPage(); - - virtual void setupUi() = 0; - - /// triggers widgiet initialization at the page level. All widgets updated to - /// current setting values - virtual void initializeWidgets (const CSMSettings::SettingMap &settings) = 0; - - /// retrieve the list of settings local to the page. - CSMSettings::SettingList *getSettings(); - - void setObjectName(); - - protected: - - /// Create a block for the page. - /// Block is constructed using passed definition struct - /// Page level-layout is created and assigned - template - AbstractBlock *buildBlock (T *def) - { - S *block = new S (this); - int ret = block->build (def); - - if (ret < 0) - return 0; - - QGroupBox *box = block->getGroupBox(); - QWidget::layout()->addWidget (box); - - return block; - } - - }; -} - -#endif // ABSTRACTPAGE_HPP diff --git a/apps/opencs/view/settings/abstractwidget.cpp b/apps/opencs/view/settings/abstractwidget.cpp deleted file mode 100644 index f268d3b27..000000000 --- a/apps/opencs/view/settings/abstractwidget.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "abstractwidget.hpp" - -#include -#include - -void CSVSettings::AbstractWidget::build(QWidget *widget, WidgetDef &def, bool noLabel) -{ - if (!mLayout) - createLayout(def.orientation, true); - - buildLabelAndWidget (widget, def, noLabel); - -} - -void CSVSettings::AbstractWidget::buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel) -{ - if (def.widgetWidth > -1) - widget->setFixedWidth (def.widgetWidth); - - if (!(def.caption.isEmpty() || noLabel) ) - { - QLabel *label = new QLabel (def.caption, &dynamic_cast( *parent())); - label->setBuddy (widget); - mLayout->addWidget (label); - - if (def.labelWidth > -1) - label->setFixedWidth(def.labelWidth); - } - - mLayout->addWidget (widget); - mLayout->setAlignment (widget, getAlignment (def.widgetAlignment)); -} - -void CSVSettings::AbstractWidget::createLayout - (Orientation direction, bool isZeroMargin) -{ - if (direction == Orient_Vertical) - mLayout = new QVBoxLayout (); - else - mLayout = new QHBoxLayout (); - - if (isZeroMargin) - mLayout->setContentsMargins(0, 0, 0, 0); -} - -QFlags CSVSettings::AbstractWidget::getAlignment (CSVSettings::Alignment flag) -{ - return QFlags(static_cast(flag)); -} - -QLayout *CSVSettings::AbstractWidget::getLayout() -{ - return mLayout; -} - -void CSVSettings::AbstractWidget::slotUpdateWidget (const QString &value) -{ - updateWidget (value); -} - -void CSVSettings::AbstractWidget::slotUpdateItem(const QString &value) -{ - emit signalUpdateItem (value); -} - -void CSVSettings::AbstractWidget::slotUpdateItem(bool value) -{ - if (value) - emit signalUpdateItem (widget()->objectName()); -} - -void CSVSettings::AbstractWidget::slotUpdateItem(int value) -{ - emit signalUpdateItem (QString::number(value)); -} - -void CSVSettings::AbstractWidget::slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous) -{} diff --git a/apps/opencs/view/settings/abstractwidget.hpp b/apps/opencs/view/settings/abstractwidget.hpp deleted file mode 100644 index 325de2bd2..000000000 --- a/apps/opencs/view/settings/abstractwidget.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef ABSTRACTWIDGET_HPP -#define ABSTRACTWIDGET_HPP - -#include -#include "support.hpp" - -class QLayout; - -namespace CSVSettings -{ - /// Abstract base class for widgets which are used in user preferences dialog - class AbstractWidget : public QObject - { - Q_OBJECT - - QLayout *mLayout; - - public: - - /// Passed layout is assigned the constructed widget. - /// if no layout is passed, one is created. - explicit AbstractWidget (QLayout *layout = 0, QWidget* parent = 0) - : QObject (parent), mLayout (layout) - {} - - /// retrieve layout for insertion into itemblock - QLayout *getLayout(); - - /// create the derived widget instance - void build (QWidget* widget, WidgetDef &def, bool noLabel = false); - - /// reference to the derived widget instance - virtual QWidget *widget() = 0; - - protected: - - /// Callback called by receiving slot for widget udpates - virtual void updateWidget (const QString &value) = 0; - - /// Converts user-defined enum to Qt equivalents - QFlags getAlignment (Alignment flag); - - private: - - /// Creates layout and assigns label and widget as appropriate - void createLayout (Orientation direction, bool isZeroMargin); - - /// Creates label and widget according to passed definition - void buildLabelAndWidget (QWidget *widget, WidgetDef &def, bool noLabel); - - - signals: - - /// outbound update signal - void signalUpdateItem (const QString &value); - - public slots: - - /// receives inbound updates - void slotUpdateWidget (const QString &value); - - /// Overloads for outbound updates from derived widget signal - void slotUpdateItem (const QString &value); - void slotUpdateItem (bool value); - void slotUpdateItem (int value); - void slotUpdateItem (QListWidgetItem* current, QListWidgetItem* previous); - }; -} -#endif // ABSTRACTWIDGET_HPP diff --git a/apps/opencs/view/settings/blankpage.cpp b/apps/opencs/view/settings/blankpage.cpp deleted file mode 100644 index 837a31bee..000000000 --- a/apps/opencs/view/settings/blankpage.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "blankpage.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_MAC -#include -#endif - -#include "../../model/settings/usersettings.hpp" -#include "groupblock.hpp" -#include "toggleblock.hpp" - -CSVSettings::BlankPage::BlankPage(QWidget *parent): - AbstractPage("Blank", parent) -{ - -} - -CSVSettings::BlankPage::BlankPage(const QString &title, QWidget *parent): - AbstractPage(title, parent) -{ - // Hacks to get the stylesheet look properly -#ifdef Q_OS_MAC - QPlastiqueStyle *style = new QPlastiqueStyle; - //profilesComboBox->setStyle(style); -#endif - - setupUi(); -} - -void CSVSettings::BlankPage::setupUi() -{ - QGroupBox *pageBox = new QGroupBox(this); - layout()->addWidget(pageBox); -} - -void CSVSettings::BlankPage::initializeWidgets (const CSMSettings::SettingMap &settings) -{ - //iterate each item in each blocks in this section - //validate the corresponding setting against the defined valuelist if any. - foreach (AbstractBlock *block, mAbstractBlocks) - block->updateSettings (settings); -} diff --git a/apps/opencs/view/settings/blankpage.hpp b/apps/opencs/view/settings/blankpage.hpp deleted file mode 100644 index 07049fb71..000000000 --- a/apps/opencs/view/settings/blankpage.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef BLANKPAGE_HPP -#define BLANKPAGE_HPP - -#include "abstractpage.hpp" - -class QGroupBox; - -namespace CSVSettings { - - class UserSettings; - class AbstractBlock; - - /// Derived page with no widgets - /// Reference use only. - class BlankPage : public AbstractPage - { - - public: - - BlankPage (QWidget *parent = 0); - BlankPage (const QString &title, QWidget *parent); - - void setupUi(); - void initializeWidgets (const CSMSettings::SettingMap &settings); - }; -} - -#endif // BLANKPAGE_HPP diff --git a/apps/opencs/view/settings/customblock.cpp b/apps/opencs/view/settings/customblock.cpp deleted file mode 100644 index bbceafabe..000000000 --- a/apps/opencs/view/settings/customblock.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "customblock.hpp" -#include "groupblock.hpp" -#include "itemblock.hpp" -#include "proxyblock.hpp" - -CSVSettings::CustomBlock::CustomBlock (QWidget *parent) : AbstractBlock (parent) -{ -} - -int CSVSettings::CustomBlock::build(GroupBlockDefList &defList, GroupBlockDefList::iterator *it) -{ - int retVal = 0; - - GroupBlockDefList::iterator defaultIt; - GroupBlockDefList::iterator listIt = defList.begin(); - GroupBlockDefList::iterator proxyIt = defaultIt; - - if (it) - listIt = *it; - - ProxyBlock *proxyBlock = new ProxyBlock(getParent()); - - for (; listIt != defList.end(); ++listIt) - { - if (!(*listIt)->isProxy) - retVal = buildGroupBlock (*listIt); - else - { - mGroupList << proxyBlock; - proxyIt = listIt; - } - } - - if (proxyIt != defaultIt) - retVal = buildProxyBlock (*proxyIt, proxyBlock); - - return retVal; -} - -CSVSettings::GroupBox *CSVSettings::CustomBlock::buildGroupBox (Orientation orientation) -{ - GroupBox *box = new GroupBox (false, mBox); - createLayout (orientation, true, box); - - return box; -} - -int CSVSettings::CustomBlock::buildGroupBlock(GroupBlockDef *def) -{ - GroupBlock *block = new GroupBlock (getParent()); - - mGroupList << block; - - connect (block, SIGNAL (signalUpdateSetting(const QString &, const QString &)), - this, SLOT (slotUpdateSetting (const QString &, const QString &))); - - return block->build(def); -} - -int CSVSettings::CustomBlock::buildProxyBlock(GroupBlockDef *def, ProxyBlock *block) -{ - if (def->settingItems.size() != 1) - return -1; - - int retVal = block->build(def); - - if (retVal != 0) - return retVal; - - // The first settingItem is the proxy setting, containing the list of settings bound to it. - foreach (QStringList *list, *(def->settingItems.at(0)->proxyList)) - { - QString proxiedBlockName = list->at(0); - - //iterate each group in the custom block, matching it to each proxied setting - //and connecting it appropriately - foreach (GroupBlock *groupBlock, mGroupList) - { - ItemBlock *proxiedBlock = groupBlock->getItemBlock (proxiedBlockName); - - if (proxiedBlock) - { - block->addSetting(proxiedBlock, list); - - //connect the proxy block's update signal to the custom block's slot - connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), - this, SLOT (slotUpdateSetting (const QString &, const QString &))); - } - } - } - - return 0; -} - -CSMSettings::SettingList *CSVSettings::CustomBlock::getSettings() -{ - CSMSettings::SettingList *settings = new CSMSettings::SettingList(); - - foreach (GroupBlock *block, mGroupList) - { - CSMSettings::SettingList *groupSettings = block->getSettings(); - - if (groupSettings) - settings->append(*groupSettings); - } - - return settings; -} - -bool CSVSettings::CustomBlock::updateSettings (const CSMSettings::SettingMap &settings) -{ - bool success = true; - - foreach (GroupBlock *block, mGroupList) - { - bool success2 = block->updateSettings (settings); - success = success && success2; - } - - return success; -} diff --git a/apps/opencs/view/settings/customblock.hpp b/apps/opencs/view/settings/customblock.hpp deleted file mode 100644 index 54c50f395..000000000 --- a/apps/opencs/view/settings/customblock.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef CUSTOMBLOCK_HPP -#define CUSTOMBLOCK_HPP - -#include "abstractblock.hpp" - -namespace CSVSettings -{ - - class ProxyBlock; - - /// Base class for customized user preference setting blocks - /// Special block classes should be derived from CustomBlock - class CustomBlock : public AbstractBlock - { - - protected: - - GroupBlockList mGroupList; - - public: - - explicit CustomBlock (QWidget *parent = 0); - - /// Update settings local to the block - bool updateSettings (const CSMSettings::SettingMap &settings); - - /// Retrieve settings local to the block - CSMSettings::SettingList *getSettings(); - - /// construct the block using the passed definition - int build (GroupBlockDefList &defList, GroupBlockDefList::Iterator *it = 0); - - protected: - - /// construct the block groupbox - GroupBox *buildGroupBox (Orientation orientation); - - private: - - /// Construction function for creating a standard GroupBlock child - int buildGroupBlock(GroupBlockDef *def); - - /// Construction function for creating a standard ProxyBlock child - int buildProxyBlock(GroupBlockDef *def, ProxyBlock *block); - }; -} -#endif // CUSTOMBLOCK_HPP diff --git a/apps/opencs/view/settings/datadisplayformatpage.cpp b/apps/opencs/view/settings/datadisplayformatpage.cpp deleted file mode 100755 index 332b68f5c..000000000 --- a/apps/opencs/view/settings/datadisplayformatpage.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "datadisplayformatpage.hpp" -#include "groupblock.hpp" -#include "../../model/settings/usersettings.hpp" - -CSVSettings::DataDisplayFormatPage::DataDisplayFormatPage(QWidget* parent) : - AbstractPage("Display Format", parent) -{ - setupUi(); -} - -CSVSettings::GroupBlockDef *CSVSettings::DataDisplayFormatPage::setupDataDisplay( const QString &title) -{ - GroupBlockDef *statusBlock = new GroupBlockDef(QString(title)); - - SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon Only"); - *(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only"); - - WidgetDef statusWidget (Widget_RadioButton); - statusWidget.valueList = statusItem->valueList; - - statusItem->widget = statusWidget; - - statusBlock->settingItems << statusItem; - - statusBlock->isZeroMargin = false; - - return statusBlock; -} - - -void CSVSettings::DataDisplayFormatPage::setupUi() -{ - - mAbstractBlocks << buildBlock (setupDataDisplay ("Record Status Display")); - mAbstractBlocks << buildBlock (setupDataDisplay ("Referenceable ID Type Display")); - - foreach (AbstractBlock *block, mAbstractBlocks) - { - connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), - this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); - } - - connect ( this, - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), - &(CSMSettings::UserSettings::instance()), - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); - -} - -void CSVSettings::DataDisplayFormatPage::initializeWidgets (const CSMSettings::SettingMap &settings) -{ - //iterate each item in each blocks in this section - //validate the corresponding setting against the defined valuelist if any. - for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin(); - it_block != mAbstractBlocks.end(); ++it_block) - (*it_block)->updateSettings (settings); -} diff --git a/apps/opencs/view/settings/datadisplayformatpage.hpp b/apps/opencs/view/settings/datadisplayformatpage.hpp deleted file mode 100755 index b785bbd23..000000000 --- a/apps/opencs/view/settings/datadisplayformatpage.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef EDITORPAGE_HPP -#define EDITORPAGE_HPP - -#include "support.hpp" -#include "abstractpage.hpp" - -namespace CSVSettings -{ - class DataDisplayFormatPage : public AbstractPage - { - Q_OBJECT - - public: - explicit DataDisplayFormatPage(QWidget *parent = 0); - - void initializeWidgets (const CSMSettings::SettingMap &settings); - void setupUi(); - - private: - - /// User preference view of the record status delegate's icon / text setting - GroupBlockDef *setupDataDisplay(const QString &); - - signals: - - /// Signals up for changes to editor application-level settings - void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); - - public slots: - }; -} - -#endif // EDITORPAGE_HPP diff --git a/apps/opencs/view/settings/editorpage.cpp b/apps/opencs/view/settings/editorpage.cpp deleted file mode 100644 index 153ac1551..000000000 --- a/apps/opencs/view/settings/editorpage.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "editorpage.hpp" -#include "groupblock.hpp" -#include "../../model/settings/usersettings.hpp" - -CSVSettings::EditorPage::EditorPage(QWidget* parent) : - AbstractPage("Display Format", parent) -{ - setupUi(); -} - -CSVSettings::GroupBlockDef *CSVSettings::EditorPage::setupRecordStatusDisplay() -{ - GroupBlockDef *statusBlock = new GroupBlockDef(QString("Record Status Display")); - - SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon and Text"); - *(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only"); - - WidgetDef statusWidget (Widget_RadioButton); - statusWidget.valueList = statusItem->valueList; - - statusItem->widget = statusWidget; - - statusBlock->settingItems << statusItem; - - return statusBlock; -} - -void CSVSettings::EditorPage::setupUi() -{ - - mAbstractBlocks << buildBlock(setupRecordStatusDisplay()); - - foreach (AbstractBlock *block, mAbstractBlocks) - { - connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), - this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); - } - - connect ( this, - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), - &(CSMSettings::UserSettings::instance()), - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); - -} - -void CSVSettings::EditorPage::initializeWidgets (const CSMSettings::SettingMap &settings) -{ - //iterate each item in each blocks in this section - //validate the corresponding setting against the defined valuelist if any. - for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin(); - it_block != mAbstractBlocks.end(); ++it_block) - (*it_block)->updateSettings (settings); -} diff --git a/apps/opencs/view/settings/editorpage.hpp b/apps/opencs/view/settings/editorpage.hpp deleted file mode 100644 index 85215edab..000000000 --- a/apps/opencs/view/settings/editorpage.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef EDITORPAGE_HPP -#define EDITORPAGE_HPP - -#include "support.hpp" -#include "abstractpage.hpp" - -namespace CSVSettings -{ - class EditorPage : public AbstractPage - { - Q_OBJECT - - public: - explicit EditorPage(QWidget *parent = 0); - - void initializeWidgets (const CSMSettings::SettingMap &settings); - void setupUi(); - - private: - - /// User preference view of the record status delegate's icon / text setting - GroupBlockDef *setupRecordStatusDisplay(); - - signals: - - /// Signals up for changes to editor application-level settings - void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); - - public slots: - }; -} - -#endif // EDITORPAGE_HPP diff --git a/apps/opencs/view/settings/groupblock.cpp b/apps/opencs/view/settings/groupblock.cpp deleted file mode 100644 index e31e526c0..000000000 --- a/apps/opencs/view/settings/groupblock.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "groupblock.hpp" -#include "itemblock.hpp" - -CSVSettings::GroupBlock::GroupBlock (QWidget* parent) - : AbstractBlock (parent) -{} - -CSVSettings::GroupBlock::GroupBlock (bool isVisible, QWidget *parent) - : AbstractBlock (isVisible, parent) -{} - -int CSVSettings::GroupBlock::build (GroupBlockDef *def) -{ - - if (def->settingItems.size() == 0) - return -1; - - int retVal = 0; - - setVisible (def->isVisible); - - mBox->setLayout(createLayout (def->widgetOrientation, def->isZeroMargin)); - - setObjectName (def->title); - mBox->setTitle (def->title); - - foreach (SettingsItemDef *itemDef, def->settingItems) - { - ItemBlock *block = new ItemBlock (mBox); - - if (block->build (*itemDef) < 0) - { - retVal = -2; - break; - } - - mItemBlockList << block; - mBox->layout()->addWidget (block->getGroupBox()); - - connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), - this, SLOT (slotUpdateSetting (const QString &, const QString &) )); - } - - return retVal; -} - -CSMSettings::SettingList *CSVSettings::GroupBlock::getSettings() -{ - CSMSettings::SettingList *settings = 0; - - foreach (ItemBlock *block, mItemBlockList) - { - if (!settings) - settings = new CSMSettings::SettingList(); - - settings->append(*(block->getSettings ())); - } - - return settings; -} - -CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (const QString &name, ItemBlockList *blockList) -{ - ItemBlock *retBlock = 0; - - if (!blockList) - blockList = &mItemBlockList; - - foreach (ItemBlock *block, *blockList) - { - if (block->objectName() == name) - { - retBlock = block; - break; - } - } - - return retBlock; -} - -CSVSettings::ItemBlock *CSVSettings::GroupBlock::getItemBlock (int index) -{ - ItemBlock *retBlock = 0; - - if (mItemBlockList.size() > index) - retBlock = mItemBlockList.at(index); - - return retBlock; -} - -bool CSVSettings::GroupBlock::updateSettings (const CSMSettings::SettingMap &settings) -{ - bool success = true; - - //update all non-proxy settings - foreach (ItemBlock *block, mItemBlockList) - { - CSMSettings::SettingContainer *setting = settings[block->objectName()]; - - if (setting) - { - bool success2 = block->update (setting->getValue()); - success = success && success2; - } - } - - return success; -} diff --git a/apps/opencs/view/settings/groupblock.hpp b/apps/opencs/view/settings/groupblock.hpp deleted file mode 100644 index 5c0754193..000000000 --- a/apps/opencs/view/settings/groupblock.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef GROUPBLOCK_HPP -#define GROUPBLOCK_HPP - -#include -#include "abstractblock.hpp" - -namespace CSVSettings -{ - class ItemBlock; - - /// Base class for group blocks. - /// Derived block classes should use CustomBlock - class GroupBlock : public AbstractBlock - { - ItemBlockList mItemBlockList; - - public: - GroupBlock (QWidget* parent = 0); - GroupBlock (bool isVisible, QWidget *parent = 0); - - /// build the gorup block based on passed definition - int build (GroupBlockDef *def); - - /// update settings local to the group block - bool updateSettings (const CSMSettings::SettingMap &settings); - - /// retrieve setting list local to the group block - CSMSettings::SettingList *getSettings(); - - /// retrieve item block by name from the passed list or local list - ItemBlock *getItemBlock (const QString &name, ItemBlockList *blockList = 0); - - /// retrieve the item block by index from the local list - ItemBlock *getItemBlock (int index); - - protected: - - /// create block layout based on passed definition - int buildLayout (GroupBlockDef &def); - - }; -} -#endif // GROUPBLOCK_HPP diff --git a/apps/opencs/view/settings/groupbox.cpp b/apps/opencs/view/settings/groupbox.cpp deleted file mode 100644 index da2cc2571..000000000 --- a/apps/opencs/view/settings/groupbox.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "groupbox.hpp" - -const QString CSVSettings::GroupBox::INVISIBLE_BOX_STYLE = - QString::fromUtf8("QGroupBox { border: 0px; padding 0px; margin: 0px;}"); - -CSVSettings::GroupBox::GroupBox(QWidget *parent) : - QGroupBox (parent) -{ - initBox(); -} - -CSVSettings::GroupBox::GroupBox (bool isVisible, QWidget *parent) : - QGroupBox (parent) -{ - initBox(isVisible); -} - -void CSVSettings::GroupBox::initBox(bool isVisible) -{ - setFlat (true); - VISIBLE_BOX_STYLE = styleSheet(); - - if (!isVisible) - setStyleSheet (INVISIBLE_BOX_STYLE); -} - -bool CSVSettings::GroupBox::borderVisibile() const -{ - return (styleSheet() != INVISIBLE_BOX_STYLE); -} - -void CSVSettings::GroupBox::setTitle (const QString &title) -{ - if (borderVisibile() ) - { - QGroupBox::setTitle (title); - setMinimumWidth(); - } -} - -void CSVSettings::GroupBox::setBorderVisibility (bool value) -{ - if (value) - setStyleSheet(VISIBLE_BOX_STYLE); - else - setStyleSheet(INVISIBLE_BOX_STYLE); -} - -void CSVSettings::GroupBox::setMinimumWidth() -{ - //set minimum width to accommodate title, if needed - //1.5 multiplier to account for bold title. - QFontMetrics fm (font()); - int minWidth = fm.width(title()); - QGroupBox::setMinimumWidth (minWidth * 1.5); -} diff --git a/apps/opencs/view/settings/groupbox.hpp b/apps/opencs/view/settings/groupbox.hpp deleted file mode 100644 index 9d3a01936..000000000 --- a/apps/opencs/view/settings/groupbox.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef GROUPBOX_HPP -#define GROUPBOX_HPP - -#include - -namespace CSVSettings -{ - /// Custom implementation of QGroupBox to be used with block classes - class GroupBox : public QGroupBox - { - static const QString INVISIBLE_BOX_STYLE; - QString VISIBLE_BOX_STYLE; //not a const... - - public: - explicit GroupBox (QWidget *parent = 0); - explicit GroupBox (bool isVisible, QWidget *parent = 0); - - void setTitle (const QString &title); - void setBorderVisibility (bool value); - bool borderVisibile() const; - - private: - void setMinimumWidth(); - void initBox(bool isVisible = true); - }; -} - -#endif // GROUPBOX_HPP diff --git a/apps/opencs/view/settings/itemblock.cpp b/apps/opencs/view/settings/itemblock.cpp deleted file mode 100644 index 9cb0ae1a1..000000000 --- a/apps/opencs/view/settings/itemblock.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "itemblock.hpp" - -#include - -CSVSettings::ItemBlock::ItemBlock (QWidget* parent) - : mSetting (0), AbstractBlock (false, parent) -{ -} - -int CSVSettings::ItemBlock::build(SettingsItemDef &iDef) -{ - buildItemBlock (iDef); - buildItemBlockWidgets (iDef); - - return 0; -} - -void CSVSettings::ItemBlock::buildItemBlockWidgets (SettingsItemDef &iDef) -{ - WidgetDef wDef = iDef.widget; - QLayout *blockLayout = 0; - QString defaultValue = iDef.defaultValue; - - switch (wDef.type) - { - - case Widget_CheckBox: - case Widget_RadioButton: - - foreach (QString item, *(iDef.valueList)) - { - wDef.caption = item; - wDef.isDefault = (item == defaultValue); - - blockLayout = buildWidget (item, wDef, blockLayout)->getLayout(); - } - - break; - - case Widget_ComboBox: - case Widget_ListBox: - - //assign the item's value list to the widget's value list. - //pass through to default to finish widget construction. - if (!wDef.valueList) - wDef.valueList = iDef.valueList; - - default: - //only one instance of this non-list widget type. - //Set it's value to the default value for the item and build the widget. - - if (wDef.value.isEmpty()) - wDef.value = iDef.defaultValue; - - buildWidget (iDef.name, wDef); - } -} - -void CSVSettings::ItemBlock::buildItemBlock (SettingsItemDef &iDef) -{ - QString defaultValue = iDef.defaultValue; - - setObjectName(iDef.name); - - mSetting = new CSMSettings::SettingsItem (objectName(), - iDef.hasMultipleValues, iDef.defaultValue, - parent()); - - if (iDef.valueList) - mSetting->setValueList(iDef.valueList); - - if (!iDef.minMax.isEmpty()) - mSetting->setValuePair(iDef.minMax); -} - - -bool CSVSettings::ItemBlock::update (const QString &value) -{ - bool success = updateItem (value); - - if (success) - signalUpdateWidget (value); - - return success; -} - - -bool CSVSettings::ItemBlock::updateItem (const QString &value) -{ - return mSetting->updateItem(value); -} - - -bool CSVSettings::ItemBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) -{ - bool success = (mSetting->getValue() != value); - - if (success) - success = updateItem(value); - - return success; -} - -CSMSettings::SettingList *CSVSettings::ItemBlock::getSettings () -{ - CSMSettings::SettingList *list = new CSMSettings::SettingList(); - list->push_back(mSetting); - - return list; -} - -QString CSVSettings::ItemBlock::getValue() const -{ - return mSetting->getValue(); -} diff --git a/apps/opencs/view/settings/itemblock.hpp b/apps/opencs/view/settings/itemblock.hpp deleted file mode 100644 index 2d1d45d41..000000000 --- a/apps/opencs/view/settings/itemblock.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef ITEMBLOCK_HPP -#define ITEMBLOCK_HPP - -#include "abstractblock.hpp" - -namespace CSVSettings -{ - - class ItemBlock : public AbstractBlock - { - CSMSettings::SettingsItem *mSetting; - WidgetList mWidgetList; - - public: - - ItemBlock (QWidget* parent = 0); - - /// pure virtual function not implemented - bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } - - CSMSettings::SettingList *getSettings (); - - QString getValue () const; - - /// item blocks encapsulate only one setting - int getSettingCount(); - - /// update setting value and corresponding widget - bool update (const QString &value); - - /// virtual construction function - int build(SettingsItemDef &iDef); - - private: - - /// custom construction function - void buildItemBlock (SettingsItemDef& iDef); - void buildItemBlockWidgets (SettingsItemDef& iDef); - - /// update the setting value - bool updateItem (const QString &); - - /// callback function triggered when update to application level is signalled - bool updateBySignal (const QString &name, const QString &value, bool &doEmit); - }; -} - -#endif // ITEMBLOCK_HPP diff --git a/apps/opencs/view/settings/proxyblock.cpp b/apps/opencs/view/settings/proxyblock.cpp deleted file mode 100644 index 81cc54fca..000000000 --- a/apps/opencs/view/settings/proxyblock.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "proxyblock.hpp" -#include "itemblock.hpp" - -CSVSettings::ProxyBlock::ProxyBlock (QWidget *parent) - : GroupBlock (parent) -{ -} -int CSVSettings::ProxyBlock::build (GroupBlockDef *proxyDef) -{ - //get the list of pre-defined values for the proxy - mValueList = proxyDef->settingItems.at(0)->valueList; - - bool success = GroupBlock::build(proxyDef); - - //connect the item block of the proxy setting to the proxy-update slot - connect (getItemBlock(0), SIGNAL (signalUpdateSetting(const QString &, const QString &)), - this, SLOT (slotUpdateProxySetting (const QString &, const QString &))); - - return success; -} - -void CSVSettings::ProxyBlock::addSetting (ItemBlock *settingBlock, QStringList *proxyList) -{ - //connect the item block of the proxied seting to the generic update slot - connect (settingBlock, SIGNAL (signalUpdateSetting(const QString &, const QString &)), - this, SLOT (slotUpdateProxySetting(const QString &, const QString &))); - - mProxiedItemBlockList << settingBlock; - mProxyList << proxyList; -} - -bool CSVSettings::ProxyBlock::updateSettings (const CSMSettings::SettingMap &settings) -{ - return updateByProxiedSettings(&settings); -} - -bool CSVSettings::ProxyBlock::updateBySignal(const QString &name, const QString &value, bool &doEmit) -{ - doEmit = false; - return updateProxiedSettings(); -} - -void CSVSettings::ProxyBlock::slotUpdateProxySetting (const QString &name, const QString &value) -{ - updateByProxiedSettings(); -} - -bool CSVSettings::ProxyBlock::updateProxiedSettings() -{ - foreach (ItemBlock *block, mProxiedItemBlockList) - { - QString value = getItemBlock(0)->getValue(); - - bool success = false; - int i = 0; - - //find the value index of the selected value in the proxy setting - for (; i < mValueList->size(); ++i) - { - success = (value == mValueList->at(i)); - - if (success) - break; - } - - if (!success) - return false; - - // update the containing the proxied item's name - foreach (QStringList *list, mProxyList) - { - if ( list->at(0) == block->objectName()) - block->update (list->at(++i)); - } - } - - return true; -} - -bool CSVSettings::ProxyBlock::updateByProxiedSettings(const CSMSettings::SettingMap *settings) -{ - bool success = false; - int commonIndex = -1; - - //update all proxy settings based on values from non-proxies - foreach (QStringList *list, mProxyList) - { - //Iterate each proxy item's proxied setting list, getting the current values - //Compare those value indices. - //If indices match, they correlate to one of the proxy's values in it's value list - - //first value is always the name of the setting the proxy setting manages - QStringList::Iterator itProxyValue = list->begin(); - QString proxiedSettingName = (*itProxyValue); - QString proxiedSettingValue = ""; - itProxyValue++; - - if (!settings) - { - //get the actual setting value - ItemBlock *block = getProxiedItemBlock (proxiedSettingName); - - if (block) - proxiedSettingValue = block->getValue(); - } - else - proxiedSettingValue = (*settings)[proxiedSettingName]->getValue(); - - int j = 0; - - //iterate each value in the proxy string list - for (; itProxyValue != (list)->end(); ++itProxyValue) - { - success = ((*itProxyValue) == proxiedSettingValue); - - if (success) - break; - - j++; - } - - //break if no match was found - if ( !success ) - break; - - if (commonIndex != -1) - success = (commonIndex == j); - else - commonIndex = j; - - //break if indices were found, but mismatch - if (!success) - break; - } - - //if successful, the proxied setting values match a pre-defined value in the - //proxy's value list. Set the proxy to that value index - if (success) - { - ItemBlock *block = getItemBlock(0); - - if (block) - block->update (mValueList->at(commonIndex)); - } - - return success; -} - -CSVSettings::ItemBlock *CSVSettings::ProxyBlock::getProxiedItemBlock (const QString &name) -{ - return getItemBlock (name, &mProxiedItemBlockList); -} diff --git a/apps/opencs/view/settings/proxyblock.hpp b/apps/opencs/view/settings/proxyblock.hpp deleted file mode 100644 index 90fb9bc97..000000000 --- a/apps/opencs/view/settings/proxyblock.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef PROXYBLOCK_HPP -#define PROXYBLOCK_HPP - -#include "groupblock.hpp" - -namespace CSVSettings -{ - class ProxyBlock : public GroupBlock - { - Q_OBJECT - - /// TODO: Combine mProxyItemBlockList and mProxyList. - ItemBlockList mProxiedItemBlockList; - ProxyList mProxyList; - QStringList *mValueList; - - public: - - explicit ProxyBlock (QWidget *parent = 0); - explicit ProxyBlock (ItemBlock *proxyItemBlock, QWidget *parent = 0); - - /// Add a block that contains a proxied setting to the proxy block. - void addSetting (ItemBlock* settingBlock, QStringList *proxyList); - - int build (GroupBlockDef *def); - - CSMSettings::SettingList *getSettings() { return 0; } - - /// Update settings local to the proxy block pushed from application level - bool updateSettings (const CSMSettings::SettingMap &settings); - - /// callback function triggered when update to the application level is signaled. - bool updateBySignal (const QString &name, const QString &value, bool &doEmit); - - private: - - /// return the item block of a proxied setting - ItemBlock *getProxiedItemBlock (const QString &name); - - /// update the proxy setting with data from the proxied settings - bool updateByProxiedSettings(const CSMSettings::SettingMap *settings = 0); - - /// update proxied settings with data from the proxy setting - bool updateProxiedSettings(); - - private slots: - - void slotUpdateProxySetting (const QString &name, const QString &value); - - }; -} -#endif // PROXYBLOCK_HPP diff --git a/apps/opencs/view/settings/settingwidget.cpp b/apps/opencs/view/settings/settingwidget.cpp deleted file mode 100644 index 2c93986e7..000000000 --- a/apps/opencs/view/settings/settingwidget.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "settingwidget.hpp" diff --git a/apps/opencs/view/settings/settingwidget.hpp b/apps/opencs/view/settings/settingwidget.hpp deleted file mode 100644 index 9f4513671..000000000 --- a/apps/opencs/view/settings/settingwidget.hpp +++ /dev/null @@ -1,214 +0,0 @@ -#ifndef SETTINGWIDGET_HPP -#define SETTINGWIDGET_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "abstractwidget.hpp" - -namespace CSVSettings -{ - - /// Generic template for radiobuttons / checkboxes - template - class SettingWidget : public AbstractWidget - { - - T1 *mWidget; - - public: - - explicit SettingWidget (WidgetDef &def, QLayout *layout, QWidget* parent = 0) - : AbstractWidget (layout, parent), mWidget (new T1 (parent)) - { - mWidget->setText(def.caption); - build (mWidget, def, true); - mWidget->setChecked(def.isDefault); - - connect (mWidget, SIGNAL (toggled (bool)), - this, SLOT (slotUpdateItem (bool))); - } - - QWidget *widget() { return mWidget; } - - private: - - void updateWidget (const QString &value) - { - if ( value == mWidget->objectName() && !mWidget->isChecked() ) - mWidget->setChecked (true); - } - }; - - /// spin box template - template <> - class SettingWidget : public AbstractWidget - { - - QSpinBox *mWidget; - - public: - - SettingWidget (WidgetDef &def, QLayout *layout, QWidget *parent = 0) - : AbstractWidget (layout, parent), mWidget (new QSpinBox (parent)) - { - def.caption += tr(" (%1 to %2)").arg(def.minMax->left).arg(def.minMax->right); - - mWidget->setMaximum (def.minMax->right.toInt()); - mWidget->setMinimum (def.minMax->left.toInt()); - mWidget->setValue (def.value.toInt()); - - build (mWidget, def); - - connect (mWidget, SIGNAL (valueChanged (int)), - this, SLOT (slotUpdateItem (int))); - - mWidget->setAlignment (getAlignment(def.valueAlignment)); - - - } - - QWidget *widget() { return mWidget; } - - private: - - void updateWidget (const QString &value) - { - int intVal = value.toInt(); - - if (intVal >= mWidget->minimum() && intVal <= mWidget->maximum() && intVal != mWidget->value()) - mWidget->setValue (intVal); - } - - signals: - - }; - - /// combo box template - template <> - class SettingWidget : public CSVSettings::AbstractWidget - { - - QComboBox *mWidget; - - - public: - - explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) - : AbstractWidget (layout, parent), mWidget (new QComboBox (parent)) - { - int i = 0; - - foreach (QString item, *(def.valueList)) - { - mWidget->addItem (item); - - if (item == def.value) - mWidget->setCurrentIndex(i); - - i++; - } - - build (mWidget, def); - - connect (mWidget, SIGNAL (currentIndexChanged (const QString &)), - this, SLOT (slotUpdateItem (const QString &))); - - //center the combo box items - mWidget->setEditable (true); - mWidget->lineEdit()->setReadOnly (true); - mWidget->lineEdit()->setAlignment (getAlignment(def.valueAlignment)); - - QFlags alignment = mWidget->lineEdit()->alignment(); - - for (int j = 0; j < mWidget->count(); j++) - mWidget->setItemData (j, QVariant(alignment), Qt::TextAlignmentRole); - } - - QWidget *widget() { return mWidget; } - - private: - - void updateWidget (const QString &value) - { - if (mWidget->currentText() != value) - mWidget->setCurrentIndex(mWidget->findText(value)); - } - - }; - - /// line edit template - template <> - class SettingWidget : public CSVSettings::AbstractWidget - { - - QLineEdit *mWidget; - - public: - - explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0) - : AbstractWidget (layout, parent), mWidget (new QLineEdit (parent)) - { - if (!def.inputMask.isEmpty()) - mWidget->setInputMask (def.inputMask); - - mWidget->setText (def.value); - - build (mWidget, def); - - connect (mWidget, SIGNAL (textChanged (const QString &)), - this, SLOT (slotUpdateItem (const QString &))); - - mWidget->setAlignment (getAlignment(def.valueAlignment)); - } - - QWidget *widget() { return mWidget; } - - void updateWidget (const QString &value) - { - if (mWidget->text() != value) - mWidget->setText(value); - } - }; - - /// list widget template - /// \todo Not fully implemented. Only widget supporting multi-valued settings - template <> - class SettingWidget : public CSVSettings::AbstractWidget - { - - QListWidget *mWidget; - - public: - - explicit SettingWidget(WidgetDef &def, QLayout *layout, QWidget *parent = 0 ) - : AbstractWidget (layout, parent), mWidget (new QListWidget (parent)) - { - int i = 0; - - foreach (QString item, *(def.valueList)) - { - mWidget->addItem (item); - - if (item == def.value) {} - i++; - } - build (mWidget, def); - } - - QWidget *widget() { return mWidget; } - - private: - void updateWidget (const QString &value) {} - }; - -} -#endif // SETTINGWIDGET_HPP diff --git a/apps/opencs/view/settings/toggleblock.cpp b/apps/opencs/view/settings/toggleblock.cpp deleted file mode 100644 index 3406a62c4..000000000 --- a/apps/opencs/view/settings/toggleblock.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "toggleblock.hpp" -#include "groupblock.hpp" -#include "groupbox.hpp" -#include "itemblock.hpp" - -CSVSettings::ToggleBlock::ToggleBlock(QWidget *parent) : - CustomBlock(parent) -{} - -int CSVSettings::ToggleBlock::build(CustomBlockDef *def) -{ - if (def->blockDefList.size()==0) - return -1; - - QList::Iterator it = def->blockDefList.begin(); - - //first def in the list is the def for the toggle block - GroupBlockDef *toggleDef = *it++; - - if (toggleDef->captions.size() != def->blockDefList.size()-1 ) - return -2; - - if (toggleDef->widgets.size() == 0) - return -3; - - //create the toogle block UI structure - QLayout *blockLayout = createLayout (def->blockOrientation, true); - GroupBox *propertyBox = buildGroupBox (toggleDef->widgetOrientation); - - mBox->setLayout(blockLayout); - mBox->setTitle (toggleDef->title); - - //build the blocks contained in the def list - //this manages proxy block construction. - //Any settings managed by the proxy setting - //must be included in the blocks defined in the list. - CustomBlock::build (def->blockDefList, &it); - - for (GroupBlockList::iterator it = mGroupList.begin(); it != mGroupList.end(); ++it) - propertyBox->layout()->addWidget ((*it)->getGroupBox()); - - //build togle widgets, linking them to the settings - GroupBox *toggleBox = buildToggleWidgets (toggleDef, def->defaultValue); - - blockLayout->addWidget(toggleBox); - blockLayout->addWidget(propertyBox); - blockLayout->setAlignment (propertyBox, Qt::AlignRight); - - return 0; -} - -CSVSettings::GroupBox *CSVSettings::ToggleBlock::buildToggleWidgets (GroupBlockDef *def, QString &defaultToggle) -{ - GroupBox *box = new GroupBox (false, getParent()); - - QLayout *layout = createLayout (def->widgetOrientation, true, static_cast(box)); - - for (int i = 0; i < def->widgets.size(); ++i) - { - QString caption = def->captions.at(i); - WidgetDef *wDef = def->widgets.at(i); - - wDef->caption = caption; - wDef->widgetAlignment = Align_Left; - - AbstractWidget *widg = buildWidget (caption, *wDef, layout, false); - - GroupBlock *block = mGroupList.at(i); - - //connect widget's update to the property block's enabled status - connect (widg->widget(), SIGNAL (toggled (bool)), block, SLOT (slotSetEnabled(bool))); - - //enable the default toggle option - block->getGroupBox()->setEnabled( caption == defaultToggle ); - - layout = widg->getLayout(); - } - - return box; -} diff --git a/apps/opencs/view/settings/toggleblock.hpp b/apps/opencs/view/settings/toggleblock.hpp deleted file mode 100644 index 4b6e8e344..000000000 --- a/apps/opencs/view/settings/toggleblock.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TOGGLEBLOCK_HPP -#define TOGGLEBLOCK_HPP - -#include - -#include "customblock.hpp" - -namespace CSVSettings -{ - class GroupBlock; - class GroupBox; - class ToggleWidget; - class ItemBlock; - - class ToggleBlock : public CustomBlock - { - - public: - explicit ToggleBlock(QWidget *parent = 0); - - int build (CustomBlockDef *def); - - private: - /// Constructor for toggle widgets that are specific to toggle block - /// Widgets are not a part of the user preference settings - GroupBox *buildToggleWidgets (GroupBlockDef *def, QString &defaultToggle); - }; -} -#endif // TOGGLEBLOCK_HPP diff --git a/apps/opencs/view/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp deleted file mode 100644 index e73e24dcb..000000000 --- a/apps/opencs/view/settings/usersettingsdialog.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "usersettingsdialog.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../model/settings/support.hpp" - -#include "datadisplayformatpage.hpp" -#include "windowpage.hpp" -#include "settingwidget.hpp" - -CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : - QMainWindow (parent), mStackedWidget (0) -{ - setWindowTitle(QString::fromUtf8 ("User Settings")); - buildPages(); - setWidgetStates (); - - connect (mListWidget, - SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), - this, - SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); - - QRect scr = QApplication::desktop()->screenGeometry(); - QRect rect = geometry(); - move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); -} - -CSVSettings::UserSettingsDialog::~UserSettingsDialog() -{ -} - -void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event) -{ - writeSettings(); -} - -void CSVSettings::UserSettingsDialog::setWidgetStates () -{ - CSMSettings::UserSettings::instance().loadSettings("opencs.cfg"); - - //iterate the tabWidget's pages (sections) - for (int i = 0; i < mStackedWidget->count(); i++) - { - //get the settings defined for the entire section - //and update widget - QString pageName = mStackedWidget->widget(i)->objectName(); - - const CSMSettings::SettingMap *settings = CSMSettings::UserSettings::instance().getSettings(pageName); - AbstractPage &page = getAbstractPage (i); - page.initializeWidgets(*settings); - } -} - -void CSVSettings::UserSettingsDialog::buildPages() -{ - //craete central widget with it's layout and immediate children - QWidget *centralWidget = new QWidget (this); - - mListWidget = new QListWidget (centralWidget); - mStackedWidget = new QStackedWidget (centralWidget); - - QGridLayout* dialogLayout = new QGridLayout(); - - mListWidget->setMinimumWidth(0); - mListWidget->setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Expanding); - - mStackedWidget->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed); - - dialogLayout->addWidget (mListWidget,0,0); - dialogLayout->addWidget (mStackedWidget,0,1, Qt::AlignTop); - - centralWidget->setLayout (dialogLayout); - - setCentralWidget (centralWidget); - setDockOptions (QMainWindow::AllowNestedDocks); - - createPage(); - createPage(); - -} - -void CSVSettings::UserSettingsDialog::writeSettings() -{ - QMap settings; - - for (int i = 0; i < mStackedWidget->count(); ++i) - { - AbstractPage &page = getAbstractPage (i); - settings [page.objectName()] = page.getSettings(); - } - CSMSettings::UserSettings::instance().writeSettings(settings); -} - -CSVSettings::AbstractPage &CSVSettings::UserSettingsDialog::getAbstractPage (int index) -{ - return dynamic_cast (*(mStackedWidget->widget (index))); -} - -void CSVSettings::UserSettingsDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) -{ - if (!current) - current = previous; - - if (!(current == previous)) - mStackedWidget->setCurrentIndex (mListWidget->row(current)); -} diff --git a/apps/opencs/view/settings/usersettingsdialog.hpp b/apps/opencs/view/settings/usersettingsdialog.hpp deleted file mode 100644 index 3b3fa5b79..000000000 --- a/apps/opencs/view/settings/usersettingsdialog.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef USERSETTINGSDIALOG_H -#define USERSETTINGSDIALOG_H - -#include -#include -#include -#include - -#include "../../model/settings/usersettings.hpp" -#include "../../model/settings/support.hpp" - -class QHBoxLayout; -class AbstractWidget; -class QStackedWidget; -class QListWidget; - -namespace CSVSettings { - - class AbstractPage; - - class UserSettingsDialog : public QMainWindow - { - Q_OBJECT - - QListWidget *mListWidget; - QStackedWidget *mStackedWidget; - - public: - UserSettingsDialog(QMainWindow *parent = 0); - ~UserSettingsDialog(); - - private: - - /// Settings are written on close - void closeEvent (QCloseEvent *event); - - /// return the setting page by name - /// performs dynamic cast to AbstractPage * - AbstractPage &getAbstractPage (int index); - void setWidgetStates (); - void buildPages(); - void writeSettings(); - - /// Templated function to create a custom user preference page - template - void createPage () - { - T *page = new T(mStackedWidget); - - mStackedWidget->addWidget (&dynamic_cast(*page)); - - new QListWidgetItem (page->objectName(), mListWidget); - - //finishing touches - QFontMetrics fm (QApplication::font()); - int textWidth = fm.width(page->objectName()); - - if ((textWidth + 50) > mListWidget->minimumWidth()) - mListWidget->setMinimumWidth(textWidth + 50); - - resize (mStackedWidget->sizeHint()); - } - - public slots: - - /// Called when a different page is selected in the left-hand list widget - void slotChangePage (QListWidgetItem*, QListWidgetItem*); - }; - -} -#endif // USERSETTINGSDIALOG_H diff --git a/apps/opencs/view/settings/windowpage.cpp b/apps/opencs/view/settings/windowpage.cpp deleted file mode 100644 index ae42623b7..000000000 --- a/apps/opencs/view/settings/windowpage.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "windowpage.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_MAC -#include -#endif - -#include "../../model/settings/usersettings.hpp" -#include "groupblock.hpp" -#include "toggleblock.hpp" -#include "../../view/settings/abstractblock.hpp" - -CSVSettings::WindowPage::WindowPage(QWidget *parent): - AbstractPage("Window Size", parent) -{ - // Hacks to get the stylesheet look properly -#ifdef Q_OS_MAC - QPlastiqueStyle *style = new QPlastiqueStyle; - //profilesComboBox->setStyle(style); -#endif - - setupUi(); -} - -CSVSettings::GroupBlockDef * CSVSettings::WindowPage::buildDefinedWindowSize() -{ - GroupBlockDef *block = new GroupBlockDef ( "Defined Size"); - - SettingsItemDef *widthByHeightItem = new SettingsItemDef ("Window Size", "640x480"); - WidgetDef widthByHeightWidget = WidgetDef (Widget_ComboBox); - widthByHeightWidget.widgetWidth = 90; - *(widthByHeightItem->valueList) << "640x480" << "800x600" << "1024x768" << "1440x900"; - - QStringList *widthProxy = new QStringList; - QStringList *heightProxy = new QStringList; - - (*widthProxy) << "Width" << "640" << "800" << "1024" << "1440"; - (*heightProxy) << "Height" << "480" << "600" << "768" << "900"; - - *(widthByHeightItem->proxyList) << widthProxy << heightProxy; - - widthByHeightItem->widget = widthByHeightWidget; - - block->settingItems << widthByHeightItem; - block->isProxy = true; - block->isVisible = false; - - return block; -} - -CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildCustomWindowSize() -{ - GroupBlockDef *block = new GroupBlockDef ("Custom Size"); - - //custom width - SettingsItemDef *widthItem = new SettingsItemDef ("Width", "640"); - widthItem->widget = WidgetDef (Widget_LineEdit); - widthItem->widget.widgetWidth = 45; - widthItem->widget.inputMask = "9999"; - - //custom height - SettingsItemDef *heightItem = new SettingsItemDef ("Height", "480"); - heightItem->widget = WidgetDef (Widget_LineEdit); - heightItem->widget.widgetWidth = 45; - heightItem->widget.caption = "x"; - heightItem->widget.inputMask = "9999"; - - block->settingItems << widthItem << heightItem; - block->widgetOrientation = Orient_Horizontal; - block->isVisible = false; - - return block; -} - -CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildWindowSizeToggle() -{ - GroupBlockDef *block = new GroupBlockDef (objectName()); - - // window size toggle - block->captions << "Pre-Defined" << "Custom"; - block->widgetOrientation = Orient_Vertical; - block->isVisible = false; - - //define a widget for each group in the toggle - for (int i = 0; i < 2; i++) - block->widgets << new WidgetDef (Widget_RadioButton); - - block->widgets.at(0)->isDefault = false; - - return block; -} - -CSVSettings::CustomBlockDef *CSVSettings::WindowPage::buildWindowSize(GroupBlockDef *toggle_def, - GroupBlockDef *defined_def, - GroupBlockDef *custom_def) -{ - CustomBlockDef *block = new CustomBlockDef(QString ("Window Size")); - - block->blockDefList << toggle_def << defined_def << custom_def; - block->defaultValue = "Custom"; - - return block; - -} - -void CSVSettings::WindowPage::setupUi() -{ - CustomBlockDef *windowSize = buildWindowSize(buildWindowSizeToggle(), - buildDefinedWindowSize(), - buildCustomWindowSize() - ); - - mAbstractBlocks << buildBlock (windowSize); - - foreach (AbstractBlock *block, mAbstractBlocks) - { - connect (block, SIGNAL (signalUpdateSetting (const QString &, const QString &)), - this, SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)) ); - } - - connect ( this, - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &)), - &(CSMSettings::UserSettings::instance()), - SIGNAL ( signalUpdateEditorSetting (const QString &, const QString &))); - -} - - -void CSVSettings::WindowPage::initializeWidgets (const CSMSettings::SettingMap &settings) -{ - //iterate each item in each blocks in this section - //validate the corresponding setting against the defined valuelist if any. - for (AbstractBlockList::Iterator it_block = mAbstractBlocks.begin(); - it_block != mAbstractBlocks.end(); ++it_block) - (*it_block)->updateSettings (settings); -} diff --git a/apps/opencs/view/settings/windowpage.hpp b/apps/opencs/view/settings/windowpage.hpp deleted file mode 100644 index 2f2830625..000000000 --- a/apps/opencs/view/settings/windowpage.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef WINDOWPAGE_H -#define WINDOWPAGE_H - -#include "abstractpage.hpp" - -class QGroupBox; - -namespace CSVSettings { - - class UserSettings; - class AbstractBlock; - - class WindowPage : public AbstractPage - { - Q_OBJECT - - public: - - WindowPage(QWidget *parent = 0); - - void setupUi(); - void initializeWidgets (const CSMSettings::SettingMap &settings); - - /// - GroupBlockDef *buildCustomWindowSize(); - GroupBlockDef *buildDefinedWindowSize(); - GroupBlockDef *buildWindowSizeToggle(); - CustomBlockDef *buildWindowSize (GroupBlockDef *, GroupBlockDef *, GroupBlockDef *); - - signals: - void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); - }; -} -#endif //WINDOWPAGE_H From 331df17b48fb66f9305aea041a094f919df5707c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Apr 2014 22:17:19 -0500 Subject: [PATCH 117/157] Added new framework files --- apps/opencs/model/settings/connector.cpp | 127 +++++++ apps/opencs/model/settings/connector.hpp | 55 +++ apps/opencs/model/settings/setting.cpp | 281 +++++++++++++++ apps/opencs/model/settings/setting.hpp | 119 +++++++ apps/opencs/model/settings/settingmanager.cpp | 330 ++++++++++++++++++ apps/opencs/model/settings/settingmanager.hpp | 82 +++++ apps/opencs/view/settings/booleanview.cpp | 91 +++++ apps/opencs/view/settings/booleanview.hpp | 44 +++ apps/opencs/view/settings/dialog.cpp | 132 +++++++ apps/opencs/view/settings/dialog.hpp | 55 +++ apps/opencs/view/settings/frame.cpp | 103 ++++++ apps/opencs/view/settings/frame.hpp | 58 +++ apps/opencs/view/settings/listview.cpp | 106 ++++++ apps/opencs/view/settings/listview.hpp | 63 ++++ apps/opencs/view/settings/page.cpp | 88 +++++ apps/opencs/view/settings/page.hpp | 54 +++ .../view/settings/resizeablestackedwidget.cpp | 40 +++ .../view/settings/resizeablestackedwidget.hpp | 23 ++ apps/opencs/view/settings/settingwindow.cpp | 114 ++++++ apps/opencs/view/settings/settingwindow.hpp | 49 +++ apps/opencs/view/settings/textview.cpp | 73 ++++ apps/opencs/view/settings/textview.hpp | 56 +++ apps/opencs/view/settings/view.cpp | 218 ++++++++++++ apps/opencs/view/settings/view.hpp | 163 +++++++++ 24 files changed, 2524 insertions(+) create mode 100644 apps/opencs/model/settings/connector.cpp create mode 100644 apps/opencs/model/settings/connector.hpp create mode 100644 apps/opencs/model/settings/setting.cpp create mode 100644 apps/opencs/model/settings/setting.hpp create mode 100644 apps/opencs/model/settings/settingmanager.cpp create mode 100644 apps/opencs/model/settings/settingmanager.hpp create mode 100644 apps/opencs/view/settings/booleanview.cpp create mode 100644 apps/opencs/view/settings/booleanview.hpp create mode 100644 apps/opencs/view/settings/dialog.cpp create mode 100644 apps/opencs/view/settings/dialog.hpp create mode 100644 apps/opencs/view/settings/frame.cpp create mode 100644 apps/opencs/view/settings/frame.hpp create mode 100644 apps/opencs/view/settings/listview.cpp create mode 100644 apps/opencs/view/settings/listview.hpp create mode 100644 apps/opencs/view/settings/page.cpp create mode 100644 apps/opencs/view/settings/page.hpp create mode 100644 apps/opencs/view/settings/resizeablestackedwidget.cpp create mode 100644 apps/opencs/view/settings/resizeablestackedwidget.hpp create mode 100644 apps/opencs/view/settings/settingwindow.cpp create mode 100644 apps/opencs/view/settings/settingwindow.hpp create mode 100644 apps/opencs/view/settings/textview.cpp create mode 100644 apps/opencs/view/settings/textview.hpp create mode 100644 apps/opencs/view/settings/view.cpp create mode 100644 apps/opencs/view/settings/view.hpp diff --git a/apps/opencs/model/settings/connector.cpp b/apps/opencs/model/settings/connector.cpp new file mode 100644 index 000000000..05a9ba8f9 --- /dev/null +++ b/apps/opencs/model/settings/connector.cpp @@ -0,0 +1,127 @@ +#include "connector.hpp" +#include "../../view/settings/view.hpp" +#include "../../view/settings/page.hpp" + +CSMSettings::Connector::Connector(CSVSettings::View *master, + QObject *parent) + : mMasterView (master), QObject(parent) +{} + +void CSMSettings::Connector::addSlaveView (CSVSettings::View *view, + QList &masterProxyValues) +{ + mSlaveViews.append (view); + + mProxyListMap[view->viewKey()].append (masterProxyValues); +} + +QList CSMSettings::Connector::getSlaveViewValues() const +{ + QList list; + + foreach (const CSVSettings::View *view, mSlaveViews) + list.append (view->selectedValues()); + + return list; +} + +bool CSMSettings::Connector::proxyListsMatch ( + const QList &list1, + const QList &list2) const +{ + bool success = true; + + for (int i = 0; i < list1.size(); i++) + { + success = stringListsMatch (list1.at(i), list2.at(i)); + + if (!success) + break; + } + return success; +} + +void CSMSettings::Connector::slotUpdateMaster() const +{ + //list of the current values for each slave. + QList slaveValueList = getSlaveViewValues(); + + int masterColumn = -1; + + /* + * A row in the master view is one of the values in the + * master view's data model. This corresponds directly to the number of + * values in a proxy list contained in the ProxyListMap member. + * Thus, we iterate each "column" in the master proxy list + * (one for each vlaue in the master. Each column represents + * one master value's corresponding list of slave values. We examine + * each master value's list, comparing it to the current slave value list, + * stopping when we find a match using proxyListsMatch(). + * + * If no match is found, clear the master view's value + */ + + for (int i = 0; i < mMasterView->rowCount(); i++) + { + QList proxyValueList; + + foreach (const QString &settingKey, mProxyListMap.keys()) + { + // append the proxy value list stored in the i'th column + // for each setting key. A setting key is the id of the setting + // in page.name format. + proxyValueList.append (mProxyListMap.value(settingKey).at(i)); + } + + if (proxyListsMatch (slaveValueList, proxyValueList)) + { + masterColumn = i; + break; + } + } + + QString masterValue = mMasterView->value (masterColumn); + mMasterView->setSelectedValue (masterValue); +} + +void CSMSettings::Connector::slotUpdateSlaves() const +{ + int row = mMasterView->currentIndex(); + + if (row == -1) + return; + + //iterate the proxy lists for the chosen master index + //and pass the list to each slave for updating + for (int i = 0; i < mSlaveViews.size(); i++) + { + QList proxyList = + mProxyListMap.value(mSlaveViews.at(i)->viewKey()); + + mSlaveViews.at(i)->setSelectedValues (proxyList.at(row)); + } +} + +bool CSMSettings::Connector::stringListsMatch ( + const QStringList &list1, + const QStringList &list2) const +{ + //returns a "sloppy" match, verifying that each list contains all the same + //items, though not necessarily in the same order. + + if (list1.size() != list2.size()) + return false; + + QStringList tempList(list2); + + //iterate each value in the list, removing one occurrence of the value in + //the other list. If no corresponding value is found, test fails + foreach (const QString &value, list1) + { + if (!tempList.contains(value)) + return false; + + tempList.removeOne(value); + } + return true; +} diff --git a/apps/opencs/model/settings/connector.hpp b/apps/opencs/model/settings/connector.hpp new file mode 100644 index 000000000..da4c36d44 --- /dev/null +++ b/apps/opencs/model/settings/connector.hpp @@ -0,0 +1,55 @@ +#ifndef CSMSETTINGS_CONNECTOR_HPP +#define CSMSETTINGS_CONNECTOR_HPP + +#include +#include +#include +#include + +#include "support.hpp" + +namespace CSVSettings { + class View; +} + +namespace CSMSettings { + + class Connector : public QObject + { + Q_OBJECT + + CSVSettings::View *mMasterView; + + //map using the view pointer as a key to it's index value + QList mSlaveViews; + + //list of proxy values for each master value. + //value list order is indexed to the master value index. + QMap < QString, QList > mProxyListMap; + + public: + explicit Connector(CSVSettings::View *master, + QObject *parent = 0); + + void setMasterView (CSVSettings::View *view); + void addSlaveView (CSVSettings::View *view, + QList &masterProxyValues); + + private: + + bool proxyListsMatch (const QList &list1, + const QList &list2) const; + + bool stringListsMatch (const QStringList &list1, + const QStringList &list2) const; + + QList getSlaveViewValues() const; + + public slots: + + void slotUpdateSlaves() const; + void slotUpdateMaster() const; + }; +} + +#endif // CSMSETTINGS_CONNECTOR_HPP diff --git a/apps/opencs/model/settings/setting.cpp b/apps/opencs/model/settings/setting.cpp new file mode 100644 index 000000000..6c7e78087 --- /dev/null +++ b/apps/opencs/model/settings/setting.cpp @@ -0,0 +1,281 @@ +#include "setting.hpp" +#include "support.hpp" + +CSMSettings::Setting::Setting() +{ + buildDefaultSetting(); +} + +CSMSettings::Setting::Setting(SettingType typ, const QString &settingName, + const QString &pageName, const QStringList &values) + : mIsEditorSetting (false) +{ + buildDefaultSetting(); + + int vType = static_cast (typ); + + if ((vType % 2) == 0) + setProperty (Property_IsMultiValue, + QVariant(true).toString()); + else + vType--; + + setProperty (Property_ViewType, QVariant (vType / 2).toString()); + setProperty (Property_Page, pageName); + setProperty (Property_Name, settingName); + setProperty (Property_DeclaredValues, values); +} + +void CSMSettings::Setting::buildDefaultSetting() +{ + int arrLen = sizeof(sPropertyDefaults) / sizeof (*sPropertyDefaults); + + for (int i = 0; i < arrLen; i++) + { + QStringList propertyList; + + if (i list; + + foreach (const QString &val, vals) + list << (QStringList() << val); + + mProxies [setting->page() + '.' + setting->name()] = list; +} + +void CSMSettings::Setting::addProxy (const Setting *setting, + const QList &list) +{ + if (serializable()) + setProperty (Property_Serializable, false); + + mProxies [setting->page() + '.' + setting->name()] = list; +} + +void CSMSettings::Setting::setColumnSpan (int value) +{ + setProperty (Property_ColumnSpan, value); +} + +int CSMSettings::Setting::columnSpan() const +{ + return property (Property_ColumnSpan).at(0).toInt(); +} + +QStringList CSMSettings::Setting::declaredValues() const +{ + return property (Property_DeclaredValues); +} + +void CSMSettings::Setting::setDefinedValues (QStringList list) +{ + setProperty (Property_DefinedValues, list); +} + +QStringList CSMSettings::Setting::definedValues() const +{ + return property (Property_DefinedValues); +} + +QStringList CSMSettings::Setting::property (SettingProperty prop) const +{ + if (prop >= mProperties.size()) + return QStringList(); + + return mProperties.at(prop); +} + +void CSMSettings::Setting::setDefaultValue (const QString &value) +{ + setDefaultValues (QStringList() << value); +} + +void CSMSettings::Setting::setDefaultValues (const QStringList &values) +{ + setProperty (Property_DefaultValues, values); +} + +QStringList CSMSettings::Setting::defaultValues() const +{ + return property (Property_DefaultValues); +} + +void CSMSettings::Setting::setDelimiter (const QString &value) +{ + setProperty (Property_Delimiter, value); +} + +QString CSMSettings::Setting::delimiter() const +{ + return property (Property_Delimiter).at(0); +} + +void CSMSettings::Setting::setEditorSetting(bool state) +{ + mIsEditorSetting = true; +} + +bool CSMSettings::Setting::isEditorSetting() const +{ + return mIsEditorSetting; +} +void CSMSettings::Setting::setIsMultiLine (bool state) +{ + setProperty (Property_IsMultiLine, state); +} + +bool CSMSettings::Setting::isMultiLine() const +{ + return (property (Property_IsMultiLine).at(0) == "true"); +} + +void CSMSettings::Setting::setIsMultiValue (bool state) +{ + setProperty (Property_IsMultiValue, state); +} + +bool CSMSettings::Setting::isMultiValue() const +{ + return (property (Property_IsMultiValue).at(0) == "true"); +} + +const CSMSettings::ProxyValueMap &CSMSettings::Setting::proxyLists() const +{ + return mProxies; +} + +void CSMSettings::Setting::setSerializable (bool state) +{ + setProperty (Property_Serializable, state); +} + +bool CSMSettings::Setting::serializable() const +{ + return (property (Property_Serializable).at(0) == "true"); +} + +void CSMSettings::Setting::setName (const QString &value) +{ + setProperty (Property_Name, value); +} + +QString CSMSettings::Setting::name() const +{ + return property (Property_Name).at(0); +} + +void CSMSettings::Setting::setPage (const QString &value) +{ + setProperty (Property_Page, value); +} + +QString CSMSettings::Setting::page() const +{ + return property (Property_Page).at(0); +} + +void CSMSettings::Setting::setRowSpan (const int value) +{ + setProperty (Property_RowSpan, value); +} + +int CSMSettings::Setting::rowSpan () const +{ + return property (Property_RowSpan).at(0).toInt(); +} + +void CSMSettings::Setting::setViewType (int vType) +{ + setProperty (Property_ViewType, vType); +} + +CSVSettings::ViewType CSMSettings::Setting::viewType() const +{ + return static_cast + (property(Property_ViewType).at(0).toInt()); +} + +void CSMSettings::Setting::setViewColumn (int value) +{ + setProperty (Property_ViewColumn, value); +} + +int CSMSettings::Setting::viewColumn() const +{ + return property (Property_ViewColumn).at(0).toInt(); +} + +void CSMSettings::Setting::setViewLocation (int row, int column) +{ + setViewRow (row); + setViewColumn (column); +} + +void CSMSettings::Setting::setViewRow (int value) +{ + setProperty (Property_ViewRow, value); +} + +int CSMSettings::Setting::viewRow() const +{ + return property (Property_ViewRow).at(0).toInt(); +} + +void CSMSettings::Setting::setWidgetWidth (int value) +{ + setProperty (Property_WidgetWidth, value); +} + +int CSMSettings::Setting::widgetWidth() const +{ + return property (Property_WidgetWidth).at(0).toInt(); +} +void CSMSettings::Setting::setProperty (SettingProperty prop, bool value) +{ + setProperty (prop, QStringList() << QVariant (value).toString()); +} + +void CSMSettings::Setting::setProperty (SettingProperty prop, int value) +{ + setProperty (prop, QStringList() << QVariant (value).toString()); +} + +void CSMSettings::Setting::setProperty (SettingProperty prop, + const QString &value) +{ + setProperty (prop, QStringList() << value); +} + +void CSMSettings::Setting::setProperty (SettingProperty prop, + const QStringList &value) +{ + if (prop < mProperties.size()) + mProperties.replace (prop, value); +} + +QDataStream &operator <<(QDataStream &stream, const CSMSettings::Setting& setting) +{ + stream << setting.properties(); + + stream << setting.proxies(); + return stream; +} + +QDataStream &operator >>(QDataStream& stream, CSMSettings::Setting& setting) +{ + // stream >> setting.properties(); + // stream >> setting.proxies(); + return stream; +} diff --git a/apps/opencs/model/settings/setting.hpp b/apps/opencs/model/settings/setting.hpp new file mode 100644 index 000000000..1463e4d7d --- /dev/null +++ b/apps/opencs/model/settings/setting.hpp @@ -0,0 +1,119 @@ +#ifndef CSMSETTINGS_SETTING_HPP +#define CSMSETTINGS_SETTING_HPP + +#include +#include +#include "support.hpp" + +namespace CSMSettings +{ + //Maps setting id ("page.name") to a list of corresponding proxy values. + //Order of proxy value stringlists corresponds to order of master proxy's + //values in it's declared value list + typedef QMap > ProxyValueMap; + + class Setting + { + QList mProperties; + QStringList mDefaults; + + bool mIsEditorSetting; + + //QString is the setting id in the form of "page.name" + //QList is a list of stringlists of proxy values. + //Order is important! Proxy stringlists are matched against + //master values by their position in the QList. + ProxyValueMap mProxies; + + public: + + + explicit Setting(); + + explicit Setting(SettingType typ, const QString &settingName, + const QString &pageName, + const QStringList &values = QStringList()); + + void addProxy (const Setting *setting, const QStringList &vals); + void addProxy (const Setting *setting, const QList &list); + + const QList &properties() const { return mProperties; } + const ProxyValueMap &proxies() const { return mProxies; } + + void setColumnSpan (int value); + int columnSpan() const; + + void setDeclaredValues (QStringList list); + QStringList declaredValues() const; + + void setDefinedValues (QStringList list); + QStringList definedValues() const; + + void setDefaultValue (const QString &value); + + void setDefaultValues (const QStringList &values); + QStringList defaultValues() const; + + void setDelimiter (const QString &value); + QString delimiter() const; + + void setEditorSetting (bool state); + bool isEditorSetting() const; + + void setIsMultiLine (bool state); + bool isMultiLine() const; + + void setIsMultiValue (bool state); + bool isMultiValue() const; + + void setName (const QString &value); + QString name() const; + + void setPage (const QString &value); + QString page() const; + + void setRowSpan (const int value); + int rowSpan() const; + + const ProxyValueMap &proxyLists() const; + + void setSerializable (bool state); + bool serializable() const; + + void setViewColumn (int value); + int viewColumn() const; + + void setViewLocation (int row = -1, int column = -1); + + void setViewRow (int value); + int viewRow() const; + + void setViewType (int vType); + CSVSettings::ViewType viewType() const; + + void setWidgetWidth (int value); + int widgetWidth() const; + + ///returns the specified property value + QStringList property (SettingProperty prop) const; + + ///boilerplate code to convert setting values of common types + void setProperty (SettingProperty prop, bool value); + void setProperty (SettingProperty prop, int value); + void setProperty (SettingProperty prop, const QString &value); + void setProperty (SettingProperty prop, const QStringList &value); + + void addProxy (Setting* setting, + QMap &proxyMap); + + protected: + void buildDefaultSetting(); + }; +} + +Q_DECLARE_METATYPE(CSMSettings::Setting) + +QDataStream &operator <<(QDataStream &stream, const CSMSettings::Setting& setting); +QDataStream &operator >>(QDataStream &stream, CSMSettings::Setting& setting); + +#endif // CSMSETTINGS_SETTING_HPP diff --git a/apps/opencs/model/settings/settingmanager.cpp b/apps/opencs/model/settings/settingmanager.cpp new file mode 100644 index 000000000..70b91ee40 --- /dev/null +++ b/apps/opencs/model/settings/settingmanager.cpp @@ -0,0 +1,330 @@ +#include +#include +#include +#include +#include + +#include "setting.hpp" +#include "settingmanager.hpp" + +CSMSettings::SettingManager::SettingManager(QObject *parent) : + QObject(parent) +{ + mReadWriteMessage = QObject::tr("
Could not open or create file for \ + writing

Please make sure you have the right\ + permissions and try again.
"); + + mReadOnlyMessage = QObject::tr("
Could not open file for \ + reading

Please make sure you have the \ + right permissions and try again.
"); + +} + +void CSMSettings::SettingManager::dumpModel() +{ + foreach (Setting *setting, mSettings) + { + if (setting->proxyLists().isEmpty()) + continue; + } +} + +CSMSettings::Setting *CSMSettings::SettingManager::createSetting + (CSMSettings::SettingType typ, const QString &page, const QString &name, + const QStringList &values) +{ + //get list of all settings for the current setting name + if (findSetting (page, name)) + { + qWarning() << "Duplicate declaration encountered: " + << (name + '.' + page); + return 0; + } + + Setting *setting = new Setting (typ, name, page, values); + + //add declaration to the model + mSettings.append (setting); + + return setting; +} + +CSMSettings::DefinitionPageMap + CSMSettings::SettingManager::readFilestream (QTextStream *stream) +{ + //regEx's for page names and keys / values + QRegExp pageRegEx ("^\\[([^]]+)\\]"); + QRegExp keyRegEx ("^([^=]+)\\s*=\\s*(.+)$"); + + QString currPage = "Unassigned"; + + DefinitionPageMap pageMap; + + if (!stream) + { + displayFileErrorMessage(mReadWriteMessage, false); + return pageMap; + } + + if (stream->atEnd()) + return pageMap; + + DefinitionMap *settingMap = new DefinitionMap(); + pageMap[currPage] = settingMap; + + while (!stream->atEnd()) + { + QString line = stream->readLine().simplified(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + //page name found + if (pageRegEx.exactMatch(line)) + { + currPage = pageRegEx.cap(1).simplified().trimmed(); + settingMap = new DefinitionMap(); + pageMap[currPage] = settingMap; + continue; + } + + //setting definition found + if ( (keyRegEx.indexIn(line) != -1)) + { + QString settingName = keyRegEx.cap(1).simplified(); + QString settingValue = keyRegEx.cap(2).simplified(); + + if (!settingMap->contains (settingName)) + settingMap->insert (settingName, new QStringList()); + + settingMap->value(settingName)->append(settingValue); + } + } + + //return empty map if no settings were ever added to + if (pageMap.size() == 1) + { + QString pageKey = pageMap.keys().at(0); + if (pageMap[pageKey]->size() == 0) + pageMap.clear(); + } + + return pageMap; +} + +bool CSMSettings::SettingManager::writeFilestream(QTextStream *stream, + const QMap &settingListMap) +{ + if (!stream) + { + displayFileErrorMessage(mReadWriteMessage, false); + return false; + } + //disabled after rolling selector class into view. Need to + //iterate views to get setting definitions before writing to file + + QStringList sectionKeys; + + foreach (const QString &key, settingListMap.keys()) + { + QStringList names = key.split('.'); + QString section = names.at(0); + + if (!sectionKeys.contains(section)) + if (!settingListMap.value(key).isEmpty()) + sectionKeys.append (section); + } + + foreach (const QString §ion, sectionKeys) + { + *stream << '[' << section << "]\n"; + foreach (const QString &key, settingListMap.keys()) + { + QStringList names = key.split('.'); + + if (names.at(0) != section) + continue; + + QStringList list = settingListMap.value(key); + + if (list.isEmpty()) + continue; + + QString name = names.at(1); + + foreach (const QString value, list) + { + if (value.isEmpty()) + continue; + + *stream << name << " = " << value << '\n'; + } + } + } + + destroyStream (stream); + return true; +} + +void CSMSettings::SettingManager::mergeSettings(DefinitionPageMap &destMap, DefinitionPageMap &srcMap) +{ + if (srcMap.isEmpty()) + return; + + foreach (const QString &pageKey, srcMap.keys()) + { + DefinitionMap *srcSetting = srcMap.value(pageKey); + //Unique Page: + //insertfrom the source map + if (!destMap.keys().contains (pageKey)) + { + destMap.insert (pageKey, srcSetting); + continue; + } + + DefinitionMap *destSetting = destMap.value(pageKey); + + //Duplicate Page: + //iterate the settings in the source and check for duplicates in the + //destination + foreach (const QString &srcKey, srcSetting->keys()) + { + //insert into destination if unique + if (!destSetting->keys().contains (srcKey)) + destSetting->insert(srcKey, srcSetting->value (srcKey)); + } + } +} + +QTextStream *CSMSettings::SettingManager::openFilestream (const QString &filePath, + bool isReadOnly) const +{ + QIODevice::OpenMode openFlags = QIODevice::Text; + + if (isReadOnly) + openFlags = QIODevice::ReadOnly | openFlags; + else + openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags; + + QFile *file = new QFile(filePath); + QTextStream *stream = 0; + + if (file->open(openFlags)) + stream = new QTextStream(file); + + if (stream) + stream->setCodec(QTextCodec::codecForName("UTF-8")); + + return stream; +} + +void CSMSettings::SettingManager::destroyStream(QTextStream *stream) const +{ + stream->device()->close(); + + delete stream; +} + +void CSMSettings::SettingManager::displayFileErrorMessage(const QString &message, + bool isReadOnly) const +{ + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + + if (!isReadOnly) + msgBox.setText (mReadWriteMessage + message); + else + msgBox.setText (message); + + msgBox.exec(); +} + +void CSMSettings::SettingManager::addDefinitions (DefinitionPageMap &pageMap) +{ + foreach (QString pageName, pageMap.keys()) + { + DefinitionMap *settingMap = pageMap.value (pageName); + + foreach (QString settingName, (*settingMap).keys()) + { + QStringList *values = settingMap->value (settingName); + Setting *setting = findSetting (pageName, settingName); + + if (!setting) + { + qWarning() << "Found definitions for undeclared setting " + << pageName << "." << settingName; + continue; + } + + if (values->size() == 0) + values->append (setting->defaultValues()); + + setting->setDefinedValues (*values); + } + } +} + +QList CSMSettings::SettingManager::findSettings + (const QStringList &list) +{ + QList settings; + + foreach (const QString &value, list) + { + QStringList names = value.split(".", QString::SkipEmptyParts); + + if (names.size() != 2) + continue; + + Setting *setting = findSetting (names.at(0), names.at(1)); + + if (!setting) + continue; + + settings.append (setting); + } + + return settings; +} + + +CSMSettings::Setting *CSMSettings::SettingManager::findSetting + (const QString &pageName, const QString &settingName) +{ + foreach (Setting *setting, mSettings) + { + if (setting->name() == settingName) + { + if (setting->page() == pageName) + return setting; + } + } + return 0; +} + +QList CSMSettings::SettingManager::findSettings + (const QString &pageName) +{ + QList settings; + + foreach (Setting *setting, mSettings) + { + if (setting->page() == pageName) + settings.append (setting); + } + return settings; +} + +CSMSettings::SettingPageMap CSMSettings::SettingManager::settingPageMap() const +{ + SettingPageMap pageMap; + + foreach (Setting *setting, mSettings) + pageMap[setting->page()].append (setting); + + return pageMap; +} diff --git a/apps/opencs/model/settings/settingmanager.hpp b/apps/opencs/model/settings/settingmanager.hpp new file mode 100644 index 000000000..8819096ad --- /dev/null +++ b/apps/opencs/model/settings/settingmanager.hpp @@ -0,0 +1,82 @@ +#ifndef CSMSETTINGS_SETTINGMANAGER_HPP +#define CSMSETTINGS_SETTINGMANAGER_HPP + +#include +#include +#include +#include + +#include "support.hpp" +#include "setting.hpp" + +namespace CSMSettings +{ + + typedef QMap DefinitionMap; + typedef QMap DefinitionPageMap; + + typedef QMap > SettingPageMap; + + class SettingManager : public QObject + { + Q_OBJECT + + QString mReadOnlyMessage; + QString mReadWriteMessage; + QList mSettings; + + public: + explicit SettingManager(QObject *parent = 0); + + ///retrieve a setting object from a given page and setting name + Setting *findSetting + (const QString &pageName, const QString &settingName); + + ///retrieve all settings for a specified page + QList findSettings (const QString &pageName); + + ///retrieve all settings named in the attached list. + ///Setting names are specified in "PageName.SettingName" format. + QList findSettings (const QStringList &list); + + ///Retreive a map of the settings, keyed by page name + SettingPageMap settingPageMap() const; + + protected: + + ///add a new setting to the model and return it + Setting *createSetting (CSMSettings::SettingType typ, + const QString &page, const QString &name, + const QStringList &values = QStringList()); + + ///add definitions to the settings specified in the page map + void addDefinitions (DefinitionPageMap &pageMap); + + ///read setting definitions from file + DefinitionPageMap readFilestream(QTextStream *stream); + + ///write setting definitions to file + bool writeFilestream (QTextStream *stream, + const QMap &settingMap); + + ///merge PageMaps of settings when loading from multiple files + void mergeSettings (DefinitionPageMap &destMap, DefinitionPageMap &srcMap); + + QTextStream *openFilestream (const QString &filePath, + bool isReadOnly) const; + + void destroyStream(QTextStream *stream) const; + + void displayFileErrorMessage(const QString &message, + bool isReadOnly) const; + + QList settings() const { return mSettings; } + void dumpModel(); + + signals: + + public slots: + + }; +} +#endif // CSMSETTINGS_SETTINGMANAGER_HPP diff --git a/apps/opencs/view/settings/booleanview.cpp b/apps/opencs/view/settings/booleanview.cpp new file mode 100644 index 000000000..1c48199d1 --- /dev/null +++ b/apps/opencs/view/settings/booleanview.cpp @@ -0,0 +1,91 @@ +#include +#include + +#include +#include +#include + +#include + +#include "booleanview.hpp" +#include "../../model/settings/setting.hpp" + +CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting, + Page *parent) + : View (setting, parent) +{ + foreach (const QString &value, setting->declaredValues()) + { + QAbstractButton *button = 0; + + if (isMultiValue()) + button = new QCheckBox (value, this); + else + button = new QRadioButton (value, this); + + connect (button, SIGNAL (clicked (bool)), + this, SLOT (slotToggled (bool))); + + button->setObjectName (value); + + addWidget (button); + + mButtons[value] = button; + } +} + +void CSVSettings::BooleanView::slotToggled (bool state) +{ + //test only for true to avoid multiple selection updates with radiobuttons + if (!isMultiValue() && !state) + return; + + QStringList values; + + foreach (QString key, mButtons.keys()) + { + if (mButtons.value(key)->isChecked()) + values.append (key); + } + setSelectedValues (values, false); + + View::updateView(); +} + +void CSVSettings::BooleanView::updateView (bool signalUpdate) const +{ + + QStringList values = selectedValues(); + + foreach (const QString &buttonName, mButtons.keys()) + { + QAbstractButton *button = mButtons[buttonName]; + + //if the value is not found in the list, the widget is checked false + bool buttonValue = values.contains(buttonName); + + //skip if the butotn value will not change + if (button->isChecked() == buttonValue) + continue; + + //disable autoexclusive if it's enabled and we're setting + //the button value to false + bool switchExclusive = (!buttonValue && button->autoExclusive()); + + if (switchExclusive) + button->setAutoExclusive (false); + + button->setChecked (buttonValue); + + if (switchExclusive) + button->setAutoExclusive(true); + } + View::updateView (signalUpdate); +} + +CSVSettings::BooleanView *CSVSettings::BooleanViewFactory::createView + (CSMSettings::Setting *setting, + Page *parent) +{ + return new BooleanView (setting, parent); +} diff --git a/apps/opencs/view/settings/booleanview.hpp b/apps/opencs/view/settings/booleanview.hpp new file mode 100644 index 000000000..52f9e05f1 --- /dev/null +++ b/apps/opencs/view/settings/booleanview.hpp @@ -0,0 +1,44 @@ +#ifndef CSVSETTINGS_BOOLEANVIEW_HPP +#define CSVSETTINGS_BOOELANVIEW_HPP + +#include +#include + +#include "view.hpp" +#include "../../model/settings/support.hpp" + +class QStringListModel; + +namespace CSVSettings +{ + class BooleanView : public View + { + Q_OBJECT + + QMap mButtons; + + public: + explicit BooleanView (CSMSettings::Setting *setting, + Page *parent); + + protected: + void updateView (bool signalUpdate = true) const; + + private slots: + void slotToggled (bool state); + }; + + class BooleanViewFactory : public QObject, public IViewFactory + { + Q_OBJECT + + public: + explicit BooleanViewFactory (QWidget *parent = 0) + : QObject (parent) + {} + + BooleanView *createView (CSMSettings::Setting *setting, + Page *parent); + }; +} +#endif // CSVSETTINGS_BOOLEANVIEW_HPP diff --git a/apps/opencs/view/settings/dialog.cpp b/apps/opencs/view/settings/dialog.cpp new file mode 100644 index 000000000..d9d5830d9 --- /dev/null +++ b/apps/opencs/view/settings/dialog.cpp @@ -0,0 +1,132 @@ +#include "dialog.hpp" + +#include +#include +#include +#include +#include + +#include "../../model/settings/usersettings.hpp" + +#include "page.hpp" + +#include + +#include + +#include +#include +#include + +#include +#include + +CSVSettings::Dialog::Dialog(QMainWindow *parent) + : mStackedWidget (0), mDebugMode (false), SettingWindow (parent) +{ + setWindowTitle(QString::fromUtf8 ("User Settings")); + + setupDialog(); + + connect (mPageListWidget, + SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); +} + +void CSVSettings::Dialog::slotChangePage + (QListWidgetItem *cur, QListWidgetItem *prev) +{ + mStackedWidget->changePage + (mPageListWidget->row (cur), mPageListWidget->row (prev)); + + layout()->activate(); + setFixedSize(minimumSizeHint()); +} + +void CSVSettings::Dialog::setupDialog() +{ + //create central widget with it's layout and immediate children + QWidget *centralWidget = new QGroupBox (this); + + centralWidget->setLayout (new QHBoxLayout()); + centralWidget->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Preferred); + setCentralWidget (centralWidget); + setDockOptions (QMainWindow::AllowNestedDocks); + + buildPageListWidget (centralWidget); + buildStackedWidget (centralWidget); +} + +void CSVSettings::Dialog::buildPages() +{ + SettingWindow::createPages (); + + QFontMetrics fm (QApplication::font()); + + foreach (Page *page, SettingWindow::pages()) + { + QString pageName = page->objectName(); + + int textWidth = fm.width(pageName); + + new QListWidgetItem (pageName, mPageListWidget); + mPageListWidget->setFixedWidth (textWidth + 50); + + mStackedWidget->addWidget (&dynamic_cast(*(page))); + } + + addDebugPage(); + + resize (mStackedWidget->sizeHint()); +} + +void CSVSettings::Dialog::addDebugPage() +{ + /* + QTreeView *tree = new QTreeView(); + + //tree->setModel( &CSMSettings::UserSettings::instance().model() ); + + mStackedWidget->addWidget(tree); + new QListWidgetItem ("Standard Item Model", mPageListWidget);*/ +} + +void CSVSettings::Dialog::buildPageListWidget (QWidget *centralWidget) +{ + mPageListWidget = new QListWidget (centralWidget); + mPageListWidget->setMinimumWidth(50); + mPageListWidget->setSizePolicy (QSizePolicy::Fixed, QSizePolicy::Expanding); + + mPageListWidget->setSelectionBehavior (QAbstractItemView::SelectItems); + + centralWidget->layout()->addWidget(mPageListWidget); +} + +void CSVSettings::Dialog::buildStackedWidget (QWidget *centralWidget) +{ + mStackedWidget = new ResizeableStackedWidget (centralWidget); + + centralWidget->layout()->addWidget (mStackedWidget); +} + +void CSVSettings::Dialog::closeEvent (QCloseEvent *event) +{ + //SettingWindow::closeEvent() must be called first to ensure + //model is updated + SettingWindow::closeEvent (event); + + saveSettings(); +} + +void CSVSettings::Dialog::show() +{ + if (pages().isEmpty()) + buildPages(); + + QPoint screenCenter = QApplication::desktop()->screenGeometry().center(); + + move (screenCenter - geometry().center()); + + QWidget::show(); +} diff --git a/apps/opencs/view/settings/dialog.hpp b/apps/opencs/view/settings/dialog.hpp new file mode 100644 index 000000000..0cfd415ac --- /dev/null +++ b/apps/opencs/view/settings/dialog.hpp @@ -0,0 +1,55 @@ +#ifndef CSVSETTINGS_DIALOG_H +#define CSVSETTINGS_DIALOG_H + +#include "settingwindow.hpp" +#include "resizeablestackedwidget.hpp" +#include + +class QStackedWidget; +class QListWidget; +class QListWidgetItem; + +namespace CSVSettings { + + class Page; + + class Dialog : public SettingWindow + { + Q_OBJECT + + QListWidget *mPageListWidget; + ResizeableStackedWidget *mStackedWidget; + bool mDebugMode; + + public: + + explicit Dialog (QMainWindow *parent = 0); + + ///Enables setting debug mode. When the dialog opens, a page is created + ///which displays the SettingModel's contents in a Tree view. + void enableDebugMode (bool state, QStandardItemModel *model = 0); + + protected: + + /// Settings are written on close + void closeEvent (QCloseEvent *event); + + void setupDialog(); + + private: + + void buildPages(); + void buildPageListWidget (QWidget *centralWidget); + void buildStackedWidget (QWidget *centralWidget); + void addDebugPage(); + + public slots: + + void show(); + + private slots: + + void slotChangePage (QListWidgetItem *, QListWidgetItem *); + }; +} +#endif // CSVSETTINGS_DIALOG_H diff --git a/apps/opencs/view/settings/frame.cpp b/apps/opencs/view/settings/frame.cpp new file mode 100644 index 000000000..db5091999 --- /dev/null +++ b/apps/opencs/view/settings/frame.cpp @@ -0,0 +1,103 @@ +#include "frame.hpp" + +#include + +const QString CSVSettings::Frame::sInvisibleBoxStyle = + QString::fromUtf8("Frame { border:2px; padding 2px; margin: 2px;}"); + +CSVSettings::Frame::Frame (bool isVisible, const QString &title, + QWidget *parent) + : mIsHorizontal (true), mLayout (new SettingLayout()), + QGroupBox (title, parent) +{ + setFlat (true); + mVisibleBoxStyle = styleSheet(); + + if (!isVisible) + setStyleSheet (sInvisibleBoxStyle); + + setLayout (mLayout); +} + +void CSVSettings::Frame::hideWidgets() +{ + for (int i = 0; i < children().size(); i++) + { + QObject *obj = children().at(i); + + Frame *widgFrame = dynamic_cast (obj); + + if (widgFrame) + { + widgFrame->hideWidgets(); + continue; + } + + QWidget *widg = static_cast (obj); + if (widg->property("sizePolicy").isValid()) + widg->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored); + } + + layout()->activate(); + setFixedSize(minimumSizeHint()); + +} + +void CSVSettings::Frame::showWidgets() +{ + for (int i = 0; i < children().size(); i++) + { + QObject *obj = children().at(i); + + Frame *widgFrame = dynamic_cast (obj); + + if (widgFrame) + { + widgFrame->showWidgets(); + continue; + } + + QWidget *widg = static_cast (obj); + + if (widg->property("sizePolicy").isValid()) + widg->setSizePolicy + (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + } + layout()->activate(); + setFixedSize(minimumSizeHint()); +} + +void CSVSettings::Frame::addWidget (QWidget *widget, int row, int column, + int rowSpan, int columnSpan) +{ + if (row == -1) + row = getNextRow(); + + if (column == -1) + column = getNextColumn(); + + mLayout->addWidget (widget, row, column, rowSpan, columnSpan); + //, Qt::AlignLeft | Qt::AlignTop); + + widget->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored); +} + +int CSVSettings::Frame::getNextRow () const +{ + int row = mLayout->rowCount(); + + if (mIsHorizontal && row > 0) + row--; + + return row; +} + +int CSVSettings::Frame::getNextColumn () const +{ + int column = 0; + + if (mIsHorizontal) + column = mLayout->columnCount(); + + return column; +} diff --git a/apps/opencs/view/settings/frame.hpp b/apps/opencs/view/settings/frame.hpp new file mode 100644 index 000000000..2b52dfd1f --- /dev/null +++ b/apps/opencs/view/settings/frame.hpp @@ -0,0 +1,58 @@ +#ifndef CSVSETTINGS_FRAME_HPP +#define CSVSETTINGS_FRAME_HPP + +#include +#include +#include +#include "../../model/settings/support.hpp" + +namespace CSVSettings +{ + class SettingLayout : public QGridLayout + { + public: + explicit SettingLayout (QWidget *parent = 0) + : QGridLayout (parent) + { + setContentsMargins(0,0,0,0); + setAlignment(Qt::AlignLeft | Qt::AlignTop); + } + }; + + /// Custom implementation of QGroupBox to act as a base for view classes + class Frame : public QGroupBox + { + static const QString sInvisibleBoxStyle; + + QString mVisibleBoxStyle; + + bool mIsHorizontal; + + SettingLayout *mLayout; + + public: + explicit Frame (bool isVisible, const QString &title = "", + QWidget *parent = 0); + + ///Adds a widget to the grid layout, setting the position + ///relative to the last added widgets, or absolutely for positive + ///row / column values + void addWidget (QWidget *widget, int row = -1, int column = -1, + int rowSpan = 1, int columnSpan = 1); + + ///Force the grid to lay out in horizontal or vertical alignments + void setHLayout() { mIsHorizontal = true; } + void setVLayout() { mIsHorizontal = false; } + + void showWidgets(); + void hideWidgets(); + + private: + + int getNextColumn() const; + int getNextRow() const; + + }; +} + +#endif // CSVSETTINGS_FRAME_HPP diff --git a/apps/opencs/view/settings/listview.cpp b/apps/opencs/view/settings/listview.cpp new file mode 100644 index 000000000..b2a47903f --- /dev/null +++ b/apps/opencs/view/settings/listview.cpp @@ -0,0 +1,106 @@ +#include "listview.hpp" +#include "../../model/settings/setting.hpp" + +#include +#include +#include + +CSVSettings::ListView::ListView(CSMSettings::Setting *setting, + Page *parent) + : mComboBox (0), mAbstractItemView (0), View(setting, parent) +{ + QWidget *widget = + buildWidget(setting->isMultiLine(), setting->widgetWidth()); + + addWidget (widget, setting->viewRow(), setting->viewColumn()); + + if (mComboBox) + buildComboBoxModel(); + + else if (mAbstractItemView) + buildAbstractItemViewModel(); +} + +void CSVSettings::ListView::buildComboBoxModel() +{ + mComboBox->setModel (dataModel()); + mComboBox->setModelColumn (0); + mComboBox->view()->setSelectionModel (selectionModel()); + + int curIdx = -1; + + if (!selectionModel()->selection().isEmpty()) + curIdx = selectionModel()->selectedIndexes().at(0).row(); + + mComboBox->setCurrentIndex (curIdx); + + connect (mComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(emitItemViewUpdate(int))); +} + +void CSVSettings::ListView::buildAbstractItemViewModel() +{ + mAbstractItemView->setModel (dataModel()); + mAbstractItemView->setSelectionModel (selectionModel()); + + //connection needs to go here for list view update to signal to + //the outside +} + +void CSVSettings::ListView::emitItemViewUpdate (int idx) +{ + emit viewUpdated (objectName(), selectedValues()); +} + +QWidget *CSVSettings::ListView::buildWidget(bool isMultiLine, int width) +{ + QWidget *widget = 0; + + if (isMultiLine) + { + mAbstractItemView = new QListView (this); + widget = mAbstractItemView; + + if (width > 0) + widget->setFixedWidth (widgetWidth (width)); + } + else + { + mComboBox = new QComboBox (this); + widget = mComboBox; + + if (width > 0) + mComboBox->setMinimumContentsLength (width); + } + + return widget; +} + +void CSVSettings::ListView::showEvent ( QShowEvent * event ) +{ + View::showEvent (event); +} + +void CSVSettings::ListView::updateView (bool signalUpdate) const +{ + QStringList values = selectedValues(); + + if (mComboBox) + { + int idx = -1; + + if (values.size() > 0) + idx = (mComboBox->findText(values.at(0))); + + mComboBox->setCurrentIndex (idx); + } + + View::updateView (signalUpdate); +} + +CSVSettings::ListView *CSVSettings::ListViewFactory::createView + (CSMSettings::Setting *setting, + Page *parent) +{ + return new ListView(setting, parent); +} diff --git a/apps/opencs/view/settings/listview.hpp b/apps/opencs/view/settings/listview.hpp new file mode 100644 index 000000000..c2860d769 --- /dev/null +++ b/apps/opencs/view/settings/listview.hpp @@ -0,0 +1,63 @@ +#ifndef CSVSETTINGS_LISTVIEW_HPP +#define CSVSETTINGS_LISTVIEW_HPP + +#include "view.hpp" + + +class QStringListModel; +class QComboBox; +class QAbstractItemView; + +namespace CSVSettings +{ + class ListView : public View + { + Q_OBJECT + + QAbstractItemView *mAbstractItemView; + QComboBox *mComboBox; + + public: + explicit ListView (CSMSettings::Setting *setting, + Page *parent); + + protected: + + void updateView (bool signalUpdate = true) const; + void showEvent ( QShowEvent * event ); + + ///Receives signal from widget and signals viwUpdated() + void slotTextEdited (QString value); + + private: + + ///Helper function to construct a model for an AbstractItemView + void buildAbstractItemViewModel(); + + ///Helper function to construct a model for a combobox + void buildComboBoxModel(); + + ///Helper function to build the view widget + QWidget *buildWidget (bool isMultiLine, int width); + + private slots: + + ///Receives updates from single-select widgets (like combobox) and + ///signals viewUpdated with the selected values. + void emitItemViewUpdate (int idx); + }; + + class ListViewFactory : public QObject, public IViewFactory + { + Q_OBJECT + + public: + explicit ListViewFactory (QWidget *parent = 0) + : QObject (parent) + {} + + ListView *createView (CSMSettings::Setting *setting, + Page *parent); + }; +} +#endif // CSVSETTINGS_LISTVIEW_HPP diff --git a/apps/opencs/view/settings/page.cpp b/apps/opencs/view/settings/page.cpp new file mode 100644 index 000000000..665326c2c --- /dev/null +++ b/apps/opencs/view/settings/page.cpp @@ -0,0 +1,88 @@ +#include "page.hpp" +#include "view.hpp" +#include "booleanview.hpp" +#include "textview.hpp" +#include "listview.hpp" + +#include "../../model/settings/usersettings.hpp" +#include "../../model/settings/connector.hpp" +#include "settingwindow.hpp" + +QMap + CSVSettings::Page::mViewFactories; + +CSVSettings::Page::Page(const QString &pageName, + QList settingList, + SettingWindow *parent) : + mParent(parent), mIsEditorPage (false), Frame(false, "", parent) +{ + setObjectName (pageName); + + if (mViewFactories.size() == 0) + buildFactories(); + + setVLayout(); + setupViews (settingList); +} + +void CSVSettings::Page::setupViews + (QList &settingList) +{ + foreach (CSMSettings::Setting *setting, settingList) + addView (setting); +} + +void CSVSettings::Page::addView (CSMSettings::Setting *setting) +{ + if (setting->viewType() == ViewType_Undefined) + return; + + View *view = mViewFactories[setting->viewType()]->createView(setting, this); + + if (!view) + return; + + mViews.append (view); + + addWidget (view, setting->viewRow(), setting->viewColumn(), + setting->rowSpan(), setting->columnSpan() ); + + //if this page is an editor page, connect each of it's views up to the + //UserSettings singleton for signaling back to OpenCS + if (setting->isEditorSetting()) { + connect (view, SIGNAL (viewUpdated(const QString&, const QStringList&)), + &CSMSettings::UserSettings::instance(), + SIGNAL (userSettingUpdated (const QString &, const QStringList &))); + } +} + +CSVSettings::View *CSVSettings::Page::findView (const QString &page, + const QString &setting) const +{ + + //if this is not the page we're looking for, + //appeal to the parent setting window to find the appropriate view + if (page != objectName()) + return mParent->findView (page, setting); + + //otherwise, return the matching view + for (int i = 0; i < mViews.size(); i++) + { + View *view = mViews.at(i); + + if (view->parentPage()->objectName() != page) + continue; + + if (view->objectName() == setting) + return view; + } + + return 0; +} + +void CSVSettings::Page::buildFactories() +{ + mViewFactories[ViewType_Boolean] = new BooleanViewFactory (this); + mViewFactories[ViewType_Text] = new TextViewFactory (this); + mViewFactories[ViewType_List] = new ListViewFactory (this); +} diff --git a/apps/opencs/view/settings/page.hpp b/apps/opencs/view/settings/page.hpp new file mode 100644 index 000000000..7f24f6a62 --- /dev/null +++ b/apps/opencs/view/settings/page.hpp @@ -0,0 +1,54 @@ +#ifndef CSVSETTINGS_PAGE_HPP +#define CSVSETTINGS_PAGE_HPP + +#include +#include +#include +#include + +#include "frame.hpp" + +#include "../../model/settings/support.hpp" + +namespace CSMSettings { class Setting; } + +namespace CSVSettings +{ + class View; + class IViewFactory; + class SettingWindow; + + class Page : public Frame + { + Q_OBJECT + + QList mViews; + SettingWindow *mParent; + static QMap mViewFactories; + bool mIsEditorPage; + + public: + explicit Page(const QString &pageName, + QList settingList, + SettingWindow *parent); + + ///Creates a new view based on the passed setting and adds it to + ///the page. + void addView (CSMSettings::Setting *setting); + + ///Iterates the views created for this page based on the passed setting + ///and returns it. + View *findView (const QString &page, const QString &setting) const; + + const QList &views () const { return mViews; } + + private: + + ///Creates views based on the passed setting list + void setupViews (QList &settingList); + + ///Creates factory objects for view construction + void buildFactories(); + }; +} +#endif // CSVSETTINGS_PAGE_HPP diff --git a/apps/opencs/view/settings/resizeablestackedwidget.cpp b/apps/opencs/view/settings/resizeablestackedwidget.cpp new file mode 100644 index 000000000..cb127cb76 --- /dev/null +++ b/apps/opencs/view/settings/resizeablestackedwidget.cpp @@ -0,0 +1,40 @@ +#include "resizeablestackedwidget.hpp" +#include "page.hpp" + +#include + +CSVSettings::ResizeableStackedWidget::ResizeableStackedWidget(QWidget *parent) : + QStackedWidget(parent) +{} + +void CSVSettings::ResizeableStackedWidget::addWidget(QWidget* pWidget) +{ + QStackedWidget::addWidget(pWidget); +} + +void CSVSettings::ResizeableStackedWidget::changePage + (int current, int previous) +{ + if (current == previous) + return; + + Page *prevPage = 0; + Page *curPage = 0; + + if (previous > -1) + prevPage = static_cast (widget (previous)); + + if (current > -1) + curPage = static_cast (widget (current)); + + if (prevPage) + prevPage->hideWidgets(); + + if (curPage) + curPage->showWidgets(); + + layout()->activate(); + setFixedSize(minimumSizeHint()); + + setCurrentIndex (current); +} diff --git a/apps/opencs/view/settings/resizeablestackedwidget.hpp b/apps/opencs/view/settings/resizeablestackedwidget.hpp new file mode 100644 index 000000000..5e894d8df --- /dev/null +++ b/apps/opencs/view/settings/resizeablestackedwidget.hpp @@ -0,0 +1,23 @@ +#ifndef CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP +#define CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP + +#include + +class QListWidgetItem; + +namespace CSVSettings +{ + class ResizeableStackedWidget : public QStackedWidget + { + Q_OBJECT + + public: + explicit ResizeableStackedWidget(QWidget *parent = 0); + + void addWidget(QWidget* pWidget); + + void changePage (int, int); + }; +} + +#endif // CSVSETTINGS_RESIZEABLESTACKEDWIDGET_HPP diff --git a/apps/opencs/view/settings/settingwindow.cpp b/apps/opencs/view/settings/settingwindow.cpp new file mode 100644 index 000000000..9283bbf51 --- /dev/null +++ b/apps/opencs/view/settings/settingwindow.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include "../../model/settings/setting.hpp" +#include "../../model/settings/connector.hpp" +#include "../../model/settings/usersettings.hpp" +#include "settingwindow.hpp" +#include "page.hpp" +#include "view.hpp" + +CSVSettings::SettingWindow::SettingWindow(QWidget *parent) + : QMainWindow(parent) +{} + +void CSVSettings::SettingWindow::createPages() +{ + CSMSettings::SettingPageMap pageMap = mModel->settingPageMap(); + + QList connectedSettings; + + foreach (const QString &pageName, pageMap.keys()) + { + QList pageSettings = pageMap.value (pageName); + + mPages.append (new Page (pageName, pageSettings, this)); + + for (int i = 0; i < pageSettings.size(); i++) + { + CSMSettings::Setting *setting = pageSettings.at(i); + + if (!setting->proxyLists().isEmpty()) + connectedSettings.append (setting); + } + } + + if (!connectedSettings.isEmpty()) + createConnections(connectedSettings); +} + +void CSVSettings::SettingWindow::createConnections + (const QList &list) +{ + foreach (const CSMSettings::Setting *setting, list) + { + View *masterView = findView (setting->page(), setting->name()); + + CSMSettings::Connector *connector = + new CSMSettings::Connector (masterView, this); + + connect (masterView, + SIGNAL (viewUpdated(const QString &, const QStringList &)), + connector, + SLOT (slotUpdateSlaves()) + ); + + const CSMSettings::ProxyValueMap &proxyMap = setting->proxyLists(); + + foreach (const QString &key, proxyMap.keys()) + { + QStringList keyPair = key.split('.'); + + if (keyPair.size() != 2) + continue; + + View *slaveView = findView (keyPair.at(0), keyPair.at(1)); + + if (!slaveView) + { + qWarning () << "Unable to create connection for view " + << key; + continue; + } + + QList proxyList = proxyMap.value (key); + connector->addSlaveView (slaveView, proxyList); + + connect (slaveView, + SIGNAL (viewUpdated(const QString &, const QStringList &)), + connector, + SLOT (slotUpdateMaster())); + } + } +} + +CSVSettings::View *CSVSettings::SettingWindow::findView + (const QString &pageName, const QString &setting) +{ + foreach (const Page *page, mPages) + { + if (page->objectName() == pageName) + return page->findView (pageName, setting); + } + return 0; +} + +void CSVSettings::SettingWindow::saveSettings() +{ + QMap settingMap; + + foreach (const Page *page, mPages) + { + foreach (const View *view, page->views()) + { + if (view->serializable()) + settingMap[view->viewKey()] = view->selectedValues(); + } + } + CSMSettings::UserSettings::instance().saveSettings (settingMap); +} + +void CSVSettings::SettingWindow::closeEvent (QCloseEvent *event) +{ + QApplication::focusWidget()->clearFocus(); +} diff --git a/apps/opencs/view/settings/settingwindow.hpp b/apps/opencs/view/settings/settingwindow.hpp new file mode 100644 index 000000000..35ae4c068 --- /dev/null +++ b/apps/opencs/view/settings/settingwindow.hpp @@ -0,0 +1,49 @@ +#ifndef CSVSETTINGS_SETTINGWINDOW_HPP +#define CSVSETTINGS_SETTINGWINDOW_HPP + +#include +#include + +#include "../../model/settings/support.hpp" + +namespace CSMSettings { + class Setting; + class SettingManager; +} + +namespace CSVSettings { + + class Page; + class View; + + typedef QList PageList; + + class SettingWindow : public QMainWindow + { + Q_OBJECT + + PageList mPages; + CSMSettings::SettingManager *mModel; + + public: + explicit SettingWindow(QWidget *parent = 0); + + View *findView (const QString &pageName, const QString &setting); + void setModel (CSMSettings::SettingManager &model) { mModel = &model; } + + protected: + + virtual void closeEvent (QCloseEvent *event); + + void createPages(); + + const PageList &pages() const { return mPages; } + + void saveSettings(); + + private: + void createConnections (const QList &list); + }; +} + +#endif // CSVSETTINGS_SETTINGWINDOW_HPP diff --git a/apps/opencs/view/settings/textview.cpp b/apps/opencs/view/settings/textview.cpp new file mode 100644 index 000000000..5e10c346f --- /dev/null +++ b/apps/opencs/view/settings/textview.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include "textview.hpp" +#include "../../model/settings/setting.hpp" + +CSVSettings::TextView::TextView(CSMSettings::Setting *setting, Page *parent) + : mDelimiter (setting->delimiter()), View (setting, parent) + +{ + if (setting->isMultiLine()) + mTextWidget = new QTextEdit ("", this); + else + mTextWidget = new QLineEdit ("", this); + + if (setting->widgetWidth() > 0) + mTextWidget->setFixedWidth (widgetWidth (setting->widgetWidth())); + + connect (mTextWidget, SIGNAL (textEdited (QString)), + this, SLOT (slotTextEdited (QString))); + + addWidget (mTextWidget, setting->viewRow(), setting->viewColumn()); +} + +bool CSVSettings::TextView::isEquivalent + (const QString &lhs, const QString &rhs) const +{ + return (lhs.trimmed() == rhs.trimmed()); +} + +void CSVSettings::TextView::setWidgetText (const QString &value) const +{ + mTextWidget->setProperty ("text", value); +} + +void CSVSettings::TextView::slotTextEdited (QString value) +{ + QStringList values = value.split (mDelimiter, QString::SkipEmptyParts); + + QStringList returnValues; + + foreach (const QString &splitValue, values) + returnValues.append (splitValue.trimmed()); + + setSelectedValues (returnValues, false); + + View::updateView(); +} + +void CSVSettings::TextView::updateView(bool signalUpdate) const +{ + QString values = selectedValues().join (mDelimiter); + + if (isEquivalent (widgetText(), values)) + return; + + setWidgetText (values); + + View::updateView (signalUpdate); +} + +QString CSVSettings::TextView::widgetText() const +{ + return mTextWidget->property("text").toString(); +} + +CSVSettings::TextView *CSVSettings::TextViewFactory::createView + (CSMSettings::Setting *setting, + Page *parent) +{ + return new TextView (setting, parent); +} + diff --git a/apps/opencs/view/settings/textview.hpp b/apps/opencs/view/settings/textview.hpp new file mode 100644 index 000000000..6d718aad8 --- /dev/null +++ b/apps/opencs/view/settings/textview.hpp @@ -0,0 +1,56 @@ +#ifndef CSVSETTINGS_TEXTVIEW_HPP +#define CSVSETTINGS_TEXTVIEW_HPP + +#include "view.hpp" +#include "../../model/settings/setting.hpp" + +namespace CSVSettings +{ + class TextView : public View + { + Q_OBJECT + + QWidget *mTextWidget; + + QString mDelimiter; + + public: + explicit TextView (CSMSettings::Setting *setting, + Page *parent = 0); + + protected: + + void updateView (bool signalUpdate = true) const; + + protected slots: + + ///Receives updates to the widget for signalling + void slotTextEdited (QString value); + + private: + + ///Comparison function that returns true if the trimmed() strings + ///are equal + bool isEquivalent (const QString &lhs, const QString &rhs) const; + + ///Convenience function to return the text of the widget + QString widgetText() const; + + ///Convenience function to set the text of the widget + void setWidgetText (const QString &value) const; + }; + + class TextViewFactory : public QObject, public IViewFactory + { + Q_OBJECT + + public: + explicit TextViewFactory (QWidget *parent = 0) + : QObject (parent) + {} + + TextView *createView (CSMSettings::Setting *setting, + Page *parent); + }; +} +#endif // CSVSETTINGS_TEXTVIEW_HPP diff --git a/apps/opencs/view/settings/view.cpp b/apps/opencs/view/settings/view.cpp new file mode 100644 index 000000000..259dd518b --- /dev/null +++ b/apps/opencs/view/settings/view.cpp @@ -0,0 +1,218 @@ +#include +#include +#include +#include + +#include "view.hpp" +#include "../../model/settings/support.hpp" +#include "../../model/settings/setting.hpp" +#include "page.hpp" + +CSVSettings::View::View(CSMSettings::Setting *setting, + Page *parent) + + : mDataModel(0), mParentPage (parent), + mHasFixedValues (!setting->declaredValues().isEmpty()), + mIsMultiValue (setting->isMultiValue()), + mViewKey (setting->page() + '.' + setting->name()), + mSerializable (setting->serializable()), + Frame(true, setting->name(), parent) +{ + setObjectName (setting->name()); + buildView(); + buildModel (setting); +} + +void CSVSettings::View::buildModel (const CSMSettings::Setting *setting) +{ + QStringList values = setting->definedValues(); + + if (values.isEmpty()) + values.append (setting->defaultValues()); + + if (mHasFixedValues) + buildFixedValueModel (setting->declaredValues()); + else + buildUpdatableValueModel (values); + + mSelectionModel = new QItemSelectionModel (mDataModel, this); + + setSelectedValues (values, false); +} + +void CSVSettings::View::buildFixedValueModel (const QStringList &values) +{ + mDataModel = new QStringListModel (values, this); +} + +void CSVSettings::View::buildUpdatableValueModel (const QStringList &values) +{ + QList itemList; + + foreach (const QString &value, values) + itemList.append (new QStandardItem(value)); + +// QSortFilterProxyModel *filter = new QSortFilterProxyModel (this); + QStandardItemModel *model = new QStandardItemModel (this); + model->appendColumn (itemList); + +// filter->setSourceModel (model); + /* filter->setFilterRegExp ("*"); + filter->setFilterKeyColumn (0); + filter->setFilterRole (Qt::DisplayRole);*/ + mDataModel = model; +} + +void CSVSettings::View::buildView() +{ + setFlat (true); + setHLayout(); +} + +int CSVSettings::View::currentIndex () const +{ + if (selectedValues().isEmpty()) + return -1; + + QString currentValue = selectedValues().at(0); + + for (int i = 0; i < mDataModel->rowCount(); i++) + if (value(i) == currentValue) + return i; + + return -1; +} + +void CSVSettings::View::refresh() const +{ + select (mSelectionModel->selection()); + updateView(); +} + +int CSVSettings::View::rowCount() const +{ + return mDataModel->rowCount(); +} + +void CSVSettings::View::select (const QItemSelection &selection) const +{ + mSelectionModel->clear(); + mSelectionModel->select(selection, QItemSelectionModel::Select); +} + +QStringList CSVSettings::View::selectedValues() const +{ + QStringList selValues; + + foreach (const QModelIndex &idx, mSelectionModel->selectedIndexes()) + selValues.append (value(idx.row())); + + return selValues; +} + +void CSVSettings::View::setSelectedValue (const QString &value, + bool doViewUpdate, bool signalUpdate) +{ + setSelectedValues (QStringList() << value, doViewUpdate, signalUpdate); +} + +void CSVSettings::View::setSelectedValues (const QStringList &list, + bool doViewUpdate, bool signalUpdate) +{ + QItemSelection selection; + + if (stringListsMatch (list, selectedValues())) + return; + + if (!mHasFixedValues) + { + QStandardItemModel *model = + static_cast (mDataModel); + + model->clear(); + model->appendColumn (toStandardItemList (list)); + + for (int i = 0; i < model->rowCount(); i++) + { + QModelIndex idx = model->index(i, 0); + selection.append (QItemSelectionRange (idx, idx)); + } + } + else + { + for (int i = 0; i < mDataModel->rowCount(); i++) + { + if (list.contains(value(i))) + { + QModelIndex idx = mDataModel->index(i, 0); + selection.append(QItemSelectionRange (idx, idx)); + } + } + } + select (selection); + + if (doViewUpdate) + updateView (signalUpdate); +} + +void CSVSettings::View::showEvent ( QShowEvent * event ) +{ + refresh(); +} + +bool CSVSettings::View::stringListsMatch ( + const QStringList &list1, + const QStringList &list2) const +{ + //returns a "sloppy" match, verifying that each list contains all the same + //items, though not necessarily in the same order. + + if (list1.size() != list2.size()) + return false; + + QStringList tempList(list2); + + //iterate each value in the list, removing one occurrence of the value in + //the other list. If no corresponding value is found, test fails + foreach (const QString &value, list1) + { + if (!tempList.contains(value)) + return false; + + tempList.removeOne(value); + } + return true; +} + +QList CSVSettings::View::toStandardItemList + (const QStringList &list) const +{ + QList itemList; + + foreach (const QString &value, list) + itemList.append (new QStandardItem (value)); + + return itemList; +} + +void CSVSettings::View::updateView (bool signalUpdate) const +{ + if (signalUpdate) + emit viewUpdated(objectName(), selectedValues()); +} + +QString CSVSettings::View::value (int row) const +{ + if (row > -1 && row < mDataModel->rowCount()) + return mDataModel->data (mDataModel->index(row, 0)).toString(); + + return ""; +} + +int CSVSettings::View::widgetWidth(int characterCount) const +{ + QString widthToken = QString().fill ('P', characterCount); + QFontMetrics fm (QApplication::font()); + + return (fm.width (widthToken)); +} diff --git a/apps/opencs/view/settings/view.hpp b/apps/opencs/view/settings/view.hpp new file mode 100644 index 000000000..c99879762 --- /dev/null +++ b/apps/opencs/view/settings/view.hpp @@ -0,0 +1,163 @@ +#ifndef CSVSETTINGS_VIEW_HPP +#define CSVSETTINGS_VIEW_HPP + +#include +#include + +#include "frame.hpp" +#include "../../model/settings/support.hpp" + +class QGroupBox; +class QStringList; +class QStandardItem; +class QItemSelection; +class QStringListModel; +class QStandardItemModel; +class QAbstractItemModel; +class QItemSelectionModel; + +namespace CSMSettings { class Setting; } + +namespace CSVSettings +{ + class Page; + + class View : public Frame + { + Q_OBJECT + + ///Pointer to the owning Page instance + Page *mParentPage; + + ///Pointer to the selection model for the view + QItemSelectionModel *mSelectionModel; + + ///Pointer to the data model for the view's selection model + QAbstractItemModel *mDataModel; + + ///State indicating whether or not the setting has a pre-defined list + ///of values, limiting possible definitions + bool mHasFixedValues; + + ///State indicating whether the view will allow multiple values + bool mIsMultiValue; + + QString mViewKey; + + bool mSerializable; + + public: + + explicit View (CSMSettings::Setting *setting, Page *parent); + + ///Physical frame in which the view UI is contained + void addViewWidget (QWidget *widget, int row = -1, int col = -1) const; + + ///Returns the index / row of the passed value, -1 if not found. + int currentIndex () const; + + ///Returns the number of rows in the view's data model + int rowCount() const; + + ///Returns bool indicating the data in this view should / should not + ///be serialized to a config file + bool serializable() const { return mSerializable; } + + ///Returns a pointer to the view's owning parent page + const Page *parentPage() const { return mParentPage; } + + ///Returns the selected items in the selection model as a QStringList + QStringList selectedValues() const; + + ///Sets the selected items in the selection model based on passed list. + ///Bools allow opt-out of updating the view + ///or signaling the view was updatedto avoid viscious cylcing. + void setSelectedValues (const QStringList &values, + bool updateView = true, + bool signalUpdate = true); + + void setSelectedValue (const QString &value, + bool updateView = true, + bool signalUpdate = true); + + + ///Returns the value of the data model at the specified row + QString value (int row) const; + + QString viewKey() const { return mViewKey; } + + protected: + + /// Returns the model which provides data for the selection model + QAbstractItemModel *dataModel() { return mDataModel; } + + ///Accessor function for subclasses + bool isMultiValue() { return mIsMultiValue; } + + ///Returns the view selection model + QItemSelectionModel *selectionModel() { return mSelectionModel;} + + ///Global callback for basic view initialization + void showEvent ( QShowEvent * event ); + + ///Virtual for updating a specific View subclass + ///bool indicates whether a signal is emitted that the view was updated + virtual void updateView (bool signalUpdate = true) const; + + ///Returns the pixel width corresponding to the specified number of + ///characters. + int widgetWidth(int characterCount) const; + + private: + + ///Constructs the view layout + void buildView(); + + ///Constructs the data and selection models + void buildModel (const CSMSettings::Setting *setting); + + ///In cases where the view has a pre-defined list of possible values, + ///a QStringListModel is created using those values. + ///View changes operate on the selection model only. + void buildFixedValueModel (const QStringList &definitions); + + ///In cases where the view does not have a pre-defined list of possible + ///values, a QStandardItemModel is created, containing the actual + ///setting definitions. View changes first update the data in the + ///model to match the data in the view. The selection model always + ///selects all values. + void buildUpdatableValueModel (const QStringList &definitions); + + ///Refreshes the view + void refresh() const; + + ///Convenince function for selection model's select() method. Also + ///clears out the model beforehand to ensure complete selection. + void select (const QItemSelection &selection) const; + + ///Compares two string lists "loosely", ensuring that all values in + ///one list are contained entirely in the other, and that neither list + ///has more values than the other. List order is not considered. + bool stringListsMatch (const QStringList &list1, + const QStringList &list2) const; + + ///Converts a string list to a list of QStandardItem pointers. + QList toStandardItemList(const QStringList &) const; + + signals: + + ///Signals that the view has been changed. + void viewUpdated(const QString &, const QStringList &) const; + + }; + + class IViewFactory + { + public: + + ///Creation interface for view factories + virtual View *createView (CSMSettings::Setting *setting, + Page *parent) = 0; + }; +} +#endif // CSVSETTINGS_VIEW_HPP From e3384e399913e2dc4a56f599fea0f1e8b4ab65b9 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Apr 2014 22:17:52 -0500 Subject: [PATCH 118/157] relocate support.hpp / remove support.cpp --- apps/opencs/CMakeLists.txt | 31 ++-- apps/opencs/model/settings/support.cpp | 1 - apps/opencs/model/settings/support.hpp | 135 +++++++++++++--- apps/opencs/view/settings/support.cpp | 1 - apps/opencs/view/settings/support.hpp | 206 ------------------------- 5 files changed, 124 insertions(+), 250 deletions(-) delete mode 100644 apps/opencs/model/settings/support.cpp delete mode 100644 apps/opencs/view/settings/support.cpp delete mode 100644 apps/opencs/view/settings/support.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index cbe90b1d3..8ab44243d 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -88,34 +88,29 @@ opencs_units_noqt (view/tools ) opencs_units (view/settings - abstractblock - proxyblock - abstractwidget - usersettingsdialog - datadisplayformatpage - windowpage + settingwindow + dialog + page + view + booleanview + textview + listview + resizeablestackedwidget ) opencs_units_noqt (view/settings - abstractpage - blankpage - groupblock - customblock - groupbox - itemblock - settingwidget - toggleblock - support + frame ) opencs_units (model/settings usersettings - settingcontainer + settingmanager + setting + connector ) -opencs_units_noqt (model/settings +opencs_hdrs_noqt (model/settings support - settingsitem ) opencs_units_noqt (model/filter diff --git a/apps/opencs/model/settings/support.cpp b/apps/opencs/model/settings/support.cpp deleted file mode 100644 index d79edfdb3..000000000 --- a/apps/opencs/model/settings/support.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "support.hpp" diff --git a/apps/opencs/model/settings/support.hpp b/apps/opencs/model/settings/support.hpp index 4ffd01b73..abc86a0cb 100644 --- a/apps/opencs/model/settings/support.hpp +++ b/apps/opencs/model/settings/support.hpp @@ -1,39 +1,126 @@ -#ifndef MODEL_SUPPORT_HPP -#define MODEL_SUPPORT_HPP +#ifndef SETTING_SUPPORT_HPP +#define SETTING_SUPPORT_HPP -#include +#include +#include +#include +#include #include -class QLayout; -class QWidget; -class QListWidgetItem; +//Typedefs +namespace CSMSettings +{ + // Definition / Declaration model typedefs + // "Pair" = Setting name and specific data + // "ListItem" = Page name and associated setting pair + typedef QPair StringPair; + typedef QPair StringListPair; + typedef QList StringListPairs; + +} + +//Enums namespace CSMSettings { - class SettingContainer; + enum SettingProperty + { + Property_Name = 0, + Property_Page = 1, + Property_ViewType = 2, + Property_IsMultiValue = 3, + Property_IsMultiLine = 4, + Property_WidgetWidth = 5, + Property_ViewRow = 6, + Property_ViewColumn = 7, + Property_Delimiter = 8, + Property_Serializable = 9, + Property_ColumnSpan = 10, + Property_RowSpan = 11, - typedef QList SettingList; - typedef QMap SettingMap; - typedef QMap SectionMap; + //Stringlists should always be the last items + Property_DefaultValues = 12, + Property_DeclaredValues = 13, + Property_DefinedValues = 14, + Property_Proxies = 15 + }; - struct QStringPair + enum SettingType { - QStringPair(): left (""), right ("") - {} + Type_MultiBool = 0, + Type_SingleBool = 1, + Type_MultiList = 2, + Type_SingleList = 3, + Type_MultiRange = 4, + Type_SingleRange = 5, + Type_MultiText = 6, + Type_SingleText = 7 + }; - QStringPair (const QString &leftValue, const QString &rightValue) - : left (leftValue), right(rightValue) - {} + enum MergeMethod + { + Merge_Accept, + Merge_Ignore, + Merge_Overwrite + }; +} - QStringPair (const QStringPair &pair) - : left (pair.left), right (pair.right) - {} +namespace CSVSettings +{ + enum ViewType + { + ViewType_Boolean = 0, + ViewType_List = 1, + ViewType_Range = 2, + ViewType_Text = 3, + ViewType_Undefined = 4 + }; - QString left; - QString right; + enum Alignment + { + Align_Left = Qt::AlignLeft, + Align_Center = Qt::AlignHCenter, + Align_Right = Qt::AlignRight + }; +} - bool isEmpty() const - { return (left.isEmpty() && right.isEmpty()); } +// +namespace CSMSettings +{ + struct PropertyDefaultValues + { + int id; + QString name; + QVariant value; + }; + + const QString sPropertyNames[] = + { + "name", "page", "view_type", "is_multi_value", + "is_multi_line", "widget_width", "view_row", "view_column", "delimiter", + "is_serializable","column_span", "row_span", + "defaults", "declarations", "definitions", "proxies" + }; + + const QString sPropertyDefaults[] = + { + "", //name + "", //page + "0", //view type + "false", //multivalue + "false", //multiline + "0", //widget width + "-1", //view row + "-1", //view column + ",", //delimiter + "true", //serialized + "1", //column span + "1", //row span + "", //default values + "", //declared values + "", //defined values + "" //proxy values }; } -#endif // MODEL_SUPPORT_HPP + +#endif // VIEW_SUPPORT_HPP diff --git a/apps/opencs/view/settings/support.cpp b/apps/opencs/view/settings/support.cpp deleted file mode 100644 index d79edfdb3..000000000 --- a/apps/opencs/view/settings/support.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "support.hpp" diff --git a/apps/opencs/view/settings/support.hpp b/apps/opencs/view/settings/support.hpp deleted file mode 100644 index 5d954505c..000000000 --- a/apps/opencs/view/settings/support.hpp +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef VIEW_SUPPORT_HPP -#define VIEW_SUPPORT_HPP - -#include -#include - -#include "../../model/settings/support.hpp" - -namespace CSVSettings -{ - struct WidgetDef; - class ItemBlock; - class GroupBlock; - struct GroupBlockDef; - - typedef QList GroupBlockDefList; - typedef QList GroupBlockList; - typedef QList ItemBlockList; - typedef QList ProxyList; - typedef QList WidgetList; - typedef QMap ItemBlockMap; - - enum Orientation - { - Orient_Horizontal, - Orient_Vertical - }; - - enum WidgetType - { - Widget_CheckBox, - Widget_ComboBox, - Widget_LineEdit, - Widget_ListBox, - Widget_RadioButton, - Widget_SpinBox, - Widget_Undefined - }; - - enum Alignment - { - Align_Left = Qt::AlignLeft, - Align_Center = Qt::AlignHCenter, - Align_Right = Qt::AlignRight - }; - - /// definition struct for widgets - struct WidgetDef - { - /// type of widget providing input - WidgetType type; - - /// width of caption label - int labelWidth; - - /// width of input widget - int widgetWidth; - - /// label / widget orientation (horizontal / vertical) - Orientation orientation; - - /// input mask (line edit only) - QString inputMask; - - /// label caption. Leave empty for multiple items. See BlockDef::captionList - QString caption; - - /// widget value. Leave empty for multiple items. See BlockDef::valueList - QString value; - - /// Min/Max QString value pair. If empty, assigned to property item value pair. - CSMSettings::QStringPair *minMax; - - /// value list for list widgets. If left empty, is assigned to property item value list during block build(). - QStringList *valueList; - - /// determined at runtime - bool isDefault; - - /// left / center / right-justify text in widget - Alignment valueAlignment; - - /// left / center / right-justify widget in group box - Alignment widgetAlignment; - - - WidgetDef() : labelWidth (-1), widgetWidth (-1), - orientation (Orient_Horizontal), - isDefault (true), valueAlignment (Align_Center), - widgetAlignment (Align_Right), - inputMask (""), value (""), - caption (""), valueList (0) - {} - - WidgetDef (WidgetType widgType) - : type (widgType), orientation (Orient_Horizontal), - caption (""), value (""), valueAlignment (Align_Center), - widgetAlignment (Align_Right), - labelWidth (-1), widgetWidth (-1), - valueList (0), isDefault (true) - {} - - }; - - /// Defines the attributes of the setting as it is represented in the config file - /// as well as the UI elements (group box and widget) that serve it. - /// Only one widget may serve as the input widget for the setting. - struct SettingsItemDef - { - /// setting name - QString name; - - /// list of valid values for the setting - QStringList *valueList; - - /// Used to populate option widget captions or list widget item lists (see WidgetDef::caption / value) - QString defaultValue; - - /// flag indicating multi-valued setting - bool hasMultipleValues; - - /// minimum / maximum value pair - CSMSettings::QStringPair minMax; - - /// definition of the input widget for this setting - WidgetDef widget; - - /// general orientation of the widget / label for this setting - Orientation orientation; - - /// list of settings and corresponding default values for proxy widget - ProxyList *proxyList; - - SettingsItemDef() : name (""), defaultValue (""), orientation (Orient_Vertical), hasMultipleValues (false) - {} - - SettingsItemDef (QString propName, QString propDefault, Orientation propOrient = Orient_Vertical) - : name (propName), defaultValue (propDefault), orientation (propOrient), - hasMultipleValues(false), valueList (new QStringList), proxyList ( new ProxyList) - {} - }; - - - /// Generic container block - struct GroupBlockDef - { - /// block title - QString title; - - /// list of captions for widgets at the block level (not associated with any particular setting) - QStringList captions; - - /// list of widgets at the block level (not associated with any particular setting) - WidgetList widgets; - - /// list of the settings which are subordinate to the setting block. - QList settingItems; - - /// general orientation of widgets in group block - Orientation widgetOrientation; - - /// determines whether or not box border/title are visible - bool isVisible; - - /// indicates whether or not this block defines a proxy block - bool isProxy; - - /// generic default value attribute - QString defaultValue; - - /// shows / hides margins - bool isZeroMargin; - - GroupBlockDef (): title(""), widgetOrientation (Orient_Vertical), isVisible (true), isProxy (false), defaultValue (""), isZeroMargin (true) - {} - - GroupBlockDef (QString blockTitle) - : title (blockTitle), widgetOrientation (Orient_Vertical), isProxy (false), isVisible (true), defaultValue (""), isZeroMargin (true) - {} - }; - - /// used to create unique, complex blocks - struct CustomBlockDef - { - /// block title - QString title; - - /// default value for widgets unique to the custom block - QString defaultValue; - - /// list of settings groups that comprise the settings within the custom block - GroupBlockDefList blockDefList; - - /// orientation of the widgets within the block - Orientation blockOrientation; - - CustomBlockDef (): title (""), defaultValue (""), blockOrientation (Orient_Horizontal) - {} - - CustomBlockDef (const QString &blockTitle) - : title (blockTitle), defaultValue (""), blockOrientation (Orient_Horizontal) - {} - }; -} - -#endif // VIEW_SUPPORT_HPP From 4b607d658f5e8f2008d06395f270b2f902ef4d3f Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Apr 2014 22:19:53 -0500 Subject: [PATCH 119/157] Re-link user settings to editor main application --- apps/opencs/editor.cpp | 13 + apps/opencs/editor.hpp | 4 +- apps/opencs/model/settings/usersettings.cpp | 455 ++++++++------------ apps/opencs/model/settings/usersettings.hpp | 45 +- apps/opencs/view/doc/subview.cpp | 9 +- apps/opencs/view/doc/subview.hpp | 4 +- apps/opencs/view/doc/view.cpp | 41 +- apps/opencs/view/doc/view.hpp | 2 + apps/opencs/view/doc/viewmanager.cpp | 17 +- apps/opencs/view/doc/viewmanager.hpp | 3 - apps/opencs/view/tools/reportsubview.cpp | 7 +- apps/opencs/view/tools/reportsubview.hpp | 5 +- apps/opencs/view/world/table.cpp | 14 +- apps/opencs/view/world/table.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 7 +- apps/opencs/view/world/tablesubview.hpp | 3 +- apps/opencs/view/world/util.cpp | 6 +- apps/opencs/view/world/util.hpp | 4 +- 18 files changed, 280 insertions(+), 361 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 87660a60b..2dfdb1de6 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -28,6 +28,7 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit) setupDataFiles (config.first); CSMSettings::UserSettings::instance().loadSettings ("opencs.cfg"); + mSettings.setModel (CSMSettings::UserSettings::instance()); ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); @@ -117,6 +118,18 @@ std::pair > CS::Editor::readConfi dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + //iterate the data directories and add them to the file dialog for loading + 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 std::make_pair (dataDirs, variables["fallback-archive"].as >()); } diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 164398fb7..4c02b462e 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -24,7 +24,7 @@ #include "view/doc/filedialog.hpp" #include "view/doc/newgame.hpp" -#include "view/settings/usersettingsdialog.hpp" +#include "view/settings/dialog.hpp" namespace OgreInit { @@ -43,7 +43,7 @@ namespace CS CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; CSVDoc::NewGameDialogue mNewGame; - CSVSettings::UserSettingsDialog mSettings; + CSVSettings::Dialog mSettings; CSVDoc::FileDialog mFileDialog; boost::filesystem::path mLocal; boost::filesystem::path mResources; diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 94cee8a43..db31f4255 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -9,11 +9,16 @@ #include #include +#include + +#include #include -#include "settingcontainer.hpp" #include +#include "setting.hpp" +#include "support.hpp" + /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ @@ -37,269 +42,235 @@ CSMSettings::UserSettings::UserSettings() assert(!mUserSettingsInstance); mUserSettingsInstance = this; - mReadWriteMessage = QObject::tr("
Could not open or create file for writing

\ - Please make sure you have the right permissions and try again.
"); - - mReadOnlyMessage = QObject::tr("
Could not open file for reading

\ - Please make sure you have the right permissions and try again.
"); - - buildEditorSettingDefaults(); + buildSettingModelDefaults(); } -void CSMSettings::UserSettings::buildEditorSettingDefaults() +void CSMSettings::UserSettings::buildSettingModelDefaults() { - SettingContainer *windowHeight = new SettingContainer("768", this); - SettingContainer *windowWidth = new SettingContainer("1024", this); - SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this); - SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this); - - windowHeight->setObjectName ("Height"); - windowWidth->setObjectName ("Width"); - rsDelegate->setObjectName ("Record Status Display"); - refIdTypeDelegate->setObjectName ("Referenceable ID Type Display"); - - SettingMap *displayFormatMap = new SettingMap; - SettingMap *windowSizeMap = new SettingMap; - - displayFormatMap->insert (rsDelegate->objectName(), rsDelegate ); - displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate); - - windowSizeMap->insert (windowWidth->objectName(), windowWidth ); - windowSizeMap->insert (windowHeight->objectName(), windowHeight ); - - mEditorSettingDefaults.insert ("Display Format", displayFormatMap); - mEditorSettingDefaults.insert ("Window Size", windowSizeMap); -} - -CSMSettings::UserSettings::~UserSettings() -{ - mUserSettingsInstance = 0; -} - -QTextStream *CSMSettings::UserSettings::openFileStream (const QString &filePath, bool isReadOnly) const -{ - QIODevice::OpenMode openFlags = QIODevice::Text; - - if (isReadOnly) - openFlags = QIODevice::ReadOnly | openFlags; - else - openFlags = QIODevice::ReadWrite | QIODevice::Truncate | openFlags; - - QFile *file = new QFile(filePath); - QTextStream *stream = 0; - - if (file->open(openFlags)) + QString section = "Window Size"; { - stream = new QTextStream(file); - stream->setCodec(QTextCodec::codecForName("UTF-8")); + Setting *width = createSetting (Type_SingleText, section, "Width"); + Setting *height = createSetting (Type_SingleText, section, "Height"); + + width->setWidgetWidth (5); + height->setWidgetWidth (5); + + width->setDefaultValues (QStringList() << "1024"); + height->setDefaultValues (QStringList() << "768"); + + width->setEditorSetting (true); + height->setEditorSetting (true); + + height->setViewLocation (2,2); + width->setViewLocation (2,1); + + /* + *Create the proxy setting for predefined values + */ + Setting *preDefined = createSetting (Type_SingleList, section, + "Pre-Defined", + QStringList() + << "640 x 480" + << "800 x 600" + << "1024 x 768" + << "1440 x 900" + ); + + preDefined->setViewLocation (1, 1); + preDefined->setWidgetWidth (10); + preDefined->setColumnSpan (2); + + preDefined->addProxy (width, + QStringList() << "640" << "800" << "1024" << "1440" + ); + + preDefined->addProxy (height, + QStringList() << "480" << "600" << "768" << "900" + ); } - return stream; - -} - -bool CSMSettings::UserSettings::writeSettings(QMap &settings) -{ - QTextStream *stream = openFileStream(mUserFilePath); - - bool success = (stream); - - if (success) + section = "Display Format"; { - QList keyList = settings.keys(); + QString defaultValue = "Icon and Text"; - foreach (QString key, keyList) - { - SettingList *sectionSettings = settings[key]; + QStringList values = QStringList() + << defaultValue << "Icon Only" << "Text Only"; - *stream << "[" << key << "]" << '\n'; + Setting *rsd = createSetting (Type_SingleBool, + section, "Record Status Display", + values); - foreach (SettingContainer *item, *sectionSettings) - *stream << item->objectName() << " = " << item->getValue() << '\n'; - } + Setting *ritd = createSetting (Type_SingleBool, + section, "Referenceable ID Type Display", + values); - stream->device()->close(); - delete stream; - stream = 0; + rsd->setEditorSetting (true); + ritd->setEditorSetting (true); } - else + + section = "Proxy Selection Test"; { - displayFileErrorMessage(mReadWriteMessage, false); + //create three setting objects, specifying the basic widget type, + //the setting view name, the page name, and the default value + Setting *masterBoolean = createSetting (Type_SingleBool, section, + "Master Proxy", + QStringList() + << "Profile One" << "Profile Two" + << "Profile Three" << "Profile Four" + ); + + Setting *slaveBoolean = createSetting (Type_MultiBool, section, + "Proxy Checkboxes", + QStringList() << "One" << "Two" + << "Three" << "Four" << "Five" + ); + + Setting *slaveSingleText = createSetting (Type_SingleText, section, + "Proxy TextBox 1" + ); + + Setting *slaveMultiText = createSetting (Type_SingleText, section, + "ProxyTextBox 2" + ); + + // There are three types of values: + // + // Declared values - Pre-determined values, typically for + // combobox drop downs and boolean (radiobutton / checkbox) labels. + // These values represent the total possible list of values that may + // define a setting. No other values are allowed. + // + // Defined values - Values which represent the atual, current value of + // a setting. For settings with declared values, this must be one or + // several declared values, as appropriate. + // + // Proxy values - values the proxy master updates the proxy slave when + // it's own definition is set / changed. These are definitions for + // proxy slave settings, but must match any declared values the proxy + // slave has, if any. + + masterBoolean->addProxy (slaveBoolean, QList () + << (QStringList() << "One" << "Three") + << (QStringList() << "One" << "Three") + << (QStringList() << "One" << "Three" << "Five") + << (QStringList() << "Two" << "Four") + ); + + masterBoolean->addProxy (slaveSingleText, QList () + << (QStringList() << "Text A") + << (QStringList() << "Text B") + << (QStringList() << "Text A") + << (QStringList() << "Text C") + ); + + masterBoolean->addProxy (slaveMultiText, QList () + << (QStringList() << "One" << "Three") + << (QStringList() << "One" << "Three") + << (QStringList() << "One" << "Three" << "Five") + << (QStringList() << "Two" << "Four") + ); + + //settings with proxies are not serialized by default + //other settings non-serialized for demo purposes + slaveBoolean->setSerializable (false); + slaveSingleText->setSerializable (false); + slaveMultiText->setSerializable (false); + + slaveBoolean->setDefaultValues (QStringList() + << "One" << "Three" << "Five"); + + slaveSingleText->setDefaultValue ("Text A"); + + slaveMultiText->setDefaultValues (QStringList() + << "One" << "Three" << "Five"); + + slaveSingleText->setWidgetWidth (24); + slaveMultiText->setWidgetWidth (24); } - - return (success); } - -const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const +CSMSettings::UserSettings::~UserSettings() { - return mSectionSettings; + mUserSettingsInstance = 0; } -const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString §ionName) const +void CSMSettings::UserSettings::loadSettings (const QString &fileName) { - return getValidSettings(sectionName); -} + mUserFilePath = QString::fromUtf8 + (mCfgMgr.getUserConfigPath().c_str()) + fileName.toUtf8(); -bool CSMSettings::UserSettings::loadFromFile(const QString &filePath) -{ - if (filePath.isEmpty()) - return false; + QString global = QString::fromUtf8 + (mCfgMgr.getGlobalPath().c_str()) + fileName.toUtf8(); - SectionMap loadedSettings; + QString local = QString::fromUtf8 + (mCfgMgr.getLocalPath().c_str()) + fileName.toUtf8(); - QTextStream *stream = openFileStream (filePath, true); + //open user and global streams + QTextStream *userStream = openFilestream (mUserFilePath, true); + QTextStream *otherStream = openFilestream (global, true); - bool success = (stream); + //failed stream, try for local + if (!otherStream) + otherStream = openFilestream (local, true); - if (success) + //error condition - notify and return + if (!otherStream || !userStream) { - //looks for a square bracket, "'\\[" - //that has one or more "not nothing" in it, "([^]]+)" - //and is closed with a square bracket, "\\]" - - QRegExp sectionRe("^\\[([^]]+)\\]"); - - //Find any character(s) that is/are not equal sign(s), "[^=]+" - //followed by an optional whitespace, an equal sign, and another optional whitespace, "\\s*=\\s*" - //and one or more periods, "(.+)" - - QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); - - CSMSettings::SettingMap *settings = 0; - QString section = "none"; - - while (!stream->atEnd()) - { - QString line = stream->readLine().simplified(); - - if (line.isEmpty() || line.startsWith("#")) - continue; - - //if a section is found, push it onto a new QStringList - //and push the QStringList onto - if (sectionRe.exactMatch(line)) - { - //add the previous section's settings to the member map - if (settings) - loadedSettings.insert(section, settings); - - //save new section and create a new list - section = sectionRe.cap(1); - settings = new SettingMap; - continue; - } + QString message = QObject::tr("
An error was encountered loading \ + user settings files.

One or several files could not \ + be read. This may be caused by a missing configuration file, \ + incorrect file permissions or a corrupted installation of \ + OpenCS.
"); - if (keyRe.indexIn(line) != -1) - { - SettingContainer *sc = new SettingContainer (keyRe.cap(2).simplified()); - sc->setObjectName(keyRe.cap(1).simplified()); - (*settings)[keyRe.cap(1).simplified()] = sc; - } + message += QObject::tr("
Global filepath: ") + global; + message += QObject::tr("
Local filepath: ") + local; + message += QObject::tr("
User filepath: ") + mUserFilePath; - } - - loadedSettings.insert(section, settings); - - stream->device()->close(); - delete stream; - stream = 0; + displayFileErrorMessage ( message, true); + return; } - mergeMap (loadedSettings); + //success condition - merge the two streams into a single map and save + DefinitionPageMap totalMap = readFilestream (userStream); + DefinitionPageMap otherMap = readFilestream(otherStream); - return success; -} + //merging other settings file in and ignore duplicate settings to + //avoid overwriting user-level settings + mergeSettings (totalMap, otherMap); -void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap §ionSettings) -{ - foreach (QString key, sectionSettings.uniqueKeys()) - { - // insert entire section if it does not already exist in the loaded files - if (mSectionSettings.find(key) == mSectionSettings.end()) - mSectionSettings.insert(key, sectionSettings.value(key)); - else - { - SettingMap *passedSettings = sectionSettings.value(key); - SettingMap *settings = mSectionSettings.value(key); - - foreach (QString key2, passedSettings->uniqueKeys()) - { - //insert section settings individially if they do not already exist - if (settings->find(key2) == settings->end()) - settings->insert(key2, passedSettings->value(key2)); - else - { - settings->value(key2)->update(passedSettings->value(key2)->getValue()); - } - } - } - } + if (!totalMap.isEmpty()) + addDefinitions (totalMap); } -void CSMSettings::UserSettings::loadSettings (const QString &fileName) +void CSMSettings::UserSettings::saveSettings + (const QMap &settingMap) { - mSectionSettings.clear(); - - //global - QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName; - bool globalOk = loadFromFile(globalFilePath); - - - //local - QString localFilePath = QString::fromStdString(mCfgMgr.getLocalPath().string()) + fileName; - bool localOk = loadFromFile(localFilePath); - - //user - mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName; - loadFromFile(mUserFilePath); - - if (!(localOk || globalOk)) + for (int i = 0; i < settings().size(); i++) { - QString message = QObject::tr("
Could not open user settings files for reading

\ - Global and local settings files could not be read.\ - You may have incorrect file permissions or the OpenCS installation may be corrupted.
"); + Setting* setting = settings().at(i); - message += QObject::tr("
Global filepath: ") + globalFilePath; - message += QObject::tr("
Local filepath: ") + localFilePath; + QString key = setting->page() + '.' + setting->name(); - displayFileErrorMessage ( message, true); + if (!settingMap.keys().contains(key)) + continue; + + setting->setDefinedValues (settingMap.value(key)); } + + writeFilestream (openFilestream (mUserFilePath, false), settingMap); } -void CSMSettings::UserSettings::updateSettings (const QString §ionName, const QString &settingName) +QString CSMSettings::UserSettings::settingValue (const QString §ion, + const QString &name) { + Setting *setting = findSetting(section, name); - SettingMap *settings = getValidSettings(sectionName); - - if (!settings) - return; - - if (settingName.isEmpty()) - { - foreach (const SettingContainer *setting, *settings) - emit signalUpdateEditorSetting (setting->objectName(), setting->getValue()); - } - else + if (setting) { - if (settings->find(settingName) != settings->end()) - { - const SettingContainer *setting = settings->value(settingName); - emit signalUpdateEditorSetting (setting->objectName(), setting->getValue()); - } + if (!setting->definedValues().isEmpty()) + return setting->definedValues().at(0); } -} - -QString CSMSettings::UserSettings::getSetting (const QString §ion, const QString &setting) const -{ - SettingMap *settings = getValidSettings(section); - - QString retVal = ""; - - if (settings->find(setting) != settings->end()) - retVal = settings->value(setting)->getValue(); - - return retVal; + return ""; } CSMSettings::UserSettings& CSMSettings::UserSettings::instance() @@ -307,49 +278,3 @@ CSMSettings::UserSettings& CSMSettings::UserSettings::instance() assert(mUserSettingsInstance); return *mUserSettingsInstance; } - -void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message, bool isReadOnly) -{ - // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle(QObject::tr("OpenCS configuration file I/O error")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - - if (!isReadOnly) - msgBox.setText (mReadWriteMessage + message); - else - msgBox.setText (message); - - msgBox.exec(); -} - -CSMSettings::SettingMap * -CSMSettings::UserSettings::getValidSettings (const QString §ionName) const -{ - SettingMap *settings = 0; - - //copy the default values for the entire section if it's not found - if (mSectionSettings.find(sectionName) == mSectionSettings.end()) - { - if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end()) - settings = mEditorSettingDefaults.value (sectionName); - } - //otherwise, iterate the section's settings, looking for missing values and replacing them with defaults. - else - { - SettingMap *loadedSettings = mSectionSettings[sectionName]; - SettingMap *defaultSettings = mEditorSettingDefaults[sectionName]; - - foreach (QString key, defaultSettings->uniqueKeys()) - { - //write the default value to the loaded settings - if (loadedSettings->find((key))==loadedSettings->end()) - loadedSettings->insert(key, defaultSettings->value(key)); - } - - settings = mSectionSettings.value (sectionName); - } - - return settings; -} diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 63e78bd61..75a3a0797 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -8,7 +8,7 @@ #include -#include "support.hpp" +#include "settingmanager.hpp" #ifndef Q_MOC_RUN #include @@ -21,17 +21,15 @@ class QFile; namespace CSMSettings { - struct UserSettings: public QObject + class UserSettings: public SettingManager { Q_OBJECT - SectionMap mSectionSettings; - SectionMap mEditorSettingDefaults; - static UserSettings *mUserSettingsInstance; QString mUserFilePath; Files::ConfigurationManager mCfgMgr; + QString mReadOnlyMessage; QString mReadWriteMessage; @@ -47,48 +45,23 @@ namespace CSMSettings { void operator= (UserSettings const &); //not implemented /// Writes settings to the last loaded settings file - bool writeSettings(QMap §ions); - - /// Called from editor to trigger signal to update the specified setting. - /// If no setting name is specified, all settings found in the specified section are updated. - void updateSettings (const QString §ionName, const QString &settingName = ""); + bool writeSettings(); /// Retrieves the settings file at all three levels (global, local and user). - - /// \todo Multi-valued settings are not fully implemented. Setting values - /// \todo loaded in later files will always overwrite previously loaded values. void loadSettings (const QString &fileName); - /// Returns the entire map of settings across all sections - const SectionMap &getSectionMap () const; - - const SettingMap *getSettings (const QString §ionName) const; + /// Writes settings to the user's config file path + void saveSettings (const QMap &settingMap); - /// Retrieves the value as a QString of the specified setting in the specified section - QString getSetting(const QString §ion, const QString &setting) const; + QString settingValue (const QString §ion, const QString &name); private: - - /// Opens a QTextStream from the provided path as read-only or read-write. - QTextStream *openFileStream (const QString &filePath, bool isReadOnly = false) const; - - /// Parses a setting file specified in filePath from the provided text stream. - bool loadFromFile (const QString &filePath = ""); - - /// merge the passed map into mSectionSettings - void mergeMap (const SectionMap &); - - void displayFileErrorMessage(const QString &message, bool isReadOnly); - - void buildEditorSettingDefaults(); - - SettingMap *getValidSettings (const QString §ionName) const; + void buildSettingModelDefaults(); signals: - void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); - + void userSettingUpdated(const QString &, const QStringList &); }; } #endif // USERSETTINGS_HPP diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 7fd005717..a80d21cb2 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -12,16 +12,15 @@ CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const return mUniversalId; } -void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QString &settingValue) -{ -} - void CSVDoc::SubView::setStatusBar (bool show) {} void CSVDoc::SubView::useHint (const std::string& hint) {} +void CSVDoc::SubView::updateUserSetting (const QString &, const QStringList &) +{} + 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 85274a18d..52e42cc0e 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -37,7 +37,9 @@ namespace CSVDoc CSMWorld::UniversalId getUniversalId() const; virtual void setEditLock (bool locked) = 0; - virtual void updateEditorSetting (const QString &, const QString &); + + virtual void updateUserSetting + (const QString &, const QStringList &); virtual void setStatusBar (bool show); ///< Default implementation: ignored diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index bc34c6118..47e1a80de 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -12,7 +12,7 @@ #include "../../model/doc/document.hpp" #include "../world/subviews.hpp" #include "../tools/subviews.hpp" -#include "../settings/usersettingsdialog.hpp" +#include "../../model/settings/usersettings.hpp" #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" @@ -235,8 +235,11 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { - QString width = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Width")); - QString height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height")); + QString width = CSMSettings::UserSettings::instance().settingValue + (QString("Window Size"), QString("Width")); + + QString height = CSMSettings::UserSettings::instance().settingValue + (QString("Window Size"), QString("Height")); resize (width.toInt(), height.toInt()); @@ -336,7 +339,10 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id, const std::strin 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"); + connect (&CSMSettings::UserSettings::instance(), + SIGNAL (userSettingUpdated (const QString &, const QStringList &)), + view, + SLOT (updateUserSetting (const QString &, const QStringList &))); view->show(); } @@ -484,24 +490,19 @@ void CSVDoc::View::resizeViewHeight (int height) resize (geometry().width(), height); } -void CSVDoc::View::updateEditorSetting (const QString &settingName, const QString &settingValue) +void CSVDoc::View::updateUserSetting + (const QString &name, const QStringList &list) { - if ( (settingName == "Record Status Display") || (settingName == "Referenceable ID Type Display") ) - { - foreach (QObject *view, mSubViewWindow.children()) - { - // not all mSubviewWindow children are CSVDoc::Subview objects - CSVDoc::SubView *subview = dynamic_cast(view); - - if (subview) - subview->updateEditorSetting (settingName, settingValue); - } - } - else if (settingName == "Width") - resizeViewWidth (settingValue.toInt()); + if (list.isEmpty()) + return; + + int value = list.at(0).toInt(); + + if (name == "Width") + resizeViewWidth (value); - else if (settingName == "Height") - resizeViewHeight (settingValue.toInt()); + else if (name == "Height") + resizeViewHeight (value); } void CSVDoc::View::toggleShowStatusBar (bool show) diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index ee7380e2b..5e6c9abc4 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -126,6 +126,8 @@ namespace CSVDoc void abortOperation (int type); + void updateUserSetting (const QString &, const QStringList &); + private slots: void newView(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 4a4dbc124..2297af0ba 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -15,7 +15,8 @@ #include "../world/vartypedelegate.hpp" #include "../world/recordstatusdelegate.hpp" #include "../world/idtypedelegate.hpp" -#include "../settings/usersettingsdialog.hpp" + +#include "../../model/settings/usersettings.hpp" #include "view.hpp" @@ -83,9 +84,6 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) for (std::size_t i=0; iadd (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory ( CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone)); - - connect (&CSMSettings::UserSettings::instance(), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), - this, SLOT (slotUpdateEditorSetting (const QString &, const QString &))); } CSVDoc::ViewManager::~ViewManager() @@ -119,6 +117,11 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); + connect (&CSMSettings::UserSettings::instance(), + SIGNAL (userSettingUpdated(const QString &, const QStringList &)), + view, + SLOT (updateUserSetting (const QString &, const QStringList &))); + updateIndices(); return view; @@ -313,9 +316,3 @@ void CSVDoc::ViewManager::exitApplication (CSVDoc::View *view) if (notifySaveOnClose (view)) QApplication::instance()->exit(); } - -void CSVDoc::ViewManager::slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) -{ - foreach (CSVDoc::View *view, mViews) - view->updateEditorSetting (settingName, settingValue); -} diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 01f495186..00e33916d 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -76,9 +76,6 @@ namespace CSVDoc void progress (int current, int max, int type, int threads, CSMDoc::Document *document); void onExitWarningHandler(int state, CSMDoc::Document* document); - - /// connected to update signal in UserSettings - void slotUpdateEditorSetting (const QString &, const QString &); }; } diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index d59f0c234..e84f5cf4b 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -33,12 +33,13 @@ void CSVTools::ReportSubView::setEditLock (bool locked) // ignored. We don't change document state anyway. } -void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QString& value) +void CSVTools::ReportSubView::updateUserSetting + (const QString &name, const QStringList &list) { - mIdTypeDelegate->updateEditorSetting (key, value); + mIdTypeDelegate->updateUserSetting (name, list); } void CSVTools::ReportSubView::show (const QModelIndex& index) { focusId (mModel->getUniversalId (index.row()), ""); -} \ No newline at end of file +} diff --git a/apps/opencs/view/tools/reportsubview.hpp b/apps/opencs/view/tools/reportsubview.hpp index 6503ebd27..9f6a4c1da 100644 --- a/apps/opencs/view/tools/reportsubview.hpp +++ b/apps/opencs/view/tools/reportsubview.hpp @@ -39,7 +39,8 @@ namespace CSVTools virtual void setEditLock (bool locked); - virtual void updateEditorSetting (const QString&, const QString&); + virtual void updateUserSetting + (const QString &, const QStringList &); private slots: @@ -47,4 +48,4 @@ namespace CSVTools }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 712b8f556..995de21fc 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -449,15 +449,19 @@ void CSVWorld::Table::previewRecord() } } -void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue) +void CSVWorld::Table::updateUserSetting + (const QString &name, const QStringList &list) { int columns = mModel->columnCount(); for (int i=0; i (*delegate). - updateEditorSetting (settingName, settingValue)) - emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); + if (dynamic_cast + (*delegate).updateUserSetting (name, list)) + { + emit dataChanged (mModel->index (0, i), + mModel->index (mModel->rowCount()-1, i)); + } } void CSVWorld::Table::tableSizeUpdate() @@ -598,4 +602,4 @@ std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column } } 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 4231a4a43..dfc74b3eb 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -78,7 +78,7 @@ namespace CSVWorld CSMWorld::UniversalId getUniversalId (int row) const; - void updateEditorSetting (const QString &settingName, const QString &settingValue); + void updateUserSetting (const QString &name, const QStringList &list); std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index a5a7e8252..c193ed32b 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -82,9 +82,10 @@ void CSVWorld::TableSubView::editRequest (const CSMWorld::UniversalId& id, const focusId (id, hint); } -void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +void CSVWorld::TableSubView::updateUserSetting + (const QString &name, const QStringList &list) { - mTable->updateEditorSetting(settingName, settingValue); + mTable->updateUserSetting(name, list); } void CSVWorld::TableSubView::setStatusBar (bool show) @@ -134,4 +135,4 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) 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 b344ba1ad..9d86c32e4 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -43,7 +43,8 @@ namespace CSVWorld virtual void setEditLock (bool locked); - virtual void updateEditorSetting (const QString& key, const QString& value); + virtual void updateUserSetting + (const QString& name, const QStringList &list); virtual void setStatusBar (bool show); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index b2a32b551..16310c8a9 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -196,8 +196,8 @@ bool CSVWorld::CommandDelegate::isEditLocked() const return mEditLock; } -bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName, - const QString &settingValue) +bool CSVWorld::CommandDelegate::updateUserSetting (const QString &name, + const QStringList &list) { return false; } @@ -263,4 +263,4 @@ void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) const CSMWorld::TableMimeData* data(dynamic_cast(event->mimeData())); emit tableMimeDataDropped(data->getData(), data->getDocumentPtr()); //WIP -} \ No newline at end of file +} diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 7664f3eae..50e2cf858 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -136,7 +136,9 @@ namespace CSVWorld bool isEditLocked() const; - virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); + virtual bool updateUserSetting + (const QString &name, const QStringList &list); + ///< \return Does column require update? virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; From e71a119c23ac5992ed016f0d3a2e4298494b309c Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 02:57:48 -0400 Subject: [PATCH 120/157] Made aifollowers run when long distances (800 or 10000, depending) from what they're following. --- apps/openmw/mwmechanics/aifollow.cpp | 60 ++++++++++++++++++---------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index c3b36516c..161c9700f 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" +#include "creaturestats.hpp" #include "movement.hpp" #include @@ -26,69 +27,77 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId) bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); - - if(target == MWWorld::Ptr()) return true; + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow - mTimer = mTimer + duration; - mStuckTimer = mStuckTimer + duration; - mTotalTime = mTotalTime + duration; + if(target == MWWorld::Ptr()) return true; //Target doesn't exist - ESM::Position pos = actor.getRefData().getPosition(); + mTimer = mTimer + duration; //Update timer + mStuckTimer = mStuckTimer + duration; //Update stuck timer + mTotalTime = mTotalTime + duration; //Update total time following - if(!mAlwaysFollow) + ESM::Position pos = actor.getRefData().getPosition(); //position of the actor + + if(!mAlwaysFollow) //Update if you only follow for a bit { - if(mTotalTime > mDuration && mDuration != 0) + if(mTotalTime > mDuration && mDuration != 0) //Check if we've run out of time return true; 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) //Close-ish to final position { - if(actor.getCell()->isExterior()) + if(actor.getCell()->isExterior()) //Outside? { - if(mCellId == "") + if(mCellId == "") //No cell to travel to return true; } else { - if(mCellId == actor.getCell()->getCell()->mName) + if(mCellId == actor.getCell()->getCell()->mName) //Cell to travel to return true; } } } + //Set the target desition from the actor ESM::Pathgrid::Point dest; dest.mX = target.getRefData().getPosition().pos[0]; dest.mY = target.getRefData().getPosition().pos[1]; dest.mZ = target.getRefData().getPosition().pos[2]; + //Current position, for pathfilding stuff ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; start.mZ = pos.pos[2]; + //Build the path to get to the destination if(mPathFinder.getPath().empty()) mPathFinder.buildPath(start, dest, actor.getCell(), true); - + //*********************** + // Checks if you can't get to the end position at all + //*********************** if(mTimer > 0.25) { - if(!mPathFinder.getPath().empty()) + if(!mPathFinder.getPath().empty()) //Path has points in it { - ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); + ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path - 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) - mPathFinder.addPointToPath(dest); + > 100*100) //End of the path is far from the destination + mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } mTimer = 0; } - if(mStuckTimer>0.5) + //************************ + // Checks if you aren't moving; you're stuck + //************************ + if(mStuckTimer>0.5) //Checks every half of a second { if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0]) +(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1]) @@ -99,6 +108,7 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) mStuckPos = pos; } + //Checks if the path isn't over, turn tomards the direction that you're going if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) { zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); @@ -110,6 +120,16 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) else actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + //Check if you're far away + if((dest.mX - start.mX)*(dest.mX - start.mX) + +(dest.mY - start.mY)*(dest.mY - start.mY) + +(dest.mZ - start.mZ)*(dest.mZ - start.mZ) > 1000*1000) + actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run + else if((dest.mX - start.mX)*(dest.mX - start.mX) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold + +(dest.mY - start.mY)*(dest.mY - start.mY) + +(dest.mZ - start.mZ)*(dest.mZ - start.mZ) < 800*800) + actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk + return false; } From cac8e52154a99b310368ceabe3b61dcf2f63d44c Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 05:12:07 -0400 Subject: [PATCH 121/157] Seperated locked and lock level, to allow for relocking doors to previous lock level. The data is stored in the esm as -lockLevel if unlocked; lockLevel if locked. While not tested, it should not present any problems. --- apps/openmw/mwclass/container.cpp | 23 +++++++++++++++-------- apps/openmw/mwclass/container.hpp | 5 ++++- apps/openmw/mwclass/door.cpp | 15 +++++++++++---- apps/openmw/mwclass/door.hpp | 6 +++++- apps/openmw/mwmechanics/security.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 5 +++-- apps/openmw/mwscript/miscextensions.cpp | 6 +++--- apps/openmw/mwworld/class.cpp | 7 ++++++- apps/openmw/mwworld/class.hpp | 2 ++ apps/openmw/mwworld/manualref.hpp | 1 + 10 files changed, 51 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 604b51990..ee6dba982 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -111,7 +111,7 @@ namespace MWClass MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); - bool needKey = ptr.getCellRef().mLockLevel>0; + bool needKey = ptr.getCellRef().mLocked; bool hasKey = false; std::string keyName; @@ -132,7 +132,7 @@ namespace MWClass if (needKey && hasKey) { MWBase::Environment::get().getWindowManager ()->messageBox (keyName + " #{sKeyUsed}"); - ptr.getCellRef().mLockLevel = 0; + unlock(ptr); // using a key disarms the trap ptr.getCellRef().mTrap = ""; } @@ -209,7 +209,7 @@ namespace MWClass info.caption = ref->mBase->mName; std::string text; - if (ref->mRef.mLockLevel > 0) + if (ref->mRef.mLocked) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; @@ -240,15 +240,22 @@ namespace MWClass void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const { - if (lockLevel<0) - lockLevel = 0; - - ptr.getCellRef().mLockLevel = lockLevel; + ptr.getCellRef().mLocked = true; + if(lockLevel>=0) //Lock level setting left as most of the code relies on this + ptr.getCellRef().mLockLevel = lockLevel; } void Container::unlock (const MWWorld::Ptr& ptr) const { - ptr.getCellRef().mLockLevel = 0; + ptr.getCellRef().mLocked= false; + } + + void Container::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) { + if (lockLevel<0) + lockLevel = 0; + + ptr.getCellRef().mLockLevel = lockLevel; + if(doLock) lock(ptr); } MWWorld::Ptr diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index c97867d35..6945ae441 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -48,12 +48,15 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. - virtual void lock (const MWWorld::Ptr& ptr, int lockLevel) const; + virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = -999) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object + ///Changes the lock level of the object + virtual void changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool lock=true); + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 3cd8237e7..2662e1c53 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -76,7 +76,7 @@ namespace MWClass MWWorld::ContainerStore &invStore = get(actor).getContainerStore(actor); - bool needKey = ptr.getCellRef().mLockLevel>0; + bool needKey = ptr.getCellRef().mLocked; bool hasKey = false; std::string keyName; @@ -98,7 +98,7 @@ namespace MWClass { if(actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); - ptr.getCellRef().mLockLevel = 0; + unlock(ptr); //Call the function here. because that makes sense. // using a key disarms the trap ptr.getCellRef().mTrap = ""; } @@ -158,15 +158,22 @@ namespace MWClass void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const { + ptr.getCellRef().mLocked = true; + if(lockLevel>=0) //Lock level setting left as most of the code relies on this + ptr.getCellRef().mLockLevel = lockLevel; + } + + void Door::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) const{ if (lockLevel<0) lockLevel = 0; ptr.getCellRef().mLockLevel = lockLevel; + if(doLock) lock(ptr); //A change in lock level almost always nesesitates a lock } void Door::unlock (const MWWorld::Ptr& ptr) const { - ptr.getCellRef().mLockLevel = 0; + ptr.getCellRef().mLocked = false; } std::string Door::getScript (const MWWorld::Ptr& ptr) const @@ -208,7 +215,7 @@ namespace MWClass text += "\n" + getDestination(*ref); } - if (ref->mRef.mLockLevel > 0) + if (ref->mRef.mLocked == true) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index 2ac342a61..e053136d5 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -36,18 +36,22 @@ namespace MWClass static std::string getDestination (const MWWorld::LiveCellRef& door); ///< @return destination cell name or token - virtual void lock (const MWWorld::Ptr& ptr, int lockLevel) const; + virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = -999) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object + ///Change the lock level + virtual void changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool lock=true) const; + virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; + private: }; } diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index edec45e15..ba2a32727 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -29,7 +29,7 @@ namespace MWMechanics void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, std::string& resultMessage, std::string& resultSound) { - if (lock.getCellRef().mLockLevel <= 0) + if (!lock.getCellRef().mLocked) return; int lockStrength = lock.getCellRef().mLockLevel; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 6ea3f5472..d861e2032 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -420,6 +420,7 @@ namespace MWMechanics { if (effectId == ESM::MagicEffect::Lock) { + target.getCellRef().mLocked = true; if (target.getCellRef().mLockLevel < magnitude) target.getCellRef().mLockLevel = magnitude; } @@ -427,12 +428,12 @@ namespace MWMechanics { if (target.getCellRef().mLockLevel <= magnitude) { - if (target.getCellRef().mLockLevel > 0) + if (target.getCellRef().mLocked) { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); } - target.getCellRef().mLockLevel = 0; + target.getCellRef().mLocked=false; } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 968d3bbd7..f10d1df96 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -131,7 +131,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer lockLevel = 100; + Interpreter::Type_Integer lockLevel = ptr.getCellRef().mLockLevel; if (arg0==1) { @@ -283,7 +283,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { // We are ignoring the DontSaveObject statement for now. Probably not worth - /// bothering with. The incompatibility we are creating should be marginal at most. + // bothering with. The incompatibility we are creating should be marginal at most. } }; @@ -320,7 +320,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push (ptr.getCellRef ().mLockLevel > 0); + runtime.push (ptr.getCellRef().mLocked); } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 99dbcc66c..4b3d40ab7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -137,6 +137,11 @@ namespace MWWorld throw std::runtime_error ("class does not support locking"); } + void Class::setLockLevel (const Ptr& ptr, int lockLevel) const + { + throw std::runtime_error ("class does not support setting lock level"); + } + void Class::unlock (const Ptr& ptr) const { throw std::runtime_error ("class does not support unlocking"); @@ -397,7 +402,7 @@ 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"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 27b842348..dc06bf6c9 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -165,6 +165,8 @@ namespace MWWorld virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) + virtual void setLockLevel(const Ptr& ptr, int lockLevel) const; + virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 0e21c55ac..174e34423 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -76,6 +76,7 @@ namespace MWWorld cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; cellRef.mLockLevel = 0; + cellRef.mLocked = false; cellRef.mReferenceBlocked = 0; mPtr.getRefData().setCount(count); } From 420163d35f6aac7a233bc57fb5bee334b4fd6fbd Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 05:19:34 -0400 Subject: [PATCH 122/157] Small changes to lock command (If no valid lockLevel exists, defaults to 100) --- apps/openmw/mwscript/miscextensions.cpp | 4 ++++ components/esm/cellref.cpp | 18 +++++++++++++++--- components/esm/cellref.hpp | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index f10d1df96..686fa94a7 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -132,6 +132,10 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer lockLevel = ptr.getCellRef().mLockLevel; + if(lockLevel<0) { //no lock level was ever set, set to 100 as default + ptr.getCellRef().mLockLevel = 100; + lockLevel = 100; + } if (arg0==1) { diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 00b15f4a3..c46d83c1b 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -54,8 +54,15 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) else mTeleport = false; - mLockLevel = -1; + mLockLevel = -999; //Set to impossible value to indicate no lock + mLocked = false; esm.getHNOT (mLockLevel, "FLTV"); + if(mLockLevel < 0 && mLockLevel != -999) //Unlocked lock, save lock level properly + mLockLevel*=-1; + else if(mLockLevel != -999){ + mLocked = true; + } + mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); @@ -113,8 +120,12 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != -1 && !inInventory) - esm.writeHNT("FLTV", mLockLevel); + if (mLockLevel != -999 && !inInventory) { + if(mLocked) + esm.writeHNT("FLTV", mLockLevel); + else //If it's not locked we simply flip the locklevel to indicate it's locked + esm.writeHNT("FLTV", -mLockLevel); + } if (!inInventory) esm.writeHNOCString ("KNAM", mKey); @@ -151,6 +162,7 @@ void ESM::CellRef::blank() mGoldValue = 0; mDestCell.clear(); mLockLevel = 0; + mLocked = false; mKey.clear(); mTrap.clear(); mReferenceBlocked = 0; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 16f6603a2..75122eacb 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -71,6 +71,7 @@ namespace ESM // Lock level for doors and containers int mLockLevel; + bool mLocked; //Door locked/unlocked std::string mKey, mTrap; // Key and trap ID names, if any // This corresponds to the "Reference Blocked" checkbox in the construction set, From 6022ffbd1ff25d56934760fd330e1c6de797bee8 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 05:54:18 -0400 Subject: [PATCH 123/157] Evidence chest now locks when new evidence is added to it --- apps/openmw/mwworld/worldimp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e3c632512..5b4c2570a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2724,6 +2724,7 @@ namespace MWWorld } } } + closestChest.getCellRef().mLocked = true; } void World::goToJail() @@ -2791,7 +2792,7 @@ namespace MWWorld message += "\n" + skillMsg; } - // TODO: Sleep the player + // TODO: Sleep the player std::vector buttons; buttons.push_back("#{sOk}"); From 61341d420678e6e5e9cae019d342d0dde2f531ad Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 10:00:18 -0400 Subject: [PATCH 124/157] Removed mLocked, kept it as "Negative lock level means unlocked" --- apps/openmw/mwclass/container.cpp | 8 ++++---- apps/openmw/mwclass/door.cpp | 10 +++++----- apps/openmw/mwmechanics/security.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- apps/openmw/mwscript/miscextensions.cpp | 5 ++--- apps/openmw/mwworld/manualref.hpp | 1 - apps/openmw/mwworld/worldimp.cpp | 2 +- components/esm/cellref.cpp | 10 ---------- components/esm/cellref.hpp | 1 - 9 files changed, 16 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index ee6dba982..1847fbd44 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -111,7 +111,7 @@ namespace MWClass MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); - bool needKey = ptr.getCellRef().mLocked; + bool needKey = ptr.getCellRef().mLockLevel > 0; bool hasKey = false; std::string keyName; @@ -209,7 +209,7 @@ namespace MWClass info.caption = ref->mBase->mName; std::string text; - if (ref->mRef.mLocked) + if (ref->mRef.mLockLevel > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; @@ -240,14 +240,14 @@ namespace MWClass void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const { - ptr.getCellRef().mLocked = true; + ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive if(lockLevel>=0) //Lock level setting left as most of the code relies on this ptr.getCellRef().mLockLevel = lockLevel; } void Container::unlock (const MWWorld::Ptr& ptr) const { - ptr.getCellRef().mLocked= false; + ptr.getCellRef().mLockLevel = -abs(ptr.getCellRef().mLockLevel); //Makes lockLevel negative } void Container::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) { diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 2662e1c53..e9f384572 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -76,7 +76,7 @@ namespace MWClass MWWorld::ContainerStore &invStore = get(actor).getContainerStore(actor); - bool needKey = ptr.getCellRef().mLocked; + bool needKey = ptr.getCellRef().mLockLevel > 0; bool hasKey = false; std::string keyName; @@ -158,9 +158,9 @@ namespace MWClass void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const { - ptr.getCellRef().mLocked = true; + ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive; if(lockLevel>=0) //Lock level setting left as most of the code relies on this - ptr.getCellRef().mLockLevel = lockLevel; + ptr.getCellRef().mLockLevel = abs(lockLevel); } void Door::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) const{ @@ -173,7 +173,7 @@ namespace MWClass void Door::unlock (const MWWorld::Ptr& ptr) const { - ptr.getCellRef().mLocked = false; + ptr.getCellRef().mLockLevel = -abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive } std::string Door::getScript (const MWWorld::Ptr& ptr) const @@ -215,7 +215,7 @@ namespace MWClass text += "\n" + getDestination(*ref); } - if (ref->mRef.mLocked == true) + if (ref->mRef.mLockLevel > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index ba2a32727..3751e5828 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -29,7 +29,7 @@ namespace MWMechanics void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, std::string& resultMessage, std::string& resultSound) { - if (!lock.getCellRef().mLocked) + if (!(lock.getCellRef().mLockLevel > 0)) //If it's unlocked back out immediately return; int lockStrength = lock.getCellRef().mLockLevel; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d861e2032..db8daff15 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -420,7 +420,7 @@ namespace MWMechanics { if (effectId == ESM::MagicEffect::Lock) { - target.getCellRef().mLocked = true; + target.getCellRef().mLockLevel = abs(target.getCellRef().mLockLevel); //Makes lockLevel positive if (target.getCellRef().mLockLevel < magnitude) target.getCellRef().mLockLevel = magnitude; } @@ -428,12 +428,12 @@ namespace MWMechanics { if (target.getCellRef().mLockLevel <= magnitude) { - if (target.getCellRef().mLocked) + if (target.getCellRef().mLockLevel > 0) { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); } - target.getCellRef().mLocked=false; + target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 686fa94a7..bc71cc494 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -132,8 +132,7 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer lockLevel = ptr.getCellRef().mLockLevel; - if(lockLevel<0) { //no lock level was ever set, set to 100 as default - ptr.getCellRef().mLockLevel = 100; + if(lockLevel==-999) { //no lock level was ever set, set to 100 as default lockLevel = 100; } @@ -324,7 +323,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push (ptr.getCellRef().mLocked); + runtime.push (ptr.getCellRef().mLockLevel > 0); } }; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 174e34423..0e21c55ac 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -76,7 +76,6 @@ namespace MWWorld cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; cellRef.mLockLevel = 0; - cellRef.mLocked = false; cellRef.mReferenceBlocked = 0; mPtr.getRefData().setCount(count); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5b4c2570a..5c9b25d28 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2724,7 +2724,7 @@ namespace MWWorld } } } - closestChest.getCellRef().mLocked = true; + closestChest.getCellRef().mLockLevel = abs(closestChest.getCellRef().mLockLevel); } void World::goToJail() diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index c46d83c1b..699d326ee 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -55,13 +55,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) mTeleport = false; mLockLevel = -999; //Set to impossible value to indicate no lock - mLocked = false; esm.getHNOT (mLockLevel, "FLTV"); - if(mLockLevel < 0 && mLockLevel != -999) //Unlocked lock, save lock level properly - mLockLevel*=-1; - else if(mLockLevel != -999){ - mLocked = true; - } mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); @@ -121,10 +115,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons } if (mLockLevel != -999 && !inInventory) { - if(mLocked) esm.writeHNT("FLTV", mLockLevel); - else //If it's not locked we simply flip the locklevel to indicate it's locked - esm.writeHNT("FLTV", -mLockLevel); } if (!inInventory) @@ -162,7 +153,6 @@ void ESM::CellRef::blank() mGoldValue = 0; mDestCell.clear(); mLockLevel = 0; - mLocked = false; mKey.clear(); mTrap.clear(); mReferenceBlocked = 0; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 75122eacb..16f6603a2 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -71,7 +71,6 @@ namespace ESM // Lock level for doors and containers int mLockLevel; - bool mLocked; //Door locked/unlocked std::string mKey, mTrap; // Key and trap ID names, if any // This corresponds to the "Reference Blocked" checkbox in the construction set, From f6deca7c80c489488412cd85959c09e8ee68f5b9 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 13:02:51 -0400 Subject: [PATCH 125/157] Fixed various issues caused by late-night coding. Also added "unlock" message to unlocked doors --- apps/openmw/mwclass/container.cpp | 16 ++++++---------- apps/openmw/mwclass/container.hpp | 5 +---- apps/openmw/mwclass/door.cpp | 19 +++++++------------ apps/openmw/mwclass/door.hpp | 5 +---- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- apps/openmw/mwscript/miscextensions.cpp | 2 +- apps/openmw/mwworld/class.cpp | 5 ----- apps/openmw/mwworld/class.hpp | 2 -- components/esm/cellref.cpp | 4 ++-- 9 files changed, 21 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 1847fbd44..be76bd0b4 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -211,6 +211,8 @@ namespace MWClass std::string text; if (ref->mRef.mLockLevel > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); + else if (ref->mRef.mLockLevel < 0) + text += "\n#{sUnlocked}"; if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; @@ -240,9 +242,10 @@ namespace MWClass void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const { - ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive - if(lockLevel>=0) //Lock level setting left as most of the code relies on this - ptr.getCellRef().mLockLevel = lockLevel; + if(lockLevel!=0) + ptr.getCellRef().mLockLevel = abs(lockLevel); //Changes lock to locklevel, in positive + else + ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //No locklevel given, just flip the oriional one } void Container::unlock (const MWWorld::Ptr& ptr) const @@ -250,13 +253,6 @@ namespace MWClass ptr.getCellRef().mLockLevel = -abs(ptr.getCellRef().mLockLevel); //Makes lockLevel negative } - void Container::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) { - if (lockLevel<0) - lockLevel = 0; - - ptr.getCellRef().mLockLevel = lockLevel; - if(doLock) lock(ptr); - } MWWorld::Ptr Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 6945ae441..f012d675c 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -48,15 +48,12 @@ namespace MWClass ///< Returns total weight of objects inside this object (including modifications from magic /// effects). Throws an exception, if the object can't hold other objects. - virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = -999) const; + virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object - ///Changes the lock level of the object - virtual void changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool lock=true); - virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index e9f384572..984e21e72 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -158,22 +158,15 @@ namespace MWClass void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const { - ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive; - if(lockLevel>=0) //Lock level setting left as most of the code relies on this - ptr.getCellRef().mLockLevel = abs(lockLevel); - } - - void Door::changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool doLock) const{ - if (lockLevel<0) - lockLevel = 0; - - ptr.getCellRef().mLockLevel = lockLevel; - if(doLock) lock(ptr); //A change in lock level almost always nesesitates a lock + if(lockLevel!=0) + ptr.getCellRef().mLockLevel = abs(lockLevel); //Changes lock to locklevel, in positive + else + ptr.getCellRef().mLockLevel = abs(ptr.getCellRef().mLockLevel); //No locklevel given, just flip the origional one } void Door::unlock (const MWWorld::Ptr& ptr) const { - ptr.getCellRef().mLockLevel = -abs(ptr.getCellRef().mLockLevel); //Makes lockLevel positive + ptr.getCellRef().mLockLevel = -abs(ptr.getCellRef().mLockLevel); //Makes lockLevel negative } std::string Door::getScript (const MWWorld::Ptr& ptr) const @@ -217,6 +210,8 @@ namespace MWClass if (ref->mRef.mLockLevel > 0) text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ref->mRef.mLockLevel); + else if (ref->mRef.mLockLevel < 0) + text += "\n#{sUnlocked}"; if (ref->mRef.mTrap != "") text += "\n#{sTrapped}"; diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index e053136d5..bddc46728 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -36,15 +36,12 @@ namespace MWClass static std::string getDestination (const MWWorld::LiveCellRef& door); ///< @return destination cell name or token - virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = -999) const; + virtual void lock (const MWWorld::Ptr& ptr, int lockLevel = 0) const; ///< Lock object virtual void unlock (const MWWorld::Ptr& ptr) const; ///< Unlock object - ///Change the lock level - virtual void changeLockLevel(const MWWorld::Ptr& ptr, int lockLevel, bool lock=true) const; - virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index db8daff15..9a6cd6b89 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -420,20 +420,20 @@ namespace MWMechanics { if (effectId == ESM::MagicEffect::Lock) { - target.getCellRef().mLockLevel = abs(target.getCellRef().mLockLevel); //Makes lockLevel positive - if (target.getCellRef().mLockLevel < magnitude) + if (target.getCellRef().mLockLevel < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude target.getCellRef().mLockLevel = magnitude; } else if (effectId == ESM::MagicEffect::Open) { if (target.getCellRef().mLockLevel <= magnitude) { + //Door not already unlocked if (target.getCellRef().mLockLevel > 0) { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); } - target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); + target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); //unlocks the door } else MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index bc71cc494..20013493f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -132,7 +132,7 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer lockLevel = ptr.getCellRef().mLockLevel; - if(lockLevel==-999) { //no lock level was ever set, set to 100 as default + if(lockLevel==0) { //no lock level was ever set, set to 100 as default lockLevel = 100; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 4b3d40ab7..f20c5f6d2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -137,11 +137,6 @@ namespace MWWorld throw std::runtime_error ("class does not support locking"); } - void Class::setLockLevel (const Ptr& ptr, int lockLevel) const - { - throw std::runtime_error ("class does not support setting lock level"); - } - void Class::unlock (const Ptr& ptr) const { throw std::runtime_error ("class does not support unlocking"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index dc06bf6c9..27b842348 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -165,8 +165,6 @@ namespace MWWorld virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) - virtual void setLockLevel(const Ptr& ptr, int lockLevel) const; - virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 699d326ee..f04e819c8 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -54,7 +54,7 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) else mTeleport = false; - mLockLevel = -999; //Set to impossible value to indicate no lock + mLockLevel = 0; //Set to 0 to indicate no lock esm.getHNOT (mLockLevel, "FLTV"); mKey = esm.getHNOString ("KNAM"); @@ -114,7 +114,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons esm.writeHNOCString("DNAM", mDestCell); } - if (mLockLevel != -999 && !inInventory) { + if (mLockLevel != 0 && !inInventory) { esm.writeHNT("FLTV", mLockLevel); } From ee581f593b29f2656160b04cd1f5ea1a32b7f776 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 13:20:43 -0400 Subject: [PATCH 126/157] Fixed issue which may occur if there's no evidence chest nearby --- apps/openmw/mwworld/worldimp.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5c9b25d28..87bdd2f9d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2695,6 +2695,7 @@ namespace MWWorld MWWorld::Ptr closestChest; float closestDistance = FLT_MAX; + //Find closest stolen_goods chest std::vector chests; mCells.getInteriorPtrs("stolen_goods", chests); @@ -2712,19 +2713,19 @@ namespace MWWorld } } - if (!closestChest.isEmpty()) + if (!closestChest.isEmpty()) //Found a close chest { ContainerStore& store = ptr.getClass().getContainerStore(ptr); - for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) + for (ContainerStoreIterator it = store.begin(); it != store.end(); ++it) //Move all stolen stuff into chest { - if (!it->getCellRef().mOwner.empty() && it->getCellRef().mOwner != "player") + if (!it->getCellRef().mOwner.empty() && it->getCellRef().mOwner != "player") //Not owned by no one/player? { closestChest.getClass().getContainerStore(closestChest).add(*it, it->getRefData().getCount(), closestChest); store.remove(*it, it->getRefData().getCount(), ptr); } } + closestChest.getCellRef().mLockLevel = abs(closestChest.getCellRef().mLockLevel); } - closestChest.getCellRef().mLockLevel = abs(closestChest.getCellRef().mLockLevel); } void World::goToJail() From ce566693397b6fd23f71221abf07181325678e2e Mon Sep 17 00:00:00 2001 From: graffy76 Date: Wed, 23 Apr 2014 17:02:37 -0500 Subject: [PATCH 127/157] Fixed failed signal/slot connection between UserSettings and CSVWorld::SubView --- apps/opencs/view/doc/subview.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 52e42cc0e..733a75bb0 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -38,9 +38,6 @@ namespace CSVDoc virtual void setEditLock (bool locked) = 0; - virtual void updateUserSetting - (const QString &, const QStringList &); - virtual void setStatusBar (bool show); ///< Default implementation: ignored @@ -50,6 +47,10 @@ namespace CSVDoc signals: void focusId (const CSMWorld::UniversalId& universalId, const std::string& hint); + + public slots: + virtual void updateUserSetting + (const QString &, const QStringList &); }; } From f90810223abeac58b05c807476a77d9cf338a7ab Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 21:02:09 -0400 Subject: [PATCH 128/157] Added quicksave and quickload --- apps/openmw/mwinput/inputmanagerimp.cpp | 43 +++++++++++++++++++++++++ apps/openmw/mwinput/inputmanagerimp.hpp | 2 ++ 2 files changed, 45 insertions(+) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1346b9e95..4bd2a1e65 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -26,6 +26,7 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwstate/character.hpp" using namespace ICS; @@ -252,6 +253,12 @@ namespace MWInput case A_ToggleHUD: MWBase::Environment::get().getWindowManager()->toggleHud(); break; + case A_QuickSave: + quickSave(); + break; + case A_QuickLoad: + quickLoad(); + break; } } } @@ -638,6 +645,36 @@ namespace MWInput } } + void InputManager::quickLoad() { + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character + if(mCurrentCharacter) { //Ensure a current character exists + const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save + if(slot) //Don't even try loading it if there's no prior save. + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! + } + } + + void InputManager::quickSave() { + const MWState::Slot* slot = NULL; + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character + if (mCurrentCharacter) //Ensure one exists, otherwise do nothing + { + //Find quicksave slot + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + if (it->mProfile.mDescription == "Quicksave") + slot = &*it; + } + //If no quicksave works create a new slot with Signature + if(slot == NULL) { + slot = mCurrentCharacter->createSlot(mCurrentCharacter->getSignature()); + if(slot==NULL) + std::cout << "Couldn't work out" << std::endl; + } + //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? + MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); + } + } void InputManager::toggleSpell() { if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; @@ -842,6 +879,8 @@ namespace MWInput defaultKeyBindings[A_Screenshot] = SDL_GetKeyFromScancode(SDL_SCANCODE_F12); defaultKeyBindings[A_ToggleHUD] = SDL_GetKeyFromScancode(SDL_SCANCODE_F11); defaultKeyBindings[A_AlwaysRun] = SDL_GetKeyFromScancode(SDL_SCANCODE_Y); + defaultKeyBindings[A_QuickSave] = SDL_GetKeyFromScancode(SDL_SCANCODE_F5); + defaultKeyBindings[A_QuickLoad] = SDL_GetKeyFromScancode(SDL_SCANCODE_F9); std::map defaultMouseButtonBindings; defaultMouseButtonBindings[A_Inventory] = SDL_BUTTON_RIGHT; @@ -918,6 +957,8 @@ namespace MWInput descriptions[A_QuickKey9] = "sQuick9Cmd"; descriptions[A_QuickKey10] = "sQuick10Cmd"; descriptions[A_AlwaysRun] = "sAlways_Run"; + descriptions[A_QuickSave] = "sQuickSaveCmd"; + descriptions[A_QuickLoad] = "sQuickLoadCmd"; if (descriptions[action] == "") return ""; // not configurable @@ -961,6 +1002,8 @@ namespace MWInput ret.push_back(A_Journal); ret.push_back(A_Rest); ret.push_back(A_Console); + ret.push_back(A_QuickSave); + ret.push_back(A_QuickLoad); ret.push_back(A_Screenshot); ret.push_back(A_QuickKeysMenu); ret.push_back(A_QuickKey1); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 87fbda25c..5eb355566 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -183,6 +183,8 @@ namespace MWInput void toggleWalking(); void toggleAutoMove(); void rest(); + void quickLoad(); + void quickSave(); void quickKey (int index); void showQuickKeysMenu(); From e873135da7f1065c1168cff1ab6f18b8d5c04b8f Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 21:04:52 -0400 Subject: [PATCH 129/157] Missed a debug message --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4bd2a1e65..7fc867186 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -668,8 +668,6 @@ namespace MWInput //If no quicksave works create a new slot with Signature if(slot == NULL) { slot = mCurrentCharacter->createSlot(mCurrentCharacter->getSignature()); - if(slot==NULL) - std::cout << "Couldn't work out" << std::endl; } //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); From 49620968b93caf88d4b49f94d0da7649aaf90a24 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 21:21:11 -0400 Subject: [PATCH 130/157] Disallowed quicksave/load in character creation --- apps/openmw/mwinput/inputmanagerimp.cpp | 42 ++++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 7fc867186..3d5d2f4a8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -646,31 +646,35 @@ namespace MWInput } void InputManager::quickLoad() { - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character - if(mCurrentCharacter) { //Ensure a current character exists - const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save - if(slot) //Don't even try loading it if there's no prior save. - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! + if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character + if(mCurrentCharacter) { //Ensure a current character exists + const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save + if(slot) //Don't even try loading it if there's no prior save. + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! + } } } void InputManager::quickSave() { - const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character - if (mCurrentCharacter) //Ensure one exists, otherwise do nothing - { - //Find quicksave slot - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation + const MWState::Slot* slot = NULL; + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character + if (mCurrentCharacter) //Ensure one exists { - if (it->mProfile.mDescription == "Quicksave") - slot = &*it; - } - //If no quicksave works create a new slot with Signature - if(slot == NULL) { - slot = mCurrentCharacter->createSlot(mCurrentCharacter->getSignature()); + //Find quicksave slot + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + if (it->mProfile.mDescription == "Quicksave") + slot = &*it; + } + //If no quicksave works create a new slot with Signature + if(slot == NULL) { + slot = mCurrentCharacter->createSlot(mCurrentCharacter->getSignature()); + } + //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? + MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); } - //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? - MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); } } void InputManager::toggleSpell() From 682c39548869593b02873a11f819a019056145fc Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 21:39:25 -0400 Subject: [PATCH 131/157] Apparently a normal save must exist for some reason, calling for a character to be made doesn't cause the signature to be created, and I don't know how to force create the signature (It's kinda driving me nuts) --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 3d5d2f4a8..db030c940 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -659,7 +659,7 @@ namespace MWInput void InputManager::quickSave() { if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character if (mCurrentCharacter) //Ensure one exists { //Find quicksave slot From a598060071bd6c3224f5f8c959ed1ab38fd16beb Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 23:12:30 -0400 Subject: [PATCH 132/157] Added autosave on rest, as well as an option in the settings menu --- apps/openmw/mwgui/waitdialog.cpp | 21 +++++++++++++++++++++ apps/openmw/mwgui/waitdialog.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 6 +----- files/mygui/openmw_settings_window.layout | 18 +++++++++++++++--- files/settings-default.cfg | 2 ++ 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 9417d2a4b..7e4522de6 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -8,6 +8,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/statemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/cellstore.hpp" @@ -15,6 +16,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwstate/charactermanager.hpp" namespace MWGui { @@ -236,6 +238,25 @@ namespace MWGui { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } + if(Settings::Manager::getBool("autosave","Saves")) + autosave(); + } + + void WaitDialog::autosave() { + if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation + const MWState::Slot* slot = NULL; + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character + if (mCurrentCharacter) //Ensure one exists + { + //Find quicksave slot + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + if (it->mProfile.mDescription == "Autosave") + slot = &*it; + } + MWBase::Environment::get().getStateManager()->saveGame("Autosave", slot); + } + } } void WaitDialog::wakeUp () diff --git a/apps/openmw/mwgui/waitdialog.hpp b/apps/openmw/mwgui/waitdialog.hpp index d96649af6..5a66c3370 100644 --- a/apps/openmw/mwgui/waitdialog.hpp +++ b/apps/openmw/mwgui/waitdialog.hpp @@ -34,6 +34,7 @@ namespace MWGui bool getSleeping() { return mWaiting && mSleeping; } void wakeUp(); + void autosave(); protected: MyGUI::TextBox* mDateTimeText; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index db030c940..c3f5ff7aa 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -659,7 +659,7 @@ namespace MWInput void InputManager::quickSave() { if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character if (mCurrentCharacter) //Ensure one exists { //Find quicksave slot @@ -668,10 +668,6 @@ namespace MWInput if (it->mProfile.mDescription == "Quicksave") slot = &*it; } - //If no quicksave works create a new slot with Signature - if(slot == NULL) { - slot = mCurrentCharacter->createSlot(mCurrentCharacter->getSignature()); - } //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); } diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index adf9f1557..e348323be 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -3,10 +3,10 @@ - - + + - + @@ -95,6 +95,18 @@ + + + + + + + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 6361476e3..f24636d15 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -174,6 +174,8 @@ best attack = false [Saves] character = +# Save when resting +autosave = true [Windows] inventory x = 0 From 6a8bf71c4c173f4c05489eb028b4c679859de476 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2014 23:33:57 -0400 Subject: [PATCH 133/157] Moved autosave to before you rest, not after it. --- apps/openmw/mwgui/waitdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 7e4522de6..b4dae090f 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -121,6 +121,8 @@ namespace MWGui MWBase::World* world = MWBase::Environment::get().getWorld(); world->getFader ()->fadeOut(0.2); setVisible(false); + if(Settings::Manager::getBool("autosave","Saves")) //autosaves + autosave(); mProgressBar.setVisible (true); mWaiting = true; @@ -238,8 +240,6 @@ namespace MWGui { MWBase::Environment::get().getWindowManager()->pushGuiMode (GM_Levelup); } - if(Settings::Manager::getBool("autosave","Saves")) - autosave(); } void WaitDialog::autosave() { From 05b21c92afde503ec2c524e66c1233f5772d1b21 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 24 Apr 2014 03:06:36 -0400 Subject: [PATCH 134/157] Moved quick save into statemanager class. Kept loader as is as it's rather specalized. --- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwgui/waitdialog.cpp | 18 +----------------- apps/openmw/mwinput/inputmanagerimp.cpp | 16 +--------------- apps/openmw/mwstate/statemanagerimp.cpp | 21 +++++++++++++++++++-- apps/openmw/mwstate/statemanagerimp.hpp | 4 ++++ 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index cd907408a..b9cf6aa7f 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -65,6 +65,10 @@ namespace MWBase /// /// \note \a slot must belong to \a character. + ///Simple saver, writes over the file if already existing + /** Used for quick save and autosave **/ + virtual void quickSave(std::string = "Quicksave")=0; + virtual MWState::Character *getCurrentCharacter (bool create = true) = 0; ///< \param create Create a new character, if there is no current character. diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index b4dae090f..023e01b06 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -122,7 +122,7 @@ namespace MWGui world->getFader ()->fadeOut(0.2); setVisible(false); if(Settings::Manager::getBool("autosave","Saves")) //autosaves - autosave(); + MWBase::Environment::get().getStateManager()->quickSave("Autosave"); mProgressBar.setVisible (true); mWaiting = true; @@ -242,22 +242,6 @@ namespace MWGui } } - void WaitDialog::autosave() { - if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation - const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character - if (mCurrentCharacter) //Ensure one exists - { - //Find quicksave slot - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) - { - if (it->mProfile.mDescription == "Autosave") - slot = &*it; - } - MWBase::Environment::get().getStateManager()->saveGame("Autosave", slot); - } - } - } void WaitDialog::wakeUp () { diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c3f5ff7aa..bc1aaf8f6 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -657,21 +657,7 @@ namespace MWInput } void InputManager::quickSave() { - if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation - const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character - if (mCurrentCharacter) //Ensure one exists - { - //Find quicksave slot - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) - { - if (it->mProfile.mDescription == "Quicksave") - slot = &*it; - } - //MWBase::Environment::get().getWindowManager()->messageBox("#{sQuick_save}"); //No message on quicksave? - MWBase::Environment::get().getStateManager()->saveGame("Quicksave", slot); - } - } + MWBase::Environment::get().getStateManager()->quickSave(); } void InputManager::toggleSpell() { diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 6b0871012..19b6ca1f3 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -223,6 +223,23 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot->mPath.parent_path().filename().string()); } +void MWState::StateManager::quickSave(std::string name) { + if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation + const MWState::Slot* slot = NULL; + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character + if (mCurrentCharacter) //Ensure one exists + { + //Find quicksave slot + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + if (it->mProfile.mDescription == name) + slot = &*it; + } + MWBase::Environment::get().getStateManager()->saveGame(name, slot); + } + } +} + void MWState::StateManager::loadGame (const Character *character, const Slot *slot) { try @@ -309,11 +326,11 @@ 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); MWWorld::ContainerStoreIterator item = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - + if (item != invStore.end()) MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*item); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 46ade236b..8082a2c78 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -49,6 +49,10 @@ namespace MWState /// /// \note Slot must belong to the current character. + ///Saves a file, using supplied filename, overwritting if needed + /** This is mostly used for quicksaving and autosaving, for they use the same name over and over again **/ + virtual void quickSave(std::string name = "Quicksave"); + virtual void loadGame (const Character *character, const Slot *slot); ///< Load a saved game file from \a slot. /// From 5b681e2199689120274e52e86585d803320552c4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 24 Apr 2014 03:14:47 -0400 Subject: [PATCH 135/157] Moved quickload to statemanager for consistency's sake, as well as make autosave only occur on sleep, not wait. --- apps/openmw/mwbase/statemanager.hpp | 4 ++++ apps/openmw/mwgui/waitdialog.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 9 +-------- apps/openmw/mwstate/statemanagerimp.cpp | 11 +++++++++++ apps/openmw/mwstate/statemanagerimp.hpp | 7 ++++++- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/statemanager.hpp b/apps/openmw/mwbase/statemanager.hpp index b9cf6aa7f..fc4a2d806 100644 --- a/apps/openmw/mwbase/statemanager.hpp +++ b/apps/openmw/mwbase/statemanager.hpp @@ -69,6 +69,10 @@ namespace MWBase /** Used for quick save and autosave **/ virtual void quickSave(std::string = "Quicksave")=0; + ///Simple loader, loads the last saved file + /** Used for quickload **/ + virtual void quickLoad()=0; + virtual MWState::Character *getCurrentCharacter (bool create = true) = 0; ///< \param create Create a new character, if there is no current character. diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 023e01b06..ed1b9e0a9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -121,7 +121,7 @@ namespace MWGui MWBase::World* world = MWBase::Environment::get().getWorld(); world->getFader ()->fadeOut(0.2); setVisible(false); - if(Settings::Manager::getBool("autosave","Saves")) //autosaves + if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping (Not resting, apparently) MWBase::Environment::get().getStateManager()->quickSave("Autosave"); mProgressBar.setVisible (true); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index bc1aaf8f6..51c0a1621 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -646,14 +646,7 @@ namespace MWInput } void InputManager::quickLoad() { - if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character - if(mCurrentCharacter) { //Ensure a current character exists - const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save - if(slot) //Don't even try loading it if there's no prior save. - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! - } - } + MWBase::Environment::get().getStateManager()->quickLoad(); } void InputManager::quickSave() { diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 19b6ca1f3..a0971b942 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -345,6 +345,17 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl } } +void MWState::StateManager::quickLoad() { + if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { + MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character + if(mCurrentCharacter) { //Ensure a current character exists + const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save + if(slot) //Don't even try loading it if there's no prior save. + MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! + } + } +} + MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) { return mCharacterManager.getCurrentCharacter (create); diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 8082a2c78..2d3ca21fb 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -50,9 +50,14 @@ namespace MWState /// \note Slot must belong to the current character. ///Saves a file, using supplied filename, overwritting if needed - /** This is mostly used for quicksaving and autosaving, for they use the same name over and over again **/ + /** This is mostly used for quicksaving and autosaving, for they use the same name over and over again + \param name Name of save, defaults to "Quicksave"**/ virtual void quickSave(std::string name = "Quicksave"); + ///Loads the last saved file + /** Used for quickload **/ + virtual void quickLoad(); + virtual void loadGame (const Character *character, const Slot *slot); ///< Load a saved game file from \a slot. /// From 42b3233bdad827479a75a070b9edfa18a888d643 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Apr 2014 09:54:47 +0200 Subject: [PATCH 136/157] removed a redundant check --- apps/openmw/mwstate/statemanagerimp.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a0971b942..56f562e04 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -227,16 +227,14 @@ void MWState::StateManager::quickSave(std::string name) { if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation const MWState::Slot* slot = NULL; MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character - if (mCurrentCharacter) //Ensure one exists + + //Find quicksave slot + for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) { - //Find quicksave slot - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) - { - if (it->mProfile.mDescription == name) - slot = &*it; - } - MWBase::Environment::get().getStateManager()->saveGame(name, slot); + if (it->mProfile.mDescription == name) + slot = &*it; } + MWBase::Environment::get().getStateManager()->saveGame(name, slot); } } From 760c8c721426f4a02711faad9ef94211f6254581 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Apr 2014 09:56:51 +0200 Subject: [PATCH 137/157] removed a redundant include --- apps/openmw/mwinput/inputmanagerimp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 51c0a1621..b37813915 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -26,7 +26,6 @@ #include "../mwworld/esmstore.hpp" #include "../mwmechanics/creaturestats.hpp" -#include "../mwstate/character.hpp" using namespace ICS; From 9db9ad410d52387a5cd4c96660fda44206eef5e3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 24 Apr 2014 10:14:17 +0200 Subject: [PATCH 138/157] fixed checks for allowing load/save and some general cleanup --- apps/openmw/mwstate/statemanagerimp.cpp | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 56f562e04..e32aad505 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -223,19 +223,23 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot slot->mPath.parent_path().filename().string()); } -void MWState::StateManager::quickSave(std::string name) { - if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { //ensure you're not in character creation - const MWState::Slot* slot = NULL; - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(true); //Get current character +void MWState::StateManager::quickSave (std::string name) +{ + if (mState!=State_Running || + MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")!=-1) // char gen + return; - //Find quicksave slot - for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) - { - if (it->mProfile.mDescription == name) - slot = &*it; - } - MWBase::Environment::get().getStateManager()->saveGame(name, slot); + const Slot* slot = NULL; + Character* mCurrentCharacter = getCurrentCharacter(true); //Get current character + + //Find quicksave slot + for (Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it) + { + if (it->mProfile.mDescription == name) + slot = &*it; } + + saveGame(name, slot); } void MWState::StateManager::loadGame (const Character *character, const Slot *slot) @@ -343,15 +347,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl } } -void MWState::StateManager::quickLoad() { - if(MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) { - MWState::Character* mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter(false); //Get current character - if(mCurrentCharacter) { //Ensure a current character exists - const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save - if(slot) //Don't even try loading it if there's no prior save. - MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! - } - } +void MWState::StateManager::quickLoad() +{ + if (Character* mCurrentCharacter = getCurrentCharacter (false)) + if (const MWState::Slot* slot = &*mCurrentCharacter->begin()) //Get newest save + loadGame (mCurrentCharacter, slot); } MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) From 3f2ae950f5b51aa3c6a86d92b9756c3b7827e866 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Thu, 24 Apr 2014 12:50:10 -0500 Subject: [PATCH 139/157] Disabled view resize for open views when user setting changes. Fixed new view size issue to match existing user settings. --- apps/opencs/model/settings/settingmanager.cpp | 12 ++++++++++++ apps/opencs/model/settings/settingmanager.hpp | 3 +++ apps/opencs/model/settings/usersettings.hpp | 4 ---- apps/opencs/view/doc/view.cpp | 13 +------------ apps/opencs/view/settings/listview.cpp | 2 +- apps/opencs/view/settings/page.cpp | 2 +- apps/opencs/view/settings/view.cpp | 7 ++++++- apps/opencs/view/world/table.cpp | 6 ++++-- apps/opencs/view/world/table.hpp | 4 ++-- apps/opencs/view/world/util.cpp | 6 ------ apps/opencs/view/world/util.hpp | 8 +++----- 11 files changed, 33 insertions(+), 34 deletions(-) diff --git a/apps/opencs/model/settings/settingmanager.cpp b/apps/opencs/model/settings/settingmanager.cpp index 70b91ee40..450252cd9 100644 --- a/apps/opencs/model/settings/settingmanager.cpp +++ b/apps/opencs/model/settings/settingmanager.cpp @@ -328,3 +328,15 @@ CSMSettings::SettingPageMap CSMSettings::SettingManager::settingPageMap() const return pageMap; } + +void CSMSettings::SettingManager::updateUserSetting(const QString &settingKey, + const QStringList &list) +{ + QStringList names = settingKey.split('.'); + + Setting *setting = findSetting (names.at(0), names.at(1)); + + setting->setDefinedValues (list); + + emit userSettingUpdated (names.at(1), list); +} diff --git a/apps/opencs/model/settings/settingmanager.hpp b/apps/opencs/model/settings/settingmanager.hpp index 8819096ad..ca8a2cc7b 100644 --- a/apps/opencs/model/settings/settingmanager.hpp +++ b/apps/opencs/model/settings/settingmanager.hpp @@ -75,8 +75,11 @@ namespace CSMSettings signals: + void userSettingUpdated (const QString &, const QStringList &); + public slots: + void updateUserSetting (const QString &, const QStringList &); }; } #endif // CSMSETTINGS_SETTINGMANAGER_HPP diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 75a3a0797..63caed923 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -58,10 +58,6 @@ namespace CSMSettings { private: void buildSettingModelDefaults(); - - signals: - - void userSettingUpdated(const QString &, const QStringList &); }; } #endif // USERSETTINGS_HPP diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 47e1a80de..397567f99 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -492,18 +492,7 @@ void CSVDoc::View::resizeViewHeight (int height) void CSVDoc::View::updateUserSetting (const QString &name, const QStringList &list) -{ - if (list.isEmpty()) - return; - - int value = list.at(0).toInt(); - - if (name == "Width") - resizeViewWidth (value); - - else if (name == "Height") - resizeViewHeight (value); -} +{} void CSVDoc::View::toggleShowStatusBar (bool show) { diff --git a/apps/opencs/view/settings/listview.cpp b/apps/opencs/view/settings/listview.cpp index b2a47903f..36cdbb0ae 100644 --- a/apps/opencs/view/settings/listview.cpp +++ b/apps/opencs/view/settings/listview.cpp @@ -49,7 +49,7 @@ void CSVSettings::ListView::buildAbstractItemViewModel() void CSVSettings::ListView::emitItemViewUpdate (int idx) { - emit viewUpdated (objectName(), selectedValues()); + updateView(); } QWidget *CSVSettings::ListView::buildWidget(bool isMultiLine, int width) diff --git a/apps/opencs/view/settings/page.cpp b/apps/opencs/view/settings/page.cpp index 665326c2c..a5711c8f8 100644 --- a/apps/opencs/view/settings/page.cpp +++ b/apps/opencs/view/settings/page.cpp @@ -52,7 +52,7 @@ void CSVSettings::Page::addView (CSMSettings::Setting *setting) if (setting->isEditorSetting()) { connect (view, SIGNAL (viewUpdated(const QString&, const QStringList&)), &CSMSettings::UserSettings::instance(), - SIGNAL (userSettingUpdated (const QString &, const QStringList &))); + SLOT (updateUserSetting (const QString &, const QStringList &))); } } diff --git a/apps/opencs/view/settings/view.cpp b/apps/opencs/view/settings/view.cpp index 259dd518b..4f93b1c0f 100644 --- a/apps/opencs/view/settings/view.cpp +++ b/apps/opencs/view/settings/view.cpp @@ -151,6 +151,11 @@ void CSVSettings::View::setSelectedValues (const QStringList &list, } select (selection); + //push changes to model side + + + //update the view if the selection was set from the model side, not by the + //user if (doViewUpdate) updateView (signalUpdate); } @@ -198,7 +203,7 @@ QList CSVSettings::View::toStandardItemList void CSVSettings::View::updateView (bool signalUpdate) const { if (signalUpdate) - emit viewUpdated(objectName(), selectedValues()); + emit viewUpdated(viewKey(), selectedValues()); } QString CSVSettings::View::value (int row) const diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 995de21fc..902ab268a 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -456,12 +456,14 @@ void CSVWorld::Table::updateUserSetting for (int i=0; i - (*delegate).updateUserSetting (name, list)) + { + dynamic_cast + (*delegate).updateUserSetting (name, list); { emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); } + } } void CSVWorld::Table::tableSizeUpdate() diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index dfc74b3eb..c2811b893 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -78,8 +78,6 @@ namespace CSVWorld CSMWorld::UniversalId getUniversalId (int row) const; - void updateUserSetting (const QString &name, const QStringList &list); - std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; signals: @@ -123,6 +121,8 @@ namespace CSVWorld void requestFocus (const std::string& id); void recordFilterChanged (boost::shared_ptr filter); + + void updateUserSetting (const QString &name, const QStringList &list); }; } diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 16310c8a9..ea8a7c541 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -196,12 +196,6 @@ bool CSVWorld::CommandDelegate::isEditLocked() const return mEditLock; } -bool CSVWorld::CommandDelegate::updateUserSetting (const QString &name, - const QStringList &list) -{ - return false; -} - void CSVWorld::CommandDelegate::setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay) const { QVariant v = index.data(Qt::EditRole); diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 50e2cf858..1c7e37818 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -136,17 +136,15 @@ namespace CSVWorld bool isEditLocked() const; - virtual bool updateUserSetting - (const QString &name, const QStringList &list); - ///< \return Does column require update? virtual void setEditorData (QWidget *editor, const QModelIndex& index, bool tryDisplay = false) const; - private slots: + public slots: - virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {} + virtual void updateUserSetting + (const QString &name, const QStringList &list) {} }; } From c3e08916dadd70ac926cb6f3d92ee8c4b4efa50f Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 24 Apr 2014 20:40:17 -0400 Subject: [PATCH 140/157] Sneak: Added support for state checking I need advice one what I should do in order to pass mActors over to the player. Particularly line 139 in player.cpp --- apps/openmw/mwbase/mechanicsmanager.hpp | 3 +++ apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 5 +++++ apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 2 ++ apps/openmw/mwworld/player.cpp | 15 +++++++++++++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 22dda0ce0..7d27e73bd 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -164,6 +164,9 @@ namespace MWBase ///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor) virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; + ///return the list of actors + virtual MWMechanics::Actors& getActors() = 0; + virtual void playerLoaded() = 0; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 84dc337c9..868a09e2e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -989,4 +989,9 @@ namespace MWMechanics { return mActors.getActorsFollowing(actor); } + + MWMechanics::Actors& MechanicsManager::getActors() + { + return *mActors; + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 761caf586..a00c79525 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -137,6 +137,8 @@ namespace MWMechanics virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); + virtual MWMechanics::Actors& getActors(); + virtual void toggleAI(); virtual bool isAIActive(); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a4a4e9568..9cc873c0f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -9,6 +9,8 @@ #include #include +#include "../mwworld/esmstore.hpp" + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" @@ -16,6 +18,8 @@ #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/actors.hpp" +#include "../mwmechanics/mechanicsmanagerimp.hpp" #include "class.hpp" #include "ptr.hpp" @@ -130,8 +134,15 @@ namespace MWWorld ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak); - // TODO show sneak indicator only when the player is not detected by any actor - MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); + // Find all the actors who might be able to see the player + std::vector neighbors; + MWBase::Environment::get().getMechanicsManager()->getActors().getObjectsInRange( Ogre::Vector3(ptr.getRefData().getPosition().pos), + esmStore.get().find("fSneakUseDist")->getInt(), neighbors); + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + if ( MechanicsManager::awarenessCheck(ptr, *it) ) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); + if (!neighbors) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); } void Player::yaw(float yaw) From 42b879a9a542a9392352be885b0b8c206f8dfe4e Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 24 Apr 2014 22:41:05 -0400 Subject: [PATCH 141/157] Reworked the accusation of actors --- apps/openmw/mwbase/mechanicsmanager.hpp | 6 ++---- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 8 ++++---- apps/openmw/mwmechanics/mechanicsmanagerimp.hpp | 3 +-- apps/openmw/mwworld/player.cpp | 8 +++++--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 7d27e73bd..d0a0783d3 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -160,13 +160,11 @@ namespace MWBase virtual bool isAIActive() = 0; virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; + virtual void getActorsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) = 0; ///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor) virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; - - ///return the list of actors - virtual MWMechanics::Actors& getActors() = 0; - + virtual void playerLoaded() = 0; }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 868a09e2e..0ef94c13c 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -985,13 +985,13 @@ namespace MWMechanics mObjects.getObjectsInRange(position, radius, objects); } - std::list MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor) + void MechanicsManager::getActorsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) { - return mActors.getActorsFollowing(actor); + mActors.getObjectsInRange(position, radius, objects); } - MWMechanics::Actors& MechanicsManager::getActors() + std::list MechanicsManager::getActorsFollowing(const MWWorld::Ptr& actor) { - return *mActors; + return mActors.getActorsFollowing(actor); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index a00c79525..603815744 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -134,11 +134,10 @@ namespace MWMechanics virtual void updateMagicEffects (const MWWorld::Ptr& ptr); virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects); + virtual void getActorsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects); virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); - virtual MWMechanics::Actors& getActors(); - virtual void toggleAI(); virtual bool isAIActive(); diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 9cc873c0f..f4ca5ee27 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -134,14 +134,16 @@ namespace MWWorld ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak); + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + // Find all the actors who might be able to see the player std::vector neighbors; - MWBase::Environment::get().getMechanicsManager()->getActors().getObjectsInRange( Ogre::Vector3(ptr.getRefData().getPosition().pos), + MWBase::Environment::get().getMechanicsManager()->getActorsInRange( Ogre::Vector3(ptr.getRefData().getPosition().pos), esmStore.get().find("fSneakUseDist")->getInt(), neighbors); for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) - if ( MechanicsManager::awarenessCheck(ptr, *it) ) + if ( MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, *it) ) MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); - if (!neighbors) + if (!neighbors.size()) MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); } From f3272c941f0caca478418e0b32cd651db33dd526 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 24 Apr 2014 22:47:45 -0400 Subject: [PATCH 142/157] Fix for bug #1080, can't read/repair/make potions/use soul gems/rest/sleep while in combat. The radius for being in combat with the player is fAlarmDistance, which looked like the only pertinent thing. --- apps/openmw/mwbase/mechanicsmanager.hpp | 7 ++++- apps/openmw/mwinput/inputmanagerimp.cpp | 9 +++++-- apps/openmw/mwmechanics/actors.cpp | 26 +++++++++++++++++-- apps/openmw/mwmechanics/actors.hpp | 7 ++++- .../mwmechanics/mechanicsmanagerimp.cpp | 23 +++++++++++----- .../mwmechanics/mechanicsmanagerimp.hpp | 4 ++- apps/openmw/mwstate/statemanagerimp.cpp | 4 +++ apps/openmw/mwworld/actionalchemy.cpp | 8 ++++++ apps/openmw/mwworld/actionread.cpp | 12 +++++++-- apps/openmw/mwworld/actionrepair.cpp | 7 +++++ apps/openmw/mwworld/actionsoulgem.cpp | 22 ++++++++++------ apps/openmw/mwworld/player.cpp | 5 ++++ apps/openmw/mwworld/player.hpp | 5 +++- 13 files changed, 114 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index e4c480a8c..44b3d0229 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -161,9 +161,14 @@ namespace MWBase virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; - ///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor) + ///return the list of actors which are following the given actor + /**ie AiFollow is active and the target is the actor**/ virtual std::list getActorsFollowing(const MWWorld::Ptr& actor) = 0; + ///Returns a list of actors who are fighting the given actor within the fAlarmDistance + /** ie AiCombat is active and the target is the actor **/ + virtual std::list getActorsFighting(const MWWorld::Ptr& actor) = 0; + virtual void playerLoaded() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 51c0a1621..75c9015a0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -19,6 +19,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" @@ -693,8 +694,12 @@ namespace MWInput if (!MWBase::Environment::get().getWindowManager()->getRestEnabled () || MWBase::Environment::get().getWindowManager()->isGuiMode ()) return; - /// \todo check if resting is currently allowed (enemies nearby?) - MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); + if(mPlayer->isInCombat()) {//Check if in combat + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); //Nope, + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_Rest); //Open rest GUI + } void InputManager::screenshot() diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 7b83ff1f1..804ec7a41 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -176,7 +176,7 @@ namespace MWMechanics adjustMagicEffects (ptr); if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats()) calculateDynamicStats (ptr); - + calculateCreatureStatModifiers (ptr, duration); // AI @@ -764,7 +764,7 @@ namespace MWMechanics creatureStats.setHostile(false); creatureStats.setAttacked(false); creatureStats.setAlarmed(false); - + // Update witness crime id npcStats.setCrimeId(-1); } @@ -1038,4 +1038,26 @@ namespace MWMechanics } return list; } + + std::list Actors::getActorsFighting(const MWWorld::Ptr& actor) { + std::list list; + std::vector neighbors; + Ogre::Vector3 position = Ogre::Vector3(actor.getRefData().getPosition().pos); + getObjectsInRange(position, + MWBase::Environment::get().getWorld()->getStore().get().find("fAlarmRadius")->getFloat(), + neighbors); //only care about those within the alarm disance + for(std::vector::iterator iter(neighbors.begin());iter != neighbors.end();iter++) + { + const MWWorld::Class &cls = MWWorld::Class::get(*iter); + CreatureStats &stats = cls.getCreatureStats(*iter); + + if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat) + { + MWMechanics::AiCombat* package = static_cast(stats.getAiSequence().getActivePackage()); + if(package->getTargetId() == actor.getCellRef().mRefID) + list.push_front(*iter); + } + } + return list; + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index d61d74258..f7dff1058 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -98,8 +98,13 @@ namespace MWMechanics void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out); + ///Returns the list of actors which are following the given actor + /**ie AiFollow is active and the target is the actor **/ std::list getActorsFollowing(const MWWorld::Ptr& actor); - /// getActorsFighting(const MWWorld::Ptr& actor); private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index d24191d88..90c00a36e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -772,6 +772,11 @@ namespace MWMechanics bool MechanicsManager::sleepInBed(const MWWorld::Ptr &ptr, const MWWorld::Ptr &bed) { + if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage2}"); + return true; + } + MWWorld::Ptr victim; if (isAllowedToUse(ptr, bed, victim)) return false; @@ -832,17 +837,17 @@ namespace MWMechanics // Find all the NPCs within the alarm radius std::vector neighbors; - mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), + mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), esmStore.get().find("fAlarmRadius")->getInt(), neighbors); // Find an actor who witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) - { + { if (*it == ptr) continue; // not the player // Was the crime seen? if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || - type == OT_Assault ) + type == OT_Assault ) { // Will the witness report the crime? @@ -853,7 +858,7 @@ namespace MWMechanics // Tell everyone, including yourself for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) - { + { if (*it1 == ptr) continue; // not the player // TODO: Add more messages @@ -861,9 +866,9 @@ namespace MWMechanics MWBase::Environment::get().getDialogueManager()->say(*it1, "thief"); else if (type == OT_Assault) MWBase::Environment::get().getDialogueManager()->say(*it1, "attack"); - + // Will other witnesses paticipate in crime - if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm + if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || type == OT_Assault ) { it1->getClass().getNpcStats(*it1).setCrimeId(id); @@ -884,7 +889,7 @@ namespace MWMechanics void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); - + // Bounty for each type of crime if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) arg = store.find("iCrimeTresspass")->getInt(); @@ -993,4 +998,8 @@ namespace MWMechanics { return mActors.getActorsFollowing(actor); } + + std::list MechanicsManager::getActorsFighting(const MWWorld::Ptr& actor) { + return mActors.getActorsFighting(actor); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 5dd758377..4d59379d6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -120,7 +120,7 @@ namespace MWMechanics /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. - /// @return was it illegal, and someone saw you doing it? + /// @return was it illegal, and someone saw you doing it? Also returns fail when enemies are nearby virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed); virtual void forceStateUpdate(const MWWorld::Ptr &ptr); @@ -137,6 +137,8 @@ namespace MWMechanics virtual std::list getActorsFollowing(const MWWorld::Ptr& actor); + virtual std::list getActorsFighting(const MWWorld::Ptr& actor); + virtual bool toggleAI(); virtual bool isAIActive(); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index a0971b942..6c834a2be 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -235,9 +235,12 @@ void MWState::StateManager::quickSave(std::string name) { if (it->mProfile.mDescription == name) slot = &*it; } + MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage4}"); //Saving... MWBase::Environment::get().getStateManager()->saveGame(name, slot); } } + else + MWBase::Environment::get().getWindowManager()->messageBox("#{sSaveGameDenied}"); //You can not save your game right now } void MWState::StateManager::loadGame (const Character *character, const Slot *slot) @@ -351,6 +354,7 @@ void MWState::StateManager::quickLoad() { if(mCurrentCharacter) { //Ensure a current character exists const MWState::Slot* slot = &*mCurrentCharacter->begin(); //Get newest save if(slot) //Don't even try loading it if there's no prior save. + //MWBase::Environment::get().getWindowManager()->messageBox("#{sLoadingMessage14}"); //it overlaps MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot); //load newest save. That was easy! } } diff --git a/apps/openmw/mwworld/actionalchemy.cpp b/apps/openmw/mwworld/actionalchemy.cpp index bba75bc49..bbba1081c 100644 --- a/apps/openmw/mwworld/actionalchemy.cpp +++ b/apps/openmw/mwworld/actionalchemy.cpp @@ -2,11 +2,19 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" namespace MWWorld { void ActionAlchemy::executeImp (const Ptr& actor) { + if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { //Ensure we're not in combat + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage3}"); + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Alchemy); } } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 67755259e..433237e26 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -3,6 +3,9 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" @@ -19,8 +22,13 @@ namespace MWWorld { } - void ActionRead::executeImp (const MWWorld::Ptr& actor) - { + void ActionRead::executeImp (const MWWorld::Ptr& actor) { + + if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { //Ensure we're not in combat + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage4}"); + return; + } + LiveCellRef *ref = getTarget().get(); if (ref->mBase->mData.mIsScroll) diff --git a/apps/openmw/mwworld/actionrepair.cpp b/apps/openmw/mwworld/actionrepair.cpp index bd5642116..a86dc38b1 100644 --- a/apps/openmw/mwworld/actionrepair.cpp +++ b/apps/openmw/mwworld/actionrepair.cpp @@ -2,6 +2,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" namespace MWWorld { @@ -12,6 +14,11 @@ namespace MWWorld void ActionRepair::executeImp (const Ptr& actor) { + if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage2}"); + return; + } + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Repair); MWBase::Environment::get().getWindowManager()->startRepairItem(getTarget()); } diff --git a/apps/openmw/mwworld/actionsoulgem.cpp b/apps/openmw/mwworld/actionsoulgem.cpp index 6746f692f..7237fd334 100644 --- a/apps/openmw/mwworld/actionsoulgem.cpp +++ b/apps/openmw/mwworld/actionsoulgem.cpp @@ -2,20 +2,26 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" namespace MWWorld { -ActionSoulgem::ActionSoulgem(const Ptr &object) - : Action(false, object) -{ + ActionSoulgem::ActionSoulgem(const Ptr &object) + : Action(false, object) + { -} + } -void ActionSoulgem::executeImp(const Ptr &actor) -{ - MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); -} + void ActionSoulgem::executeImp(const Ptr &actor) + { + if(MWBase::Environment::get().getWorld()->getPlayer().isInCombat()) { //Ensure we're not in combat + MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage5}"); + return; + } + MWBase::Environment::get().getWindowManager()->showSoulgemDialog(getTarget()); + } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index a4a4e9568..9202118a4 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" @@ -166,6 +167,10 @@ namespace MWWorld mTeleported = teleported; } + bool Player::isInCombat() { + return MWBase::Environment::get().getMechanicsManager()->getActorsFighting(getPlayer()).size() != 0; + } + void Player::markPosition(CellStore *markedCell, ESM::Position markedPosition) { mMarkedCell = markedCell; diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 7dbaaddb4..7e3f7a3cf 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -44,7 +44,7 @@ namespace MWWorld int mCurrentCrimeId; // the id assigned witnesses int mPayedCrimeId; // the last id payed off (0 bounty) - + public: Player(const ESM::NPC *player, const MWBase::World& world); @@ -90,6 +90,9 @@ namespace MWWorld bool wasTeleported() const; void setTeleported(bool teleported); + ///Checks all actors to see if anyone has an aipackage against you + bool isInCombat(); + void clear(); void write (ESM::ESMWriter& writer) const; From 4a4c08946c5b75701a5eb9b3e878c0727380909d Mon Sep 17 00:00:00 2001 From: Jeffrey Haines Date: Thu, 24 Apr 2014 23:02:11 -0400 Subject: [PATCH 143/157] Checks the state of the passed sneak variable --- apps/openmw/mwworld/player.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index f4ca5ee27..8588547d2 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -134,17 +134,25 @@ namespace MWWorld ptr.getClass().getCreatureStats(ptr).setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, sneak); - const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - - // Find all the actors who might be able to see the player - std::vector neighbors; - MWBase::Environment::get().getMechanicsManager()->getActorsInRange( Ogre::Vector3(ptr.getRefData().getPosition().pos), - esmStore.get().find("fSneakUseDist")->getInt(), neighbors); - for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) - if ( MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, *it) ) - MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); - if (!neighbors.size()) - MWBase::Environment::get().getWindowManager()->setSneakVisibility(sneak); + if (sneak == true) + { + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); + + // Find all the actors who might be able to see the player + std::vector neighbors; + MWBase::Environment::get().getMechanicsManager()->getActorsInRange( Ogre::Vector3(ptr.getRefData().getPosition().pos), + esmStore.get().find("fSneakUseDist")->getInt(), neighbors); + for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) + { + if ( MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, *it) ) + { + MWBase::Environment::get().getWindowManager()->setSneakVisibility(false); + break; + } + } + if (neighbors.size() == 0) + MWBase::Environment::get().getWindowManager()->setSneakVisibility(true); + } } void Player::yaw(float yaw) From 2e06414b43663684df1ec2b8e2b59391b2f32f17 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 25 Apr 2014 07:16:40 -0500 Subject: [PATCH 144/157] Fixed broken delegate display modes (Display Format settings). Moved UserSetting update functions to DataDisplayDelegate. --- apps/opencs/model/settings/settingmanager.cpp | 2 +- apps/opencs/model/settings/usersettings.cpp | 9 ++-- apps/opencs/model/settings/usersettings.hpp | 2 +- apps/opencs/view/doc/view.cpp | 4 +- .../opencs/view/world/datadisplaydelegate.cpp | 44 +++++++++++++++++-- .../opencs/view/world/datadisplaydelegate.hpp | 12 ++++- apps/opencs/view/world/idtypedelegate.cpp | 25 ++--------- apps/opencs/view/world/idtypedelegate.hpp | 3 -- .../view/world/recordstatusdelegate.cpp | 23 ++-------- .../view/world/recordstatusdelegate.hpp | 3 -- 10 files changed, 67 insertions(+), 60 deletions(-) diff --git a/apps/opencs/model/settings/settingmanager.cpp b/apps/opencs/model/settings/settingmanager.cpp index 450252cd9..eec4c54cf 100644 --- a/apps/opencs/model/settings/settingmanager.cpp +++ b/apps/opencs/model/settings/settingmanager.cpp @@ -338,5 +338,5 @@ void CSMSettings::SettingManager::updateUserSetting(const QString &settingKey, setting->setDefinedValues (list); - emit userSettingUpdated (names.at(1), list); + emit userSettingUpdated (settingKey, list); } diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index db31f4255..badb73ece 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -260,13 +260,16 @@ void CSMSettings::UserSettings::saveSettings writeFilestream (openFilestream (mUserFilePath, false), settingMap); } -QString CSMSettings::UserSettings::settingValue (const QString §ion, - const QString &name) +QString CSMSettings::UserSettings::settingValue (const QString &settingKey) { - Setting *setting = findSetting(section, name); + QStringList names = settingKey.split('.'); + qDebug () << "looking for " << names.at(0) << ',' << names.at(1); + + Setting *setting = findSetting(names.at(0), names.at(1)); if (setting) { + qDebug() << "setting found"; if (!setting->definedValues().isEmpty()) return setting->definedValues().at(0); } diff --git a/apps/opencs/model/settings/usersettings.hpp b/apps/opencs/model/settings/usersettings.hpp index 63caed923..f0ed7af41 100644 --- a/apps/opencs/model/settings/usersettings.hpp +++ b/apps/opencs/model/settings/usersettings.hpp @@ -53,7 +53,7 @@ namespace CSMSettings { /// Writes settings to the user's config file path void saveSettings (const QMap &settingMap); - QString settingValue (const QString §ion, const QString &name); + QString settingValue (const QString &settingKey); private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 397567f99..acb272553 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -236,10 +236,10 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to mViewTotal (totalViews) { QString width = CSMSettings::UserSettings::instance().settingValue - (QString("Window Size"), QString("Width")); + ("Window Size.Width"); QString height = CSMSettings::UserSettings::instance().settingValue - (QString("Window Size"), QString("Height")); + ("Window Size.Height"); resize (width.toInt(), height.toInt()); diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index d838395f6..ef0da56ac 100755 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -1,16 +1,26 @@ #include "datadisplaydelegate.hpp" +#include "../../model/settings/usersettings.hpp" + #include #include CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const IconList &icons, - QUndoStack &undoStack, QObject *parent) - : EnumDelegate (values, undoStack, parent), mDisplayMode (Mode_TextOnly), mIcons (icons) - , mIconSize (QSize(16, 16)), mIconLeftOffset(3), mTextLeftOffset(8) + QUndoStack &undoStack, + const QString &settingKey, + QObject *parent) + : EnumDelegate (values, undoStack, parent), mDisplayMode (Mode_TextOnly), + mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), + mTextLeftOffset(8), mSettingKey (settingKey) { mTextAlignment.setAlignment (Qt::AlignLeft | Qt::AlignVCenter ); buildPixmaps(); + + QString value = + CSMSettings::UserSettings::instance().settingValue (settingKey); + + updateDisplayMode(value); } void CSVWorld::DataDisplayDelegate::buildPixmaps () @@ -89,6 +99,30 @@ void CSVWorld::DataDisplayDelegate::paintIcon (QPainter *painter, const QStyleOp painter->drawPixmap (iconRect, mPixmaps.at(index).second); } +void CSVWorld::DataDisplayDelegate::updateUserSetting (const QString &name, + const QStringList &list) +{ + if (list.isEmpty()) + return; + + QString value = list.at(0); + + if (name == mSettingKey) + updateDisplayMode (value); +} + +void CSVWorld::DataDisplayDelegate::updateDisplayMode (const QString &mode) +{ + if (mode == "Icon and Text") + mDisplayMode = Mode_IconAndText; + + else if (mode == "Icon Only") + mDisplayMode = Mode_IconOnly; + + else if (mode == "Text Only") + mDisplayMode = Mode_TextOnly; +} + CSVWorld::DataDisplayDelegate::~DataDisplayDelegate() { mIcons.clear(); @@ -106,5 +140,7 @@ CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate (Q QObject *parent) const { - return new DataDisplayDelegate (mValues, mIcons, undoStack, parent); + return new DataDisplayDelegate (mValues, mIcons, undoStack, "", parent); } + + diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index d23b86631..f11c4a2b9 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -35,10 +35,14 @@ namespace CSVWorld int mIconLeftOffset; int mTextLeftOffset; + QString mSettingKey; + public: explicit DataDisplayDelegate (const ValueList & values, const IconList & icons, - QUndoStack& undoStack, QObject *parent); + QUndoStack& undoStack, + const QString &settingKey, + QObject *parent); ~DataDisplayDelegate(); @@ -53,8 +57,14 @@ namespace CSVWorld /// offset the horizontal position of the text from the right edge of the icon. Default is 8 pixels. void setTextLeftOffset (int offset); + ///update the display mode for the delegate + void updateUserSetting (const QString &name, const QStringList &list); + private: + /// update the display mode based on a passed string + void updateDisplayMode (const QString &); + /// custom paint function for painting the icon. Mode_IconAndText and Mode_Icon only. void paintIcon (QPainter *painter, const QStyleOptionViewItem &option, int i) const; diff --git a/apps/opencs/view/world/idtypedelegate.cpp b/apps/opencs/view/world/idtypedelegate.cpp index ce4e8f014..485ca57ac 100755 --- a/apps/opencs/view/world/idtypedelegate.cpp +++ b/apps/opencs/view/world/idtypedelegate.cpp @@ -4,30 +4,11 @@ CSVWorld::IdTypeDelegate::IdTypeDelegate (const ValueList &values, const IconList &icons, QUndoStack& undoStack, QObject *parent) - : DataDisplayDelegate (values, icons, undoStack, parent) + : DataDisplayDelegate (values, icons, undoStack, + "Display Format.Referenceable ID Type Display", + parent) {} -bool CSVWorld::IdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) -{ - /// \todo make the setting key a member variable, that is initialised from a constructor argument - if (settingName == "Referenceable ID Type Display") - { - if (settingValue == "Icon and Text") - mDisplayMode = Mode_IconAndText; - - else if (settingValue == "Icon Only") - mDisplayMode = Mode_IconOnly; - - else if (settingValue == "Text Only") - mDisplayMode = Mode_TextOnly; - - return true; - } - - return false; -} - - CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() { for (int i=0; i enums = diff --git a/apps/opencs/view/world/recordstatusdelegate.hpp b/apps/opencs/view/world/recordstatusdelegate.hpp index d9126fee0..1b42223af 100644 --- a/apps/opencs/view/world/recordstatusdelegate.hpp +++ b/apps/opencs/view/world/recordstatusdelegate.hpp @@ -20,9 +20,6 @@ namespace CSVWorld explicit RecordStatusDelegate(const ValueList& values, const IconList& icons, QUndoStack& undoStack, QObject *parent = 0); - - virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); - }; class RecordStatusDelegateFactory : public DataDisplayDelegateFactory From 42b332775a4b6623f4cacd3c7de25f18ead56b40 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 25 Apr 2014 15:30:46 +0200 Subject: [PATCH 145/157] removed some debug statements --- apps/opencs/model/settings/usersettings.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index badb73ece..808fbfc70 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -11,8 +11,6 @@ #include #include -#include - #include #include @@ -263,13 +261,11 @@ void CSMSettings::UserSettings::saveSettings QString CSMSettings::UserSettings::settingValue (const QString &settingKey) { QStringList names = settingKey.split('.'); - qDebug () << "looking for " << names.at(0) << ',' << names.at(1); Setting *setting = findSetting(names.at(0), names.at(1)); if (setting) { - qDebug() << "setting found"; if (!setting->definedValues().isEmpty()) return setting->definedValues().at(0); } From 5b011e43a7a30f6d428f095031f4593e968b1f8b Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 23 Apr 2014 19:49:09 +0200 Subject: [PATCH 146/157] Fix a crash after loading game --- apps/openmw/mwgui/hud.cpp | 8 +++++++- apps/openmw/mwgui/hud.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 06a228a1f..02cc5c6a9 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -52,7 +52,7 @@ namespace MWGui , mWeaponVisible(true) , mSpellVisible(true) , mWorldMouseOver(false) - , mEnemyHealthTimer(0) + , mEnemyHealthTimer(-1) , mIsDrowning(false) , mWeaponSpellTimer(0.f) , mDrowningFlashTheta(0.f) @@ -639,4 +639,10 @@ namespace MWGui updateEnemyHealthBar(); } + void HUD::resetEnemy() + { + mEnemy = MWWorld::Ptr(); + mEnemyHealthTimer = -1; + } + } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 1645d8db0..38535630f 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -56,6 +56,7 @@ namespace MWGui void update(); void setEnemy(const MWWorld::Ptr& enemy); + void resetEnemy(); private: MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index db19070a6..2b9840876 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -289,6 +289,10 @@ namespace MWGui void WindowManager::setNewGame(bool newgame) { + // This method will always be called after loading a savegame or starting a new game + // Reset enemy, it could be a dangling pointer from a previous game + mHud->resetEnemy(); + if (newgame) { disallowAll(); From 91e50585ff1984f0f44a9983d38c16552f144cc7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 24 Apr 2014 05:17:01 +0200 Subject: [PATCH 147/157] AiWander: make sure to walk, not run --- apps/openmw/mwmechanics/aitravel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index c62c4e970..024656b38 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -8,6 +8,7 @@ #include "steering.hpp" #include "movement.hpp" +#include "creaturestats.hpp" namespace MWMechanics { @@ -30,6 +31,8 @@ namespace MWMechanics Movement &movement = actor.getClass().getMovementSettings(actor); const ESM::Cell *cell = actor.getCell()->getCell(); + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); + MWWorld::Ptr player = world->getPlayerPtr(); if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { From f05606657cbfabbdc03fdff822149a2b83bc41c0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Apr 2014 10:01:03 +0200 Subject: [PATCH 148/157] Correctly insert pasted text at cursor position --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index deeb93440..42362fde7 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -484,7 +484,7 @@ namespace MWInput if (text) { - edit->addText(MyGUI::UString(text)); + edit->insertText(MyGUI::UString(text), edit->getTextCursor()); SDL_free(text); } } From cd1b4218e79f7c7cad2d7ff32962eb281deb01d4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 03:15:50 +0200 Subject: [PATCH 149/157] Fix dynamically placed objects being discarded when loading game --- apps/openmw/mwworld/cellstore.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 6bc7657e4..bbd968a7b 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -52,7 +52,7 @@ namespace iter!=collection.mList.end(); ++iter) { if (iter->mData.getCount()==0 && iter->mRef.mRefNum.mContentFile==-1) - continue; // deleted file that did not came from a content file -> ignore + continue; // deleted reference that did not come from a content file -> ignore RecordType state; iter->save (state); @@ -72,13 +72,17 @@ namespace RecordType state; state.load (reader); - std::map::const_iterator iter = - contentFileMap.find (state.mRef.mRefNum.mContentFile); + // If the reference came from a content file, make sure this content file is loaded + if (state.mRef.mRefNum.mContentFile != -1) + { + std::map::const_iterator iter = + contentFileMap.find (state.mRef.mRefNum.mContentFile); - if (iter==contentFileMap.end()) - return; // content file has been removed -> skip + if (iter==contentFileMap.end()) + return; // content file has been removed -> skip - state.mRef.mRefNum.mContentFile = iter->second; + state.mRef.mRefNum.mContentFile = iter->second; + } if (!MWWorld::LiveCellRef::checkState (state)) return; // not valid anymore with current content files -> skip From 6eca5ac4bbab19dcf2a2a751431a655108718d45 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 07:46:19 +0200 Subject: [PATCH 150/157] Fixes #1296: Streamlined cellChanged detection --- apps/openmw/engine.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 508b195e9..03361408c 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -59,9 +59,6 @@ void OMW::Engine::executeLocalScripts() MWScript::InterpreterContext interpreterContext ( &script.second.getRefData().getLocals(), script.second); MWBase::Environment::get().getScriptManager()->run (script.first, interpreterContext); - - if (MWBase::Environment::get().getWorld()->hasCellChanged()) - break; } localScripts.setIgnore (MWWorld::Ptr()); @@ -101,15 +98,10 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // global scripts MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); - // local scripts - executeLocalScripts(); // This does not handle the case where a global script causes a - // cell change, followed by a cell change in a local script during - // the same frame. + executeLocalScripts(); - if (changed) // keep change flag for another frame, if cell changed happened in local script - MWBase::Environment::get().getWorld()->markCellAsUnchanged(); + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); if (!paused) MWBase::Environment::get().getWorld()->advanceTime( From b13b25dd1b1a94c13b61f569051382f7218304a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 11:03:17 +0200 Subject: [PATCH 151/157] Fixes #1295: Support partial matches in Cell filter --- apps/openmw/mwdialogue/filter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 18ae7dd1b..b1c700e31 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -111,8 +111,14 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const // check cell if (!info.mCell.empty()) - if (!Misc::StringUtils::ciEqual(player.getCell()->getCell()->mName, info.mCell)) + { + // supports partial matches, just like getPcCell + const std::string& playerCell = player.getCell()->getCell()->mName; + bool match = playerCell.length()>=info.mCell.length() && + Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell); + if (!match) return false; + } return true; } From 45d2a00717a504d65679978954876120f9ec00ca Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 11:36:27 +0200 Subject: [PATCH 152/157] Fixes #1293: Allow interpolation type 0 in case of 0 keys --- apps/openmw/mwrender/npcanimation.cpp | 3 ++- components/nif/niffile.hpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index a09a58191..14f5cc4e7 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -68,7 +68,8 @@ namespace MWRender float HeadAnimationTime::getValue() const { - // TODO: Handle eye blinking (time is in the text keys) + // TODO use time from text keys (Talk Start/Stop, Blink Start/Stop) + // TODO: Handle eye blinking if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) return 0; else diff --git a/components/nif/niffile.hpp b/components/nif/niffile.hpp index 79e1cc2a5..d4b042726 100644 --- a/components/nif/niffile.hpp +++ b/components/nif/niffile.hpp @@ -199,6 +199,11 @@ struct KeyListT { key.mContinuity = nif->getFloat(); } } + else if (mInterpolationType == 0) + { + if (count != 0) + nif->file->fail("Interpolation type 0 doesn't work with keys"); + } else nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType)); } From ef39b0f6abb7fb6de34fa0934398798a09a0f9fe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 11:41:44 +0200 Subject: [PATCH 153/157] Fixes #1291: Faction rank saving issue A value of 0 is the first rank, -1 means not a member --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e11e3b0c4..819c2701c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -491,7 +491,7 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) if (iter->second.mExpelled) mExpelled.insert (iter->first); - if (iter->second.mRank) + if (iter->second.mRank >= 0) mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank)); if (iter->second.mReputation) From f921f2e7db8e62cb1f1588b26b2969a4286855a6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 12:04:37 +0200 Subject: [PATCH 154/157] Make PcRaiseRank, PcLowerRank and PcJoinFaction properly accept references instead of using the actor that the player talked to last. This also solves a potential crash when no actor has been talked to yet, e.g. immediately after loading a savegame. --- apps/openmw/mwbase/dialoguemanager.hpp | 3 --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 5 ---- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 3 --- apps/openmw/mwscript/docs/vmformat.txt | 5 +++- apps/openmw/mwscript/statsextensions.cpp | 25 ++++++++++++------- components/compiler/extensions0.cpp | 6 ++--- components/compiler/opcodes.hpp | 4 +++ 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 3d70fdc6a..e1d1246c8 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -45,9 +45,6 @@ namespace MWBase virtual void goodbye() = 0; - virtual MWWorld::Ptr getActor() const = 0; - ///< Return the actor the player is currently talking to. - virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0; //calbacks for the GUI diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 88f1302bb..32535f33e 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -468,11 +468,6 @@ namespace MWDialogue mIsInChoice = true; } - MWWorld::Ptr DialogueManager::getActor() const - { - return mActor; - } - void DialogueManager::goodbye() { mIsInChoice = true; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index b9284dc1a..a2e31e791 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -68,9 +68,6 @@ namespace MWDialogue virtual void goodbye(); - virtual MWWorld::Ptr getActor() const; - ///< Return the actor the player is currently talking to. - virtual bool checkServiceRefused (); virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 70186a089..ceea34466 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -54,7 +54,10 @@ op 0x20025: AiFollowCell, explicit reference op 0x20026: ModRegion op 0x20027: RemoveSoulGem op 0x20028: RemoveSoulGem, explicit reference -opcodes 0x20029-0x3ffff unused +op 0x20029: PCRaiseRank, explicit reference +op 0x2002a: PCLowerRank, explicit reference +op 0x2002b: PCJoinFaction, explicit reference +opcodes 0x2002c-0x3ffff unused Segment 4: (not implemented yet) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 80467f58a..36083e4b0 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -29,10 +29,8 @@ namespace { - std::string getDialogueActorFaction() + std::string getDialogueActorFaction(MWWorld::Ptr actor) { - MWWorld::Ptr actor = MWBase::Environment::get().getDialogueManager()->getActor(); - const MWMechanics::NpcStats &stats = MWWorld::Class::get (actor).getNpcStats (actor); if (stats.getFactionRanks().empty()) @@ -523,6 +521,7 @@ namespace MWScript } }; + template class OpPCJoinFaction : public Interpreter::Opcode1 { public: @@ -533,7 +532,8 @@ namespace MWScript if(arg0==0) { - factionID = getDialogueActorFaction(); + MWWorld::Ptr actor = R()(runtime); + factionID = getDialogueActorFaction(actor); } else { @@ -552,6 +552,7 @@ namespace MWScript } }; + template class OpPCRaiseRank : public Interpreter::Opcode1 { public: @@ -562,7 +563,8 @@ namespace MWScript if(arg0==0) { - factionID = getDialogueActorFaction(); + MWWorld::Ptr actor = R()(runtime); + factionID = getDialogueActorFaction(actor); } else { @@ -585,6 +587,7 @@ namespace MWScript } }; + template class OpPCLowerRank : public Interpreter::Opcode1 { public: @@ -595,7 +598,8 @@ namespace MWScript if(arg0==0) { - factionID = getDialogueActorFaction(); + MWWorld::Ptr actor = R()(runtime); + factionID = getDialogueActorFaction(actor); } else { @@ -1180,9 +1184,12 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeGetSpell, new OpGetSpell); interpreter.installSegment5 (Compiler::Stats::opcodeGetSpellExplicit, new OpGetSpell); - interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRank,new OpPCRaiseRank); - interpreter.installSegment3(Compiler::Stats::opcodePCLowerRank,new OpPCLowerRank); - interpreter.installSegment3(Compiler::Stats::opcodePCJoinFaction,new OpPCJoinFaction); + interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRank,new OpPCRaiseRank); + interpreter.installSegment3(Compiler::Stats::opcodePCLowerRank,new OpPCLowerRank); + interpreter.installSegment3(Compiler::Stats::opcodePCJoinFaction,new OpPCJoinFaction); + interpreter.installSegment3(Compiler::Stats::opcodePCRaiseRankExplicit,new OpPCRaiseRank); + interpreter.installSegment3(Compiler::Stats::opcodePCLowerRankExplicit,new OpPCLowerRank); + interpreter.installSegment3(Compiler::Stats::opcodePCJoinFactionExplicit,new OpPCJoinFaction); interpreter.installSegment3(Compiler::Stats::opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(Compiler::Stats::opcodeGetPCRankExplicit,new OpGetPCRank); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 78b6409f2..531fe2959 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -409,9 +409,9 @@ namespace Compiler opcodeResurrectExplicit); extensions.registerFunction ("getspell", 'l', "c", opcodeGetSpell, opcodeGetSpellExplicit); - extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank); - extensions.registerInstruction("pclowerrank","/S",opcodePCLowerRank); - extensions.registerInstruction("pcjoinfaction","/S",opcodePCJoinFaction); + extensions.registerInstruction("pcraiserank","/S",opcodePCRaiseRank, opcodePCRaiseRankExplicit); + extensions.registerInstruction("pclowerrank","/S",opcodePCLowerRank, opcodePCLowerRankExplicit); + extensions.registerInstruction("pcjoinfaction","/S",opcodePCJoinFaction, opcodePCJoinFactionExplicit); extensions.registerInstruction ("moddisposition","l",opcodeModDisposition, opcodeModDispositionExplicit); extensions.registerInstruction ("setdisposition","l",opcodeSetDisposition, diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 1dbdbf7e7..27ee182cf 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -330,6 +330,10 @@ namespace Compiler const int opcodePCRaiseRank = 0x2000b; const int opcodePCLowerRank = 0x2000c; const int opcodePCJoinFaction = 0x2000d; + const int opcodePCRaiseRankExplicit = 0x20029; + const int opcodePCLowerRankExplicit = 0x2002a; + const int opcodePCJoinFactionExplicit = 0x2002b; + const int opcodeGetPCRank = 0x2000e; const int opcodeGetPCRankExplicit = 0x2000f; const int opcodeModDisposition = 0x200014d; From 30666f2cce17357336b70507ef9428dd4e433f77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 13:42:32 +0200 Subject: [PATCH 155/157] Fixes #1297: Store global map markers in savegame --- apps/openmw/mwgui/mapwindow.cpp | 37 +++++-- apps/openmw/mwgui/mapwindow.hpp | 3 + apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwrender/globalmap.cpp | 142 ++++++++++--------------- apps/openmw/mwrender/globalmap.hpp | 7 +- components/esm/globalmap.cpp | 17 +++ components/esm/globalmap.hpp | 3 + 7 files changed, 112 insertions(+), 99 deletions(-) diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 1cc9610df..aa48631e4 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -14,6 +14,8 @@ #include "../mwrender/globalmap.hpp" +#include "../components/esm/globalmap.hpp" + #include "widgets.hpp" namespace MWGui @@ -436,7 +438,6 @@ namespace MWGui worldY * mGlobalMapRender->getHeight()+6, 12, 12); - static int _counter=0; MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget("ButtonImage", widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast(_counter)); @@ -452,6 +453,11 @@ namespace MWGui markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("Caption_TextOneLine", name); + + CellId cell; + cell.first = x; + cell.second = y; + mMarkers.push_back(cell); } void MapWindow::cellExplored(int x, int y) @@ -580,6 +586,7 @@ namespace MWGui void MapWindow::clear() { + mMarkers.clear(); mGlobalMapRender->clear(); while (mEventBoxGlobal->getChildCount()) @@ -590,19 +597,31 @@ namespace MWGui void MapWindow::write(ESM::ESMWriter &writer) { - mGlobalMapRender->write(writer); + ESM::GlobalMap map; + mGlobalMapRender->write(map); + + map.mMarkers = mMarkers; + + writer.startRecord(ESM::REC_GMAP); + map.save(writer); + writer.endRecord(ESM::REC_GMAP); } void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type) { - std::vector > exploredCells; - mGlobalMapRender->readRecord(reader, type, exploredCells); - - for (std::vector >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it) + if (type == ESM::REC_GMAP) { - const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first, it->second); - if (cell && !cell->mName.empty()) - addVisitedLocation(cell->mName, it->first, it->second); + ESM::GlobalMap map; + map.load(reader); + + mGlobalMapRender->read(map); + + for (std::vector::iterator it = map.mMarkers.begin(); it != map.mMarkers.end(); ++it) + { + const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get().search(it->first, it->second); + if (cell && !cell->mName.empty()) + addVisitedLocation(cell->mName, it->first, it->second); + } } } } diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index 249477551..0b549147c 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -55,6 +55,9 @@ namespace MWGui bool mChanged; bool mFogOfWar; + typedef std::pair CellId; + std::vector mMarkers; + std::vector mMapWidgets; std::vector mFogWidgets; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 2b9840876..0457fcb59 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -187,7 +187,7 @@ namespace MWGui MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); - SDL_ShowCursor(false); + //SDL_ShowCursor(false); mCursorManager->setEnabled(true); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 76ad1890f..0537ed516 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -231,9 +231,8 @@ namespace MWRender mOverlayTexture->getBuffer()->blitFromMemory(pb); } - void GlobalMap::write(ESM::ESMWriter &writer) + void GlobalMap::write(ESM::GlobalMap& map) { - ESM::GlobalMap map; map.mBounds.mMinX = mMinX; map.mBounds.mMaxX = mMaxX; map.mBounds.mMinY = mMinY; @@ -244,95 +243,68 @@ namespace MWRender Ogre::DataStreamPtr encoded = image.encode("png"); map.mImageData.resize(encoded->size()); encoded->read(&map.mImageData[0], encoded->size()); - - writer.startRecord(ESM::REC_GMAP); - map.save(writer); - writer.endRecord(ESM::REC_GMAP); } - void GlobalMap::readRecord(ESM::ESMReader &reader, int32_t type, std::vector >& exploredCells) + void GlobalMap::read(ESM::GlobalMap& map) { - if (type == ESM::REC_GMAP) - { - ESM::GlobalMap map; - map.load(reader); - - const ESM::GlobalMap::Bounds& bounds = map.mBounds; - - if (bounds.mMaxX-bounds.mMinX <= 0) - return; - if (bounds.mMaxY-bounds.mMinY <= 0) - return; - - if (bounds.mMinX > bounds.mMaxX - || bounds.mMinY > bounds.mMaxY) - throw std::runtime_error("invalid map bounds"); + const ESM::GlobalMap::Bounds& bounds = map.mBounds; - Ogre::Image image; - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size())); - image.load(stream, "png"); - - int xLength = (bounds.mMaxX-bounds.mMinX+1); - int yLength = (bounds.mMaxY-bounds.mMinY+1); - - // Size of one cell in image space - int cellImageSizeSrc = image.getWidth() / xLength; - if (int(image.getHeight() / yLength) != cellImageSizeSrc) - throw std::runtime_error("cell size must be quadratic"); - - // Determine which cells were explored by reading the image data - for (int x=0; x < xLength; ++x) - { - for (int y=0; y < yLength; ++y) - { - unsigned int imageX = (x) * cellImageSizeSrc; - // NB y + 1, because we want the top left corner, not bottom left where the origin of the cell is - unsigned int imageY = (yLength - (y + 1)) * cellImageSizeSrc; + if (bounds.mMaxX-bounds.mMinX <= 0) + return; + if (bounds.mMaxY-bounds.mMinY <= 0) + return; - assert(imageX < image.getWidth()); - assert(imageY < image.getHeight()); + if (bounds.mMinX > bounds.mMaxX + || bounds.mMinY > bounds.mMaxY) + throw std::runtime_error("invalid map bounds"); - if (image.getColourAt(imageX, imageY, 0).a > 0) - exploredCells.push_back(std::make_pair(x+bounds.mMinX,y+bounds.mMinY)); - } - } + Ogre::Image image; + Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&map.mImageData[0], map.mImageData.size())); + image.load(stream, "png"); + + int xLength = (bounds.mMaxX-bounds.mMinX+1); + int yLength = (bounds.mMaxY-bounds.mMinY+1); + + // Size of one cell in image space + int cellImageSizeSrc = image.getWidth() / xLength; + if (int(image.getHeight() / yLength) != cellImageSizeSrc) + throw std::runtime_error("cell size must be quadratic"); + + // If cell bounds of the currently loaded content and the loaded savegame do not match, + // we need to resize source/dest boxes to accommodate + // This means nonexisting cells will be dropped silently + int cellImageSizeDst = 24; + + // Completely off-screen? -> no need to blit anything + if (bounds.mMaxX < mMinX + || bounds.mMaxY < mMinY + || bounds.mMinX > mMaxX + || bounds.mMinY > mMaxY) + return; - // If cell bounds of the currently loaded content and the loaded savegame do not match, - // we need to resize source/dest boxes to accommodate - // This means nonexisting cells will be dropped silently - int cellImageSizeDst = 24; - - // Completely off-screen? -> no need to blit anything - if (bounds.mMaxX < mMinX - || bounds.mMaxY < mMinY - || bounds.mMinX > mMaxX - || bounds.mMinY > mMaxY) - return; - - int leftDiff = (mMinX - bounds.mMinX); - int topDiff = (bounds.mMaxY - mMaxY); - int rightDiff = (bounds.mMaxX - mMaxX); - int bottomDiff = (mMinY - bounds.mMinY); - Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc), - std::max(0, topDiff * cellImageSizeSrc), - std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc), - std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc)); - - Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst), - std::max(0, -topDiff * cellImageSizeDst), - std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst), - std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst)); - - // Looks like there is no interface for blitting from memory with src/dst boxes. - // So we create a temporary texture for blitting. - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(), - image.getHeight(), 0, Ogre::PF_A8B8G8R8); - tex->loadImage(image); - - mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox); - - Ogre::TextureManager::getSingleton().remove("@temp"); - } + int leftDiff = (mMinX - bounds.mMinX); + int topDiff = (bounds.mMaxY - mMaxY); + int rightDiff = (bounds.mMaxX - mMaxX); + int bottomDiff = (mMinY - bounds.mMinY); + Ogre::Image::Box srcBox ( std::max(0, leftDiff * cellImageSizeSrc), + std::max(0, topDiff * cellImageSizeSrc), + std::min(image.getWidth(), image.getWidth() - rightDiff * cellImageSizeSrc), + std::min(image.getHeight(), image.getHeight() - bottomDiff * cellImageSizeSrc)); + + Ogre::Image::Box destBox ( std::max(0, -leftDiff * cellImageSizeDst), + std::max(0, -topDiff * cellImageSizeDst), + std::min(mOverlayTexture->getWidth(), mOverlayTexture->getWidth() + rightDiff * cellImageSizeDst), + std::min(mOverlayTexture->getHeight(), mOverlayTexture->getHeight() + bottomDiff * cellImageSizeDst)); + + // Looks like there is no interface for blitting from memory with src/dst boxes. + // So we create a temporary texture for blitting. + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton().createManual("@temp", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, image.getWidth(), + image.getHeight(), 0, Ogre::PF_A8B8G8R8); + tex->loadImage(image); + + mOverlayTexture->getBuffer()->blit(tex->getBuffer(), srcBox, destBox); + + Ogre::TextureManager::getSingleton().remove("@temp"); } } diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index 5fe878cd4..6075d042e 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -12,8 +12,7 @@ namespace Loading namespace ESM { - class ESMWriter; - class ESMReader; + class GlobalMap; } namespace MWRender @@ -40,8 +39,8 @@ namespace MWRender /// Clears the overlay void clear(); - void write (ESM::ESMWriter& writer); - void readRecord (ESM::ESMReader& reader, int32_t type, std::vector >& exploredCells); + void write (ESM::GlobalMap& map); + void read (ESM::GlobalMap& map); private: std::string mCacheDir; diff --git a/components/esm/globalmap.cpp b/components/esm/globalmap.cpp index 1fa5f907e..f17c071ff 100644 --- a/components/esm/globalmap.cpp +++ b/components/esm/globalmap.cpp @@ -14,6 +14,15 @@ void ESM::GlobalMap::load (ESMReader &esm) esm.getSubHeader(); mImageData.resize(esm.getSubSize()); esm.getExact(&mImageData[0], mImageData.size()); + + while (esm.isNextSub("MRK_")) + { + esm.getSubHeader(); + CellId cell; + esm.getT(cell.first); + esm.getT(cell.second); + mMarkers.push_back(cell); + } } void ESM::GlobalMap::save (ESMWriter &esm) const @@ -23,4 +32,12 @@ void ESM::GlobalMap::save (ESMWriter &esm) const esm.startSubRecord("DATA"); esm.write(&mImageData[0], mImageData.size()); esm.endRecord("DATA"); + + for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) + { + esm.startSubRecord("MRK_"); + esm.writeT(it->first); + esm.writeT(it->second); + esm.endRecord("MRK_"); + } } diff --git a/components/esm/globalmap.hpp b/components/esm/globalmap.hpp index 5d036c736..158f70a6e 100644 --- a/components/esm/globalmap.hpp +++ b/components/esm/globalmap.hpp @@ -25,6 +25,9 @@ namespace ESM std::vector mImageData; + typedef std::pair CellId; + std::vector mMarkers; + void load (ESMReader &esm); void save (ESMWriter &esm) const; }; From d5dd864404c783464cb089ba9eeca8d1b48eb6ea Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 14:33:45 +0200 Subject: [PATCH 156/157] Fixes #1236: Disable all controls while playing movie --- apps/openmw/mwinput/inputmanagerimp.cpp | 15 +++++++++------ apps/openmw/mwinput/inputmanagerimp.hpp | 4 +++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 42362fde7..8095b0d05 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -114,6 +114,7 @@ namespace MWInput , mTimeIdle(0.f) , mOverencumberedMessageDelay(0.f) , mAlwaysRunActive(false) + , mControlsDisabled(false) { Ogre::RenderWindow* window = ogre.getWindow (); @@ -265,19 +266,20 @@ namespace MWInput void InputManager::update(float dt, bool disableControls, bool disableEvents) { + mControlsDisabled = disableControls; + mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); 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 (!disableControls) - mInputBinder->update(dt); - - if (disableControls) + if (mControlsDisabled) return; + // update values of channels (as a result of pressed keys) + mInputBinder->update(dt); + bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console; @@ -509,7 +511,8 @@ namespace MWInput } } - mInputBinder->keyPressed (arg); + if (!mControlsDisabled) + mInputBinder->keyPressed (arg); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5eb355566..3787a9c07 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 disableControls, bool disableEvents=false); + virtual void update(float dt, bool disableControls=false, bool disableEvents=false); void setPlayer (MWWorld::Player* player) { mPlayer = player; } @@ -145,6 +145,8 @@ namespace MWInput bool mInvertY; + bool mControlsDisabled; + float mCameraSensitivity; float mUISensitivity; float mCameraYMultiplier; From 04964595efb1d1ab55a68f49c43172aae54de16b Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 26 Apr 2014 16:44:20 +0200 Subject: [PATCH 157/157] Fixes #1270, Fixes #1201: Update active weapon/spell icons every frame --- apps/openmw/mwgui/quickkeysmenu.cpp | 1 - apps/openmw/mwgui/spellwindow.cpp | 1 - apps/openmw/mwmechanics/character.cpp | 4 ---- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 13 +++++++++++++ apps/openmw/mwmechanics/spellcasting.cpp | 1 - apps/openmw/mwstate/statemanagerimp.cpp | 7 ------- apps/openmw/mwworld/inventorystore.cpp | 15 --------------- apps/openmw/mwworld/worldimp.cpp | 1 - 8 files changed, 13 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 4c0faeac1..dc7226909 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -325,7 +325,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); } } diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index b052739bd..a00c2480e 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -322,7 +322,6 @@ namespace MWGui } store.setSelectedEnchantItem(it); - MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); updateSpells(); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 93c789af1..4cabaee09 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -740,10 +740,6 @@ bool CharacterController::updateWeaponState() MWBase::Environment::get().getWindowManager()->messageBox(resultMessage); if(!resultSound.empty()) MWBase::Environment::get().getSoundManager()->playSound(resultSound, 1.0f, 1.0f); - - // Set again, just to update the charge bar - if(item.getRefData().getCount()) - MWBase::Environment::get().getWindowManager()->setSelectedWeapon(item); } else if (ammunition) { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2b1fdd365..ff9bacf7b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -325,6 +325,19 @@ namespace MWMechanics winMgr->updateSkillArea(); winMgr->setValue("level", stats.getLevel()); + + // Update the equipped weapon icon + MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if (weapon == inv.end()) + winMgr->unsetSelectedWeapon(); + else + winMgr->setSelectedWeapon(*weapon); + + // Update the selected spell icon + MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); + if (enchantItem != inv.end()) + winMgr->setSelectedEnchantItem(*enchantItem); } if (mUpdatePlayer) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 9a6cd6b89..21aee5b98 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -575,7 +575,6 @@ namespace MWMechanics { if (mCaster.getRefData().getHandle() == "player") { - MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3); } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 0b3f3438d..acf1cf3f2 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -335,13 +335,6 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl 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); - 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(); MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition()); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index e00276293..9610171b2 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -8,7 +8,6 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwmechanics/npcstats.hpp" @@ -150,10 +149,6 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite fireEquipmentChangedEvent(); updateMagicEffects(actor); - - // Update HUD icon for player weapon - if (slot == MWWorld::InventoryStore::Slot_CarriedRight) - MWBase::Environment::get().getWindowManager()->setSelectedWeapon(*getSlot(slot)); } void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) @@ -494,7 +489,6 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor && *mSelectedEnchantItem == item && actor.getRefData().getHandle() == "player") { mSelectedEnchantItem = end(); - MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); } updateRechargingItems(); @@ -532,18 +526,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c if (script != "") (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - // Update HUD icon when removing player weapon or selected enchanted item. - // We have to check for both as the weapon could also be the enchanted item. - if (slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - // weapon - MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); - } if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) { - // enchanted item mSelectedEnchantItem = end(); - MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 87bdd2f9d..46bb4db58 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2056,7 +2056,6 @@ namespace MWWorld { // Update the GUI only when called on the player MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); - windowManager->unsetSelectedWeapon(); if (werewolf) {