forked from teamnwah/openmw-tes3coop
Refactor ActiveSpells to track range type. Added basic self range magic.
This commit is contained in:
parent
9e2b1942fc
commit
cbe96a2170
10 changed files with 187 additions and 29 deletions
|
@ -406,6 +406,8 @@ namespace MWBase
|
|||
virtual bool getGodModeState() = 0;
|
||||
|
||||
virtual bool toggleGodMode() = 0;
|
||||
|
||||
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,8 @@ namespace MWGui
|
|||
}
|
||||
|
||||
// add lasting effect spells/potions etc
|
||||
|
||||
// TODO: Move this to ActiveSpells
|
||||
const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin();
|
||||
it != activeSpells.end(); ++it)
|
||||
|
@ -105,31 +107,36 @@ namespace MWGui
|
|||
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
effectIt != list.mList.end(); ++effectIt, ++i)
|
||||
{
|
||||
if (effectIt->mRange != it->second.mRange)
|
||||
continue;
|
||||
|
||||
float randomFactor = it->second.mRandom[i];
|
||||
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mSource = it->second.mName;
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * it->second.second;
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * randomFactor;
|
||||
effectInfo.mRemainingTime = effectIt->mDuration +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
// ingredients need special casing for their magnitude / duration
|
||||
/// \todo duplicated from ActiveSpells, helper function?
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (it->first))
|
||||
{
|
||||
effectInfo.mRemainingTime = effectIt->mDuration * it->second.second +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
effectInfo.mRemainingTime = effectIt->mDuration * randomFactor +
|
||||
(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
effectInfo.mMagnitude = static_cast<int> (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost));
|
||||
effectInfo.mMagnitude = static_cast<int> (0.05*randomFactor / (0.1 * magicEffect->mData.mBaseCost));
|
||||
}
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
|
@ -288,6 +295,10 @@ namespace MWGui
|
|||
|
||||
ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id)
|
||||
{
|
||||
if (const ESM::Enchantment* enchantment =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search (id))
|
||||
return enchantment->mEffects;
|
||||
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mEffects;
|
||||
|
|
|
@ -467,7 +467,7 @@ namespace MWGui
|
|||
}
|
||||
Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget<Widgets::MWDynamicStat>
|
||||
("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge");
|
||||
chargeWidget->setValue(charge, charge);
|
||||
chargeWidget->setValue(charge, maxCharge);
|
||||
totalSize.height += 24;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
|
@ -64,15 +65,19 @@ namespace MWMechanics
|
|||
{
|
||||
std::pair<ESM::EffectList, std::pair<bool, bool> > effects = getEffectList (iter->first);
|
||||
|
||||
const MWWorld::TimeStamp& start = iter->second.first;
|
||||
float magnitude = iter->second.second;
|
||||
const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
||||
iter!=effects.first.mList.end(); ++iter)
|
||||
int i = 0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIter (effects.first.mList.begin());
|
||||
effectIter!=effects.first.mList.end(); ++effectIter, ++i)
|
||||
{
|
||||
if (iter->mDuration)
|
||||
float magnitude = iter->second.mRandom[i];
|
||||
if (effectIter->mRange != iter->second.mRange)
|
||||
continue;
|
||||
|
||||
if (effectIter->mDuration)
|
||||
{
|
||||
int duration = iter->mDuration;
|
||||
int duration = effectIter->mDuration;
|
||||
|
||||
if (effects.second.first)
|
||||
duration *= magnitude;
|
||||
|
@ -89,9 +94,9 @@ namespace MWMechanics
|
|||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
iter->mEffectID);
|
||||
effectIter->mEffectID);
|
||||
|
||||
if (iter->mDuration==0)
|
||||
if (effectIter->mDuration==0)
|
||||
{
|
||||
param.mMagnitude =
|
||||
static_cast<int> (magnitude / (0.1 * magicEffect->mData.mBaseCost));
|
||||
|
@ -104,9 +109,9 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
param.mMagnitude = static_cast<int> (
|
||||
(iter->mMagnMax-iter->mMagnMin)*magnitude + iter->mMagnMin);
|
||||
(effectIter->mMagnMax-effectIter->mMagnMin)*magnitude + effectIter->mMagnMin);
|
||||
|
||||
mEffects.add (*iter, param);
|
||||
mEffects.add (*effectIter, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +120,10 @@ namespace MWMechanics
|
|||
|
||||
std::pair<ESM::EffectList, std::pair<bool, bool> > ActiveSpells::getEffectList (const std::string& id) const
|
||||
{
|
||||
if (const ESM::Enchantment* enchantment =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search (id))
|
||||
return std::make_pair (enchantment->mEffects, std::make_pair(false, false));
|
||||
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return std::make_pair (spell->mEffects, std::make_pair(false, false));
|
||||
|
@ -156,7 +165,7 @@ namespace MWMechanics
|
|||
: mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||
{}
|
||||
|
||||
bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor)
|
||||
bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range, const std::string& name)
|
||||
{
|
||||
std::pair<ESM::EffectList, std::pair<bool, bool> > effects = getEffectList (id);
|
||||
bool stacks = effects.second.second;
|
||||
|
@ -196,11 +205,41 @@ namespace MWMechanics
|
|||
random *= 0.25 * x;
|
||||
}
|
||||
|
||||
ActiveSpellParams params;
|
||||
for (unsigned int i=0; i<effects.first.mList.size(); ++i)
|
||||
params.mRandom.push_back(static_cast<float> (std::rand()) / RAND_MAX);
|
||||
params.mRange = range;
|
||||
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||
params.mName = name;
|
||||
|
||||
if (iter==mSpells.end() || stacks)
|
||||
mSpells.insert (std::make_pair (id,
|
||||
std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random)));
|
||||
mSpells.insert (std::make_pair (id, params));
|
||||
else
|
||||
iter->second = std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random);
|
||||
iter->second = params;
|
||||
|
||||
// Play sounds
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.first.mList.begin());
|
||||
iter!=effects.first.mList.end(); ++iter)
|
||||
{
|
||||
if (iter->mRange != range)
|
||||
continue;
|
||||
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
iter->mEffectID);
|
||||
|
||||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(!magicEffect->mHitSound.empty())
|
||||
sndMgr->playSound3D(actor, magicEffect->mHitSound, 1.0f, 1.0f);
|
||||
else
|
||||
sndMgr->playSound3D(actor, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
mSpellsChanged = true;
|
||||
|
||||
|
@ -249,13 +288,14 @@ namespace MWMechanics
|
|||
duration = iter->mDuration;
|
||||
}
|
||||
|
||||
if (effects.second.first)
|
||||
duration *= iterator->second.second;
|
||||
// Scale duration by magnitude if needed
|
||||
if (effects.second.first && iterator->second.mRandom.size())
|
||||
duration *= iterator->second.mRandom.front();
|
||||
|
||||
double scaledDuration = duration *
|
||||
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
|
||||
|
||||
double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp()-iterator->second.first;
|
||||
double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp()-iterator->second.mTimeStamp;
|
||||
|
||||
if (usedUp>=scaledDuration)
|
||||
return 0;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
|
@ -22,6 +24,22 @@ namespace MWWorld
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
struct ActiveSpellParams
|
||||
{
|
||||
// Only apply effects of this range type
|
||||
ESM::RangeType mRange;
|
||||
|
||||
// When the spell was added
|
||||
MWWorld::TimeStamp mTimeStamp;
|
||||
|
||||
// Random factor for each effect
|
||||
std::vector<float> mRandom;
|
||||
|
||||
// Display name, we need this for enchantments, which don't have a name - so you need to supply the
|
||||
// name of the item with the enchantment to addSpell
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
/// \brief Lasting spell effects
|
||||
///
|
||||
/// \note The name of this class is slightly misleading, since it also handels lasting potion
|
||||
|
@ -30,7 +48,7 @@ namespace MWMechanics
|
|||
{
|
||||
public:
|
||||
|
||||
typedef std::multimap<std::string, std::pair<MWWorld::TimeStamp, float> > TContainer;
|
||||
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
private:
|
||||
|
@ -51,9 +69,13 @@ namespace MWMechanics
|
|||
|
||||
ActiveSpells();
|
||||
|
||||
bool addSpell (const std::string& id, const MWWorld::Ptr& actor);
|
||||
bool addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range = ESM::RT_Self, const std::string& name = "");
|
||||
///< Overwrites an existing spell with the same ID. If the spell does not have any
|
||||
/// non-instant effects, it is ignored.
|
||||
/// @param id
|
||||
/// @param actor
|
||||
/// @param range Only effects with range type \a range will be applied
|
||||
/// @param name Display name for enchantments, since they don't have a name in their record
|
||||
///
|
||||
/// \return Has the spell been added?
|
||||
|
||||
|
|
|
@ -273,7 +273,7 @@ void MWMechanics::Alchemy::addPotion (const std::string& name)
|
|||
|
||||
newRecord.mName = name;
|
||||
|
||||
int index = static_cast<int> (std::rand()/static_cast<double> (RAND_MAX)*6);
|
||||
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" };
|
||||
|
|
|
@ -531,6 +531,11 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
|
|||
else
|
||||
sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f);
|
||||
}
|
||||
if (inv.getSelectedEnchantItem() != inv.end())
|
||||
{
|
||||
// Enchanted items cast immediately (no animation)
|
||||
MWBase::Environment::get().getWorld()->castSpell(mPtr);
|
||||
}
|
||||
}
|
||||
else if(mWeaponType == WeapType_PickProbe)
|
||||
{
|
||||
|
|
|
@ -653,6 +653,9 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
|||
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
|
||||
else if(evt.compare(off, len, "hit") == 0)
|
||||
MWWorld::Class::get(mPtr).hit(mPtr);
|
||||
|
||||
else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release")
|
||||
MWBase::Environment::get().getWorld()->castSpell(mPtr);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/spellsuccess.hpp"
|
||||
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
#include "../mwrender/animation.hpp"
|
||||
|
@ -1983,4 +1985,75 @@ namespace MWWorld
|
|||
return mGodMode;
|
||||
}
|
||||
|
||||
void World::castSpell(const Ptr &actor)
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
stats.setAttackingOrSpell(false);
|
||||
|
||||
std::string selectedSpell = stats.getSpells().getSelectedSpell();
|
||||
if (!selectedSpell.empty())
|
||||
{
|
||||
const ESM::Spell* spell = getStore().get<ESM::Spell>().search(selectedSpell);
|
||||
|
||||
// Check mana
|
||||
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
|
||||
if (magicka.getCurrent() < spell->mData.mCost)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce mana
|
||||
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
|
||||
stats.setMagicka(magicka);
|
||||
|
||||
// Check success
|
||||
int successChance = MWMechanics::getSpellSuccessChance(selectedSpell, actor);
|
||||
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (roll >= successChance)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
|
||||
// TODO sound: "Spell Failure <School>"
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, ESM::RT_Self);
|
||||
// TODO: RT_Range, RT_Touch
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
||||
if (inv.getSelectedEnchantItem() != inv.end())
|
||||
{
|
||||
MWWorld::Ptr item = *inv.getSelectedEnchantItem();
|
||||
std::string id = item.getClass().getEnchantment(item);
|
||||
const ESM::Enchantment* enchantment = getStore().get<ESM::Enchantment>().search (id);
|
||||
|
||||
// Check if there's enough charge left
|
||||
const float enchantCost = enchantment->mData.mCost;
|
||||
MWMechanics::NpcStats &stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified();
|
||||
const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10);
|
||||
|
||||
if (item.getCellRef().mEnchantmentCharge == -1)
|
||||
item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge;
|
||||
|
||||
if (item.getCellRef().mEnchantmentCharge < castCost)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reduce charge
|
||||
item.getCellRef().mEnchantmentCharge -= castCost;
|
||||
|
||||
std::string itemName = item.getClass().getName(item);
|
||||
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(id, actor, ESM::RT_Self, itemName);
|
||||
|
||||
|
||||
// TODO: RT_Range, RT_Touch
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -453,6 +453,8 @@ namespace MWWorld
|
|||
virtual bool getGodModeState();
|
||||
|
||||
virtual bool toggleGodMode();
|
||||
|
||||
virtual void castSpell (const MWWorld::Ptr& actor);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue