diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index ccffc6b21..e9854b246 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -40,6 +40,9 @@ namespace MWBase virtual void keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; virtual void questionAnswered (const std::string& answer) = 0; + + virtual void persuade (int type) = 0; + virtual int getTemporaryDispositionChange () const = 0; }; } diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 0be111ea7..cdee048db 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -95,8 +95,9 @@ namespace MWBase PT_Bribe100, PT_Bribe1000 }; - virtual float getPersuasionDispositionChange (MWWorld::Ptr npc, PersuasionType type, bool& success) const = 0; - ///< Get amount to adjust temporary disposition for a given persuasion action on an NPC + virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, + float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; + ///< Perform a persuasion action on NPC }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c7f33504e..790d824f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -96,6 +96,7 @@ namespace MWClass data->mCreatureStats.setLevel(ref->mBase->mNpdt52.mLevel); data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt52.mDisposition); + data->mNpcStats.setReputation(ref->mBase->mNpdt52.mReputation); } else { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 46fab0a4b..3eabe7383 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -12,6 +12,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/journal.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/refdata.hpp" @@ -584,6 +585,8 @@ namespace MWDialogue DialogueManager::DialogueManager (const Compiler::Extensions& extensions) : mCompilerContext (MWScript::CompilerContext::Type_Dialgoue), mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream) + , mTemporaryDispositionChange(0.f) + , mPermanentDispositionChange(0.f) { mChoice = -1; mIsInChoice = false; @@ -868,6 +871,12 @@ namespace MWDialogue void DialogueManager::goodbyeSelected() { MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); + + // Apply disposition change to NPC's base disposition + MWMechanics::NpcStats npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor); + npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange); + mPermanentDispositionChange = 0; + mTemporaryDispositionChange = 0; } void DialogueManager::questionAnswered (const std::string& answer) @@ -944,4 +953,52 @@ namespace MWDialogue win->goodbye(); } + + void DialogueManager::persuade(int type) + { + bool success; + float temp, perm; + MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange( + mActor, MWBase::MechanicsManager::PersuasionType(type), mTemporaryDispositionChange, + success, temp, perm); + mTemporaryDispositionChange += temp; + mPermanentDispositionChange += perm; + + // change temp disposition so that final disposition is between 0...100 + int curDisp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + if (curDisp + mTemporaryDispositionChange < 0) + mTemporaryDispositionChange = -curDisp; + else if (curDisp + mTemporaryDispositionChange > 100) + mTemporaryDispositionChange = 100 - curDisp; + + // practice skill + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); + + + // add status message to dialogue window + std::string text; + + if (type == MWBase::MechanicsManager::PT_Admire) + text = "sAdmire"; + else if (type == MWBase::MechanicsManager::PT_Taunt) + text = "sTaunt"; + else if (type == MWBase::MechanicsManager::PT_Intimidate) + text = "sIntimidate"; + else + text = "sBribe"; + + text += (success ? "Success" : "Fail"); + + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + win->addTitle(MyGUI::LanguageManager::getInstance().replaceTags("#{"+text+"}")); + + /// \todo text from INFO record, how to get the ID? + } + + int DialogueManager::getTemporaryDispositionChange() const + { + return mTemporaryDispositionChange; + } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index e3e9fd752..d0f48b65a 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -49,6 +49,9 @@ namespace MWDialogue ESM::DialInfo mLastDialogue; bool mIsInChoice; + float mTemporaryDispositionChange; + float mPermanentDispositionChange; + public: DialogueManager (const Compiler::Extensions& extensions); @@ -69,6 +72,8 @@ namespace MWDialogue virtual void goodbyeSelected(); virtual void questionAnswered (const std::string& answer); + virtual void persuade (int type); + virtual int getTemporaryDispositionChange () const; }; } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index be7bfde53..d475bc56b 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -14,6 +14,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwmechanics/npcstats.hpp" + #include "dialogue_history.hpp" #include "widgets.hpp" #include "list.hpp" @@ -98,7 +100,8 @@ void PersuasionDialog::onPersuade(MyGUI::Widget *sender) type = MWBase::MechanicsManager::PT_Bribe1000; } - eventPersuade(type, true, 0); + MWBase::Environment::get().getDialogueManager()->persuade(type); + setVisible(false); } @@ -128,7 +131,6 @@ DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) center(); mPersuasionDialog.setVisible(false); - mPersuasionDialog.eventPersuade += MyGUI::newDelegate(this, &DialogueWindow::onPersuade); //History view getWidget(mHistory, "History"); @@ -212,6 +214,7 @@ void DialogueWindow::onSelectTopic(std::string topic) } if (topic == gmst.find("sPersuasion")->getString()) { + mPersuasionDialog.setPtr(mPtr); mPersuasionDialog.setVisible(true); } else if (topic == gmst.find("sSpells")->getString()) @@ -391,8 +394,6 @@ void DialogueWindow::updateOptions() void DialogueWindow::goodbye() { - // Apply temporary disposition change to NPC's base disposition - mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get().find("sGoodbye")->getString()); mTopicsList->setEnabled(false); mEnabled = false; @@ -407,30 +408,12 @@ void DialogueWindow::onFrame() { if(mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) { + int disp = std::max(0, std::min(100, + MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) + + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange())); mDispositionBar->setProgressRange(100); - mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); + mDispositionBar->setProgressPosition(disp); mDispositionText->eraseText(0, mDispositionText->getTextLength()); - mDispositionText->addText("#B29154"+boost::lexical_cast(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); + mDispositionText->addText("#B29154"+boost::lexical_cast(disp)+std::string("/100")+"#B29154"); } } - -void DialogueWindow::onPersuade(int type, bool success, float dispositionChange) -{ - std::string text; - - if (type == MWBase::MechanicsManager::PT_Admire) - text = "sAdmire"; - else if (type == MWBase::MechanicsManager::PT_Taunt) - text = "sTaunt"; - else if (type == MWBase::MechanicsManager::PT_Intimidate) - text = "sIntimidate"; - else - text = "sBribe"; - - text += success ? "Fail" : "Success"; - - mHistory->addDialogHeading( MyGUI::LanguageManager::getInstance().replaceTags("#{"+text+"}")); - - /// \todo text from INFO record, how to get the ID? - //mHistory->addDialogText(); -} diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 3d0d74d95..134792226 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -33,9 +33,7 @@ namespace MWGui virtual void open(); - typedef MyGUI::delegates::CMultiDelegate3 EventHandle_Persuade; - - EventHandle_Persuade eventPersuade; + void setPtr(MWWorld::Ptr ptr) { mPtr = ptr; } private: MyGUI::Button* mCancelButton; @@ -49,6 +47,8 @@ namespace MWGui void onCancel (MyGUI::Widget* sender); void onPersuade (MyGUI::Widget* sender); + + MWWorld::Ptr mPtr; }; class DialogueWindow: public WindowBase, public ReferenceInterface @@ -113,11 +113,6 @@ namespace MWGui MyGUI::EditPtr mDispositionText; PersuasionDialog mPersuasionDialog; - - - float mTemporaryDispositionChange; - - void onPersuade (int type, bool success, float dispositionChange); }; } #endif diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 134a7fb06..797369e26 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -491,8 +491,150 @@ namespace MWMechanics } - float MechanicsManager::getPersuasionDispositionChange (MWWorld::Ptr npc, PersuasionType type, bool& success) const + void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, + float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) { - return 0.f; + const MWWorld::Store &gmst = + MWBase::Environment::get().getWorld()->getStore().get(); + + MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); + MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); + + MWMechanics::NpcStats npcSkill = MWWorld::Class::get(npc).getNpcStats(npc); + MWMechanics::CreatureStats npcStats = MWWorld::Class::get(npc).getCreatureStats(npc); + + + float persTerm = playerStats.getAttribute(ESM::Attribute::Personality).getModified() + / gmst.find("fPersonalityMod")->getFloat(); + + float luckTerm = playerStats.getAttribute(ESM::Attribute::Luck).getModified() + / gmst.find("fLuckMod")->getFloat(); + + float repTerm = playerSkill.getReputation() * gmst.find("fReputationMod")->getFloat(); + + float levelTerm = playerStats.getLevel() * gmst.find("fLevelMod")->getFloat(); + + float fatigueTerm = playerStats.getFatigueTerm(); + + float playerRating1 = (repTerm + luckTerm + persTerm + playerSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float playerRating2 = playerRating1 + levelTerm; + float playerRating3 = (playerSkill.getSkill(ESM::Skill::Mercantile).getModified() + luckTerm + persTerm) * fatigueTerm; + + float npcRating1 = (repTerm + luckTerm + persTerm + playerSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float npcRating2 = (levelTerm + repTerm + luckTerm + persTerm + npcSkill.getSkill(ESM::Skill::Speechcraft).getModified()) * fatigueTerm; + float npcRating3 = (playerSkill.getSkill(ESM::Skill::Mercantile).getModified() + repTerm + luckTerm + persTerm) * fatigueTerm; + + int currentDisposition = std::min(100, std::max(0, int(getDerivedDisposition(npc) + currentTemporaryDispositionDelta))); + + float d = 1 - 0.02 * abs(currentDisposition - 50); + float target1 = d * (playerRating1 - npcRating1 + 50); + float target2 = d * (playerRating2 - npcRating2 + 50); + + float bribeMod; + if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat(); + if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat(); + else bribeMod = gmst.find("fBribe1000Mod")->getFloat(); + + float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod; + + float iPerMinChance = gmst.find("iPerMinChance")->getInt(); + float iPerMinChange = gmst.find("iPerMinChange")->getInt(); + float fPerDieRollMult = gmst.find("fPerDieRollMult")->getFloat(); + float fPerTempMult = gmst.find("fPerTempMult")->getFloat(); + + float x,y; + + float roll = static_cast (std::rand()) / RAND_MAX * 100; + + if (type == PT_Admire) + { + target1 = std::max(iPerMinChance, target1); + success = (roll <= target1); + float c = int(fPerDieRollMult * (target1 - roll)); + x = success ? std::max(iPerMinChange, c) : c; + } + else if (type == PT_Intimidate) + { + target2 = std::max(iPerMinChance, target2); + + success = (roll <= target2); + + float r; + if (roll != target2) + r = int(target2 - roll); + else + r = 1; + + if (roll <= target2) + { + float s = int(r * fPerDieRollMult * fPerTempMult); + + npcStats.setFlee ( std::max(0, std::min(100, npcStats.getFlee() + int(std::max(iPerMinChange, s))))); + npcStats.setFight ( std::max(0, std::min(100, npcStats.getFight() + int(std::min(-iPerMinChange, -s))))); + } + + float c = -std::abs(int(r * fPerDieRollMult)); + if (success) + { + if (std::abs(c) < iPerMinChange) + { + x = 0; + y = -iPerMinChange; + } + else + { + x = -int(c * fPerTempMult); + y = c; + } + } + else + { + x = int(c * fPerTempMult); + y = c; + } + } + else if (type == PT_Taunt) + { + target1 = std::max(iPerMinChance, target1); + success = (roll <= target1); + + float c = std::abs(int(target1 - roll)); + + if (roll <= target1) + { + float s = c * fPerDieRollMult * fPerTempMult; + + npcStats.setFlee ( std::max(0, std::min(100, npcStats.getFlee() + std::min(-int(iPerMinChange), int(-s))))); + npcStats.setFight ( std::max(0, std::min(100, npcStats.getFight() + std::max(int(iPerMinChange), int(s))))); + } + x = int(-c * fPerDieRollMult); + + if (success && std::abs(x) < iPerMinChange) + x = -iPerMinChange; + } + else // Bribe + { + target3 = std::max(iPerMinChance, target3); + success = (roll <= target3); + float c = int((target3 - roll) * fPerDieRollMult); + + x = success ? std::max(iPerMinChange, c) : c; + } + + tempChange = type == PT_Intimidate ? x : int(x * fPerTempMult); + + + float cappedDispositionChange = tempChange; + if (currentDisposition + tempChange > 100.f) + cappedDispositionChange = 100 - currentDisposition; + if (currentDisposition + tempChange < 0.f) + cappedDispositionChange = -currentDisposition; + + permChange = int(cappedDispositionChange / fPerTempMult); + if (type == PT_Intimidate) + { + permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; + } } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 1ec65a8af..b03eacecc 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -88,8 +88,9 @@ namespace MWMechanics virtual int countDeaths (const std::string& id) const; ///< Return the number of deaths for actors with the given ID. - virtual float getPersuasionDispositionChange (MWWorld::Ptr npc, PersuasionType type, bool& success) const; - ///< Get amount to adjust temporary disposition for a given persuasion action on an NPC + virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, + float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); + ///< Perform a persuasion action on NPC }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 70fd29684..f37a45b49 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -19,7 +19,7 @@ MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing), mBounty (0) -, mLevelProgress(0), mDisposition(0) +, mLevelProgress(0), mDisposition(0), mReputation(0) { mSkillIncreases.resize (ESM::Attribute::Length); @@ -259,3 +259,13 @@ void MWMechanics::NpcStats::setBounty (int bounty) { mBounty = bounty; } + +int MWMechanics::NpcStats::getReputation() const +{ + return mReputation; +} + +void MWMechanics::NpcStats::setReputation(int reputation) +{ + mReputation = reputation; +} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 3e39eb7f1..b6abbd342 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -47,6 +47,7 @@ namespace MWMechanics unsigned int mMovementFlags; Stat mSkill[27]; int mBounty; + int mReputation; int mLevelProgress; // 0-10 @@ -66,6 +67,10 @@ namespace MWMechanics void setBaseDisposition(int disposition); + int getReputation() const; + + void setReputation(int reputation); + bool getMovementFlag (Flag flag) const; void setMovementFlag (Flag flag, bool state);