forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'scrawl/master'
This commit is contained in:
commit
38a413a483
33 changed files with 446 additions and 224 deletions
|
@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics
|
|||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
||||
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
|
||||
|
|
|
@ -518,7 +518,8 @@ void OMW::Engine::activate()
|
|||
return;
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if (player.getClass().getCreatureStats(player).getKnockedDown())
|
||||
if (player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0
|
||||
|| player.getClass().getCreatureStats(player).getKnockedDown())
|
||||
return;
|
||||
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||
|
|
|
@ -111,7 +111,7 @@ namespace MWGui
|
|||
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));
|
||||
|
||||
std::stringstream castCost;
|
||||
castCost << mEnchanting.getCastCost();
|
||||
castCost << mEnchanting.getEffectiveCastCost();
|
||||
mCastCost->setCaption(castCost.str());
|
||||
|
||||
mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice()));
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MWGui
|
|||
{
|
||||
|
||||
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)
|
||||
{
|
||||
MagicEffectInfo newEffectSource;
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace MWGui
|
|||
virtual ~EffectSourceVisitor() {}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -103,9 +103,7 @@ namespace MWGui
|
|||
&& item.getClass().canBeEquipped(item, mActor).first == 0)
|
||||
continue;
|
||||
|
||||
float enchantCost = enchant->mData.mCost;
|
||||
int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant);
|
||||
int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
|
||||
int castCost = MWMechanics::getEffectiveEnchantmentCastCost(enchant->mData.mCost, mActor);
|
||||
|
||||
std::string cost = boost::lexical_cast<std::string>(castCost);
|
||||
int currentCharge = int(item.getCellRef().getEnchantmentCharge());
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace MWMechanics
|
|||
float magnitude = effectIt->mMagnitude;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||
|
|
|
@ -82,6 +82,9 @@ namespace MWMechanics
|
|||
/// Remove all active effects with this effect id
|
||||
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)
|
||||
void purgeAll (float chance);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "aipursue.hpp"
|
||||
|
||||
#include "actor.hpp"
|
||||
#include "summoning.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -105,7 +106,7 @@ public:
|
|||
, mCommanded(false){}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -203,7 +180,7 @@ namespace MWMechanics
|
|||
: mCreature(trappedCreature) {}
|
||||
|
||||
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)
|
||||
{
|
||||
if (key.mId != ESM::MagicEffect::Soultrap)
|
||||
|
@ -782,131 +759,11 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
// Update summon effects
|
||||
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";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
UpdateSummonedCreatures updateSummonedCreatures(ptr);
|
||||
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
|
||||
if (ptr.getClass().hasInventoryStore(ptr))
|
||||
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
|
||||
updateSummonedCreatures.finish();
|
||||
}
|
||||
|
||||
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)
|
||||
|
|
|
@ -580,7 +580,7 @@ namespace MWMechanics
|
|||
buildNewPath(actor, target); //may fail to build a path, check before use
|
||||
|
||||
//delete visited path node
|
||||
mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
|
||||
// This works on the borders between the path grid and areas with no waypoints.
|
||||
if(inLOS && mPathFinder.getPath().size() > 1)
|
||||
|
|
|
@ -202,7 +202,7 @@ namespace MWMechanics
|
|||
// Are we there yet?
|
||||
bool& chooseAction = storage.mChooseAction;
|
||||
if(walking &&
|
||||
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2], 64.f))
|
||||
{
|
||||
stopWalking(actor, storage);
|
||||
moveNow = false;
|
||||
|
|
|
@ -638,7 +638,7 @@ namespace MWMechanics
|
|||
mDeathAnimation = index;
|
||||
}
|
||||
|
||||
std::map<int, int>& CreatureStats::getSummonedCreatureMap()
|
||||
std::map<CreatureStats::SummonKey, int>& CreatureStats::getSummonedCreatureMap()
|
||||
{
|
||||
return mSummonedCreatures;
|
||||
}
|
||||
|
|
|
@ -67,8 +67,11 @@ namespace MWMechanics
|
|||
// The index of the death animation that was played
|
||||
unsigned char mDeathAnimation;
|
||||
|
||||
// <ESM::MagicEffect index, ActorId>
|
||||
std::map<int, int> mSummonedCreatures;
|
||||
public:
|
||||
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.
|
||||
// This may be necessary when the creature is in an inactive cell.
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
@ -216,8 +219,8 @@ namespace MWMechanics
|
|||
void setBlock(bool value);
|
||||
bool getBlock() const;
|
||||
|
||||
std::map<int, int>& getSummonedCreatureMap();
|
||||
std::vector<int>& getSummonedCreatureGraveyard();
|
||||
std::map<SummonKey, int>& getSummonedCreatureMap(); // <SummonKey, ActorId of summoned creature>
|
||||
std::vector<int>& getSummonedCreatureGraveyard(); // ActorIds
|
||||
|
||||
enum Flag
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "creaturestats.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -55,7 +56,7 @@ namespace MWMechanics
|
|||
enchantment.mData.mCharge = getGemCharge();
|
||||
enchantment.mData.mAutocalc = 0;
|
||||
enchantment.mData.mType = mCastStyle;
|
||||
enchantment.mData.mCost = getCastCost();
|
||||
enchantment.mData.mCost = getBaseCastCost();
|
||||
|
||||
store.remove(mSoulGemPtr, 1, player);
|
||||
|
||||
|
@ -65,7 +66,7 @@ namespace MWMechanics
|
|||
|
||||
if(mSelfEnchanting)
|
||||
{
|
||||
if(std::rand()/static_cast<double> (RAND_MAX)*100 < getEnchantChance())
|
||||
if(getEnchantChance()<std::rand()/static_cast<double> (RAND_MAX)*100)
|
||||
return false;
|
||||
|
||||
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
||||
|
@ -199,23 +200,19 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
|
||||
int Enchanting::getCastCost() const
|
||||
int Enchanting::getBaseCastCost() const
|
||||
{
|
||||
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
||||
return 0;
|
||||
|
||||
const float enchantCost = getEnchantPoints();
|
||||
return getEnchantPoints();
|
||||
}
|
||||
|
||||
int Enchanting::getEffectiveCastCost() const
|
||||
{
|
||||
int baseCost = getBaseCastCost();
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWMechanics::NpcStats &stats = player.getClass().getNpcStats(player);
|
||||
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified();
|
||||
|
||||
/*
|
||||
* Each point of enchant skill above/under 10 subtracts/adds
|
||||
* one percent of enchantment cost while minimum is 1.
|
||||
*/
|
||||
const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10);
|
||||
|
||||
return static_cast<int>((castCost < 1) ? 1 : castCost);
|
||||
return getEffectiveEnchantmentCastCost(baseCost, player);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ namespace MWMechanics
|
|||
void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object)
|
||||
int getCastStyle() const;
|
||||
int getEnchantPoints() const;
|
||||
int getCastCost() const;
|
||||
int getBaseCastCost() const; // To be saved in the enchantment's record
|
||||
int getEffectiveCastCost() const; // Effective cost taking player Enchant skill into account, used for preview purposes in the UI
|
||||
int getEnchantPrice() const;
|
||||
int getMaxEnchantValue() const;
|
||||
int getGemCharge() const;
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace MWMechanics
|
|||
struct EffectSourceVisitor
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -282,28 +282,13 @@ namespace MWMechanics
|
|||
return Ogre::Math::ATan2(directionX,directionY).valueDegrees();
|
||||
}
|
||||
|
||||
bool PathFinder::checkWaypoint(float x, float y, float z)
|
||||
bool PathFinder::checkPathCompleted(float x, float y, float z, float tolerance)
|
||||
{
|
||||
if(mPath.empty())
|
||||
return true;
|
||||
|
||||
ESM::Pathgrid::Point nextPoint = *mPath.begin();
|
||||
if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64)
|
||||
{
|
||||
mPath.pop_front();
|
||||
if(mPath.empty()) mIsPathConstructed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PathFinder::checkPathCompleted(float x, float y, float z)
|
||||
{
|
||||
if(mPath.empty())
|
||||
return true;
|
||||
|
||||
ESM::Pathgrid::Point nextPoint = *mPath.begin();
|
||||
if(sqrDistanceZCorrected(nextPoint, x, y, z) < 64*64)
|
||||
if(sqrDistanceZCorrected(nextPoint, x, y, z) < tolerance*tolerance)
|
||||
{
|
||||
mPath.pop_front();
|
||||
if(mPath.empty())
|
||||
|
|
|
@ -39,11 +39,8 @@ namespace MWMechanics
|
|||
void buildPath(const ESM::Pathgrid::Point &startPoint, const ESM::Pathgrid::Point &endPoint,
|
||||
const MWWorld::CellStore* cell, bool allowShortcuts = true);
|
||||
|
||||
bool checkPathCompleted(float x, float y, float z);
|
||||
///< \Returns true if the last point of the path has been reached.
|
||||
|
||||
bool checkWaypoint(float x, float y, float z);
|
||||
///< \Returns true if a way point was reached
|
||||
bool checkPathCompleted(float x, float y, float z, float tolerance=32.f);
|
||||
///< \Returns true if we are within \a tolerance units of the last path point.
|
||||
|
||||
float getZAngleToNext(float x, float y) const;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "magiceffects.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "summoning.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -470,6 +471,20 @@ namespace MWMechanics
|
|||
else
|
||||
applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);
|
||||
|
||||
// Re-casting a summon effect will remove the creature from previous castings of that effect.
|
||||
if (effectIt->mEffectID >= ESM::MagicEffect::SummonScamp
|
||||
&& effectIt->mEffectID <= ESM::MagicEffect::SummonStormAtronach
|
||||
&& !target.isEmpty() && target.getClass().isActor())
|
||||
{
|
||||
CreatureStats& targetStats = target.getClass().getCreatureStats(target);
|
||||
std::map<CreatureStats::SummonKey, int>::iterator found = targetStats.getSummonedCreatureMap().find(std::make_pair(effectIt->mEffectID, mId));
|
||||
if (found != targetStats.getSummonedCreatureMap().end())
|
||||
{
|
||||
cleanupSummonedCreature(targetStats, found->second);
|
||||
targetStats.getSummonedCreatureMap().erase(found);
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent.
|
||||
// This was probably just done to have the effect visible in the magic menu for a while
|
||||
// to notify the player they've been damaged?
|
||||
|
@ -705,9 +720,7 @@ namespace MWMechanics
|
|||
// Check if there's enough charge left
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
{
|
||||
const float enchantCost = enchantment->mData.mCost;
|
||||
int eSkill = mCaster.getClass().getSkill(mCaster, ESM::Skill::Enchant);
|
||||
const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
|
||||
const int castCost = getEffectiveEnchantmentCastCost(enchantment->mData.mCost, mCaster);
|
||||
|
||||
if (item.getCellRef().getEnchantmentCharge() == -1)
|
||||
item.getCellRef().setEnchantmentCharge(enchantment->mData.mCharge);
|
||||
|
@ -931,4 +944,16 @@ namespace MWMechanics
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr &actor)
|
||||
{
|
||||
/*
|
||||
* Each point of enchant skill above/under 10 subtracts/adds
|
||||
* one percent of enchantment cost while minimum is 1.
|
||||
*/
|
||||
int eSkill = actor.getClass().getSkill(actor, ESM::Skill::Enchant);
|
||||
const float result = castCost - (castCost / 100) * (eSkill - 10);
|
||||
|
||||
return static_cast<int>((result < 1) ? 1 : result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ namespace MWMechanics
|
|||
float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
|
||||
const ESM::Spell* spell = NULL, const MagicEffects* effects = NULL);
|
||||
|
||||
int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor);
|
||||
|
||||
class CastSpell
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -249,7 +249,7 @@ namespace MWMechanics
|
|||
random = it->second.at(i);
|
||||
|
||||
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
|
|
@ -8,6 +8,7 @@
|
|||
#include <OgreSceneManager.h>
|
||||
#include <OgreHardwareVertexBuffer.h>
|
||||
#include <OgreHighLevelGpuProgramManager.h>
|
||||
#include <OgreParticle.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubEntity.h>
|
||||
|
@ -431,7 +432,10 @@ void SkyManager::updateRain(float dt)
|
|||
Ogre::Vector3 pos = it->first->getPosition();
|
||||
pos.z -= mRainSpeed * dt;
|
||||
it->first->setPosition(pos);
|
||||
if (pos.z < -minHeight)
|
||||
if (pos.z < -minHeight
|
||||
// Here we might want to add a "splash" effect later
|
||||
|| MWBase::Environment::get().getWorld()->isUnderwater(
|
||||
MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), it->first->_getDerivedPosition()))
|
||||
{
|
||||
it->second.setNull();
|
||||
mSceneMgr->destroySceneNode(it->first);
|
||||
|
@ -458,6 +462,12 @@ void SkyManager::updateRain(float dt)
|
|||
// Create a separate node to control the offset, since a node with setInheritOrientation(false) will still
|
||||
// consider the orientation of the parent node for its position, just not for its orientation
|
||||
float startHeight = 700;
|
||||
Ogre::Vector3 worldPos = mParticleNode->_getDerivedPosition();
|
||||
worldPos += Ogre::Vector3(xOffs, yOffs, startHeight);
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(
|
||||
MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), worldPos))
|
||||
return;
|
||||
|
||||
Ogre::SceneNode* offsetNode = mParticleNode->createChildSceneNode(Ogre::Vector3(xOffs,yOffs,startHeight));
|
||||
|
||||
// Spawn a new rain object for each instance.
|
||||
|
@ -490,6 +500,30 @@ void SkyManager::update(float duration)
|
|||
for (unsigned int i=0; i<mParticle->mControllers.size(); ++i)
|
||||
mParticle->mControllers[i].update();
|
||||
|
||||
for (unsigned int i=0; i<mParticle->mParticles.size(); ++i)
|
||||
{
|
||||
Ogre::ParticleSystem* psys = mParticle->mParticles[i];
|
||||
Ogre::ParticleIterator pi = psys->_getIterator();
|
||||
while (!pi.end())
|
||||
{
|
||||
Ogre::Particle *p = pi.getNext();
|
||||
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
|
||||
Ogre::Vector3 pos = p->mPosition;
|
||||
Ogre::Real& timeToLive = p->mTimeToLive;
|
||||
#else
|
||||
Ogre::Vector3 pos = p->position;
|
||||
Ogre::Real& timeToLive = p->timeToLive;
|
||||
#endif
|
||||
|
||||
if (psys->getKeepParticlesInLocalSpace() && psys->getParentNode())
|
||||
pos = psys->getParentNode()->convertLocalToWorldPosition(pos);
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(
|
||||
MWBase::Environment::get().getWorld()->getPlayerPtr().getCell(), pos))
|
||||
timeToLive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mIsStorm)
|
||||
mParticleNode->setOrientation(Ogre::Vector3::UNIT_Y.getRotationTo(mStormDirection));
|
||||
}
|
||||
|
|
|
@ -388,6 +388,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
case ESM::REC_ENAB:
|
||||
case ESM::REC_LEVC:
|
||||
case ESM::REC_LEVI:
|
||||
case ESM::REC_CAM_:
|
||||
|
||||
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
|
||||
break;
|
||||
|
@ -439,6 +440,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
|
||||
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false);
|
||||
|
||||
// Vanilla MW will restart startup scripts when a save game is loaded. This is unintuive,
|
||||
// but some mods may be using it as a reload detector.
|
||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
||||
|
||||
// Do not trigger erroneous cellChanged events
|
||||
|
|
|
@ -583,7 +583,8 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
|
|||
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().getRefId()][i];
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
|
||||
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;
|
||||
}
|
||||
|
@ -639,6 +640,52 @@ void MWWorld::InventoryStore::purgeEffect(short 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()
|
||||
{
|
||||
mSlots.clear();
|
||||
|
|
|
@ -203,6 +203,9 @@ namespace MWWorld
|
|||
void purgeEffect (short effectId);
|
||||
///< Remove a magic effect
|
||||
|
||||
void purgeEffect (short effectId, const std::string& sourceId);
|
||||
///< Remove a magic effect
|
||||
|
||||
virtual void clear();
|
||||
///< Empty container.
|
||||
|
||||
|
|
|
@ -296,15 +296,17 @@ namespace MWWorld
|
|||
std::string loadingExteriorText = "#{sLoadingMessage3}";
|
||||
loadingListener->setLabel(loadingExteriorText);
|
||||
|
||||
const int halfGridSize = Settings::Manager::getInt("exterior grid size", "Cells")/2;
|
||||
|
||||
CellStoreCollection::iterator active = mActiveCells.begin();
|
||||
while (active!=mActiveCells.end())
|
||||
{
|
||||
if ((*active)->getCell()->isExterior())
|
||||
{
|
||||
if (std::abs (X-(*active)->getCell()->getGridX())<=1 &&
|
||||
std::abs (Y-(*active)->getCell()->getGridY())<=1)
|
||||
if (std::abs (X-(*active)->getCell()->getGridX())<=halfGridSize &&
|
||||
std::abs (Y-(*active)->getCell()->getGridY())<=halfGridSize)
|
||||
{
|
||||
// keep cells within the new 3x3 grid
|
||||
// keep cells within the new grid
|
||||
++active;
|
||||
continue;
|
||||
}
|
||||
|
@ -314,9 +316,9 @@ namespace MWWorld
|
|||
|
||||
int refsToLoad = 0;
|
||||
// get the number of refs to load
|
||||
for (int x=X-1; x<=X+1; ++x)
|
||||
for (int x=X-halfGridSize; x<=X+halfGridSize; ++x)
|
||||
{
|
||||
for (int y=Y-1; y<=Y+1; ++y)
|
||||
for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y)
|
||||
{
|
||||
CellStoreCollection::iterator iter = mActiveCells.begin();
|
||||
|
||||
|
@ -339,9 +341,9 @@ namespace MWWorld
|
|||
loadingListener->setProgressRange(refsToLoad);
|
||||
|
||||
// Load cells
|
||||
for (int x=X-1; x<=X+1; ++x)
|
||||
for (int x=X-halfGridSize; x<=X+halfGridSize; ++x)
|
||||
{
|
||||
for (int y=Y-1; y<=Y+1; ++y)
|
||||
for (int y=Y-halfGridSize; y<=Y+halfGridSize; ++y)
|
||||
{
|
||||
CellStoreCollection::iterator iter = mActiveCells.begin();
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ namespace MWWorld
|
|||
setupPlayer();
|
||||
|
||||
renderPlayer();
|
||||
mRendering->resetCamera();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
||||
|
||||
|
@ -304,7 +305,8 @@ namespace MWWorld
|
|||
+1 // player record
|
||||
+1 // weather record
|
||||
+1 // actorId counter
|
||||
+1; // levitation/teleport enabled state
|
||||
+1 // levitation/teleport enabled state
|
||||
+1; // camera
|
||||
}
|
||||
|
||||
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
|
@ -333,6 +335,11 @@ namespace MWWorld
|
|||
writer.writeHNT("LEVT", mLevitationEnabled);
|
||||
writer.endRecord(ESM::REC_ENAB);
|
||||
progress.increaseProgress();
|
||||
|
||||
writer.startRecord(ESM::REC_CAM_);
|
||||
writer.writeHNT("FIRS", isFirstPerson());
|
||||
writer.endRecord(ESM::REC_CAM_);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
|
||||
void World::readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
|
@ -347,6 +354,12 @@ namespace MWWorld
|
|||
reader.getHNT(mTeleportEnabled, "TELE");
|
||||
reader.getHNT(mLevitationEnabled, "LEVT");
|
||||
return;
|
||||
case ESM::REC_CAM_:
|
||||
bool firstperson;
|
||||
reader.getHNT(firstperson, "FIRS");
|
||||
if (firstperson != isFirstPerson())
|
||||
togglePOV();
|
||||
break;
|
||||
default:
|
||||
if (!mStore.readRecord (reader, type) &&
|
||||
!mGlobalVariables.readRecord (reader, type) &&
|
||||
|
@ -2073,7 +2086,6 @@ namespace MWWorld
|
|||
MWBase::Environment::get().getMechanicsManager()->add(mPlayer->getPlayer());
|
||||
|
||||
mPhysics->addActor(mPlayer->getPlayer());
|
||||
mRendering->resetCamera();
|
||||
}
|
||||
|
||||
int World::canRest ()
|
||||
|
|
|
@ -94,9 +94,10 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
{
|
||||
int magicEffect;
|
||||
esm.getHT(magicEffect);
|
||||
std::string source = esm.getHNOString("SOUR");
|
||||
int actorId;
|
||||
esm.getHNT (actorId, "ACID");
|
||||
mSummonedCreatureMap[magicEffect] = actorId;
|
||||
mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId;
|
||||
}
|
||||
|
||||
while (esm.isNextSub("GRAV"))
|
||||
|
@ -204,9 +205,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
mAiSequence.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);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace ESM
|
|||
bool mHasAiSettings;
|
||||
StatState<int> mAiSettings[4];
|
||||
|
||||
std::map<int, int> mSummonedCreatureMap;
|
||||
std::map<std::pair<int, std::string>, int> mSummonedCreatureMap;
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
||||
ESM::TimeStamp mTradeTime;
|
||||
|
|
|
@ -114,6 +114,7 @@ enum RecNameInts
|
|||
REC_DCOU = FourCC<'D','C','O','U'>::value,
|
||||
REC_MARK = FourCC<'M','A','R','K'>::value,
|
||||
REC_ENAB = FourCC<'E','N','A','B'>::value,
|
||||
REC_CAM_ = FourCC<'C','A','M','_'>::value,
|
||||
|
||||
// format 1
|
||||
REC_FILT = 0x544C4946,
|
||||
|
|
|
@ -123,6 +123,9 @@ local map resolution = 256
|
|||
local map widget size = 512
|
||||
local map hud widget size = 256
|
||||
|
||||
[Cells]
|
||||
exterior grid size = 3
|
||||
|
||||
[Viewing distance]
|
||||
# Limit the rendering distance of small objects
|
||||
limit small object distance = false
|
||||
|
|
Loading…
Reference in a new issue