1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 04:19:55 +00:00
openmw-tes3mp/apps/openmw/mwmechanics/alchemy.cpp
Emanuel Guevel 1e4a854433 Remove static method MWWorld::Class::get(&Ptr)
It was just adding a level of indirection to Ptr.getClass().
All the call were replaced by that instead. The number of lines changed
is important, but the change itself is trivial, so everything should be
fine. :)
2014-05-22 20:50:00 +02:00

462 lines
13 KiB
C++

#include "alchemy.hpp"
#include <cassert>
#include <cstdlib>
#include <algorithm>
#include <stdexcept>
#include <map>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadappa.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadmgef.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/manualref.hpp"
#include "magiceffects.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
std::set<MWMechanics::EffectKey> MWMechanics::Alchemy::listEffects() const
{
std::map<EffectKey, int> effects;
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
{
if (!iter->isEmpty())
{
const MWWorld::LiveCellRef<ESM::Ingredient> *ingredient = iter->get<ESM::Ingredient>();
for (int i=0; i<4; ++i)
if (ingredient->mBase->mData.mEffectID[i]!=-1)
{
EffectKey key (
ingredient->mBase->mData.mEffectID[i], ingredient->mBase->mData.mSkills[i]!=-1 ?
ingredient->mBase->mData.mSkills[i] : ingredient->mBase->mData.mAttributes[i]);
++effects[key];
}
}
}
std::set<EffectKey> effects2;
for (std::map<EffectKey, int>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)
if (iter->second>1)
effects2.insert (iter->first);
return effects2;
}
void MWMechanics::Alchemy::applyTools (int flags, float& value) const
{
bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude);
bool duration = !(flags & ESM::MagicEffect::NoDuration);
bool negative = flags & (ESM::MagicEffect::Harmful);
int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic;
int setup = 0;
if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty())
setup = 1;
else if (!mTools[tool].isEmpty())
setup = 2;
else if (!mTools[ESM::Apparatus::Calcinator].isEmpty())
setup = 3;
else
return;
float toolQuality = setup==1 || setup==2 ? mTools[tool].get<ESM::Apparatus>()->mBase->mData.mQuality : 0;
float calcinatorQuality = setup==1 || setup==3 ?
mTools[ESM::Apparatus::Calcinator].get<ESM::Apparatus>()->mBase->mData.mQuality : 0;
float quality = 1;
switch (setup)
{
case 1:
quality = negative ? 2 * toolQuality + 3 * calcinatorQuality :
(magnitude && duration ?
2 * toolQuality + calcinatorQuality : 2/3.0 * (toolQuality + calcinatorQuality) + 0.5);
break;
case 2:
quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5);
break;
case 3:
quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5;
break;
}
if (setup==3 || !negative)
{
value += quality;
}
else
{
if (quality==0)
throw std::runtime_error ("invalid derived alchemy apparatus quality");
value /= quality;
}
}
void MWMechanics::Alchemy::updateEffects()
{
mEffects.clear();
mValue = 0;
if (countIngredients()<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty())
return;
// find effects
std::set<EffectKey> effects (listEffects());
// general alchemy factor
float x = getChance();
x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->mBase->mData.mQuality;
x *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionStrengthMult")->getFloat();
// value
mValue = static_cast<int> (
x * MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iAlchemyMod")->getFloat());
// build quantified effect list
for (std::set<EffectKey>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)
{
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (iter->mId);
if (magicEffect->mData.mBaseCost<=0)
{
std::ostringstream os;
os << "invalid base cost for magic effect " << iter->mId;
throw std::runtime_error (os.str());
}
float fPotionT1MagMul =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1MagMult")->getFloat();
if (fPotionT1MagMul<=0)
throw std::runtime_error ("invalid gmst: fPotionT1MagMul");
float fPotionT1DurMult =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fPotionT1DurMult")->getFloat();
if (fPotionT1DurMult<=0)
throw std::runtime_error ("invalid gmst: fPotionT1DurMult");
float magnitude = magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude ?
1 : (x / fPotionT1MagMul) / magicEffect->mData.mBaseCost;
float duration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ?
1 : (x / fPotionT1DurMult) / magicEffect->mData.mBaseCost;
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
applyTools (magicEffect->mData.mFlags, magnitude);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
applyTools (magicEffect->mData.mFlags, duration);
duration = static_cast<int> (duration+0.5);
magnitude = static_cast<int> (magnitude+0.5);
if (magnitude>0 && duration>0)
{
ESM::ENAMstruct effect;
effect.mEffectID = iter->mId;
effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work
effect.mRange = 0;
effect.mArea = 0;
effect.mDuration = duration;
effect.mMagnMin = effect.mMagnMax = magnitude;
mEffects.push_back (effect);
}
}
}
const ESM::Potion *MWMechanics::Alchemy::getRecord() const
{
const MWWorld::Store<ESM::Potion> &potions =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>();
MWWorld::Store<ESM::Potion>::iterator iter = potions.begin();
for (; iter != potions.end(); ++iter)
{
if (iter->mEffects.mList.size() != mEffects.size())
continue;
bool mismatch = false;
for (int i=0; i<static_cast<int> (iter->mEffects.mList.size()); ++i)
{
const ESM::ENAMstruct& first = iter->mEffects.mList[i];
const ESM::ENAMstruct& second = mEffects[i];
if (first.mEffectID!=second.mEffectID ||
first.mArea!=second.mArea ||
first.mRange!=second.mRange ||
first.mSkill!=second.mSkill ||
first.mAttribute!=second.mAttribute ||
first.mMagnMin!=second.mMagnMin ||
first.mMagnMax!=second.mMagnMax ||
first.mDuration!=second.mDuration)
{
mismatch = true;
break;
}
}
if (!mismatch)
return &(*iter);
}
return 0;
}
void MWMechanics::Alchemy::removeIngredients()
{
bool needsUpdate = false;
for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
if (!iter->isEmpty())
{
iter->getContainerStore()->remove(*iter, 1, mAlchemist);
if (iter->getRefData().getCount()<1)
{
needsUpdate = true;
*iter = MWWorld::Ptr();
}
}
if (needsUpdate)
updateEffects();
}
void MWMechanics::Alchemy::addPotion (const std::string& name)
{
const ESM::Potion *record = getRecord();
if (!record)
{
ESM::Potion newRecord;
newRecord.mData.mWeight = 0;
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
if (!iter->isEmpty())
newRecord.mData.mWeight += iter->get<ESM::Ingredient>()->mBase->mData.mWeight;
newRecord.mData.mWeight /= countIngredients();
newRecord.mData.mValue = mValue;
newRecord.mData.mAutoCalc = 0;
newRecord.mName = name;
int index = static_cast<int> (std::rand()/(static_cast<double> (RAND_MAX)+1)*6);
assert (index>=0 && index<6);
static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" };
newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif";
newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds";
newRecord.mEffects.mList = mEffects;
record = MWBase::Environment::get().getWorld()->createRecord (newRecord);
}
mAlchemist.getClass().getContainerStore (mAlchemist).add (record->mId, 1, mAlchemist);
}
void MWMechanics::Alchemy::increaseSkill()
{
mAlchemist.getClass().skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0);
}
float MWMechanics::Alchemy::getChance() const
{
const CreatureStats& creatureStats = mAlchemist.getClass().getCreatureStats (mAlchemist);
const NpcStats& npcStats = mAlchemist.getClass().getNpcStats (mAlchemist);
return
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.1 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified());
}
int MWMechanics::Alchemy::countIngredients() const
{
int ingredients = 0;
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
if (!iter->isEmpty())
++ingredients;
return ingredients;
}
void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)
{
mAlchemist = npc;
mIngredients.resize (4);
std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr());
mTools.resize (4);
std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr());
mEffects.clear();
MWWorld::ContainerStore& store = npc.getClass().getContainerStore (npc);
for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus));
iter!=store.end(); ++iter)
{
MWWorld::LiveCellRef<ESM::Apparatus>* ref = iter->get<ESM::Apparatus>();
int type = ref->mBase->mData.mType;
if (type<0 || type>=static_cast<int> (mTools.size()))
throw std::runtime_error ("invalid apparatus type");
if (!mTools[type].isEmpty())
if (ref->mBase->mData.mQuality<=mTools[type].get<ESM::Apparatus>()->mBase->mData.mQuality)
continue;
mTools[type] = *iter;
}
}
MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::beginTools() const
{
return mTools.begin();
}
MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::endTools() const
{
return mTools.end();
}
MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::beginIngredients() const
{
return mIngredients.begin();
}
MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients() const
{
return mIngredients.end();
}
void MWMechanics::Alchemy::clear()
{
mAlchemist = MWWorld::Ptr();
mTools.clear();
mIngredients.clear();
mEffects.clear();
}
int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
{
// find a free slot
int slot = -1;
for (int i=0; i<static_cast<int> (mIngredients.size()); ++i)
if (mIngredients[i].isEmpty())
{
slot = i;
break;
}
if (slot==-1)
return -1;
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
if (!iter->isEmpty() && ingredient.get<ESM::Ingredient>()==iter->get<ESM::Ingredient>())
return -1;
mIngredients[slot] = ingredient;
updateEffects();
return slot;
}
void MWMechanics::Alchemy::removeIngredient (int index)
{
if (index>=0 && index<static_cast<int> (mIngredients.size()))
{
mIngredients[index] = MWWorld::Ptr();
updateEffects();
}
}
MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const
{
return mEffects.begin();
}
MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
{
return mEffects.end();
}
std::string MWMechanics::Alchemy::getPotionName() const
{
if (const ESM::Potion *potion = getRecord())
return potion->mName;
return "";
}
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name)
{
if (mTools[ESM::Apparatus::MortarPestle].isEmpty())
return Result_NoMortarAndPestle;
if (countIngredients()<2)
return Result_LessThanTwoIngredients;
if (name.empty() && getPotionName().empty())
return Result_NoName;
if (beginEffects()==endEffects())
return Result_NoEffects;
if (getChance()<std::rand()/static_cast<double> (RAND_MAX)*100)
{
removeIngredients();
return Result_RandomFailure;
}
addPotion (name);
removeIngredients();
increaseSkill();
return Result_Success;
}