diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 306ef41738..660e451156 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 - aiescort aiactivate repair enchanting pathfinding + aiescort aiactivate repair enchanting pathfinding security ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 60cb186e2c..85c2cf5fb1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -233,6 +233,7 @@ namespace MWGui { // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); + mModel->update(); for (size_t i=0; igetItemCount(); ++i) { if (i==0) @@ -257,7 +258,6 @@ namespace MWGui { onTakeAllButtonClicked(mTakeButton); - /// \todo if corpse is non-disposable: messagebox #{sDisposeCorpseFail} if (MWWorld::Class::get(mPtr).isPersistent(mPtr)) MWBase::Environment::get().getWindowManager()->messageBox("#{sDisposeCorpseFail}"); else diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b31d51e6e5..00c520de9e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -241,6 +241,10 @@ namespace MWInput case A_ToggleHUD: mWindows.toggleHud(); break; + case A_Use: + if (!MWBase::Environment::get().getWindowManager()->isGuiMode()) + mPlayer.use(); + break; } } } @@ -803,6 +807,7 @@ namespace MWInput { std::map descriptions; + descriptions[A_Use] = "sUse"; descriptions[A_Activate] = "sActivate"; descriptions[A_MoveBackward] = "sBack"; descriptions[A_MoveForward] = "sForward"; @@ -865,6 +870,7 @@ namespace MWInput ret.push_back(A_AlwaysRun); ret.push_back(A_Sneak); ret.push_back(A_Activate); + ret.push_back(A_Use); ret.push_back(A_ToggleWeapon); ret.push_back(A_ToggleSpell); ret.push_back(A_AutoMove); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp new file mode 100644 index 0000000000..818412b8ef --- /dev/null +++ b/apps/openmw/mwmechanics/security.cpp @@ -0,0 +1,118 @@ +#include "security.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" + +#include "npcstats.hpp" +#include "creaturestats.hpp" + +namespace MWMechanics +{ + + void Security::pickLock(const MWWorld::Ptr &actor, const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick) + { + if (lock.getCellRef().mLockLevel <= 0) + return; + + int lockStrength = lock.getCellRef().mLockLevel; + + float pickQuality = lockpick.get()->mBase->mData.mQuality; + + CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats(actor); + float pcAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + float pcLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); + float securitySkill = npcStats.getSkill(ESM::Skill::Security).getModified(); + float fatigueTerm = creatureStats.getFatigueTerm(); + + float fPickLockMult = MWBase::Environment::get().getWorld()->getStore().get().find("fPickLockMult")->getFloat(); + + float x = 0.2 * pcAgility + 0.1 * pcLuck + securitySkill; + x *= pickQuality * fatigueTerm; + x += fPickLockMult * lockStrength; + + bool isPlayer = actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + if (x <= 0) + { + if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLockImpossible}"); + } + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + MWWorld::Class::get(lock).unlock(lock); + if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLockSuccess}"); + MWWorld::Class::get(actor).skillUsageSucceeded(actor, ESM::Skill::Security, 1); + } + else if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sLockFail}"); + } + + if (lockpick.getCellRef().mCharge == -1) + lockpick.getCellRef().mCharge = lockpick.get()->mBase->mData.mUses; + --lockpick.getCellRef().mCharge; + if (!lockpick.getCellRef().mCharge) + lockpick.getRefData().setCount(0); + } + + void Security::probeTrap(const MWWorld::Ptr &actor, const MWWorld::Ptr &trap, const MWWorld::Ptr &probe) + { + if (trap.getCellRef().mTrap == "") + return; + + CreatureStats& creatureStats = MWWorld::Class::get(actor).getCreatureStats(actor); + NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats(actor); + + float probeQuality = probe.get()->mBase->mData.mQuality; + + const ESM::Spell* trapSpell = MWBase::Environment::get().getWorld()->getStore().get().find(trap.getCellRef().mTrap); + float trapSpellPoints = trapSpell->mData.mCost; + + float pcAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + float pcLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); + float securitySkill = npcStats.getSkill(ESM::Skill::Security).getModified(); + float fatigueTerm = creatureStats.getFatigueTerm(); + + float fTrapCostMult = MWBase::Environment::get().getWorld()->getStore().get().find("fTrapCostMult")->getFloat(); + + float x = 0.2 * pcAgility + 0.1 * pcLuck + securitySkill; + x += fTrapCostMult * trapSpellPoints; + x *= probeQuality * fatigueTerm; + + bool isPlayer = actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + if (x <= 0) + { + if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTrapImpossible}"); + } + else + { + int roll = static_cast (std::rand()) / RAND_MAX * 100; + if (roll <= x) + { + trap.getCellRef().mTrap = ""; + if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTrapSuccess}"); + MWWorld::Class::get(actor).skillUsageSucceeded(actor, ESM::Skill::Security, 0); + } + else if (isPlayer) + MWBase::Environment::get().getWindowManager()->messageBox("#{sTrapFail}"); + } + + if (probe.getCellRef().mCharge == -1) + probe.getCellRef().mCharge = probe.get()->mBase->mData.mUses; + --probe.getCellRef().mCharge; + if (!probe.getCellRef().mCharge) + probe.getRefData().setCount(0); + } + +} diff --git a/apps/openmw/mwmechanics/security.hpp b/apps/openmw/mwmechanics/security.hpp new file mode 100644 index 0000000000..4d655f6e0b --- /dev/null +++ b/apps/openmw/mwmechanics/security.hpp @@ -0,0 +1,19 @@ +#ifndef MWMECHANICS_SECURITY_H +#define MWMECHANICS_SECURITY_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + /// @brief implementation of Security skill + class Security + { + public: + static void pickLock (const MWWorld::Ptr& actor, const MWWorld::Ptr& lock, const MWWorld::Ptr& lockpick); + static void probeTrap (const MWWorld::Ptr& actor, const MWWorld::Ptr& trap, const MWWorld::Ptr& probe); + }; + +} + +#endif diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7dc6cfc132..853ffc3752 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -728,4 +728,12 @@ void Animation::showWeapons(bool showWeapon) { } +bool Animation::isPriorityActive(int priority) const +{ + for (AnimStateMap::const_iterator it = mStates.begin(); it != mStates.end(); ++it) + if (it->second.mPriority == priority) + return true; + return false; +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 443b5b7b4c..31be0fb2a7 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -148,6 +148,9 @@ public: bool hasAnimation(const std::string &anim); + bool isPriorityActive (int priority) const; + ///< Is there an animation playing with the given priority? + // Specifies the axis' to accumulate on. Non-accumulated axis will just // move visually, but not affect the actual movement. Each x/y/z value // should be on the scale of 0 to 1. diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c931c8e00a..e4bba289f2 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -193,6 +193,7 @@ namespace MWRender mNode->setOrientation (Ogre::Quaternion::IDENTITY); mRenderTarget->update(); + mSelectionBuffer->update(); } @@ -203,8 +204,8 @@ namespace MWRender void InventoryPreview::onSetup () { - if (!mSelectionBuffer) - mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); + delete mSelectionBuffer; + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0); mAnimation->showWeapons(true); diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index d8f43f71b7..562cb3784a 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -73,8 +73,6 @@ namespace MWRender int getSlotSelected(int posX, int posY); - void setNpcAnimation (NpcAnimation* anim); - private: OEngine::Render::SelectionBuffer* mSelectionBuffer; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f0df1ba4b2..b5f2ea031a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -128,8 +128,6 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) assert(viewMode != VM_HeadOnly); mViewMode = viewMode; - Ogre::SceneNode *node = mInsert->getParentSceneNode(); - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 08d908e626..ca604f55db 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -2,11 +2,18 @@ #include "player.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/ptr.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/character.hpp" +#include "../mwmechanics/security.hpp" + +#include "../mwrender/animation.hpp" #include "class.hpp" @@ -133,6 +140,42 @@ namespace MWWorld MWWorld::Class::get(ptr).getMovementSettings(ptr).mRotation[1] = roll; } + void Player::use() + { + MWWorld::InventoryStore& store = MWWorld::Class::get(getPlayer()).getInventoryStore(getPlayer()); + MWWorld::ContainerStoreIterator equipped = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if (getDrawState() == MWMechanics::DrawState_Weapon) + { + if (equipped != store.end()) + { + MWWorld::Ptr item = *equipped; + MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(getPlayer()); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject(); + + if (anim->isPriorityActive(MWMechanics::Priority_Weapon)) + return; + + if (item.getTypeName() == typeid(ESM::Lockpick).name()) + { + if (!target.isEmpty()) + MWMechanics::Security::pickLock(getPlayer(), target, item); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + else if (item.getTypeName() == typeid(ESM::Probe).name()) + { + if (!target.isEmpty()) + MWMechanics::Security::probeTrap(getPlayer(), target, item); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + } + + // tool used up? + if (!item.getRefData().getCount()) + MWBase::Environment::get().getWindowManager()->setSelectedWeapon(MWWorld::Ptr()); + } + } + } + MWMechanics::DrawState_ Player::getDrawState() { MWWorld::Ptr ptr = getPlayer(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index f51d8f8905..fd25f749fb 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -58,6 +58,9 @@ namespace MWWorld void setForwardBackward (int value); void setUpDown(int value); + void use (); + ///< Use item equipped on right hand, or fists + void setRunState(bool run); void setSneak(bool sneak);