mirror of
https://github.com/OpenMW/openmw.git
synced 2026-01-19 03:00:53 +00:00
Replace actor ids in magic effects with refnums
This commit is contained in:
parent
8020bfcafd
commit
c4bc040b4b
26 changed files with 155 additions and 147 deletions
|
|
@ -294,7 +294,7 @@ namespace MWBase
|
|||
/// It only applies to the current form the NPC is in.
|
||||
virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0;
|
||||
virtual void cleanupSummonedCreature(ESM::RefNum creature) = 0;
|
||||
|
||||
virtual void confiscateStolenItemToOwner(
|
||||
const MWWorld::Ptr& player, const MWWorld::Ptr& item, const MWWorld::Ptr& victim, int count)
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ namespace MWGui
|
|||
// Clean up summoned creatures as well
|
||||
auto& creatureMap = creatureStats.getSummonedCreatureMap();
|
||||
for (const auto& creature : creatureMap)
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(ptr, creature.second);
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(creature.second);
|
||||
creatureMap.clear();
|
||||
|
||||
// Check if we are a summon and inform our master we've bit the dust
|
||||
|
|
@ -323,7 +323,7 @@ namespace MWGui
|
|||
const auto& summoner = package->getTarget();
|
||||
auto& summons = summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap();
|
||||
auto it = std::find_if(summons.begin(), summons.end(),
|
||||
[&](const auto& entry) { return entry.second == creatureStats.getActorId(); });
|
||||
[&](const auto& entry) { return entry.second == ptr.getCellRef().getRefNum(); });
|
||||
if (it != summons.end())
|
||||
{
|
||||
auto summon = *it;
|
||||
|
|
|
|||
|
|
@ -545,9 +545,8 @@ namespace MWLua
|
|||
return sol::make_object(lua, LObject(itemPtr));
|
||||
});
|
||||
activeSpellT["caster"]
|
||||
= sol::readonly_property([lua = state.lua_state()](const ActiveSpell& activeSpell) -> sol::object {
|
||||
auto caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(
|
||||
activeSpell.mParams.getCasterActorId());
|
||||
= sol::readonly_property([lua = lua.lua_state()](const ActiveSpell& activeSpell) -> sol::object {
|
||||
auto caster = MWBase::Environment::get().getWorldModel()->getPtr(activeSpell.mParams.getCaster());
|
||||
if (caster.isEmpty())
|
||||
return sol::nil;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <components/misc/strings/algorithm.hpp>
|
||||
|
||||
#include <components/esm/generatedrefid.hpp>
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/loadench.hpp>
|
||||
#include <components/esm3/loadmgef.hpp>
|
||||
#include <components/esm3/loadstat.hpp>
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/worldmodel.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -103,20 +105,19 @@ namespace MWMechanics
|
|||
const MWWorld::Ptr& caster, const ESM::RefId& id, std::string_view sourceName, ESM::RefNum item)
|
||||
: mSourceSpellId(id)
|
||||
, mDisplayName(sourceName)
|
||||
, mCasterActorId(-1)
|
||||
, mItem(item)
|
||||
, mFlags()
|
||||
, mWorsenings(-1)
|
||||
{
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
mCasterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
mCaster = caster.getCellRef().getRefNum();
|
||||
}
|
||||
|
||||
ActiveSpells::ActiveSpellParams::ActiveSpellParams(
|
||||
const ESM::Spell* spell, const MWWorld::Ptr& actor, bool ignoreResistances)
|
||||
: mSourceSpellId(spell->mId)
|
||||
, mDisplayName(spell->mName)
|
||||
, mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
, mCaster(actor.getCellRef().getRefNum())
|
||||
, mFlags()
|
||||
, mWorsenings(-1)
|
||||
{
|
||||
|
|
@ -131,7 +132,7 @@ namespace MWMechanics
|
|||
const MWWorld::ConstPtr& item, const ESM::Enchantment* enchantment, const MWWorld::Ptr& actor)
|
||||
: mSourceSpellId(item.getCellRef().getRefId())
|
||||
, mDisplayName(item.getClass().getName(item))
|
||||
, mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
, mCaster(actor.getCellRef().getRefNum())
|
||||
, mItem(item.getCellRef().getRefNum())
|
||||
, mFlags()
|
||||
, mWorsenings(-1)
|
||||
|
|
@ -146,7 +147,7 @@ namespace MWMechanics
|
|||
, mSourceSpellId(params.mSourceSpellId)
|
||||
, mEffects(params.mEffects)
|
||||
, mDisplayName(params.mDisplayName)
|
||||
, mCasterActorId(params.mCasterActorId)
|
||||
, mCaster(params.mCaster)
|
||||
, mItem(params.mItem)
|
||||
, mFlags(params.mFlags)
|
||||
, mWorsenings(params.mWorsenings)
|
||||
|
|
@ -157,7 +158,7 @@ namespace MWMechanics
|
|||
ActiveSpells::ActiveSpellParams::ActiveSpellParams(const ActiveSpellParams& params, const MWWorld::Ptr& actor)
|
||||
: mSourceSpellId(params.mSourceSpellId)
|
||||
, mDisplayName(params.mDisplayName)
|
||||
, mCasterActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
, mCaster(actor.getCellRef().getRefNum())
|
||||
, mItem(params.mItem)
|
||||
, mFlags(params.mFlags)
|
||||
, mWorsenings(-1)
|
||||
|
|
@ -171,7 +172,7 @@ namespace MWMechanics
|
|||
params.mSourceSpellId = mSourceSpellId;
|
||||
params.mEffects = mEffects;
|
||||
params.mDisplayName = mDisplayName;
|
||||
params.mCasterActorId = mCasterActorId;
|
||||
params.mCaster = mCaster;
|
||||
params.mItem = mItem;
|
||||
params.mFlags = mFlags;
|
||||
params.mWorsenings = mWorsenings;
|
||||
|
|
@ -375,8 +376,7 @@ namespace MWMechanics
|
|||
bool ActiveSpells::updateActiveSpell(
|
||||
const MWWorld::Ptr& ptr, float duration, Collection::iterator& spellIt, UpdateContext& context)
|
||||
{
|
||||
const auto caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(
|
||||
spellIt->mCasterActorId); // Maybe make this search outside active grid?
|
||||
const auto caster = MWBase::Environment::get().getWorldModel()->getPtr(spellIt->mCaster);
|
||||
bool removedSpell = false;
|
||||
std::optional<ActiveSpellParams> reflected;
|
||||
for (auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();)
|
||||
|
|
@ -484,8 +484,8 @@ namespace MWMechanics
|
|||
if (!spell.hasFlag(ESM::ActiveSpells::Flag_Stackable))
|
||||
{
|
||||
auto found = std::find_if(mSpells.begin(), mSpells.end(), [&](const auto& existing) {
|
||||
return spell.mSourceSpellId == existing.mSourceSpellId
|
||||
&& spell.mCasterActorId == existing.mCasterActorId && spell.mItem == existing.mItem;
|
||||
return spell.mSourceSpellId == existing.mSourceSpellId && spell.mCaster == existing.mCaster
|
||||
&& spell.mItem == existing.mItem;
|
||||
});
|
||||
if (found != mSpells.end())
|
||||
{
|
||||
|
|
@ -659,9 +659,9 @@ namespace MWMechanics
|
|||
ptr);
|
||||
}
|
||||
|
||||
void ActiveSpells::purge(const MWWorld::Ptr& ptr, int casterActorId)
|
||||
void ActiveSpells::purge(const MWWorld::Ptr& ptr, ESM::RefNum actor)
|
||||
{
|
||||
purge([=](const ActiveSpellParams& params) { return params.mCasterActorId == casterActorId; }, ptr);
|
||||
purge([=](const ActiveSpellParams& params) { return params.mCaster == actor; }, ptr);
|
||||
}
|
||||
|
||||
void ActiveSpells::clear(const MWWorld::Ptr& ptr)
|
||||
|
|
@ -698,6 +698,22 @@ namespace MWMechanics
|
|||
}
|
||||
for (const ESM::ActiveSpells::ActiveSpellParams& spell : state.mQueue)
|
||||
mQueue.emplace_back(ActiveSpellParams{ spell });
|
||||
if (state.mActorIdConverter)
|
||||
{
|
||||
const auto convertSummons = [converter = state.mActorIdConverter](auto& collection) {
|
||||
for (ActiveSpellParams& params : collection)
|
||||
{
|
||||
converter->convert(params.mCaster, params.mCaster.mIndex);
|
||||
for (ESM::ActiveEffect& effect : params.mEffects)
|
||||
{
|
||||
if (ESM::RefNum* refNum = std::get_if<ESM::RefNum>(&effect.mArg))
|
||||
converter->convert(*refNum, refNum->mIndex);
|
||||
}
|
||||
}
|
||||
};
|
||||
convertSummons(mSpells);
|
||||
convertSummons(mQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveSpells::unloadActor(const MWWorld::Ptr& ptr)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace MWMechanics
|
|||
ESM::RefId mSourceSpellId;
|
||||
std::vector<ActiveEffect> mEffects;
|
||||
std::string mDisplayName;
|
||||
int mCasterActorId;
|
||||
ESM::RefNum mCaster;
|
||||
ESM::RefNum mItem;
|
||||
ESM::ActiveSpells::Flags mFlags;
|
||||
int mWorsenings;
|
||||
|
|
@ -69,7 +69,7 @@ namespace MWMechanics
|
|||
const std::vector<ActiveEffect>& getEffects() const { return mEffects; }
|
||||
std::vector<ActiveEffect>& getEffects() { return mEffects; }
|
||||
|
||||
int getCasterActorId() const { return mCasterActorId; }
|
||||
ESM::RefNum getCaster() const { return mCaster; }
|
||||
|
||||
int getWorsenings() const { return mWorsenings; }
|
||||
|
||||
|
|
@ -157,8 +157,8 @@ namespace MWMechanics
|
|||
void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr);
|
||||
void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr);
|
||||
|
||||
/// Remove all effects that were cast by \a casterActorId
|
||||
void purge(const MWWorld::Ptr& ptr, int casterActorId);
|
||||
/// Remove all effects that were cast by \a actor
|
||||
void purge(const MWWorld::Ptr& ptr, ESM::RefNum actor);
|
||||
|
||||
/// Remove all spells
|
||||
void clear(const MWWorld::Ptr& ptr);
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ namespace
|
|||
{
|
||||
if (effect.mEffectId != ESM::MagicEffect::Soultrap || effect.mMagnitude <= 0.f)
|
||||
continue;
|
||||
MWWorld::Ptr caster = world->searchPtrViaActorId(params.getCasterActorId());
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorldModel()->getPtr(params.getCaster());
|
||||
if (caster.isEmpty() || !caster.getClass().isActor())
|
||||
continue;
|
||||
|
||||
|
|
@ -791,8 +791,7 @@ namespace MWMechanics
|
|||
{
|
||||
bool actorKilled = false;
|
||||
|
||||
MWWorld::Ptr caster
|
||||
= MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.getCasterActorId());
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorldModel()->getPtr(spell.getCaster());
|
||||
if (caster.isEmpty())
|
||||
continue;
|
||||
for (const auto& effect : spell.getEffects())
|
||||
|
|
@ -833,7 +832,7 @@ namespace MWMechanics
|
|||
if (!creature.isInCell())
|
||||
return;
|
||||
|
||||
if (!creatureStats.getSummonedCreatureMap().empty() || !creatureStats.getSummonedCreatureGraveyard().empty())
|
||||
if (!creatureStats.getSummonedCreatureMap().empty())
|
||||
updateSummons(creature, mTimerDisposeSummonsCorpses == 0.f);
|
||||
}
|
||||
|
||||
|
|
@ -1816,7 +1815,7 @@ namespace MWMechanics
|
|||
= stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Vampirism).getMagnitude();
|
||||
stats.getActiveSpells().clear(actor.getPtr());
|
||||
// Make sure spell effects are removed
|
||||
purgeSpellEffects(stats.getActorId());
|
||||
purgeSpellEffects(actor.getPtr().getCellRef().getRefNum());
|
||||
|
||||
stats.getMagicEffects().add(ESM::MagicEffect::Vampirism, vampirism);
|
||||
|
||||
|
|
@ -1834,9 +1833,9 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void Actors::cleanupSummonedCreature(MWMechanics::CreatureStats& casterStats, int creatureActorId) const
|
||||
void Actors::cleanupSummonedCreature(ESM::RefNum creature) const
|
||||
{
|
||||
const MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
|
||||
const MWWorld::Ptr ptr = MWBase::Environment::get().getWorldModel()->getPtr(creature);
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||
|
|
@ -1849,24 +1848,16 @@ namespace MWMechanics
|
|||
ptr.getRefData().getPosition().asVec3());
|
||||
|
||||
// Remove the summoned creature's summoned creatures as well
|
||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||
auto& creatureMap = stats.getSummonedCreatureMap();
|
||||
for (const auto& creature : creatureMap)
|
||||
cleanupSummonedCreature(stats, creature.second);
|
||||
auto& creatureMap = ptr.getClass().getCreatureStats(ptr).getSummonedCreatureMap();
|
||||
for (const auto& [_, refNum] : creatureMap)
|
||||
cleanupSummonedCreature(refNum);
|
||||
creatureMap.clear();
|
||||
}
|
||||
else if (creatureActorId != -1)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
purgeSpellEffects(creatureActorId);
|
||||
purgeSpellEffects(creature);
|
||||
}
|
||||
|
||||
void Actors::purgeSpellEffects(int casterActorId) const
|
||||
void Actors::purgeSpellEffects(ESM::RefNum creature) const
|
||||
{
|
||||
for (const Actor& actor : mActors)
|
||||
{
|
||||
|
|
@ -1874,7 +1865,7 @@ namespace MWMechanics
|
|||
continue;
|
||||
MWMechanics::ActiveSpells& spells
|
||||
= actor.getPtr().getClass().getCreatureStats(actor.getPtr()).getActiveSpells();
|
||||
spells.purge(actor.getPtr(), casterActorId);
|
||||
spells.purge(actor.getPtr(), creature);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ namespace MWMechanics
|
|||
{
|
||||
class Actor;
|
||||
class CharacterController;
|
||||
class CreatureStats;
|
||||
class SidingCache;
|
||||
|
||||
class Actors
|
||||
|
|
@ -128,7 +127,7 @@ namespace MWMechanics
|
|||
|
||||
bool isAnyObjectInRange(const osg::Vec3f& position, float radius) const;
|
||||
|
||||
void cleanupSummonedCreature(CreatureStats& casterStats, int creatureActorId) const;
|
||||
void cleanupSummonedCreature(ESM::RefNum creature) const;
|
||||
|
||||
/// Returns the list of actors which are siding with the given actor in fights
|
||||
/**ie AiFollow or AiEscort is active and the target is the actor **/
|
||||
|
|
@ -189,7 +188,7 @@ namespace MWMechanics
|
|||
|
||||
void killDeadActors();
|
||||
|
||||
void purgeSpellEffects(int casterActorId) const;
|
||||
void purgeSpellEffects(ESM::RefNum creature) const;
|
||||
|
||||
void predictAndAvoidCollisions(float duration) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace MWMechanics
|
|||
|
||||
AiFollow::AiFollow(const ESM::AiSequence::AiFollow* follow)
|
||||
: TypedAiPackage<AiFollow>(
|
||||
makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded).withRepeat(follow->mRepeat))
|
||||
makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded).withRepeat(follow->mRepeat))
|
||||
, mAlwaysFollow(follow->mAlwaysFollow)
|
||||
, mDuration(follow->mData.mDuration)
|
||||
, mRemainingDuration(follow->mRemainingDuration)
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ namespace MWMechanics
|
|||
bool mShortcutProhibited = false; // shortcutting may be prohibited after unsuccessful attempt
|
||||
|
||||
friend class AiSequence;
|
||||
|
||||
private:
|
||||
bool isNearInactiveCell(osg::Vec3f position);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ namespace MWMechanics
|
|||
if (target.isEmpty() || !target.getCellRef().getCount() || !target.getRefData().isEnabled())
|
||||
return true;
|
||||
|
||||
// This is equivalent to checking if the actor is registered with the mechanics manager since every actor has a script
|
||||
// This is equivalent to checking if the actor is registered with the mechanics manager since every actor has a
|
||||
// script
|
||||
if (const MWLua::LocalScripts* scripts = target.getRefData().getLuaScripts())
|
||||
{
|
||||
if (!scripts->isActive())
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/creaturestats.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
|
|
@ -550,7 +551,6 @@ namespace MWMechanics
|
|||
mMagicEffects.writeState(state.mMagicEffects);
|
||||
|
||||
state.mSummonedCreatures = mSummonedCreatures;
|
||||
state.mSummonGraveyard = mSummonGraveyard;
|
||||
|
||||
state.mHasAiSettings = true;
|
||||
for (size_t i = 0; i < state.mAiSettings.size(); ++i)
|
||||
|
|
@ -596,7 +596,7 @@ namespace MWMechanics
|
|||
mActorId = state.mActorId;
|
||||
mDeathAnimation = state.mDeathAnimation;
|
||||
mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);
|
||||
// mHitAttemptActorId = state.mHitAttemptActorId;
|
||||
// mHitAttemptActor = state.mHitAttemptActor;
|
||||
|
||||
mSpells.readState(state.mSpells, this);
|
||||
mActiveSpells.readState(state.mActiveSpells);
|
||||
|
|
@ -604,13 +604,19 @@ namespace MWMechanics
|
|||
mMagicEffects.readState(state.mMagicEffects);
|
||||
|
||||
mSummonedCreatures = state.mSummonedCreatures;
|
||||
mSummonGraveyard = state.mSummonGraveyard;
|
||||
|
||||
if (state.mHasAiSettings)
|
||||
for (size_t i = 0; i < state.mAiSettings.size(); ++i)
|
||||
mAiSettings[i].readState(state.mAiSettings[i]);
|
||||
if (state.mRecalcDynamicStats)
|
||||
recalculateMagicka();
|
||||
if (state.mAiSequence.mActorIdConverter)
|
||||
{
|
||||
for (auto& [_, refNum] : mSummonedCreatures)
|
||||
state.mAiSequence.mActorIdConverter->convert(refNum, refNum.mIndex);
|
||||
auto& graveyard = state.mAiSequence.mActorIdConverter->mGraveyard;
|
||||
graveyard.insert(graveyard.end(), state.mSummonGraveyard.begin(), state.mSummonGraveyard.end());
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)
|
||||
|
|
@ -677,16 +683,11 @@ namespace MWMechanics
|
|||
return mTimeOfDeath;
|
||||
}
|
||||
|
||||
std::multimap<int, int>& CreatureStats::getSummonedCreatureMap()
|
||||
std::multimap<int, ESM::RefNum>& CreatureStats::getSummonedCreatureMap()
|
||||
{
|
||||
return mSummonedCreatures;
|
||||
}
|
||||
|
||||
std::vector<int>& CreatureStats::getSummonedCreatureGraveyard()
|
||||
{
|
||||
return mSummonGraveyard;
|
||||
}
|
||||
|
||||
void CreatureStats::updateAwareness(float duration)
|
||||
{
|
||||
mAwarenessTimer += duration;
|
||||
|
|
|
|||
|
|
@ -84,11 +84,7 @@ namespace MWMechanics
|
|||
MWWorld::TimeStamp mTimeOfDeath;
|
||||
|
||||
private:
|
||||
std::multimap<int, int> mSummonedCreatures; // <Effect, ActorId>
|
||||
|
||||
// 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.
|
||||
std::vector<int> mSummonGraveyard;
|
||||
std::multimap<int, ESM::RefNum> mSummonedCreatures; // <Effect, ActorId>
|
||||
|
||||
float mAwarenessTimer = 0.f;
|
||||
int mAwarenessRoll = -1;
|
||||
|
|
@ -237,8 +233,7 @@ namespace MWMechanics
|
|||
void setBlock(bool value);
|
||||
bool getBlock() const;
|
||||
|
||||
std::multimap<int, int>& getSummonedCreatureMap(); // <Effect, ActorId of summoned creature>
|
||||
std::vector<int>& getSummonedCreatureGraveyard(); // ActorIds
|
||||
std::multimap<int, ESM::RefNum>& getSummonedCreatureMap(); // <Effect, ActorId of summoned creature>
|
||||
|
||||
enum Flag
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2042,9 +2042,9 @@ namespace MWMechanics
|
|||
skill.setModifier(acrobatics->mWerewolfValue - skill.getModified());
|
||||
}
|
||||
|
||||
void MechanicsManager::cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId)
|
||||
void MechanicsManager::cleanupSummonedCreature(ESM::RefNum creature)
|
||||
{
|
||||
mActors.cleanupSummonedCreature(caster.getClass().getCreatureStats(caster), creatureActorId);
|
||||
mActors.cleanupSummonedCreature(creature);
|
||||
}
|
||||
|
||||
void MechanicsManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ namespace MWMechanics
|
|||
void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) override;
|
||||
void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) override;
|
||||
|
||||
void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) override;
|
||||
void cleanupSummonedCreature(ESM::RefNum creature) override;
|
||||
|
||||
void confiscateStolenItemToOwner(
|
||||
const MWWorld::Ptr& player, const MWWorld::Ptr& item, const MWWorld::Ptr& victim, int count) override;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/worldmodel.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
@ -1106,6 +1107,7 @@ namespace MWMechanics
|
|||
const MWWorld::Ptr& target, ActiveSpells::ActiveSpellParams& spellParams, const ESM::ActiveEffect& effect)
|
||||
{
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
const auto worldModel = MWBase::Environment::get().getWorldModel();
|
||||
auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects();
|
||||
switch (effect.mEffectId)
|
||||
{
|
||||
|
|
@ -1192,14 +1194,14 @@ namespace MWMechanics
|
|||
case ESM::MagicEffect::SummonCreature04:
|
||||
case ESM::MagicEffect::SummonCreature05:
|
||||
{
|
||||
int actorId = effect.getActorId();
|
||||
if (actorId != -1)
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, actorId);
|
||||
ESM::RefNum actor = effect.getActor();
|
||||
if (actor.isSet())
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(actor);
|
||||
auto& summons = target.getClass().getCreatureStats(target).getSummonedCreatureMap();
|
||||
auto [begin, end] = summons.equal_range(effect.mEffectId);
|
||||
for (auto it = begin; it != end; ++it)
|
||||
{
|
||||
if (it->second == actorId)
|
||||
if (it->second == actor)
|
||||
{
|
||||
summons.erase(it);
|
||||
break;
|
||||
|
|
@ -1284,7 +1286,7 @@ namespace MWMechanics
|
|||
break;
|
||||
case ESM::MagicEffect::AbsorbAttribute:
|
||||
{
|
||||
const auto caster = world->searchPtrViaActorId(spellParams.getCasterActorId());
|
||||
const auto caster = worldModel->getPtr(spellParams.getCaster());
|
||||
restoreAttribute(target, effect, effect.mMagnitude);
|
||||
if (!caster.isEmpty())
|
||||
fortifyAttribute(caster, effect, -effect.mMagnitude);
|
||||
|
|
@ -1294,7 +1296,7 @@ namespace MWMechanics
|
|||
{
|
||||
if (target.getClass().isNpc())
|
||||
restoreSkill(target, effect, effect.mMagnitude);
|
||||
const auto caster = world->searchPtrViaActorId(spellParams.getCasterActorId());
|
||||
const auto caster = worldModel->getPtr(spellParams.getCaster());
|
||||
if (!caster.isEmpty() && caster.getClass().isNpc())
|
||||
fortifySkill(caster, effect, -effect.mMagnitude);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,10 +82,10 @@ namespace
|
|||
|
||||
bool isSpellActive(const MWWorld::Ptr& caster, const MWWorld::Ptr& target, const ESM::RefId& id)
|
||||
{
|
||||
int actorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
ESM::RefNum actor = caster.getCellRef().getRefNum();
|
||||
const auto& active = target.getClass().getCreatureStats(target).getActiveSpells();
|
||||
return std::find_if(active.begin(), active.end(), [&](const auto& spell) {
|
||||
return spell.getCasterActorId() == actorId && spell.getSourceSpellId() == id;
|
||||
return spell.getCaster() == actor && spell.getSourceSpellId() == id;
|
||||
}) != active.end();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/worldmodel.hpp"
|
||||
|
||||
#include "../mwrender/animation.hpp"
|
||||
|
||||
|
|
@ -80,10 +81,10 @@ namespace MWMechanics
|
|||
return ESM::RefId();
|
||||
}
|
||||
|
||||
int summonCreature(int effectId, const MWWorld::Ptr& summoner)
|
||||
ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner)
|
||||
{
|
||||
const ESM::RefId& creatureID = getSummonedCreature(effectId);
|
||||
int creatureActorId = -1;
|
||||
ESM::RefNum creature;
|
||||
if (!creatureID.empty())
|
||||
{
|
||||
try
|
||||
|
|
@ -91,13 +92,12 @@ namespace MWMechanics
|
|||
auto world = MWBase::Environment::get().getWorld();
|
||||
MWWorld::ManualRef ref(world->getStore(), creatureID, 1);
|
||||
MWWorld::Ptr placed = world->safePlaceObject(ref.getPtr(), summoner, summoner.getCell(), 0, 120.f);
|
||||
|
||||
MWMechanics::CreatureStats& summonedCreatureStats = placed.getClass().getCreatureStats(placed);
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(placed);
|
||||
creature = placed.getCellRef().getRefNum();
|
||||
|
||||
// Make the summoned creature follow its master and help in fights
|
||||
AiFollow package(summoner);
|
||||
summonedCreatureStats.getAiSequence().stack(package, placed);
|
||||
creatureActorId = summonedCreatureStats.getActorId();
|
||||
placed.getClass().getCreatureStats(placed).getAiSequence().stack(package, placed);
|
||||
|
||||
MWRender::Animation* anim = world->getAnimation(placed);
|
||||
if (anim)
|
||||
|
|
@ -117,9 +117,9 @@ namespace MWMechanics
|
|||
// log
|
||||
}
|
||||
|
||||
summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap().emplace(effectId, creatureActorId);
|
||||
summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap().emplace(effectId, creature);
|
||||
}
|
||||
return creatureActorId;
|
||||
return creature;
|
||||
}
|
||||
|
||||
void updateSummons(const MWWorld::Ptr& summoner, bool cleanup)
|
||||
|
|
@ -127,24 +127,18 @@ namespace MWMechanics
|
|||
MWMechanics::CreatureStats& creatureStats = summoner.getClass().getCreatureStats(summoner);
|
||||
auto& creatureMap = creatureStats.getSummonedCreatureMap();
|
||||
|
||||
std::vector<int> graveyard = creatureStats.getSummonedCreatureGraveyard();
|
||||
creatureStats.getSummonedCreatureGraveyard().clear();
|
||||
|
||||
for (const int creature : graveyard)
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, creature);
|
||||
|
||||
if (!cleanup)
|
||||
return;
|
||||
|
||||
for (auto it = creatureMap.begin(); it != creatureMap.end();)
|
||||
{
|
||||
if (it->second == -1)
|
||||
if (!it->second.isSet())
|
||||
{
|
||||
// Keep the spell effect active if we failed to spawn anything
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorldModel()->getPtr(it->second);
|
||||
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead()
|
||||
&& ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())
|
||||
{
|
||||
|
|
@ -158,15 +152,15 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<int, int>& summon)
|
||||
void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<int, ESM::RefNum>& summon)
|
||||
{
|
||||
auto& creatureStats = summoner.getClass().getCreatureStats(summoner);
|
||||
creatureStats.getActiveSpells().purge(
|
||||
[summon](const auto& spell, const auto& effect) {
|
||||
return effect.mEffectId == summon.first && effect.getActorId() == summon.second;
|
||||
return effect.mEffectId == summon.first && effect.getActor() == summon.second;
|
||||
},
|
||||
summoner);
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, summon.second);
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summon.second);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <components/esm3/refnum.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class RefId;
|
||||
|
|
@ -18,9 +21,9 @@ namespace MWMechanics
|
|||
|
||||
ESM::RefId getSummonedCreature(int effectId);
|
||||
|
||||
void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<int, int>& summon);
|
||||
void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair<int, ESM::RefNum>& summon);
|
||||
|
||||
int summonCreature(int effectId, const MWWorld::Ptr& summoner);
|
||||
ESM::RefNum summonCreature(int effectId, const MWWorld::Ptr& summoner);
|
||||
|
||||
void updateSummons(const MWWorld::Ptr& summoner, bool cleanup);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -651,6 +651,12 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
|||
MWBase::Environment::get().getWorldScene()->markCellAsUnchanged();
|
||||
|
||||
MWBase::Environment::get().getLuaManager()->gameLoaded();
|
||||
for (int actorId : actorIdConverter.mGraveyard)
|
||||
{
|
||||
auto mapped = actorIdConverter.mMappings.find(actorId);
|
||||
if (mapped != actorIdConverter.mMappings.end())
|
||||
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mapped->second);
|
||||
}
|
||||
}
|
||||
catch (const SaveVersionTooNewError& e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace MWWorld
|
|||
ESM::ActiveSpells::ActiveSpellParams params;
|
||||
params.mSourceSpellId = id;
|
||||
params.mDisplayName = spell->mName;
|
||||
params.mCasterActorId = creatureStats.mActorId;
|
||||
params.mCaster.mIndex = creatureStats.mActorId;
|
||||
if (spell->mData.mType == ESM::Spell::ST_Ability)
|
||||
params.mFlags = ESM::Compatibility::ActiveSpells::Type_Ability_Flags;
|
||||
else
|
||||
|
|
@ -137,7 +137,7 @@ namespace MWWorld
|
|||
ESM::ActiveSpells::ActiveSpellParams params;
|
||||
params.mSourceSpellId = id;
|
||||
params.mDisplayName = std::move(name);
|
||||
params.mCasterActorId = creatureStats.mActorId;
|
||||
params.mCaster.mIndex = creatureStats.mActorId;
|
||||
params.mFlags = ESM::Compatibility::ActiveSpells::Type_Enchantment_Flags;
|
||||
params.mWorsenings = -1;
|
||||
params.mNextWorsening = ESM::TimeStamp();
|
||||
|
|
@ -196,7 +196,7 @@ namespace MWWorld
|
|||
{
|
||||
if (effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex)
|
||||
{
|
||||
effect.mArg = actorId;
|
||||
effect.mArg = ESM::RefNum{ .mIndex = static_cast<uint32_t>(actorId), .mContentFile = -1 };
|
||||
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
|
||||
found = true;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm3/actoridconverter.hpp>
|
||||
#include <components/esm3/esmreader.hpp>
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm3/loadbsgn.hpp>
|
||||
|
|
@ -344,6 +345,8 @@ namespace MWWorld
|
|||
MWBase::Environment::get().getWorldModel()->deregisterLiveCellRef(mPlayer);
|
||||
mPlayer.load(player.mObject);
|
||||
MWBase::Environment::get().getWorldModel()->registerPtr(getPlayer());
|
||||
if (ESM::ActorIdConverter* converter = reader.getActorIdConverter())
|
||||
converter->mMappings.emplace(player.mObject.mCreatureStats.mActorId, mPlayer.mRef.getRefNum());
|
||||
|
||||
for (size_t i = 0; i < mSaveAttributes.size(); ++i)
|
||||
mSaveAttributes[i] = player.mSaveAttributes[i];
|
||||
|
|
|
|||
|
|
@ -70,25 +70,6 @@ namespace ESM
|
|||
return false;
|
||||
}
|
||||
|
||||
struct ToInt
|
||||
{
|
||||
int effectId;
|
||||
|
||||
int operator()(const ESM::RefId& id) const
|
||||
{
|
||||
if (!id.empty())
|
||||
{
|
||||
if (affectsAttribute(effectId))
|
||||
return ESM::Attribute::refIdToIndex(id);
|
||||
else if (affectsSkill(effectId))
|
||||
return ESM::Skill::refIdToIndex(id);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int operator()(int actor) const { return actor; }
|
||||
};
|
||||
|
||||
void saveImpl(ESMWriter& esm, const std::vector<ActiveSpells::ActiveSpellParams>& spells, NAME tag)
|
||||
{
|
||||
for (const auto& params : spells)
|
||||
|
|
@ -96,7 +77,7 @@ namespace ESM
|
|||
esm.writeHNRefId(tag, params.mSourceSpellId);
|
||||
esm.writeHNRefId("SPID", params.mActiveSpellId);
|
||||
|
||||
esm.writeHNT("CAST", params.mCasterActorId);
|
||||
esm.writeFormId(params.mCaster, true, "CAST");
|
||||
esm.writeHNString("DISP", params.mDisplayName);
|
||||
esm.writeHNT("FLAG", params.mFlags);
|
||||
if (params.mItem.isSet())
|
||||
|
|
@ -110,9 +91,16 @@ namespace ESM
|
|||
for (auto& effect : params.mEffects)
|
||||
{
|
||||
esm.writeHNT("MGEF", effect.mEffectId);
|
||||
int arg = std::visit(ToInt{ effect.mEffectId }, effect.mArg);
|
||||
if (arg != -1)
|
||||
esm.writeHNT("ARG_", arg);
|
||||
if (const ESM::RefId* id = std::get_if<ESM::RefId>(&effect.mArg))
|
||||
{
|
||||
if (!id->empty())
|
||||
esm.writeHNRefId("ARG_", *id);
|
||||
}
|
||||
else if (const ESM::RefNum* actor = std::get_if<ESM::RefNum>(&effect.mArg))
|
||||
{
|
||||
if (actor->isSet())
|
||||
esm.writeFormId(*actor, true, "SUM_");
|
||||
}
|
||||
esm.writeHNT("MAGN", effect.mMagnitude);
|
||||
esm.writeHNT("MAGN", effect.mMinMagnitude);
|
||||
esm.writeHNT("MAGN", effect.mMaxMagnitude);
|
||||
|
|
@ -134,7 +122,10 @@ namespace ESM
|
|||
params.mSourceSpellId = esm.getRefId();
|
||||
if (format > MaxActiveSpellTypeVersion)
|
||||
params.mActiveSpellId = esm.getHNRefId("SPID");
|
||||
esm.getHNT(params.mCasterActorId, "CAST");
|
||||
if (format <= MaxActorIdSaveGameFormatVersion)
|
||||
esm.getHNT(params.mCaster.mIndex, "CAST");
|
||||
else
|
||||
params.mCaster = esm.getFormId("CAST");
|
||||
params.mDisplayName = esm.getHNString("DISP");
|
||||
if (format <= MaxClearModifiersFormatVersion)
|
||||
params.mFlags = Compatibility::ActiveSpells::Type_Temporary_Flags;
|
||||
|
|
@ -186,17 +177,24 @@ namespace ESM
|
|||
{
|
||||
ActiveEffect effect;
|
||||
esm.getHT(effect.mEffectId);
|
||||
int32_t arg = -1;
|
||||
esm.getHNOT(arg, "ARG_");
|
||||
if (arg >= 0)
|
||||
if (format <= MaxActorIdSaveGameFormatVersion)
|
||||
{
|
||||
if (isSummon(effect.mEffectId))
|
||||
effect.mArg = arg;
|
||||
else if (affectsAttribute(effect.mEffectId))
|
||||
effect.mArg = ESM::Attribute::indexToRefId(arg);
|
||||
else if (affectsSkill(effect.mEffectId))
|
||||
effect.mArg = ESM::Skill::indexToRefId(arg);
|
||||
int32_t arg = -1;
|
||||
esm.getHNOT(arg, "ARG_");
|
||||
if (arg >= 0)
|
||||
{
|
||||
if (isSummon(effect.mEffectId))
|
||||
effect.mArg = RefNum{ .mIndex = static_cast<uint32_t>(arg), .mContentFile = -1 };
|
||||
else if (affectsAttribute(effect.mEffectId))
|
||||
effect.mArg = ESM::Attribute::indexToRefId(arg);
|
||||
else if (affectsSkill(effect.mEffectId))
|
||||
effect.mArg = ESM::Skill::indexToRefId(arg);
|
||||
}
|
||||
}
|
||||
else if (esm.peekNextSub("ARG_"))
|
||||
effect.mArg = esm.getHNRefId("ARG_");
|
||||
else if (esm.peekNextSub("SUM_"))
|
||||
effect.mArg = esm.getFormId("SUM_");
|
||||
esm.getHNT(effect.mMagnitude, "MAGN");
|
||||
if (format <= MaxClearModifiersFormatVersion)
|
||||
{
|
||||
|
|
@ -235,21 +233,22 @@ namespace ESM
|
|||
|
||||
void ActiveSpells::load(ESMReader& esm)
|
||||
{
|
||||
mActorIdConverter = esm.getActorIdConverter();
|
||||
loadImpl(esm, mSpells, "ID__");
|
||||
loadImpl(esm, mQueue, "QID_");
|
||||
}
|
||||
|
||||
RefId ActiveEffect::getSkillOrAttribute() const
|
||||
{
|
||||
if (const auto* id = std::get_if<ESM::RefId>(&mArg))
|
||||
if (const ESM::RefId* id = std::get_if<ESM::RefId>(&mArg))
|
||||
return *id;
|
||||
return {};
|
||||
}
|
||||
|
||||
int ActiveEffect::getActorId() const
|
||||
RefNum ActiveEffect::getActor() const
|
||||
{
|
||||
if (const auto* id = std::get_if<int>(&mArg))
|
||||
return *id;
|
||||
return -1;
|
||||
if (const ESM::RefNum* actor = std::get_if<ESM::RefNum>(&mArg))
|
||||
return *actor;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace ESM
|
|||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
class ActorIdConverter;
|
||||
|
||||
// Parameters of an effect concerning lasting effects.
|
||||
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
|
||||
|
|
@ -34,14 +35,14 @@ namespace ESM
|
|||
float mMagnitude;
|
||||
float mMinMagnitude;
|
||||
float mMaxMagnitude;
|
||||
std::variant<RefId, int> mArg; // skill, attribute, or summon
|
||||
std::variant<RefId, RefNum> mArg; // skill, attribute, or summon
|
||||
float mDuration;
|
||||
float mTimeLeft;
|
||||
int32_t mEffectIndex;
|
||||
int32_t mFlags;
|
||||
|
||||
RefId getSkillOrAttribute() const;
|
||||
int getActorId() const;
|
||||
RefNum getActor() const;
|
||||
};
|
||||
|
||||
// format 0, saved games only
|
||||
|
|
@ -65,7 +66,7 @@ namespace ESM
|
|||
RefId mSourceSpellId;
|
||||
std::vector<ActiveEffect> mEffects;
|
||||
std::string mDisplayName;
|
||||
int32_t mCasterActorId;
|
||||
RefNum mCaster;
|
||||
RefNum mItem;
|
||||
Flags mFlags;
|
||||
int32_t mWorsenings;
|
||||
|
|
@ -74,6 +75,7 @@ namespace ESM
|
|||
|
||||
std::vector<ActiveSpellParams> mSpells;
|
||||
std::vector<ActiveSpellParams> mQueue;
|
||||
ActorIdConverter* mActorIdConverter = nullptr;
|
||||
|
||||
void load(ESMReader& esm);
|
||||
void save(ESMWriter& esm) const;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace ESM
|
|||
|
||||
public:
|
||||
std::map<int, ESM::RefNum> mMappings;
|
||||
std::vector<int> mGraveyard;
|
||||
|
||||
void apply();
|
||||
|
||||
|
|
|
|||
|
|
@ -251,11 +251,6 @@ namespace ESM
|
|||
esm.writeHNT("ACID", actorId);
|
||||
}
|
||||
|
||||
for (int32_t key : mSummonGraveyard)
|
||||
{
|
||||
esm.writeHNT("GRAV", key);
|
||||
}
|
||||
|
||||
esm.writeHNT("AISE", mHasAiSettings);
|
||||
if (mHasAiSettings)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace ESM
|
|||
std::array<StatState<int>, 4> mAiSettings;
|
||||
|
||||
std::map<SummonKey, int> mSummonedCreatureMap;
|
||||
std::multimap<int, int> mSummonedCreatures;
|
||||
std::multimap<int, RefNum> mSummonedCreatures;
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
||||
TimeStamp mTradeTime;
|
||||
|
|
|
|||
Loading…
Reference in a new issue