forked from mirror/openmw-tes3mp
Allow separate summoned creature instances for each spell ID (Fixes #2194)
This commit is contained in:
parent
464f8abb3f
commit
f267497c03
17 changed files with 329 additions and 167 deletions
|
@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
||||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||||
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor
|
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwstate
|
add_openmw_dir (mwstate
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
|
void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
|
||||||
const std::string& sourceName, int casterActorId,
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
float magnitude, float remainingTime, float totalTime)
|
float magnitude, float remainingTime, float totalTime)
|
||||||
{
|
{
|
||||||
MagicEffectInfo newEffectSource;
|
MagicEffectInfo newEffectSource;
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace MWGui
|
||||||
virtual ~EffectSourceVisitor() {}
|
virtual ~EffectSourceVisitor() {}
|
||||||
|
|
||||||
virtual void visit (MWMechanics::EffectKey key,
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
const std::string& sourceName, int casterActorId,
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
float magnitude, float remainingTime = -1, float totalTime = -1);
|
float magnitude, float remainingTime = -1, float totalTime = -1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -195,7 +195,7 @@ namespace MWMechanics
|
||||||
float magnitude = effectIt->mMagnitude;
|
float magnitude = effectIt->mMagnitude;
|
||||||
|
|
||||||
if (magnitude)
|
if (magnitude)
|
||||||
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
|
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,6 +229,22 @@ namespace MWMechanics
|
||||||
mSpellsChanged = true;
|
mSpellsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId)
|
||||||
|
{
|
||||||
|
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||||
|
{
|
||||||
|
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
|
||||||
|
effectIt != it->second.mEffects.end();)
|
||||||
|
{
|
||||||
|
if (effectIt->mEffectId == effectId && it->first == sourceId)
|
||||||
|
effectIt = it->second.mEffects.erase(effectIt);
|
||||||
|
else
|
||||||
|
++effectIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mSpellsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
void ActiveSpells::purge(int casterActorId)
|
void ActiveSpells::purge(int casterActorId)
|
||||||
{
|
{
|
||||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||||
|
|
|
@ -82,6 +82,9 @@ namespace MWMechanics
|
||||||
/// Remove all active effects with this effect id
|
/// Remove all active effects with this effect id
|
||||||
void purgeEffect (short effectId);
|
void purgeEffect (short effectId);
|
||||||
|
|
||||||
|
/// Remove all active effects with this effect id and source id
|
||||||
|
void purgeEffect (short effectId, const std::string& sourceId);
|
||||||
|
|
||||||
/// Remove all active effects, if roll succeeds (for each effect)
|
/// Remove all active effects, if roll succeeds (for each effect)
|
||||||
void purgeAll (float chance);
|
void purgeAll (float chance);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include "aipursue.hpp"
|
#include "aipursue.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
|
#include "summoning.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -105,7 +106,7 @@ public:
|
||||||
, mCommanded(false){}
|
, mCommanded(false){}
|
||||||
|
|
||||||
virtual void visit (MWMechanics::EffectKey key,
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
const std::string& sourceName, int casterActorId,
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
float magnitude, float remainingTime = -1, float totalTime = -1)
|
float magnitude, float remainingTime = -1, float totalTime = -1)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
@ -165,30 +166,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)
|
|
||||||
{
|
|
||||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
|
|
||||||
if (!ptr.isEmpty())
|
|
||||||
{
|
|
||||||
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
|
|
||||||
// plays though, which is a rather lame exploit in vanilla.
|
|
||||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
|
||||||
|
|
||||||
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
|
||||||
.search("VFX_Summon_End");
|
|
||||||
if (fx)
|
|
||||||
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
|
|
||||||
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We didn't find the creature. It's probably in an inactive cell.
|
|
||||||
// Add to graveyard so we can delete it when the cell becomes active.
|
|
||||||
std::vector<int>& graveyard = casterStats.getSummonedCreatureGraveyard();
|
|
||||||
graveyard.push_back(creatureActorId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -203,7 +180,7 @@ namespace MWMechanics
|
||||||
: mCreature(trappedCreature) {}
|
: mCreature(trappedCreature) {}
|
||||||
|
|
||||||
virtual void visit (MWMechanics::EffectKey key,
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
const std::string& sourceName, int casterActorId,
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
float magnitude, float remainingTime = -1, float totalTime = -1)
|
float magnitude, float remainingTime = -1, float totalTime = -1)
|
||||||
{
|
{
|
||||||
if (key.mId != ESM::MagicEffect::Soultrap)
|
if (key.mId != ESM::MagicEffect::Soultrap)
|
||||||
|
@ -782,131 +759,11 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update summon effects
|
UpdateSummonedCreatures updateSummonedCreatures(ptr);
|
||||||
static std::map<int, std::string> summonMap;
|
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
|
||||||
if (summonMap.empty())
|
if (ptr.getClass().hasInventoryStore(ptr))
|
||||||
{
|
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
|
||||||
summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID";
|
updateSummonedCreatures.finish();
|
||||||
summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID";
|
|
||||||
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
|
|
||||||
for (std::map<int, std::string>::iterator it = summonMap.begin(); it != summonMap.end(); ++it)
|
|
||||||
{
|
|
||||||
bool found = creatureMap.find(it->first) != creatureMap.end();
|
|
||||||
int magnitude = creatureStats.getMagicEffects().get(it->first).getMagnitude();
|
|
||||||
if (found != (magnitude > 0))
|
|
||||||
{
|
|
||||||
if (magnitude > 0)
|
|
||||||
{
|
|
||||||
ESM::Position ipos = ptr.getRefData().getPosition();
|
|
||||||
Ogre::Vector3 pos(ipos.pos);
|
|
||||||
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
|
|
||||||
const float distance = 50;
|
|
||||||
pos = pos + distance*rot.yAxis();
|
|
||||||
ipos.pos[0] = pos.x;
|
|
||||||
ipos.pos[1] = pos.y;
|
|
||||||
ipos.pos[2] = pos.z;
|
|
||||||
ipos.rot[0] = 0;
|
|
||||||
ipos.rot[1] = 0;
|
|
||||||
ipos.rot[2] = 0;
|
|
||||||
|
|
||||||
std::string creatureID =
|
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString();
|
|
||||||
|
|
||||||
if (!creatureID.empty())
|
|
||||||
{
|
|
||||||
MWWorld::CellStore* store = ptr.getCell();
|
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
|
|
||||||
ref.getPtr().getCellRef().setPosition(ipos);
|
|
||||||
|
|
||||||
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
|
|
||||||
|
|
||||||
// Make the summoned creature follow its master and help in fights
|
|
||||||
AiFollow package(ptr.getCellRef().getRefId());
|
|
||||||
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
|
|
||||||
int creatureActorId = summonedCreatureStats.getActorId();
|
|
||||||
|
|
||||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
|
||||||
|
|
||||||
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
|
|
||||||
if (anim)
|
|
||||||
{
|
|
||||||
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
|
||||||
.search("VFX_Summon_Start");
|
|
||||||
if (fx)
|
|
||||||
anim->addEffect("meshes\\" + fx->mModel, -1, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
creatureMap.insert(std::make_pair(it->first, creatureActorId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Effect has ended
|
|
||||||
std::map<int, int>::iterator foundCreature = creatureMap.find(it->first);
|
|
||||||
cleanupSummonedCreature(creatureStats, foundCreature->second);
|
|
||||||
creatureMap.erase(foundCreature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::map<int, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
|
||||||
{
|
|
||||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
|
||||||
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead())
|
|
||||||
{
|
|
||||||
// Purge the magic effect so a new creature can be summoned if desired
|
|
||||||
creatureStats.getActiveSpells().purgeEffect(it->first);
|
|
||||||
if (ptr.getClass().hasInventoryStore(ptr))
|
|
||||||
ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first);
|
|
||||||
|
|
||||||
cleanupSummonedCreature(creatureStats, it->second);
|
|
||||||
creatureMap.erase(it++);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
|
|
||||||
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
|
|
||||||
{
|
|
||||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
|
|
||||||
if (!ptr.isEmpty())
|
|
||||||
{
|
|
||||||
it = graveyard.erase(it);
|
|
||||||
|
|
||||||
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
|
||||||
.search("VFX_Summon_End");
|
|
||||||
if (fx)
|
|
||||||
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
|
|
||||||
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)
|
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)
|
||||||
|
|
|
@ -638,7 +638,7 @@ namespace MWMechanics
|
||||||
mDeathAnimation = index;
|
mDeathAnimation = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<int, int>& CreatureStats::getSummonedCreatureMap()
|
std::map<CreatureStats::SummonKey, int>& CreatureStats::getSummonedCreatureMap()
|
||||||
{
|
{
|
||||||
return mSummonedCreatures;
|
return mSummonedCreatures;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,11 @@ namespace MWMechanics
|
||||||
// The index of the death animation that was played
|
// The index of the death animation that was played
|
||||||
unsigned char mDeathAnimation;
|
unsigned char mDeathAnimation;
|
||||||
|
|
||||||
// <ESM::MagicEffect index, ActorId>
|
public:
|
||||||
std::map<int, int> mSummonedCreatures;
|
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||||
|
private:
|
||||||
|
std::map<SummonKey, int> mSummonedCreatures; // <SummonKey, ActorId>
|
||||||
|
|
||||||
// Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet.
|
// Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet.
|
||||||
// This may be necessary when the creature is in an inactive cell.
|
// This may be necessary when the creature is in an inactive cell.
|
||||||
std::vector<int> mSummonGraveyard;
|
std::vector<int> mSummonGraveyard;
|
||||||
|
@ -216,8 +219,8 @@ namespace MWMechanics
|
||||||
void setBlock(bool value);
|
void setBlock(bool value);
|
||||||
bool getBlock() const;
|
bool getBlock() const;
|
||||||
|
|
||||||
std::map<int, int>& getSummonedCreatureMap();
|
std::map<SummonKey, int>& getSummonedCreatureMap(); // <SummonKey, ActorId of summoned creature>
|
||||||
std::vector<int>& getSummonedCreatureGraveyard();
|
std::vector<int>& getSummonedCreatureGraveyard(); // ActorIds
|
||||||
|
|
||||||
enum Flag
|
enum Flag
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace MWMechanics
|
||||||
struct EffectSourceVisitor
|
struct EffectSourceVisitor
|
||||||
{
|
{
|
||||||
virtual void visit (MWMechanics::EffectKey key,
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
const std::string& sourceName, int casterActorId,
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
float magnitude, float remainingTime = -1, float totalTime = -1) = 0;
|
float magnitude, float remainingTime = -1, float totalTime = -1) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "magiceffects.hpp"
|
#include "magiceffects.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
|
#include "summoning.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
|
@ -249,7 +249,7 @@ namespace MWMechanics
|
||||||
random = it->second.at(i);
|
random = it->second.at(i);
|
||||||
|
|
||||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
|
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
|
||||||
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude);
|
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, spell->mId, -1, magnitude);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
195
apps/openmw/mwmechanics/summoning.cpp
Normal file
195
apps/openmw/mwmechanics/summoning.cpp
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#include "summoning.hpp"
|
||||||
|
|
||||||
|
#include <OgreVector3.h>
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/manualref.hpp"
|
||||||
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
|
#include "../mwrender/animation.hpp"
|
||||||
|
|
||||||
|
#include "creaturestats.hpp"
|
||||||
|
#include "aifollow.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
|
||||||
|
if (!ptr.isEmpty())
|
||||||
|
{
|
||||||
|
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
|
||||||
|
// plays though, which is a rather lame exploit in vanilla.
|
||||||
|
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||||
|
|
||||||
|
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
||||||
|
.search("VFX_Summon_End");
|
||||||
|
if (fx)
|
||||||
|
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
|
||||||
|
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We didn't find the creature. It's probably in an inactive cell.
|
||||||
|
// Add to graveyard so we can delete it when the cell becomes active.
|
||||||
|
std::vector<int>& graveyard = casterStats.getSummonedCreatureGraveyard();
|
||||||
|
graveyard.push_back(creatureActorId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSummonedCreatures::UpdateSummonedCreatures(const MWWorld::Ptr &actor)
|
||||||
|
: mActor(actor)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime)
|
||||||
|
{
|
||||||
|
if (key.mId >= ESM::MagicEffect::SummonScamp
|
||||||
|
&& key.mId <= ESM::MagicEffect::SummonStormAtronach && magnitude > 0)
|
||||||
|
{
|
||||||
|
mActiveEffects.insert(std::make_pair(key.mId, sourceId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSummonedCreatures::finish()
|
||||||
|
{
|
||||||
|
static std::map<int, std::string> summonMap;
|
||||||
|
if (summonMap.empty())
|
||||||
|
{
|
||||||
|
summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID";
|
||||||
|
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
|
||||||
|
}
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
|
||||||
|
|
||||||
|
// Update summon effects
|
||||||
|
std::map<CreatureStats::SummonKey, int>& creatureMap = creatureStats.getSummonedCreatureMap();
|
||||||
|
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||||
|
{
|
||||||
|
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
// Effect has ended
|
||||||
|
cleanupSummonedCreature(creatureStats, it->second);
|
||||||
|
creatureMap.erase(it++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::set<std::pair<int, std::string> >::iterator it = mActiveEffects.begin(); it != mActiveEffects.end(); ++it)
|
||||||
|
{
|
||||||
|
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
ESM::Position ipos = mActor.getRefData().getPosition();
|
||||||
|
Ogre::Vector3 pos(ipos.pos);
|
||||||
|
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
|
||||||
|
const float distance = 50;
|
||||||
|
pos = pos + distance*rot.yAxis();
|
||||||
|
ipos.pos[0] = pos.x;
|
||||||
|
ipos.pos[1] = pos.y;
|
||||||
|
ipos.pos[2] = pos.z;
|
||||||
|
ipos.rot[0] = 0;
|
||||||
|
ipos.rot[1] = 0;
|
||||||
|
ipos.rot[2] = 0;
|
||||||
|
|
||||||
|
const std::string& creatureGmst = summonMap[it->first];
|
||||||
|
std::string creatureID =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
|
||||||
|
|
||||||
|
if (!creatureID.empty())
|
||||||
|
{
|
||||||
|
MWWorld::CellStore* store = mActor.getCell();
|
||||||
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
|
||||||
|
ref.getPtr().getCellRef().setPosition(ipos);
|
||||||
|
|
||||||
|
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
|
||||||
|
|
||||||
|
// Make the summoned creature follow its master and help in fights
|
||||||
|
AiFollow package(mActor.getCellRef().getRefId());
|
||||||
|
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
|
||||||
|
int creatureActorId = summonedCreatureStats.getActorId();
|
||||||
|
|
||||||
|
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
||||||
|
|
||||||
|
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
|
||||||
|
if (anim)
|
||||||
|
{
|
||||||
|
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
||||||
|
.search("VFX_Summon_Start");
|
||||||
|
if (fx)
|
||||||
|
anim->addEffect("meshes\\" + fx->mModel, -1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
creatureMap.insert(std::make_pair(*it, creatureActorId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
||||||
|
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead())
|
||||||
|
{
|
||||||
|
// Purge the magic effect so a new creature can be summoned if desired
|
||||||
|
creatureStats.getActiveSpells().purgeEffect(it->first.first, it->first.second);
|
||||||
|
if (mActor.getClass().hasInventoryStore(ptr))
|
||||||
|
mActor.getClass().getInventoryStore(mActor).purgeEffect(it->first.first, it->first.second);
|
||||||
|
|
||||||
|
cleanupSummonedCreature(creatureStats, it->second);
|
||||||
|
creatureMap.erase(it++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
|
||||||
|
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
|
||||||
|
if (!ptr.isEmpty())
|
||||||
|
{
|
||||||
|
it = graveyard.erase(it);
|
||||||
|
|
||||||
|
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
||||||
|
.search("VFX_Summon_End");
|
||||||
|
if (fx)
|
||||||
|
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
|
||||||
|
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
apps/openmw/mwmechanics/summoning.hpp
Normal file
35
apps/openmw/mwmechanics/summoning.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef OPENMW_MECHANICS_SUMMONING_H
|
||||||
|
#define OPENMW_MECHANICS_SUMMONING_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "magiceffects.hpp"
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
class CreatureStats;
|
||||||
|
|
||||||
|
struct UpdateSummonedCreatures : public EffectSourceVisitor
|
||||||
|
{
|
||||||
|
UpdateSummonedCreatures(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
|
virtual void visit (MWMechanics::EffectKey key,
|
||||||
|
const std::string& sourceName, const std::string& sourceId, int casterActorId,
|
||||||
|
float magnitude, float remainingTime = -1, float totalTime = -1);
|
||||||
|
|
||||||
|
/// To call after all effect sources have been visited
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MWWorld::Ptr mActor;
|
||||||
|
|
||||||
|
std::set<std::pair<int, std::string> > mActiveEffects;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -583,7 +583,8 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
|
||||||
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i];
|
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i];
|
||||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
|
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
|
||||||
magnitude *= params.mMultiplier;
|
magnitude *= params.mMultiplier;
|
||||||
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude);
|
if (magnitude > 0)
|
||||||
|
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), (**iter).getCellRef().getRefId(), -1, magnitude);
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
@ -639,6 +640,52 @@ void MWWorld::InventoryStore::purgeEffect(short effectId)
|
||||||
mMagicEffects.remove(MWMechanics::EffectKey(effectId));
|
mMagicEffects.remove(MWMechanics::EffectKey(effectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId)
|
||||||
|
{
|
||||||
|
TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId);
|
||||||
|
if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (*iter==end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((*iter)->getClass().getId(**iter) != sourceId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string enchantmentId = (*iter)->getClass().getEnchantment (**iter);
|
||||||
|
|
||||||
|
if (!enchantmentId.empty())
|
||||||
|
{
|
||||||
|
const ESM::Enchantment& enchantment =
|
||||||
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find (enchantmentId);
|
||||||
|
|
||||||
|
if (enchantment.mData.mType != ESM::Enchantment::ConstantEffect)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::vector<EffectParams>& params = effectMagnitudeIt->second;
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment.mEffects.mList.begin());
|
||||||
|
effectIt!=enchantment.mEffects.mList.end(); ++effectIt, ++i)
|
||||||
|
{
|
||||||
|
if (effectIt->mEffectID != effectId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
|
||||||
|
magnitude *= params[i].mMultiplier;
|
||||||
|
|
||||||
|
if (magnitude)
|
||||||
|
mMagicEffects.add (*effectIt, -magnitude);
|
||||||
|
|
||||||
|
params[i].mMultiplier = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::clear()
|
void MWWorld::InventoryStore::clear()
|
||||||
{
|
{
|
||||||
mSlots.clear();
|
mSlots.clear();
|
||||||
|
|
|
@ -203,6 +203,9 @@ namespace MWWorld
|
||||||
void purgeEffect (short effectId);
|
void purgeEffect (short effectId);
|
||||||
///< Remove a magic effect
|
///< Remove a magic effect
|
||||||
|
|
||||||
|
void purgeEffect (short effectId, const std::string& sourceId);
|
||||||
|
///< Remove a magic effect
|
||||||
|
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
///< Empty container.
|
///< Empty container.
|
||||||
|
|
||||||
|
|
|
@ -94,9 +94,10 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
int magicEffect;
|
int magicEffect;
|
||||||
esm.getHT(magicEffect);
|
esm.getHT(magicEffect);
|
||||||
|
std::string source = esm.getHNOString("SOUR");
|
||||||
int actorId;
|
int actorId;
|
||||||
esm.getHNT (actorId, "ACID");
|
esm.getHNT (actorId, "ACID");
|
||||||
mSummonedCreatureMap[magicEffect] = actorId;
|
mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (esm.isNextSub("GRAV"))
|
while (esm.isNextSub("GRAV"))
|
||||||
|
@ -204,9 +205,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
||||||
mAiSequence.save(esm);
|
mAiSequence.save(esm);
|
||||||
mMagicEffects.save(esm);
|
mMagicEffects.save(esm);
|
||||||
|
|
||||||
for (std::map<int, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it)
|
for (std::map<std::pair<int, std::string>, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it)
|
||||||
{
|
{
|
||||||
esm.writeHNT ("SUMM", it->first);
|
esm.writeHNT ("SUMM", it->first.first);
|
||||||
|
esm.writeHNString ("SOUR", it->first.second);
|
||||||
esm.writeHNT ("ACID", it->second);
|
esm.writeHNT ("ACID", it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace ESM
|
||||||
bool mHasAiSettings;
|
bool mHasAiSettings;
|
||||||
StatState<int> mAiSettings[4];
|
StatState<int> mAiSettings[4];
|
||||||
|
|
||||||
std::map<int, int> mSummonedCreatureMap;
|
std::map<std::pair<int, std::string>, int> mSummonedCreatureMap;
|
||||||
std::vector<int> mSummonGraveyard;
|
std::vector<int> mSummonGraveyard;
|
||||||
|
|
||||||
ESM::TimeStamp mTradeTime;
|
ESM::TimeStamp mTradeTime;
|
||||||
|
|
Loading…
Reference in a new issue