forked from teamnwah/openmw-tes3coop
Allow separate summoned creature instances for each spell ID (Fixes #2194)
parent
464f8abb3f
commit
f267497c03
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue