1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-04 01:36:40 +00:00

Merge branch 'dial_me_maybe' into 'master'

Change disposition to work like vanilla

Closes #5100 and #5842

See merge request OpenMW/openmw!599
This commit is contained in:
psi29a 2021-07-12 14:22:26 +00:00
commit cd9fb2adad
7 changed files with 72 additions and 54 deletions

View file

@ -4,10 +4,12 @@
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions) Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
Bug #3905: Great House Dagoth issues Bug #3905: Great House Dagoth issues
Bug #5100: Persuasion doesn't always clamp the resulting disposition
Bug #5120: Scripted object spawning updates physics system Bug #5120: Scripted object spawning updates physics system
Bug #5379: Wandering NPCs falling through cantons Bug #5379: Wandering NPCs falling through cantons
Bug #5453: Magic effect VFX are offset for creatures Bug #5453: Magic effect VFX are offset for creatures
Bug #5483: AutoCalc flag is not used to calculate spells cost Bug #5483: AutoCalc flag is not used to calculate spells cost
Bug #5842: GetDisposition adds temporary disposition change from different actors
Bug #6037: Morrowind Content Language Cannot be Set to English in OpenMW Launcher Bug #6037: Morrowind Content Language Cannot be Set to English in OpenMW Launcher
Bug #6066: addtopic "return" does not work from within script. No errors thrown Bug #6066: addtopic "return" does not work from within script. No errors thrown
Bug #6067: esp loader fails in for certain subrecord orders Bug #6067: esp loader fails in for certain subrecord orders

View file

@ -94,7 +94,6 @@ namespace MWBase
virtual bool checkServiceRefused (ResponseCallback* callback, ServiceType service = ServiceType::Any) = 0; virtual bool checkServiceRefused (ResponseCallback* callback, ServiceType service = ServiceType::Any) = 0;
virtual void persuade (int type, ResponseCallback* callback) = 0; virtual void persuade (int type, ResponseCallback* callback) = 0;
virtual int getTemporaryDispositionChange () const = 0;
/// @note Controlled by an option, gets discarded when dialogue ends by default /// @note Controlled by an option, gets discarded when dialogue ends by default
virtual void applyBarterDispositionChange (int delta) = 0; virtual void applyBarterDispositionChange (int delta) = 0;

View file

@ -100,7 +100,7 @@ namespace MWBase
virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0; virtual int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) = 0;
///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.
virtual int getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange = true) = 0; virtual int getDerivedDisposition(const MWWorld::Ptr& ptr, bool clamp = true) = 0;
///< Calculate the diposition of an NPC toward the player. ///< Calculate the diposition of an NPC toward the player.
virtual int countDeaths (const std::string& id) const = 0; virtual int countDeaths (const std::string& id) const = 0;
@ -156,7 +156,7 @@ namespace MWBase
PT_Bribe100, PT_Bribe100,
PT_Bribe1000 PT_Bribe1000
}; };
virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange) = 0; virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, int& tempChange, int& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;

View file

@ -52,8 +52,9 @@ namespace MWDialogue
, mCompilerContext (MWScript::CompilerContext::Type_Dialogue) , mCompilerContext (MWScript::CompilerContext::Type_Dialogue)
, mErrorHandler() , mErrorHandler()
, mTalkedTo(false) , mTalkedTo(false)
, mTemporaryDispositionChange(0.f) , mOriginalDisposition(0)
, mPermanentDispositionChange(0.f) , mCurrentDisposition(0)
, mPermanentDispositionChange(0)
{ {
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
@ -65,7 +66,8 @@ namespace MWDialogue
{ {
mKnownTopics.clear(); mKnownTopics.clear();
mTalkedTo = false; mTalkedTo = false;
mTemporaryDispositionChange = 0; mOriginalDisposition = 0;
mCurrentDisposition = 0;
mPermanentDispositionChange = 0; mPermanentDispositionChange = 0;
} }
@ -98,6 +100,20 @@ namespace MWDialogue
} }
} }
void DialogueManager::updateOriginalDisposition()
{
if(mActor.getClass().isNpc())
{
const auto& stats = mActor.getClass().getNpcStats(mActor);
// Disposition changed by script; discard our preconceived notions
if(stats.getBaseDisposition() != mCurrentDisposition)
{
mCurrentDisposition = stats.getBaseDisposition();
mOriginalDisposition = mCurrentDisposition;
}
}
}
bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback) bool DialogueManager::startDialogue (const MWWorld::Ptr& actor, ResponseCallback* callback)
{ {
updateGlobals(); updateGlobals();
@ -107,8 +123,7 @@ namespace MWDialogue
return false; return false;
mLastTopic = ""; mLastTopic = "";
mPermanentDispositionChange = 0; // Note that we intentionally don't reset mPermanentDispositionChange
mTemporaryDispositionChange = 0;
mChoice = -1; mChoice = -1;
mIsInChoice = false; mIsInChoice = false;
@ -398,19 +413,21 @@ namespace MWDialogue
void DialogueManager::goodbyeSelected() void DialogueManager::goodbyeSelected()
{ {
// Apply disposition change to NPC's base disposition // Apply disposition change to NPC's base disposition if we **think** we need to change something
if (mActor.getClass().isNpc()) if ((mPermanentDispositionChange || mOriginalDisposition != mCurrentDisposition) && mActor.getClass().isNpc())
{ {
// Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate) updateOriginalDisposition();
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
if (curDisp + mPermanentDispositionChange < 0)
mPermanentDispositionChange = -curDisp;
MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor); MWMechanics::NpcStats& npcStats = mActor.getClass().getNpcStats(mActor);
npcStats.setBaseDisposition(static_cast<int>(npcStats.getBaseDisposition() + mPermanentDispositionChange)); // Clamp permanent disposition change so that final disposition doesn't go below 0 (could happen with intimidate)
npcStats.setBaseDisposition(0);
int zero = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false);
int disposition = std::min(100 - zero, std::max(mOriginalDisposition + mPermanentDispositionChange, -zero));
npcStats.setBaseDisposition(disposition);
} }
mPermanentDispositionChange = 0; mPermanentDispositionChange = 0;
mTemporaryDispositionChange = 0; mOriginalDisposition = 0;
mCurrentDisposition = 0;
} }
void DialogueManager::questionAnswered (int answer, ResponseCallback* callback) void DialogueManager::questionAnswered (int answer, ResponseCallback* callback)
@ -490,20 +507,17 @@ namespace MWDialogue
void DialogueManager::persuade(int type, ResponseCallback* callback) void DialogueManager::persuade(int type, ResponseCallback* callback)
{ {
bool success; bool success;
float temp, perm; int temp, perm;
MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange( MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange(
mActor, MWBase::MechanicsManager::PersuasionType(type), mActor, MWBase::MechanicsManager::PersuasionType(type),
success, temp, perm); success, temp, perm);
mTemporaryDispositionChange += temp; updateOriginalDisposition();
if(temp > 0 && perm > 0 && mOriginalDisposition + perm + mPermanentDispositionChange < 0)
perm = -(mOriginalDisposition + mPermanentDispositionChange);
mCurrentDisposition += temp;
mActor.getClass().getNpcStats(mActor).setBaseDisposition(mCurrentDisposition);
mPermanentDispositionChange += perm; mPermanentDispositionChange += perm;
// change temp disposition so that final disposition is between 0...100
float curDisp = static_cast<float>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor, false));
if (curDisp + mTemporaryDispositionChange < 0)
mTemporaryDispositionChange = -curDisp;
else if (curDisp + mTemporaryDispositionChange > 100)
mTemporaryDispositionChange = 100 - curDisp;
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); player.getClass().skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1);
@ -539,16 +553,16 @@ namespace MWDialogue
executeTopic (text + (success ? " Success" : " Fail"), callback); executeTopic (text + (success ? " Success" : " Fail"), callback);
} }
int DialogueManager::getTemporaryDispositionChange() const
{
return static_cast<int>(mTemporaryDispositionChange);
}
void DialogueManager::applyBarterDispositionChange(int delta) void DialogueManager::applyBarterDispositionChange(int delta)
{ {
mTemporaryDispositionChange += delta; if(mActor.getClass().isNpc())
if (Settings::Manager::getBool("barter disposition change is permanent", "Game")) {
mPermanentDispositionChange += delta; updateOriginalDisposition();
mCurrentDisposition += delta;
mActor.getClass().getNpcStats(mActor).setBaseDisposition(mCurrentDisposition);
if (Settings::Manager::getBool("barter disposition change is permanent", "Game"))
mPermanentDispositionChange += delta;
}
} }
bool DialogueManager::checkServiceRefused(ResponseCallback* callback, ServiceType service) bool DialogueManager::checkServiceRefused(ResponseCallback* callback, ServiceType service)

View file

@ -47,8 +47,9 @@ namespace MWDialogue
std::vector<std::pair<std::string, int> > mChoices; std::vector<std::pair<std::string, int> > mChoices;
float mTemporaryDispositionChange; int mOriginalDisposition;
float mPermanentDispositionChange; int mCurrentDisposition;
int mPermanentDispositionChange;
void parseText (const std::string& text); void parseText (const std::string& text);
@ -62,6 +63,8 @@ namespace MWDialogue
const ESM::Dialogue* searchDialogue(const std::string& id); const ESM::Dialogue* searchDialogue(const std::string& id);
void updateOriginalDisposition();
public: public:
DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage); DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage);
@ -96,7 +99,6 @@ namespace MWDialogue
void questionAnswered (int answer, ResponseCallback* callback) override; void questionAnswered (int answer, ResponseCallback* callback) override;
void persuade (int type, ResponseCallback* callback) override; void persuade (int type, ResponseCallback* callback) override;
int getTemporaryDispositionChange () const override;
/// @note Controlled by an option, gets discarded when dialogue ends by default /// @note Controlled by an option, gets discarded when dialogue ends by default
void applyBarterDispositionChange (int delta) override; void applyBarterDispositionChange (int delta) override;

View file

@ -483,7 +483,7 @@ namespace MWMechanics
mUpdatePlayer = true; mUpdatePlayer = true;
} }
int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange) int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr, bool clamp)
{ {
const MWMechanics::NpcStats& npcSkill = ptr.getClass().getNpcStats(ptr); const MWMechanics::NpcStats& npcSkill = ptr.getClass().getNpcStats(ptr);
float x = static_cast<float>(npcSkill.getBaseDisposition()); float x = static_cast<float>(npcSkill.getBaseDisposition());
@ -562,11 +562,9 @@ namespace MWMechanics
x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).getMagnitude(); x += ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Charm).getMagnitude();
if(addTemporaryDispositionChange) if(clamp)
x += MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(); return std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used
return int(x);
int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used
return effective_disposition;
} }
int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) int MechanicsManager::getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying)
@ -603,7 +601,7 @@ namespace MWMechanics
return mActors.countDeaths (id); return mActors.countDeaths (id);
} }
void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange) void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, int& tempChange, int& permChange)
{ {
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
@ -727,19 +725,22 @@ namespace MWMechanics
x = success ? std::max(iPerMinChange, c) : c; x = success ? std::max(iPerMinChange, c) : c;
} }
tempChange = type == PT_Intimidate ? x : int(x * fPerTempMult); tempChange = type == PT_Intimidate ? int(x) : int(x * fPerTempMult);
float cappedDispositionChange = tempChange; int cappedDispositionChange = tempChange;
if (currentDisposition + tempChange > 100.f) if (currentDisposition + tempChange > 100)
cappedDispositionChange = static_cast<float>(100 - currentDisposition); cappedDispositionChange = 100 - currentDisposition;
if (currentDisposition + tempChange < 0.f) if (currentDisposition + tempChange < 0)
cappedDispositionChange = static_cast<float>(-currentDisposition); {
cappedDispositionChange = -currentDisposition;
tempChange = 0;
}
permChange = floor(cappedDispositionChange / fPerTempMult); permChange = floor(cappedDispositionChange / fPerTempMult);
if (type == PT_Intimidate) if (type == PT_Intimidate)
{ {
permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : y; permChange = success ? -int(cappedDispositionChange/ fPerTempMult) : int(y);
} }
} }
@ -1722,7 +1723,7 @@ namespace MWMechanics
int disposition = 50; int disposition = 50;
if (ptr.getClass().isNpc()) if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr, true); disposition = getDerivedDisposition(ptr);
int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified() int fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
+ static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition))); + static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition)));

View file

@ -87,13 +87,13 @@ namespace MWMechanics
int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) override; int getBarterOffer(const MWWorld::Ptr& ptr,int basePrice, bool buying) override;
///< This is used by every service to determine the price of objects given the trading skills of the player and NPC. ///< This is used by every service to determine the price of objects given the trading skills of the player and NPC.
int getDerivedDisposition(const MWWorld::Ptr& ptr, bool addTemporaryDispositionChange = true) override; int getDerivedDisposition(const MWWorld::Ptr& ptr, bool clamp = true) override;
///< Calculate the diposition of an NPC toward the player. ///< Calculate the diposition of an NPC toward the player.
int countDeaths (const std::string& id) const override; int countDeaths (const std::string& id) const override;
///< Return the number of deaths for actors with the given ID. ///< Return the number of deaths for actors with the given ID.
void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, float& tempChange, float& permChange) override; void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, bool& success, int& tempChange, int& permChange) override;
///< 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! /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check!