mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-15 23:19:56 +00:00
8f4506f5b6
Currently no visual effects on losing health, the breathing sound doesn't change (we don't have one), the breath bar doesn't turn red when no breath left and it doesn't pulse from black to red.
693 lines
27 KiB
C++
693 lines
27 KiB
C++
|
|
#include "mechanicsmanagerimp.hpp"
|
|
|
|
#include "../mwworld/esmstore.hpp"
|
|
#include "../mwworld/inventorystore.hpp"
|
|
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwbase/windowmanager.hpp"
|
|
#include "../mwbase/dialoguemanager.hpp"
|
|
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/player.hpp"
|
|
|
|
namespace MWMechanics
|
|
{
|
|
void MechanicsManager::buildPlayer()
|
|
{
|
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
|
|
|
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr);
|
|
MWMechanics::NpcStats& npcStats = MWWorld::Class::get (ptr).getNpcStats (ptr);
|
|
|
|
const ESM::NPC *player = ptr.get<ESM::NPC>()->mBase;
|
|
|
|
// reset
|
|
creatureStats.setLevel(player->mNpdt52.mLevel);
|
|
creatureStats.getSpells().clear();
|
|
creatureStats.setMagicEffects(MagicEffects());
|
|
|
|
for (int i=0; i<27; ++i)
|
|
npcStats.getSkill (i).setBase (player->mNpdt52.mSkills[i]);
|
|
|
|
creatureStats.getAttribute(0).setBase (player->mNpdt52.mStrength);
|
|
creatureStats.getAttribute(1).setBase (player->mNpdt52.mIntelligence);
|
|
creatureStats.getAttribute(2).setBase (player->mNpdt52.mWillpower);
|
|
creatureStats.getAttribute(3).setBase (player->mNpdt52.mAgility);
|
|
creatureStats.getAttribute(4).setBase (player->mNpdt52.mSpeed);
|
|
creatureStats.getAttribute(5).setBase (player->mNpdt52.mEndurance);
|
|
creatureStats.getAttribute(6).setBase (player->mNpdt52.mPersonality);
|
|
creatureStats.getAttribute(7).setBase (player->mNpdt52.mLuck);
|
|
|
|
const MWWorld::ESMStore &esmStore =
|
|
MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
// race
|
|
if (mRaceSelected)
|
|
{
|
|
const ESM::Race *race =
|
|
esmStore.get<ESM::Race>().find(player->mRace);
|
|
|
|
bool male = (player->mFlags & ESM::NPC::Female) == 0;
|
|
|
|
for (int i=0; i<8; ++i)
|
|
{
|
|
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];
|
|
|
|
creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale);
|
|
}
|
|
|
|
for (int i=0; i<27; ++i)
|
|
{
|
|
int bonus = 0;
|
|
|
|
for (int i2=0; i2<7; ++i2)
|
|
if (race->mData.mBonus[i2].mSkill==i)
|
|
{
|
|
bonus = race->mData.mBonus[i2].mBonus;
|
|
break;
|
|
}
|
|
|
|
npcStats.getSkill (i).setBase (5 + bonus);
|
|
}
|
|
|
|
for (std::vector<std::string>::const_iterator iter (race->mPowers.mList.begin());
|
|
iter!=race->mPowers.mList.end(); ++iter)
|
|
{
|
|
creatureStats.getSpells().add (*iter);
|
|
}
|
|
}
|
|
|
|
// birthsign
|
|
const std::string &signId =
|
|
MWBase::Environment::get().getWorld()->getPlayer().getBirthSign();
|
|
|
|
if (!signId.empty())
|
|
{
|
|
const ESM::BirthSign *sign =
|
|
esmStore.get<ESM::BirthSign>().find(signId);
|
|
|
|
for (std::vector<std::string>::const_iterator iter (sign->mPowers.mList.begin());
|
|
iter!=sign->mPowers.mList.end(); ++iter)
|
|
{
|
|
creatureStats.getSpells().add (*iter);
|
|
}
|
|
}
|
|
|
|
// class
|
|
if (mClassSelected)
|
|
{
|
|
const ESM::Class *class_ =
|
|
esmStore.get<ESM::Class>().find(player->mClass);
|
|
|
|
for (int i=0; i<2; ++i)
|
|
{
|
|
int attribute = class_->mData.mAttribute[i];
|
|
if (attribute>=0 && attribute<8)
|
|
{
|
|
creatureStats.getAttribute(attribute).setBase (
|
|
creatureStats.getAttribute(attribute).getBase() + 10);
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<2; ++i)
|
|
{
|
|
int bonus = i==0 ? 10 : 25;
|
|
|
|
for (int i2=0; i2<5; ++i2)
|
|
{
|
|
int index = class_->mData.mSkills[i2][i];
|
|
|
|
if (index>=0 && index<27)
|
|
{
|
|
npcStats.getSkill (index).setBase (
|
|
npcStats.getSkill (index).getBase() + bonus);
|
|
}
|
|
}
|
|
}
|
|
|
|
const MWWorld::Store<ESM::Skill> &skills =
|
|
esmStore.get<ESM::Skill>();
|
|
|
|
MWWorld::Store<ESM::Skill>::iterator iter = skills.begin();
|
|
for (; iter != skills.end(); ++iter)
|
|
{
|
|
if (iter->mData.mSpecialization==class_->mData.mSpecialization)
|
|
{
|
|
int index = iter->mIndex;
|
|
|
|
if (index>=0 && index<27)
|
|
{
|
|
npcStats.getSkill (index).setBase (
|
|
npcStats.getSkill (index).getBase() + 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// forced update and current value adjustments
|
|
mActors.updateActor (ptr, 0);
|
|
|
|
for (int i=0; i<3; ++i)
|
|
{
|
|
DynamicStat<float> stat = creatureStats.getDynamic (i);
|
|
stat.setCurrent (stat.getModified());
|
|
creatureStats.setDynamic (i, stat);
|
|
}
|
|
|
|
// auto-equip again. we need this for when the race is changed to a beast race
|
|
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr);
|
|
for (int i=0; i<MWWorld::InventoryStore::Slots; ++i)
|
|
invStore.equip(i, invStore.end());
|
|
invStore.autoEquip(ptr);
|
|
}
|
|
|
|
MechanicsManager::MechanicsManager()
|
|
: mUpdatePlayer (true), mClassSelected (false),
|
|
mRaceSelected (false)
|
|
{
|
|
buildPlayer();
|
|
}
|
|
|
|
void MechanicsManager::add(const MWWorld::Ptr& ptr)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
mActors.addActor(ptr);
|
|
else
|
|
mObjects.addObject(ptr);
|
|
}
|
|
|
|
void MechanicsManager::remove(const MWWorld::Ptr& ptr)
|
|
{
|
|
if(ptr == mWatched)
|
|
mWatched = MWWorld::Ptr();
|
|
mActors.removeActor(ptr);
|
|
mObjects.removeObject(ptr);
|
|
}
|
|
|
|
void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
mActors.updateActor(old, ptr);
|
|
else
|
|
mObjects.updateObject(old, ptr);
|
|
}
|
|
|
|
|
|
void MechanicsManager::drop(const MWWorld::CellStore *cellStore)
|
|
{
|
|
if(!mWatched.isEmpty() && mWatched.getCell() == cellStore)
|
|
mWatched = MWWorld::Ptr();
|
|
|
|
mActors.dropActors(cellStore);
|
|
mObjects.dropObjects(cellStore);
|
|
}
|
|
|
|
|
|
void MechanicsManager::watchActor(const MWWorld::Ptr& ptr)
|
|
{
|
|
mWatched = ptr;
|
|
}
|
|
|
|
void MechanicsManager::update(float duration, bool paused)
|
|
{
|
|
if (!mWatched.isEmpty())
|
|
{
|
|
MWMechanics::CreatureStats& stats =
|
|
MWWorld::Class::get (mWatched).getCreatureStats (mWatched);
|
|
|
|
MWMechanics::NpcStats& npcStats =
|
|
MWWorld::Class::get (mWatched).getNpcStats (mWatched);
|
|
|
|
static const char *attributeNames[8] =
|
|
{
|
|
"AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5",
|
|
"AttribVal6", "AttribVal7", "AttribVal8"
|
|
};
|
|
|
|
static const char *dynamicNames[3] =
|
|
{
|
|
"HBar", "MBar", "FBar"
|
|
};
|
|
|
|
for (int i=0; i<8; ++i)
|
|
{
|
|
if (stats.getAttribute(i)!=mWatchedCreature.getAttribute(i))
|
|
{
|
|
mWatchedCreature.setAttribute(i, stats.getAttribute(i));
|
|
|
|
MWBase::Environment::get().getWindowManager()->setValue (attributeNames[i], stats.getAttribute(i));
|
|
}
|
|
}
|
|
|
|
if (stats.getHealth() != mWatchedCreature.getHealth()) {
|
|
mWatchedCreature.setHealth(stats.getHealth());
|
|
MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[0], stats.getHealth());
|
|
}
|
|
if (stats.getMagicka() != mWatchedCreature.getMagicka()) {
|
|
mWatchedCreature.setMagicka(stats.getMagicka());
|
|
MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[1], stats.getMagicka());
|
|
}
|
|
if (stats.getFatigue() != mWatchedCreature.getFatigue()) {
|
|
mWatchedCreature.setFatigue(stats.getFatigue());
|
|
MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue());
|
|
}
|
|
|
|
if(npcStats.getTimeToStartDrowning() != mWatchedNpc.getTimeToStartDrowning())
|
|
{
|
|
mWatchedNpc.setTimeToStartDrowning(npcStats.getTimeToStartDrowning());
|
|
if(npcStats.getTimeToStartDrowning()>=20.0)
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(false);
|
|
}
|
|
else
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(true);
|
|
MWBase::Environment::get().getWindowManager()->setDrowningTimeLeft(npcStats.getTimeToStartDrowning());
|
|
}
|
|
}
|
|
|
|
bool update = false;
|
|
|
|
//Loop over ESM::Skill::SkillEnum
|
|
for(int i = 0; i < 27; ++i)
|
|
{
|
|
if(npcStats.getSkill (i) != mWatchedNpc.getSkill (i))
|
|
{
|
|
update = true;
|
|
mWatchedNpc.getSkill (i) = npcStats.getSkill (i);
|
|
MWBase::Environment::get().getWindowManager()->setValue((ESM::Skill::SkillEnum)i, npcStats.getSkill (i));
|
|
}
|
|
}
|
|
|
|
if (update)
|
|
MWBase::Environment::get().getWindowManager()->updateSkillArea();
|
|
|
|
MWBase::Environment::get().getWindowManager()->setValue ("level", stats.getLevel());
|
|
}
|
|
|
|
if (mUpdatePlayer)
|
|
{
|
|
// basic player profile; should not change anymore after the creation phase is finished.
|
|
MWBase::WindowManager *winMgr =
|
|
MWBase::Environment::get().getWindowManager();
|
|
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
const ESM::NPC *player =
|
|
world->getPlayer().getPlayer().get<ESM::NPC>()->mBase;
|
|
|
|
const ESM::Race *race =
|
|
world->getStore().get<ESM::Race>().find(player->mRace);
|
|
const ESM::Class *cls =
|
|
world->getStore().get<ESM::Class>().find(player->mClass);
|
|
|
|
winMgr->setValue ("name", player->mName);
|
|
winMgr->setValue ("race", race->mName);
|
|
winMgr->setValue ("class", cls->mName);
|
|
|
|
mUpdatePlayer = false;
|
|
|
|
MWBase::WindowManager::SkillList majorSkills (5);
|
|
MWBase::WindowManager::SkillList minorSkills (5);
|
|
|
|
for (int i=0; i<5; ++i)
|
|
{
|
|
minorSkills[i] = cls->mData.mSkills[i][0];
|
|
majorSkills[i] = cls->mData.mSkills[i][1];
|
|
}
|
|
|
|
winMgr->configureSkills (majorSkills, minorSkills);
|
|
|
|
// HACK? The player has been changed, so a new Animation object may
|
|
// have been made for them. Make sure they're properly updated.
|
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
|
mActors.removeActor(ptr);
|
|
mActors.addActor(ptr);
|
|
}
|
|
|
|
mActors.update(duration, paused);
|
|
mObjects.update(duration, paused);
|
|
}
|
|
|
|
void MechanicsManager::restoreDynamicStats()
|
|
{
|
|
mActors.restoreDynamicStats ();
|
|
}
|
|
|
|
void MechanicsManager::setPlayerName (const std::string& name)
|
|
{
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
|
|
ESM::NPC player =
|
|
*world->getPlayer().getPlayer().get<ESM::NPC>()->mBase;
|
|
player.mName = name;
|
|
|
|
world->createRecord(player);
|
|
|
|
mUpdatePlayer = true;
|
|
}
|
|
|
|
void MechanicsManager::setPlayerRace (const std::string& race, bool male, const std::string &head, const std::string &hair)
|
|
{
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
|
|
ESM::NPC player =
|
|
*world->getPlayer().getPlayer().get<ESM::NPC>()->mBase;
|
|
|
|
player.mRace = race;
|
|
player.mHead = head;
|
|
player.mHair = hair;
|
|
player.setIsMale(male);
|
|
|
|
world->createRecord(player);
|
|
|
|
mRaceSelected = true;
|
|
buildPlayer();
|
|
mUpdatePlayer = true;
|
|
}
|
|
|
|
void MechanicsManager::setPlayerBirthsign (const std::string& id)
|
|
{
|
|
MWBase::Environment::get().getWorld()->getPlayer().setBirthSign(id);
|
|
buildPlayer();
|
|
mUpdatePlayer = true;
|
|
}
|
|
|
|
void MechanicsManager::setPlayerClass (const std::string& id)
|
|
{
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
|
|
ESM::NPC player =
|
|
*world->getPlayer().getPlayer().get<ESM::NPC>()->mBase;
|
|
player.mClass = id;
|
|
|
|
world->createRecord(player);
|
|
|
|
mClassSelected = true;
|
|
buildPlayer();
|
|
mUpdatePlayer = true;
|
|
}
|
|
|
|
void MechanicsManager::setPlayerClass (const ESM::Class &cls)
|
|
{
|
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
|
|
|
const ESM::Class *ptr = world->createRecord(cls);
|
|
|
|
ESM::NPC player =
|
|
*world->getPlayer().getPlayer().get<ESM::NPC>()->mBase;
|
|
player.mClass = ptr->mId;
|
|
|
|
world->createRecord(player);
|
|
|
|
mClassSelected = true;
|
|
buildPlayer();
|
|
mUpdatePlayer = true;
|
|
}
|
|
|
|
int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr)
|
|
{
|
|
MWMechanics::NpcStats npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr);
|
|
float x = npcSkill.getBaseDisposition();
|
|
|
|
MWWorld::LiveCellRef<ESM::NPC>* npc = ptr.get<ESM::NPC>();
|
|
MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
|
MWWorld::LiveCellRef<ESM::NPC>* player = playerPtr.get<ESM::NPC>();
|
|
MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr);
|
|
MWMechanics::NpcStats playerNpcStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr);
|
|
|
|
if (Misc::StringUtils::lowerCase(npc->mBase->mRace) == Misc::StringUtils::lowerCase(player->mBase->mRace)) x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispRaceMod")->getFloat();
|
|
|
|
x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispPersonalityMult")->getFloat()
|
|
* (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispPersonalityBase")->getFloat());
|
|
|
|
float reaction = 0;
|
|
int rank = 0;
|
|
std::string npcFaction = "";
|
|
if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first;
|
|
|
|
if (playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerNpcStats.getFactionRanks().end())
|
|
{
|
|
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin();
|
|
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); ++it)
|
|
{
|
|
if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction)) reaction = it->mReaction;
|
|
}
|
|
rank = playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second;
|
|
}
|
|
else if (npcFaction != "")
|
|
{
|
|
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin();
|
|
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();++it)
|
|
{
|
|
if(playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerNpcStats.getFactionRanks().end() )
|
|
{
|
|
if(it->mReaction<reaction) reaction = it->mReaction;
|
|
}
|
|
}
|
|
rank = 0;
|
|
}
|
|
else
|
|
{
|
|
reaction = 0;
|
|
rank = 0;
|
|
}
|
|
x += (MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionRankMult")->getFloat() * rank
|
|
+ MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionRankBase")->getFloat())
|
|
* MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispFactionMod")->getFloat() * reaction;
|
|
|
|
x -= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispCrimeMod")->getFloat() * playerNpcStats.getBounty();
|
|
if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease())
|
|
x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispDiseaseMod")->getFloat();
|
|
|
|
if (playerNpcStats.getDrawState() == MWMechanics::DrawState_Weapon)
|
|
x += MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDispWeaponDrawn")->getFloat();
|
|
|
|
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)
|
|
{
|
|
if (ptr.getTypeName() == typeid(ESM::Creature).name())
|
|
return basePrice;
|
|
|
|
MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(ptr).getNpcStats(ptr);
|
|
MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
|
|
|
|
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);
|
|
|
|
// I suppose the temporary disposition change _has_ to be considered here,
|
|
// otherwise one would get different prices when exiting and re-entering the dialogue window...
|
|
int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr)
|
|
+ MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100));
|
|
float a = std::min(playerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f);
|
|
float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
|
|
float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
|
|
float d = std::min(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f);
|
|
float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
|
|
float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
|
|
|
|
float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm();
|
|
float npcTerm = (d + e + f) * sellerStats.getFatigueTerm();
|
|
float buyTerm = 0.01 * (100 - 0.5 * (pcTerm - npcTerm));
|
|
float sellTerm = 0.01 * (50 - 0.5 * (npcTerm - pcTerm));
|
|
|
|
float x;
|
|
if(buying) x = buyTerm;
|
|
else x = std::min(buyTerm, sellTerm);
|
|
int offerPrice;
|
|
if (x < 1)
|
|
offerPrice = int(x * basePrice);
|
|
else
|
|
offerPrice = basePrice + int((x - 1) * basePrice);
|
|
offerPrice = std::max(1, offerPrice);
|
|
return offerPrice;
|
|
}
|
|
|
|
int MechanicsManager::countDeaths (const std::string& id) const
|
|
{
|
|
return mActors.countDeaths (id);
|
|
}
|
|
|
|
|
|
void MechanicsManager::getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type,
|
|
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange)
|
|
{
|
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
|
|
|
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();
|
|
else 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 = 0;
|
|
float y = 0;
|
|
|
|
float roll = static_cast<float> (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.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + int(std::max(iPerMinChange, s)))));
|
|
npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + 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.setAiSetting (2, std::max(0, std::min(100, npcStats.getAiSetting (2) + std::min(-int(iPerMinChange), int(-s)))));
|
|
npcStats.setAiSetting (1, std::max(0, std::min(100, npcStats.getAiSetting (1) + 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;
|
|
}
|
|
}
|
|
|
|
void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
mActors.forceStateUpdate(ptr);
|
|
}
|
|
|
|
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
mActors.playAnimationGroup(ptr, groupName, mode, number);
|
|
else
|
|
mObjects.playAnimationGroup(ptr, groupName, mode, number);
|
|
}
|
|
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
mActors.skipAnimation(ptr);
|
|
else
|
|
mObjects.skipAnimation(ptr);
|
|
}
|
|
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName)
|
|
{
|
|
if(MWWorld::Class::get(ptr).isActor())
|
|
return mActors.checkAnimationPlaying(ptr, groupName);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
}
|