Issue #61: potion creation (1st part; still missing some implementations)

actorid
Marc Zinnschlag 12 years ago
parent 332039da10
commit 5fbca239dd

@ -1,15 +1,259 @@
#include "alchemy.hpp"
#include <cstdlib>
#include <algorithm>
#include <stdexcept>
#include <components/esm/loadskil.hpp>
#include <components/esm/loadappa.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadmgef.hpp>
#include <components/esm_store/store.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "magiceffects.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
std::set<MWMechanics::EffectKey> MWMechanics::Alchemy::listEffects() const
{
std::set<EffectKey> 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->base->mData.mEffectID[i]!=-1)
effects.insert (EffectKey (
ingredient->base->mData.mEffectID[i], ingredient->base->mData.mSkills[i]!=-1 ?
ingredient->base->mData.mSkills[i] : ingredient->base->mData.mAttributes[i]));
}
}
return effects;
}
void MWMechanics::Alchemy::filterEffects (std::set<EffectKey>& effects) const
{
std::set<EffectKey>::iterator iter = effects.begin();
while (iter!=effects.end())
{
bool remove = false;
const EffectKey& key = *iter;
{ // dodge pointless g++ warning
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
{
bool found = false;
const MWWorld::LiveCellRef<ESM::Ingredient> *ingredient = iter->get<ESM::Ingredient>();
for (int i=0; i<4; ++i)
if (key.mId==ingredient->base->mData.mEffectID[i] &&
(key.mArg==ingredient->base->mData.mSkills[i] ||
key.mArg==ingredient->base->mData.mAttributes[i]))
{
found = true;
break;
}
if (!found)
{
remove = true;
break;
}
}
}
if (remove)
effects.erase (iter++);
else
++iter;
}
}
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::Negative | ESM::MagicEffect::Negative2);
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>()->base->mData.mQuality : 0;
float calcinatorQuality = setup==1 || setup==3 ?
mTools[ESM::Apparatus::Calcinator].get<ESM::Apparatus>()->base->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();
int ingredients = 0;
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
if (!iter->isEmpty())
++ingredients;
if (ingredients<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty())
return;
// find effects
std::set<EffectKey> effects (listEffects());
filterEffects (effects);
// general alchemy factor
float x = getChance();
x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->base->mData.mQuality;
x *= MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionStrengthMult")->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().magicEffects.find (iter->mId);
if (magicEffect->mData.mBaseCost<=0)
throw std::runtime_error ("invalid base cost for magic effect " + iter->mId);
float fPotionT1MagMul =
MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionT1MagMul")->getFloat();
if (fPotionT1MagMul<=0)
throw std::runtime_error ("invalid gmst: fPotionT1MagMul");
float fPotionT1DurMult =
MWBase::Environment::get().getWorld()->getStore().gameSettings.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
{
}
void MWMechanics::Alchemy::removeIngredients()
{
}
void MWMechanics::Alchemy::addPotion (const std::string& name)
{
}
void MWMechanics::Alchemy::increaseSkill()
{
}
float MWMechanics::Alchemy::getChance() const
{
const CreatureStats& creatureStats = MWWorld::Class::get (mAlchemist).getCreatureStats (mAlchemist);
const NpcStats& npcStats = MWWorld::Class::get (mAlchemist).getNpcStats (mAlchemist);
return
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
0.1 * creatureStats.getAttribute (1).getModified()
+ 0.1 * creatureStats.getAttribute (7).getModified());
}
void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)
{
mNpc = npc;
mAlchemist = npc;
mIngredients.resize (4);
@ -19,6 +263,8 @@ void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)
std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr());
mEffects.clear();
MWWorld::ContainerStore& store = MWWorld::Class::get (npc).getContainerStore (npc);
for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus));
@ -61,9 +307,10 @@ MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients(
void MWMechanics::Alchemy::clear()
{
mNpc = MWWorld::Ptr();
mAlchemist = MWWorld::Ptr();
mTools.clear();
mIngredients.clear();
mEffects.clear();
}
int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
@ -86,6 +333,8 @@ int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
return -1;
mIngredients[slot] = ingredient;
updateEffects();
return slot;
}
@ -93,6 +342,61 @@ int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
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;
int ingredients = 0;
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
if (!iter->isEmpty())
++ingredients;
if (ingredients<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;
}

@ -2,11 +2,16 @@
#define GAME_MWMECHANICS_ALCHEMY_H
#include <vector>
#include <set>
#include <components/esm/effectlist.hpp>
#include "../mwworld/ptr.hpp"
namespace MWMechanics
{
struct EffectKey;
/// \brief Potion creation via alchemy skill
class Alchemy
{
@ -17,13 +22,54 @@ namespace MWMechanics
typedef std::vector<MWWorld::Ptr> TIngredientsContainer;
typedef TIngredientsContainer::const_iterator TIngredientsIterator;
typedef std::vector<ESM::ENAMstruct> TEffectsContainer;
typedef TEffectsContainer::const_iterator TEffectsIterator;
enum Result
{
Result_Success,
Result_NoMortarAndPestle,
Result_LessThanTwoIngredients,
Result_NoName,
Result_NoEffects,
Result_RandomFailure
};
private:
MWWorld::Ptr mNpc;
MWWorld::Ptr mAlchemist;
TToolsContainer mTools;
TIngredientsContainer mIngredients;
TEffectsContainer mEffects;
std::set<EffectKey> listEffects() const;
///< List all effects of all used ingredients.
void filterEffects (std::set<EffectKey>& effects) const;
///< Filter out effects not shared by all ingredients.
void applyTools (int flags, float& value) const;
void updateEffects();
const ESM::Potion *getRecord() const;
///< Return existing recrod for created potion (may return 0)
void removeIngredients();
///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and
/// update effect list accordingly.
void addPotion (const std::string& name);
///< Add a potion to the alchemist's inventory.
void increaseSkill();
///< Increase alchemist's skill.
float getChance() const;
///< Return chance of success.
public:
void setAlchemist (const MWWorld::Ptr& npc);
@ -49,6 +95,20 @@ namespace MWMechanics
void removeIngredient (int index);
///< Remove ingredient from slot (calling this function on an empty slot is a no-op).
TEffectsIterator beginEffects() const;
TEffectsIterator endEffects() const;
std::string getPotionName() const;
///< Return the name of the potion that would be created when calling create (if a record for such
/// a potion already exists) or return an empty string.
Result create (const std::string& name);
///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and
/// adjust the skills of the alchemist accordingly.
/// \param name must not be an empty string, unless there is already a potion record (
/// getPotionName() does not return an empty string).
};
}

@ -14,6 +14,8 @@ struct MagicEffect
enum Flags
{
NoDuration = 0x4,
NoMagnitude = 0x8,
Negative2 = 0x10,
SpellMaking = 0x0200,
Enchanting = 0x0400,
Negative = 0x0800 // A harmful effect. Will determine whether

Loading…
Cancel
Save