forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
72a81a2381
16 changed files with 300 additions and 60 deletions
|
@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||||
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
|
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 security spellsuccess spellcasting
|
||||||
disease
|
disease pickpocket
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwbase
|
add_openmw_dir (mwbase
|
||||||
|
|
|
@ -88,6 +88,9 @@ namespace MWBase
|
||||||
virtual int countDeaths (const std::string& id) const = 0;
|
virtual int countDeaths (const std::string& id) const = 0;
|
||||||
///< Return the number of deaths for actors with the given ID.
|
///< Return the number of deaths for actors with the given ID.
|
||||||
|
|
||||||
|
/// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check!
|
||||||
|
virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0;
|
||||||
|
|
||||||
enum PersuasionType
|
enum PersuasionType
|
||||||
{
|
{
|
||||||
PT_Admire,
|
PT_Admire,
|
||||||
|
|
|
@ -613,7 +613,7 @@ namespace MWClass
|
||||||
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
||||||
// something, alert the character controller, scripts, etc.
|
// something, alert the character controller, scripts, etc.
|
||||||
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(ptr, "thief");
|
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
|
||||||
|
|
||||||
if(object.isEmpty())
|
if(object.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/pickpocket.hpp"
|
||||||
|
|
||||||
#include "countdialog.hpp"
|
#include "countdialog.hpp"
|
||||||
#include "tradewindow.hpp"
|
#include "tradewindow.hpp"
|
||||||
#include "inventorywindow.hpp"
|
#include "inventorywindow.hpp"
|
||||||
|
@ -123,6 +126,7 @@ namespace MWGui
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
, mModel(NULL)
|
, mModel(NULL)
|
||||||
, mSortModel(NULL)
|
, mSortModel(NULL)
|
||||||
|
, mPickpocketDetected(false)
|
||||||
{
|
{
|
||||||
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
||||||
getWidget(mTakeButton, "TakeButton");
|
getWidget(mTakeButton, "TakeButton");
|
||||||
|
@ -171,6 +175,9 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
||||||
{
|
{
|
||||||
|
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
|
||||||
|
return;
|
||||||
|
|
||||||
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +215,7 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::open(const MWWorld::Ptr& container, bool loot)
|
void ContainerWindow::open(const MWWorld::Ptr& container, bool loot)
|
||||||
{
|
{
|
||||||
|
mPickpocketDetected = false;
|
||||||
mPtr = container;
|
mPtr = container;
|
||||||
|
|
||||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot)
|
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot)
|
||||||
|
@ -230,6 +238,28 @@ namespace MWGui
|
||||||
setTitle(MWWorld::Class::get(container).getName(container));
|
setTitle(MWWorld::Class::get(container).getName(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContainerWindow::close()
|
||||||
|
{
|
||||||
|
WindowBase::close();
|
||||||
|
|
||||||
|
// Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened)
|
||||||
|
if (!MWBase::Environment::get().getWindowManager()->containsMode(GM_Container)
|
||||||
|
&& !mPickpocketDetected // If it was already detected while taking an item, no need to check now
|
||||||
|
)
|
||||||
|
{
|
||||||
|
MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(),
|
||||||
|
mPtr);
|
||||||
|
if (pickpocket.finish())
|
||||||
|
{
|
||||||
|
// TODO: crime
|
||||||
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
||||||
|
mPickpocketDetected = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
||||||
{
|
{
|
||||||
if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop)
|
if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop)
|
||||||
|
@ -255,8 +285,13 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount);
|
const ItemStack& item = mModel->getItem(i);
|
||||||
mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount);
|
|
||||||
|
if (!onTakeItem(item, item.mCount))
|
||||||
|
break;
|
||||||
|
|
||||||
|
playerModel->copyItem(item, item.mCount);
|
||||||
|
mModel->removeItem(item, item.mCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
|
@ -283,4 +318,22 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContainerWindow::onTakeItem(const ItemStack &item, int count)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<PickpocketItemModel*>(mModel))
|
||||||
|
{
|
||||||
|
MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(),
|
||||||
|
mPtr);
|
||||||
|
if (pickpocket.pick(item.mBase, count))
|
||||||
|
{
|
||||||
|
// TODO: crime
|
||||||
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
|
||||||
|
mPickpocketDetected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,13 @@ namespace MWGui
|
||||||
ContainerWindow(DragAndDrop* dragAndDrop);
|
ContainerWindow(DragAndDrop* dragAndDrop);
|
||||||
|
|
||||||
void open(const MWWorld::Ptr& container, bool loot=false);
|
void open(const MWWorld::Ptr& container, bool loot=false);
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DragAndDrop* mDragAndDrop;
|
DragAndDrop* mDragAndDrop;
|
||||||
|
|
||||||
|
bool mPickpocketDetected;
|
||||||
|
|
||||||
MWGui::ItemView* mItemView;
|
MWGui::ItemView* mItemView;
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
ItemModel* mModel;
|
ItemModel* mModel;
|
||||||
|
@ -73,6 +76,9 @@ namespace MWGui
|
||||||
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
|
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
|
||||||
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
|
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
/// @return is taking the item allowed?
|
||||||
|
bool onTakeItem(const ItemStack& item, int count);
|
||||||
|
|
||||||
virtual void onReferenceUnavailable();
|
virtual void onReferenceUnavailable();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,50 +158,49 @@ namespace MWMechanics
|
||||||
calculateDynamicStats (ptr);
|
calculateDynamicStats (ptr);
|
||||||
calculateCreatureStatModifiers (ptr, duration);
|
calculateCreatureStatModifiers (ptr, duration);
|
||||||
|
|
||||||
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
// AI
|
||||||
|
if(MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||||
{
|
{
|
||||||
// AI
|
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
|
||||||
if(MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
//engage combat or not?
|
||||||
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||||
|
if(ptr != player && !creatureStats.isHostile())
|
||||||
{
|
{
|
||||||
CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
|
ESM::Position playerpos = player.getRefData().getPosition();
|
||||||
//engage combat or not?
|
ESM::Position actorpos = ptr.getRefData().getPosition();
|
||||||
if(ptr != MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && !creatureStats.isHostile())
|
float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0])
|
||||||
|
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
|
||||||
|
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
|
||||||
|
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
|
||||||
|
float disp = 100; //creatures don't have disposition, so set it to 100 by default
|
||||||
|
if(ptr.getTypeName() == typeid(ESM::NPC).name())
|
||||||
{
|
{
|
||||||
ESM::Position playerpos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition();
|
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
|
||||||
ESM::Position actorpos = ptr.getRefData().getPosition();
|
}
|
||||||
float d = sqrt((actorpos.pos[0] - playerpos.pos[0])*(actorpos.pos[0] - playerpos.pos[0])
|
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player)
|
||||||
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr);
|
||||||
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
|
if( ( (fight == 100 )
|
||||||
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
|
|| (fight >= 95 && d <= 3000)
|
||||||
float disp = 100; //creatures don't have disposition, so set it to 100 by default
|
|| (fight >= 90 && d <= 2000)
|
||||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())
|
|| (fight >= 80 && d <= 1000)
|
||||||
{
|
|| (fight >= 80 && disp <= 40)
|
||||||
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
|
|| (fight >= 70 && disp <= 35 && d <= 1000)
|
||||||
}
|
|| (fight >= 60 && disp <= 30 && d <= 1000)
|
||||||
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
|| (fight >= 50 && disp == 0)
|
||||||
if( ( (fight == 100 )
|
|| (fight >= 40 && disp <= 10 && d <= 500) )
|
||||||
|| (fight >= 95 && d <= 3000)
|
&& LOS
|
||||||
|| (fight >= 90 && d <= 2000)
|
)
|
||||||
|| (fight >= 80 && d <= 1000)
|
{
|
||||||
|| (fight >= 80 && disp <= 40)
|
creatureStats.getAiSequence().stack(AiCombat("player"));
|
||||||
|| (fight >= 70 && disp <= 35 && d <= 1000)
|
creatureStats.setHostile(true);
|
||||||
|| (fight >= 60 && disp <= 30 && d <= 1000)
|
|
||||||
|| (fight >= 50 && disp == 0)
|
|
||||||
|| (fight >= 40 && disp <= 10 && d <= 500) )
|
|
||||||
&& LOS
|
|
||||||
)
|
|
||||||
{
|
|
||||||
creatureStats.getAiSequence().stack(AiCombat("player"));
|
|
||||||
creatureStats.setHostile(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
creatureStats.getAiSequence().execute (ptr,duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fatigue restoration
|
creatureStats.getAiSequence().execute (ptr,duration);
|
||||||
calculateRestoration(ptr, duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fatigue restoration
|
||||||
|
calculateRestoration(ptr, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
|
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
|
#include <OgreSceneNode.h>
|
||||||
|
|
||||||
#include "spellcasting.hpp"
|
#include "spellcasting.hpp"
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -725,4 +727,74 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return mAI;
|
return mAI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer)
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
|
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
|
||||||
|
float invisibility = stats.getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude;
|
||||||
|
if (invisibility > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float sneakTerm = 0;
|
||||||
|
if (ptr.getClass().getStance(ptr, MWWorld::Class::Sneak)
|
||||||
|
&& !MWBase::Environment::get().getWorld()->isSwimming(ptr)
|
||||||
|
&& MWBase::Environment::get().getWorld()->isOnGround(ptr))
|
||||||
|
{
|
||||||
|
static float fSneakSkillMult = store.find("fSneakSkillMult")->getFloat();
|
||||||
|
static float fSneakBootMult = store.find("fSneakBootMult")->getFloat();
|
||||||
|
float sneak = 0;
|
||||||
|
if (ptr.getClass().isNpc())
|
||||||
|
sneak = ptr.getClass().getNpcStats(ptr).getSkill(ESM::Skill::Sneak).getModified();
|
||||||
|
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||||
|
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||||
|
float bootWeight = 0;
|
||||||
|
if (ptr.getClass().isNpc())
|
||||||
|
{
|
||||||
|
MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
|
||||||
|
MWWorld::ContainerStoreIterator it = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
|
||||||
|
if (it != inv.end())
|
||||||
|
bootWeight = it->getClass().getWeight(*it);
|
||||||
|
}
|
||||||
|
sneakTerm = fSneakSkillMult * sneak + 0.2 * agility + 0.1 * luck + bootWeight * fSneakBootMult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float fSneakDistBase = store.find("fSneakDistanceBase")->getFloat();
|
||||||
|
static float fSneakDistMult = store.find("fSneakDistanceMultiplier")->getFloat();
|
||||||
|
|
||||||
|
Ogre::Vector3 pos1 (ptr.getRefData().getPosition().pos);
|
||||||
|
Ogre::Vector3 pos2 (observer.getRefData().getPosition().pos);
|
||||||
|
float distTerm = fSneakDistBase + fSneakDistMult * pos1.distance(pos2);
|
||||||
|
|
||||||
|
float chameleon = stats.getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude;
|
||||||
|
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
|
||||||
|
|
||||||
|
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
|
||||||
|
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||||
|
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||||
|
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).mMagnitude;
|
||||||
|
int obsSneak = 0;
|
||||||
|
if (observer.getClass().isNpc())
|
||||||
|
obsSneak = observer.getClass().getNpcStats(observer).getSkill(ESM::Skill::Sneak).getModified();
|
||||||
|
|
||||||
|
float obsTerm = obsSneak + 0.2 * obsAgility + 0.1 * obsLuck - obsBlind;
|
||||||
|
|
||||||
|
// is ptr behind the observer?
|
||||||
|
static float fSneakNoViewMult = store.find("fSneakNoViewMult")->getFloat();
|
||||||
|
static float fSneakViewMult = store.find("fSneakViewMult")->getFloat();
|
||||||
|
float y = 0;
|
||||||
|
Ogre::Vector3 vec = pos1 - pos2;
|
||||||
|
Ogre::Radian angle = observer.getRefData().getBaseNode()->getOrientation().yAxis().angleBetween(vec);
|
||||||
|
if (angle < Ogre::Degree(90))
|
||||||
|
y = obsTerm * observerStats.getFatigueTerm() * fSneakNoViewMult;
|
||||||
|
else
|
||||||
|
y = obsTerm * observerStats.getFatigueTerm() * fSneakViewMult;
|
||||||
|
|
||||||
|
float target = x - y;
|
||||||
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
|
|
||||||
|
return (roll >= target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,9 @@ namespace MWMechanics
|
||||||
void toLower(std::string npcFaction);
|
void toLower(std::string npcFaction);
|
||||||
///< Perform a persuasion action on NPC
|
///< Perform a persuasion action on NPC
|
||||||
|
|
||||||
|
/// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check!
|
||||||
|
virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer);
|
||||||
|
|
||||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
|
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||||
|
|
||||||
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
||||||
|
|
67
apps/openmw/mwmechanics/pickpocket.cpp
Normal file
67
apps/openmw/mwmechanics/pickpocket.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "pickpocket.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "npcstats.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim)
|
||||||
|
: mThief(thief)
|
||||||
|
, mVictim(victim)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)
|
||||||
|
{
|
||||||
|
NpcStats& stats = ptr.getClass().getNpcStats(ptr);
|
||||||
|
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||||
|
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||||
|
float sneak = stats.getSkill(ESM::Skill::Sneak).getModified();
|
||||||
|
return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pickpocket::getDetected(float valueTerm)
|
||||||
|
{
|
||||||
|
float x = getChanceModifier(mThief);
|
||||||
|
float y = getChanceModifier(mVictim, valueTerm);
|
||||||
|
|
||||||
|
float t = 2*x - y;
|
||||||
|
|
||||||
|
NpcStats& pcStats = mThief.getClass().getNpcStats(mThief);
|
||||||
|
float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified();
|
||||||
|
int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
|
.find("iPickMinChance")->getInt();
|
||||||
|
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
|
.find("iPickMaxChance")->getInt();
|
||||||
|
|
||||||
|
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||||
|
if (t < pcSneak / iPickMinChance)
|
||||||
|
{
|
||||||
|
return (roll > int(pcSneak / iPickMinChance));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t = std::min(float(iPickMaxChance), t);
|
||||||
|
return (roll > int(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pickpocket::pick(MWWorld::Ptr item, int count)
|
||||||
|
{
|
||||||
|
float stackValue = item.getClass().getValue(item) * count;
|
||||||
|
float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
|
.find("fPickPocketMod")->getFloat();
|
||||||
|
float valueTerm = 10 * fPickPocketMod * stackValue;
|
||||||
|
|
||||||
|
return getDetected(valueTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pickpocket::finish()
|
||||||
|
{
|
||||||
|
return getDetected(0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
apps/openmw/mwmechanics/pickpocket.hpp
Normal file
30
apps/openmw/mwmechanics/pickpocket.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef OPENMW_MECHANICS_PICKPOCKET_H
|
||||||
|
#define OPENMW_MECHANICS_PICKPOCKET_H
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
class Pickpocket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim);
|
||||||
|
|
||||||
|
/// Steal some items
|
||||||
|
/// @return Was the thief detected?
|
||||||
|
bool pick (MWWorld::Ptr item, int count);
|
||||||
|
/// End the pickpocketing process
|
||||||
|
/// @return Was the thief detected?
|
||||||
|
bool finish ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool getDetected(float valueTerm);
|
||||||
|
float getChanceModifier(const MWWorld::Ptr& ptr, float add=0);
|
||||||
|
MWWorld::Ptr mThief;
|
||||||
|
MWWorld::Ptr mVictim;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -231,6 +231,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
float mProgress;
|
float mProgress;
|
||||||
public:
|
public:
|
||||||
|
SkillValue() : mProgress(0) {}
|
||||||
float getProgress() const { return mProgress; }
|
float getProgress() const { return mProgress; }
|
||||||
void setProgress(float progress) { mProgress = progress; }
|
void setProgress(float progress) { mProgress = progress; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -358,19 +358,21 @@ namespace MWScript
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class R>
|
template<class R>
|
||||||
class OpGetDetected : public Interpreter::Opcode1
|
class OpGetDetected : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
{
|
{
|
||||||
|
MWWorld::Ptr observer = R()(runtime);
|
||||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
Interpreter::Type_Integer value = false; // TODO replace with implementation
|
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||||
|
|
||||||
std::cout << "AiGetDetected: " << actorID << ", " << value << std::endl;
|
Interpreter::Type_Integer value =
|
||||||
|
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
|
||||||
|
|
||||||
runtime.push (value);
|
runtime.push (value);
|
||||||
}
|
}
|
||||||
|
@ -432,8 +434,8 @@ namespace MWScript
|
||||||
new OpGetAiPackageDone<ExplicitRef>);
|
new OpGetAiPackageDone<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage<ExplicitRef>);
|
||||||
interpreter.installSegment3 (Compiler::Ai::opcodeGetDetected, new OpGetDetected<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetDetected, new OpGetDetected<ImplicitRef>);
|
||||||
interpreter.installSegment3 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetDetectedExplicit, new OpGetDetected<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSight, new OpGetLineOfSight<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeGetLineOfSightExplicit, new OpGetLineOfSight<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Ai::opcodeToggleAI, new OpToggleAI<ImplicitRef>);
|
||||||
|
|
|
@ -79,13 +79,13 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr,
|
||||||
{
|
{
|
||||||
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner);
|
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner);
|
||||||
|
|
||||||
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
|
// Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves
|
||||||
if ((actorPtr.getRefData().getHandle() != "player")
|
if ((actorPtr.getRefData().getHandle() != "player")
|
||||||
&& !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf())
|
&& !(MWWorld::Class::get(actorPtr).getNpcStats(actorPtr).isWerewolf())
|
||||||
&& !actorPtr.getClass().getCreatureStats(actorPtr).isDead())
|
&& !actorPtr.getClass().getCreatureStats(actorPtr).isDead())
|
||||||
{
|
{
|
||||||
std::string type = itemPtr.getTypeName();
|
std::string type = itemPtr.getTypeName();
|
||||||
if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name()))
|
if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name()) || (type == typeid(ESM::Weapon).name()))
|
||||||
autoEquip(actorPtr);
|
autoEquip(actorPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||||
|
|
||||||
// Only autoEquip if we are the original owner of the item.
|
// Only autoEquip if we are the original owner of the item.
|
||||||
// This stops merchants from auto equipping anything you sell to them.
|
// This stops merchants from auto equipping anything you sell to them.
|
||||||
if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID))
|
// ...unless this is a companion, he should always equip items given to him.
|
||||||
|
if (!Misc::StringUtils::ciEqual(test.getCellRef().mOwner, actor.getCellRef().mRefID) &&
|
||||||
|
(actor.getClass().getScript(actor).empty() ||
|
||||||
|
!actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test);
|
int testSkill = MWWorld::Class::get (test).getEquipmentSkill (test);
|
||||||
|
|
|
@ -1840,13 +1840,6 @@ namespace MWWorld
|
||||||
|
|
||||||
bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc)
|
bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc)
|
||||||
{
|
{
|
||||||
// This is a placeholder! Needs to go into an NPC awareness check function (see
|
|
||||||
// https://wiki.openmw.org/index.php?title=Research:NPC_AI_Behaviour#NPC_Awareness_Check )
|
|
||||||
if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Invisibility).mMagnitude)
|
|
||||||
return false;
|
|
||||||
if (targetNpc.getClass().getCreatureStats(targetNpc).getMagicEffects().get(ESM::MagicEffect::Chameleon).mMagnitude > 100)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
|
Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(npc.getRefData().getHandle())->getHalfExtents();
|
||||||
float* pos1 = npc.getRefData().getPosition().pos;
|
float* pos1 = npc.getRefData().getPosition().pos;
|
||||||
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();
|
Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetNpc.getRefData().getHandle())->getHalfExtents();
|
||||||
|
|
|
@ -1209,20 +1209,27 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo
|
||||||
|
|
||||||
if(isskinned)
|
if(isskinned)
|
||||||
{
|
{
|
||||||
|
// Apparently both are allowed. Sigh.
|
||||||
|
// This could also mean that filters are supposed to work on the actual node
|
||||||
|
// hierarchy, rather than just trishapes, and the 'tri ' should be omitted?
|
||||||
std::string filter = "@shape=tri "+bonename;
|
std::string filter = "@shape=tri "+bonename;
|
||||||
|
std::string filter2 = "@shape="+bonename;
|
||||||
Misc::StringUtils::toLower(filter);
|
Misc::StringUtils::toLower(filter);
|
||||||
|
Misc::StringUtils::toLower(filter2);
|
||||||
for(size_t i = 0;i < scene->mEntities.size();i++)
|
for(size_t i = 0;i < scene->mEntities.size();i++)
|
||||||
{
|
{
|
||||||
Ogre::Entity *entity = scene->mEntities[i];
|
Ogre::Entity *entity = scene->mEntities[i];
|
||||||
if(entity->hasSkeleton())
|
if(entity->hasSkeleton())
|
||||||
{
|
{
|
||||||
if(entity == scene->mSkelBase ||
|
if(entity == scene->mSkelBase ||
|
||||||
entity->getMesh()->getName().find(filter) != std::string::npos)
|
entity->getMesh()->getName().find(filter) != std::string::npos
|
||||||
|
|| entity->getMesh()->getName().find(filter2) != std::string::npos)
|
||||||
parentNode->attachObject(entity);
|
parentNode->attachObject(entity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(entity->getMesh()->getName().find(filter) == std::string::npos)
|
if(entity->getMesh()->getName().find(filter) == std::string::npos
|
||||||
|
|| entity->getMesh()->getName().find(filter2) == std::string::npos)
|
||||||
entity->detachFromParent();
|
entity->detachFromParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<MyGUI type="Layout">
|
<MyGUI type="Layout">
|
||||||
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 600 300" name="_Main">
|
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 600 300" name="_Main">
|
||||||
<Property key="MinSize" value="245 145"/>
|
<Property key="MinSize" value="245 145"/>
|
||||||
|
<Property key="Visible" value="false"/>
|
||||||
|
|
||||||
<!-- Items -->
|
<!-- Items -->
|
||||||
<Widget type="ItemView" skin="MW_ItemView" position="5 5 575 225" name="ItemView" align="Left Top Stretch">
|
<Widget type="ItemView" skin="MW_ItemView" position="5 5 575 225" name="ItemView" align="Left Top Stretch">
|
||||||
|
|
Loading…
Reference in a new issue