1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-22 04:23:53 +00:00

Merge remote-tracking branch 'scrawl/master'

This commit is contained in:
Marc Zinnschlag 2014-05-13 10:00:18 +02:00
commit 6c8e4b27d3
42 changed files with 622 additions and 109 deletions

View file

@ -357,9 +357,6 @@ namespace MWClass
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "",
MWBase::Environment::get().getWorld()->getStore()); MWBase::Environment::get().getWorld()->getStore());
// Relates to NPC gold reset delay
data->mNpcStats.setTradeTime(MWWorld::TimeStamp(0.0, 0));
data->mNpcStats.setGoldPool(gold); data->mNpcStats.setGoldPool(gold);
// store // store

View file

@ -233,6 +233,9 @@ namespace MWGui
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
MWBase::Environment::get().getWorld()->breakInvisibility(
MWBase::Environment::get().getWorld()->getPlayerPtr());
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition(); MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();
float mouseX = cursorPosition.left / float(viewSize.width); float mouseX = cursorPosition.left / float(viewSize.width);

View file

@ -541,9 +541,11 @@ namespace MWGui
int count = object.getRefData().getCount(); int count = object.getRefData().getCount();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWBase::Environment::get().getWorld()->breakInvisibility(player);
// add to player inventory // add to player inventory
// can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object // can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player); MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player);
// remove from world // remove from world
MWBase::Environment::get().getWorld()->deleteObject (object); MWBase::Environment::get().getWorld()->deleteObject (object);

View file

@ -465,7 +465,18 @@ namespace MWGui
void MapWindow::cellExplored(int x, int y) void MapWindow::cellExplored(int x, int y)
{ {
mGlobalMapRender->exploreCell(x,y); mQueuedToExplore.push_back(std::make_pair(x,y));
}
void MapWindow::onFrame(float dt)
{
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
{
mGlobalMapRender->exploreCell(it->first, it->second);
}
mQueuedToExplore.clear();
NoDrop::onFrame(dt);
} }
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id) void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)

View file

@ -97,14 +97,17 @@ namespace MWGui
void renderGlobalMap(Loading::Listener* loadingListener); void renderGlobalMap(Loading::Listener* loadingListener);
void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map // adds the marker to the global map
void addVisitedLocation(const std::string& name, int x, int y);
// reveals this cell's map on the global map
void cellExplored(int x, int y); void cellExplored(int x, int y);
void setGlobalMapPlayerPosition (float worldX, float worldY); void setGlobalMapPlayerPosition (float worldX, float worldY);
virtual void open(); virtual void open();
void onFrame(float dt) { NoDrop::onFrame(dt); } void onFrame(float dt);
/// Clear all savegame-specific data /// Clear all savegame-specific data
void clear(); void clear();
@ -132,6 +135,10 @@ namespace MWGui
typedef std::pair<int, int> CellId; typedef std::pair<int, int> CellId;
std::vector<CellId> mMarkers; std::vector<CellId> mMarkers;
// Cells that should be explored in the next frame (i.e. their map revealed on the global map)
// We can't do this immediately, because the map update is not immediate either (see mNeedMapUpdate in scene.cpp)
std::vector<CellId> mQueuedToExplore;
MyGUI::Button* mEventBoxGlobal; MyGUI::Button* mEventBoxGlobal;
MyGUI::Button* mEventBoxLocal; MyGUI::Button* mEventBoxLocal;

View file

@ -1326,9 +1326,6 @@ namespace MWGui
void WindowManager::updatePlayer() void WindowManager::updatePlayer()
{ {
unsetSelectedSpell();
unsetSelectedWeapon();
mInventoryWindow->updatePlayer(); mInventoryWindow->updatePlayer();
} }
@ -1425,6 +1422,14 @@ namespace MWGui
mQuickKeysMenu->write(writer); mQuickKeysMenu->write(writer);
progress.increaseProgress(); progress.increaseProgress();
if (!mSelectedSpell.empty())
{
writer.startRecord(ESM::REC_ASPL);
writer.writeHNString("ID__", mSelectedSpell);
writer.endRecord(ESM::REC_ASPL);
progress.increaseProgress();
}
} }
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
@ -1433,12 +1438,18 @@ namespace MWGui
mMap->readRecord(reader, type); mMap->readRecord(reader, type);
else if (type == ESM::REC_KEYS) else if (type == ESM::REC_KEYS)
mQuickKeysMenu->readRecord(reader, type); mQuickKeysMenu->readRecord(reader, type);
else if (type == ESM::REC_ASPL)
{
reader.getSubNameIs("ID__");
mSelectedSpell = reader.getHString();
}
} }
int WindowManager::countSavedGameRecords() const int WindowManager::countSavedGameRecords() const
{ {
return 1 // Global map return 1 // Global map
+ 1; // QuickKeysMenu + 1 // QuickKeysMenu
+ (!mSelectedSpell.empty() ? 1 : 0);
} }
bool WindowManager::isSavingAllowed() const bool WindowManager::isSavingAllowed() const

View file

@ -920,9 +920,6 @@ namespace MWMechanics
spells.purge(iter->first.getRefData().getHandle()); spells.purge(iter->first.getRefData().getHandle());
} }
// FIXME: see http://bugs.openmw.org/issues/869
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if (iter->second->kill()) if (iter->second->kill())
{ {
++mDeathCount[cls.getId(iter->first)]; ++mDeathCount[cls.getId(iter->first)];
@ -939,6 +936,8 @@ namespace MWMechanics
stats.setMagicEffects(MWMechanics::MagicEffects()); stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0); calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if (cls.isEssential(iter->first)) if (cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
} }

View file

@ -17,8 +17,10 @@ namespace MWMechanics
mAttacked (false), mHostile (false), mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackingOrSpell(false),
mIsWerewolf(false), mIsWerewolf(false),
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f),
mTradeTime(0,0), mGoldPool(0)
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i] = 0; mAiSettings[i] = 0;
@ -348,20 +350,6 @@ namespace MWMechanics
return mLastHitObject; return mLastHitObject;
} }
bool CreatureStats::canUsePower(const std::string &power) const
{
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
return false;
}
void CreatureStats::usePower(const std::string &power)
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void CreatureStats::addToFallHeight(float height) void CreatureStats::addToFallHeight(float height)
{ {
mFallHeight += height; mFallHeight += height;
@ -481,20 +469,75 @@ namespace MWMechanics
void CreatureStats::writeState (ESM::CreatureStats& state) const void CreatureStats::writeState (ESM::CreatureStats& state) const
{ {
for (int i=0; i<8; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].writeState (state.mAttributes[i]); mAttributes[i].writeState (state.mAttributes[i]);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].writeState (state.mDynamic[i]); mDynamic[i].writeState (state.mDynamic[i]);
state.mTradeTime = mTradeTime.toEsm();
state.mGoldPool = mGoldPool;
state.mDead = mDead;
state.mDied = mDied;
state.mFriendlyHits = mFriendlyHits;
state.mTalkedTo = mTalkedTo;
state.mAlarmed = mAlarmed;
state.mAttacked = mAttacked;
state.mHostile = mHostile;
state.mAttackingOrSpell = mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
state.mKnockdown = mKnockdown;
state.mKnockdownOneFrame = mKnockdownOneFrame;
state.mKnockdownOverOneFrame = mKnockdownOverOneFrame;
state.mHitRecovery = mHitRecovery;
state.mBlock = mBlock;
state.mMovementFlags = mMovementFlags;
state.mAttackStrength = mAttackStrength;
state.mFallHeight = mFallHeight;
state.mLastHitObject = mLastHitObject;
state.mRecalcDynamicStats = mRecalcDynamicStats;
state.mDrawState = mDrawState;
state.mLevel = mLevel;
mSpells.writeState(state.mSpells);
} }
void CreatureStats::readState (const ESM::CreatureStats& state) void CreatureStats::readState (const ESM::CreatureStats& state)
{ {
for (int i=0; i<8; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].readState (state.mAttributes[i]); mAttributes[i].readState (state.mAttributes[i]);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].readState (state.mDynamic[i]); mDynamic[i].readState (state.mDynamic[i]);
mTradeTime = MWWorld::TimeStamp(state.mTradeTime);
mGoldPool = state.mGoldPool;
mFallHeight = state.mFallHeight;
mDead = state.mDead;
mDied = state.mDied;
mFriendlyHits = state.mFriendlyHits;
mTalkedTo = state.mTalkedTo;
mAlarmed = state.mAlarmed;
mAttacked = state.mAttacked;
mHostile = state.mHostile;
mAttackingOrSpell = state.mAttackingOrSpell;
// TODO: rewrite. does this really need 3 separate bools?
mKnockdown = state.mKnockdown;
mKnockdownOneFrame = state.mKnockdownOneFrame;
mKnockdownOverOneFrame = state.mKnockdownOverOneFrame;
mHitRecovery = state.mHitRecovery;
mBlock = state.mBlock;
mMovementFlags = state.mMovementFlags;
mAttackStrength = state.mAttackStrength;
mFallHeight = state.mFallHeight;
mLastHitObject = state.mLastHitObject;
mRecalcDynamicStats = state.mRecalcDynamicStats;
mDrawState = DrawState_(state.mDrawState);
mLevel = state.mLevel;
mSpells.readState(state.mSpells);
} }
// Relates to NPC gold reset delay // Relates to NPC gold reset delay

View file

@ -55,15 +55,15 @@ namespace MWMechanics
// Do we need to recalculate stats derived from attributes or other factors? // Do we need to recalculate stats derived from attributes or other factors?
bool mRecalcDynamicStats; bool mRecalcDynamicStats;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
int mGoldPool; // the pool of merchant gold not in inventory int mGoldPool; // the pool of merchant gold not in inventory
protected: protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
bool mIsWerewolf; bool mIsWerewolf;
AttributeValue mWerewolfAttributes[8]; AttributeValue mWerewolfAttributes[8];
int mLevel; int mLevel;
public: public:
@ -84,9 +84,6 @@ namespace MWMechanics
/// @return total fall height /// @return total fall height
float land(); float land();
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
const AttributeValue & getAttribute(int index) const; const AttributeValue & getAttribute(int index) const;
const DynamicStat<float> & getHealth() const; const DynamicStat<float> & getHealth() const;

View file

@ -6,9 +6,9 @@ namespace MWMechanics
/// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die! /// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die!
enum DrawState_ enum DrawState_
{ {
DrawState_Weapon = 0, DrawState_Nothing = 0,
DrawState_Spell = 1, DrawState_Weapon = 1,
DrawState_Nothing = 2 DrawState_Spell = 2
}; };
} }

View file

@ -342,7 +342,9 @@ namespace MWMechanics
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem(); MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
if (enchantItem != inv.end()) if (enchantItem != inv.end())
winMgr->setSelectedEnchantItem(*enchantItem); winMgr->setSelectedEnchantItem(*enchantItem);
else if (winMgr->getSelectedSpell() == "") else if (!winMgr->getSelectedSpell().empty())
winMgr->setSelectedSpell(winMgr->getSelectedSpell(), int(MWMechanics::getSpellSuccessChance(winMgr->getSelectedSpell(), mWatched)));
else
winMgr->unsetSelectedSpell(); winMgr->unsetSelectedSpell();
} }

View file

@ -432,9 +432,9 @@ float MWMechanics::NpcStats::getTimeToStartDrowning() const
{ {
return mTimeToStartDrowning; return mTimeToStartDrowning;
} }
void MWMechanics::NpcStats::setTimeToStartDrowning(float time) void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
{ {
assert(time>=0 && time<=20);
mTimeToStartDrowning=time; mTimeToStartDrowning=time;
} }
@ -446,11 +446,16 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mDisposition = mDisposition; state.mDisposition = mDisposition;
for (int i=0; i<27; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
{ {
mSkill[i].writeState (state.mSkills[i].mRegular); mSkill[i].writeState (state.mSkills[i].mRegular);
mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf); mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf);
} }
for (int i=0; i<ESM::Attribute::Length; ++i)
{
mWerewolfAttributes[i].writeState (state.mWerewolfAttributes[i]);
}
state.mIsWerewolf = mIsWerewolf;
state.mCrimeId = mCrimeId; state.mCrimeId = mCrimeId;
@ -467,10 +472,9 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mReputation = mReputation; state.mReputation = mReputation;
state.mWerewolfKills = mWerewolfKills; state.mWerewolfKills = mWerewolfKills;
state.mProfit = mProfit; state.mProfit = mProfit;
state.mAttackStrength = mAttackStrength;
state.mLevelProgress = mLevelProgress; state.mLevelProgress = mLevelProgress;
for (int i=0; i<8; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
state.mSkillIncrease[i] = mSkillIncreases[i]; state.mSkillIncrease[i] = mSkillIncreases[i];
std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds));
@ -500,21 +504,26 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
mDisposition = state.mDisposition; mDisposition = state.mDisposition;
for (int i=0; i<27; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
{ {
mSkill[i].readState (state.mSkills[i].mRegular); mSkill[i].readState (state.mSkills[i].mRegular);
mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf); mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf);
} }
for (int i=0; i<ESM::Attribute::Length; ++i)
{
mWerewolfAttributes[i].readState (state.mWerewolfAttributes[i]);
}
mIsWerewolf = state.mIsWerewolf;
mCrimeId = state.mCrimeId; mCrimeId = state.mCrimeId;
mBounty = state.mBounty; mBounty = state.mBounty;
mReputation = state.mReputation; mReputation = state.mReputation;
mWerewolfKills = state.mWerewolfKills; mWerewolfKills = state.mWerewolfKills;
mProfit = state.mProfit; mProfit = state.mProfit;
mAttackStrength = state.mAttackStrength;
mLevelProgress = state.mLevelProgress; mLevelProgress = state.mLevelProgress;
for (int i=0; i<8; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = state.mSkillIncrease[i]; mSkillIncreases[i] = state.mSkillIncrease[i];
for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin()); for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());

View file

@ -31,8 +31,8 @@ namespace MWMechanics
std::map<std::string, int> mFactionRank; std::map<std::string, int> mFactionRank;
int mDisposition; int mDisposition;
SkillValue mSkill[27]; SkillValue mSkill[ESM::Skill::Length];
SkillValue mWerewolfSkill[27]; SkillValue mWerewolfSkill[ESM::Skill::Length];
int mBounty; int mBounty;
std::set<std::string> mExpelled; std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation; std::map<std::string, int> mFactionReputation;
@ -40,7 +40,6 @@ namespace MWMechanics
int mCrimeId; int mCrimeId;
int mWerewolfKills; int mWerewolfKills;
int mProfit; int mProfit;
float mAttackStrength;
int mLevelProgress; // 0-10 int mLevelProgress; // 0-10

View file

@ -4,6 +4,7 @@
#include <cstdlib> #include <cstdlib>
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -30,10 +31,19 @@ namespace MWMechanics
{ {
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
std::vector<float> random; std::map<const int, float> random;
random.resize(spell->mEffects.mList.size());
for (unsigned int i=0; i<random.size();++i) // Determine the random magnitudes (unless this is a castable spell, in which case
random[i] = static_cast<float> (std::rand()) / RAND_MAX; // they will be determined when the spell is cast)
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
{
for (unsigned int i=0; i<spell->mEffects.mList.size();++i)
{
if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax)
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
}
}
mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random)); mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random));
} }
} }
@ -67,7 +77,11 @@ namespace MWMechanics
int i=0; int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it) for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
{ {
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * iter->second[i]); float random = 1.f;
if (iter->second.find(i) != iter->second.end())
random = iter->second.at(i);
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random);
++i; ++i;
} }
} }
@ -192,9 +206,60 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin(); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
effectIt != list.mList.end(); ++effectIt, ++i) effectIt != list.mList.end(); ++effectIt, ++i)
{ {
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i]; float random = 1.f;
if (it->second.find(i) != it->second.end())
random = it->second.at(i);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude); visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude);
} }
} }
} }
bool Spells::canUsePower(const std::string &power) const
{
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
return true;
else
return false;
}
void Spells::usePower(const std::string &power)
{
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
}
void Spells::readState(const ESM::SpellState &state)
{
mSpells = state.mSpells;
mSelectedSpell = state.mSelectedSpell;
// Discard spells that are no longer available due to changed content files
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(iter->first);
if (!spell)
{
if (iter->first == mSelectedSpell)
mSelectedSpell = "";
mSpells.erase(iter++);
}
else
++iter;
}
// No need to discard spells here (doesn't really matter if non existent ids are kept)
for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)
mUsedPowers[it->first] = MWWorld::TimeStamp(it->second);
}
void Spells::writeState(ESM::SpellState &state) const
{
state.mSpells = mSpells;
state.mSelectedSpell = mSelectedSpell;
for (std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
state.mUsedPowers[it->first] = it->second.toEsm();
}
} }

View file

@ -7,12 +7,16 @@
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/timestamp.hpp"
#include "magiceffects.hpp" #include "magiceffects.hpp"
namespace ESM namespace ESM
{ {
struct Spell; struct Spell;
struct SpellState;
} }
namespace MWMechanics namespace MWMechanics
@ -22,21 +26,29 @@ namespace MWMechanics
/// \brief Spell list /// \brief Spell list
/// ///
/// This class manages known spells as well as abilities, powers and permanent negative effects like /// This class manages known spells as well as abilities, powers and permanent negative effects like
/// diseases. /// diseases. It also keeps track of used powers (which can only be used every 24h).
class Spells class Spells
{ {
public: public:
typedef std::map<std::string, std::vector<float> > TContainer; // ID, normalised magnitudes
typedef std::map<std::string, std::map<const int, float> > TContainer; // ID, <effect index, normalised random magnitude>
typedef TContainer::const_iterator TIterator; typedef TContainer::const_iterator TIterator;
private: private:
TContainer mSpells; TContainer mSpells;
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
std::string mSelectedSpell; std::string mSelectedSpell;
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
public: public:
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
void purgeCommonDisease(); void purgeCommonDisease();
void purgeBlightDisease(); void purgeBlightDisease();
void purgeCorprusDisease(); void purgeCorprusDisease();
@ -72,6 +84,9 @@ namespace MWMechanics
bool hasBlightDisease() const; bool hasBlightDisease() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
void readState (const ESM::SpellState& state);
void writeState (ESM::SpellState& state) const;
}; };
} }

View file

@ -168,7 +168,10 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax)
mCameraPosNode->setPosition(Vector3(0,0,0)); mCameraPosNode->setPosition(Vector3(0,0,0));
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name); // Note: using force=true for exterior cell maps.
// They must be updated even if they were visited before, because the set of surrounding active cells might be different
// (and objects in a different cell can "bleed" into another cell's map if they cross the border)
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name, true);
if (mBuffers.find(name) == mBuffers.end()) if (mBuffers.find(name) == mBuffers.end())
{ {
@ -296,7 +299,8 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
ESM::FogState* fog = cell->getFog(); ESM::FogState* fog = cell->getFog();
// We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same. // We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same.
assert (i < int(fog->mFogTextures.size())); if (i >= int(fog->mFogTextures.size()))
throw std::runtime_error("fog texture count mismatch");
ESM::FogTexture& esm = fog->mFogTextures[i]; ESM::FogTexture& esm = fog->mFogTextures[i];
loadFogOfWar(texturePrefix, esm); loadFogOfWar(texturePrefix, esm);
@ -338,8 +342,6 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
PF_A8R8G8B8, PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY); TU_DYNAMIC_WRITE_ONLY);
} }
else
tex->unload();
return tex; return tex;
} }
@ -351,12 +353,14 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
Ogre::Image image; Ogre::Image image;
image.load(stream, "tga"); image.load(stream, "tga");
assert (image.getWidth() == sFogOfWarResolution && image.getHeight() == sFogOfWarResolution); if (image.getWidth() != sFogOfWarResolution || image.getHeight() != sFogOfWarResolution)
throw std::runtime_error("fog texture size mismatch");
std::string texName = texturePrefix + "_fog"; std::string texName = texturePrefix + "_fog";
Ogre::TexturePtr tex = createFogOfWarTexture(texName); Ogre::TexturePtr tex = createFogOfWarTexture(texName);
tex->unload();
tex->loadImage(image); tex->loadImage(image);
// create a buffer to use for dynamic operations // create a buffer to use for dynamic operations
@ -369,7 +373,7 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
void LocalMap::render(const float x, const float y, void LocalMap::render(const float x, const float y,
const float zlow, const float zhigh, const float zlow, const float zhigh,
const float xw, const float yw, const std::string& texture) const float xw, const float yw, const std::string& texture, bool force)
{ {
mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 ); mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 );
mCellCamera->setNearClipDistance(50); mCellCamera->setNearClipDistance(50);
@ -408,6 +412,11 @@ void LocalMap::render(const float x, const float y,
PF_R8G8B8); PF_R8G8B8);
tex->getBuffer()->blit(mRenderTexture->getBuffer()); tex->getBuffer()->blit(mRenderTexture->getBuffer());
} }
else if (force)
{
mRenderTarget->update();
tex->getBuffer()->blit(mRenderTexture->getBuffer());
}
mRenderingManager->enableLights(true); mRenderingManager->enableLights(true);
mLight->setVisible(false); mLight->setVisible(false);

View file

@ -106,10 +106,11 @@ namespace MWRender
float mAngle; float mAngle;
const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle); const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle);
/// @param force Always render, even if we already have a cached map
void render(const float x, const float y, void render(const float x, const float y,
const float zlow, const float zhigh, const float zlow, const float zhigh,
const float xw, const float yw, const float xw, const float yw,
const std::string& texture); const std::string& texture, bool force=false);
// Creates a fog of war texture and initializes it to full black // Creates a fog of war texture and initializes it to full black
void createFogOfWar(const std::string& texturePrefix); void createFogOfWar(const std::string& texturePrefix);

View file

@ -380,6 +380,10 @@ void RenderingManager::update (float duration, bool paused)
mCamera->update(duration, paused); mCamera->update(duration, paused);
Ogre::SceneNode *node = data.getBaseNode();
Ogre::Quaternion orient = node->_getDerivedOrientation();
mLocalMap->updatePlayer(playerPos, orient);
if(paused) if(paused)
return; return;
@ -393,10 +397,6 @@ void RenderingManager::update (float duration, bool paused)
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
Ogre::SceneNode *node = data.getBaseNode();
Ogre::Quaternion orient = node->_getDerivedOrientation();
mLocalMap->updatePlayer(playerPos, orient);
mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam)); mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam));

View file

@ -326,6 +326,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_GMAP: case ESM::REC_GMAP:
case ESM::REC_KEYS: case ESM::REC_KEYS:
case ESM::REC_ASPL:
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val); MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
break; break;

View file

@ -3,6 +3,9 @@
#include "class.hpp" #include "class.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWWorld namespace MWWorld
{ {
ActionApply::ActionApply (const Ptr& target, const std::string& id) ActionApply::ActionApply (const Ptr& target, const std::string& id)
@ -11,6 +14,8 @@ namespace MWWorld
void ActionApply::executeImp (const Ptr& actor) void ActionApply::executeImp (const Ptr& actor)
{ {
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor); MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor);
} }
@ -22,6 +27,8 @@ namespace MWWorld
void ActionApplyWithSkill::executeImp (const Ptr& actor) void ActionApplyWithSkill::executeImp (const Ptr& actor)
{ {
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
if (MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor) && mUsageType!=-1) if (MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor) && mUsageType!=-1)
MWWorld::Class::get (getTarget()).skillUsageSucceeded (actor, mSkillIndex, mUsageType); MWWorld::Class::get (getTarget()).skillUsageSucceeded (actor, mSkillIndex, mUsageType);
} }

View file

@ -199,7 +199,7 @@ void ESMStore::setUp()
if (!mRaces.find (player->mRace) || if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass)) !mClasses.find (player->mClass))
throw std::runtime_error ("Invalid player record (race or class unavilable"); throw std::runtime_error ("Invalid player record (race or class unavailable");
} }
return true; return true;

View file

@ -87,6 +87,7 @@ namespace MWWorld
float mMultiplier; float mMultiplier;
}; };
// TODO: store in savegame
typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes; typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;
TEffectMagnitudes mPermanentMagicEffectMagnitudes; TEffectMagnitudes mPermanentMagicEffectMagnitudes;

View file

@ -626,12 +626,12 @@ namespace MWWorld
bool cmode = act->getCollisionMode(); bool cmode = act->getCollisionMode();
if(cmode) if(cmode)
{ {
act->enableCollisions(false); act->enableCollisionMode(false);
return false; return false;
} }
else else
{ {
act->enableCollisions(true); act->enableCollisionMode(true);
return true; return true;
} }
} }

View file

@ -85,7 +85,17 @@ namespace
namespace MWWorld namespace MWWorld
{ {
void Scene::update (float duration, bool paused){ void Scene::update (float duration, bool paused)
{
if (mNeedMapUpdate)
{
// Note: exterior cell maps must be updated, even if they were visited before, because the set of surrounding cells might be different
// (and objects in a different cell can "bleed" into another cells map if they cross the border)
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
mRendering.requestMap(*active);
mNeedMapUpdate = false;
}
mRendering.update (duration, paused); mRendering.update (duration, paused);
} }
@ -197,8 +207,9 @@ namespace MWWorld
mRendering.updateTerrain(); mRendering.updateTerrain();
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active) // Delay the map update until scripts have been given a chance to run.
mRendering.requestMap(*active); // If we don't do this, objects that should be disabled will still appear on the map.
mNeedMapUpdate = true;
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell); MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
} }
@ -342,7 +353,7 @@ namespace MWWorld
//We need the ogre renderer and a scene node. //We need the ogre renderer and a scene node.
Scene::Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics) Scene::Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics)
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering) : mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNeedMapUpdate(false)
{ {
} }

View file

@ -58,6 +58,8 @@ namespace MWWorld
PhysicsSystem *mPhysics; PhysicsSystem *mPhysics;
MWRender::RenderingManager& mRendering; MWRender::RenderingManager& mRendering;
bool mNeedMapUpdate;
void playerCellChange (CellStore *cell, const ESM::Position& position, void playerCellChange (CellStore *cell, const ESM::Position& position,
bool adjustPlayerPos = true); bool adjustPlayerPos = true);

View file

@ -1,10 +1,10 @@
#include "timestamp.hpp" #include "timestamp.hpp"
#include <cmath> #include <cmath>
#include <stdexcept> #include <stdexcept>
#include <components/esm/defs.hpp>
namespace MWWorld namespace MWWorld
{ {
TimeStamp::TimeStamp (float hour, int day) TimeStamp::TimeStamp (float hour, int day)
@ -105,4 +105,18 @@ namespace MWWorld
return hours + 24*days; return hours + 24*days;
} }
ESM::TimeStamp TimeStamp::toEsm() const
{
ESM::TimeStamp ret;
ret.mDay = mDay;
ret.mHour = mHour;
return ret;
}
TimeStamp::TimeStamp(const ESM::TimeStamp &esm)
{
mDay = esm.mDay;
mHour = esm.mHour;
}
} }

View file

@ -1,6 +1,11 @@
#ifndef GAME_MWWORLD_TIMESTAMP_H #ifndef GAME_MWWORLD_TIMESTAMP_H
#define GAME_MWWORLD_TIMESTAMP_H #define GAME_MWWORLD_TIMESTAMP_H
namespace ESM
{
class TimeStamp;
}
namespace MWWorld namespace MWWorld
{ {
/// \brief In-game time stamp /// \brief In-game time stamp
@ -14,9 +19,12 @@ namespace MWWorld
public: public:
explicit TimeStamp (float hour = 0, int day = 0); explicit TimeStamp (float hour = 0, int day = 0);
///< \oaram hour [0, 23) ///< \param hour [0, 23)
/// \param day >=0 /// \param day >=0
explicit TimeStamp (const ESM::TimeStamp& esm);
ESM::TimeStamp toEsm () const;
float getHour() const; float getHour() const;
int getDay() const; int getDay() const;

View file

@ -1491,6 +1491,9 @@ namespace MWWorld
{ {
MWWorld::LiveCellRef<ESM::Door>& ref = *it; MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if (!ref.mData.isEnabled())
continue;
if (ref.mRef.mTeleport) if (ref.mRef.mTeleport)
{ {
World::DoorMarker newMarker; World::DoorMarker newMarker;
@ -1954,7 +1957,7 @@ namespace MWWorld
{ {
OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle()); OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
physicActor->enableCollisions(enable); physicActor->enableCollisionBody(enable);
} }
bool World::findInteriorPosition(const std::string &name, ESM::Position &pos) bool World::findInteriorPosition(const std::string &name, ESM::Position &pos)
@ -2161,8 +2164,8 @@ namespace MWWorld
// If this is a power, check if it was already used in the last 24h // If this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power) if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
{ {
if (stats.canUsePower(spell->mId)) if (stats.getSpells().canUsePower(spell->mId))
stats.usePower(spell->mId); stats.getSpells().usePower(spell->mId);
else else
{ {
message = "#{sPowerAlreadyUsed}"; message = "#{sPowerAlreadyUsed}";

View file

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate npcstats creaturestats weatherstate quickkeys fogstate spellstate
) )
add_component_dir (misc add_component_dir (misc

View file

@ -8,13 +8,143 @@ void ESM::CreatureStats::load (ESMReader &esm)
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].load (esm); mDynamic[i].load (esm);
mGoldPool = 0;
esm.getHNOT (mGoldPool, "GOLD");
mTradeTime.mDay = 0;
mTradeTime.mHour = 0;
esm.getHNOT (mTradeTime, "TIME");
mDead = false;
esm.getHNOT (mDead, "DEAD");
mDied = false;
esm.getHNOT (mDied, "DIED");
mFriendlyHits = 0;
esm.getHNOT (mFriendlyHits, "FRHT");
mTalkedTo = false;
esm.getHNOT (mTalkedTo, "TALK");
mAlarmed = false;
esm.getHNOT (mAlarmed, "ALRM");
mHostile = false;
esm.getHNOT (mHostile, "HOST");
mAttackingOrSpell = false;
esm.getHNOT (mAttackingOrSpell, "ATCK");
mKnockdown = false;
esm.getHNOT (mKnockdown, "KNCK");
mKnockdownOneFrame = false;
esm.getHNOT (mKnockdownOneFrame, "KNC1");
mKnockdownOverOneFrame = false;
esm.getHNOT (mKnockdownOverOneFrame, "KNCO");
mHitRecovery = false;
esm.getHNOT (mHitRecovery, "HITR");
mBlock = false;
esm.getHNOT (mBlock, "BLCK");
mMovementFlags = 0;
esm.getHNOT (mMovementFlags, "MOVE");
mAttackStrength = 0;
esm.getHNOT (mAttackStrength, "ASTR");
mFallHeight = 0;
esm.getHNOT (mFallHeight, "FALL");
mLastHitObject = esm.getHNOString ("LHIT");
mRecalcDynamicStats = false;
esm.getHNOT (mRecalcDynamicStats, "CALC");
mDrawState = 0;
esm.getHNOT (mDrawState, "DRAW");
mLevel = 1;
esm.getHNOT (mLevel, "LEVL");
mSpells.load(esm);
} }
void ESM::CreatureStats::save (ESMWriter &esm) const void ESM::CreatureStats::save (ESMWriter &esm) const
{ {
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
mAttributes[i].save (esm); mAttributes[i].save (esm);
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
mDynamic[i].save (esm); mDynamic[i].save (esm);
if (mGoldPool)
esm.writeHNT ("GOLD", mGoldPool);
esm.writeHNT ("TIME", mTradeTime);
if (mDead)
esm.writeHNT ("DEAD", mDead);
if (mDied)
esm.writeHNT ("DIED", mDied);
if (mFriendlyHits)
esm.writeHNT ("FRHT", mFriendlyHits);
if (mTalkedTo)
esm.writeHNT ("TALK", mTalkedTo);
if (mAlarmed)
esm.writeHNT ("ALRM", mAlarmed);
if (mHostile)
esm.writeHNT ("HOST", mHostile);
if (mAttackingOrSpell)
esm.writeHNT ("ATCK", mAttackingOrSpell);
if (mKnockdown)
esm.writeHNT ("KNCK", mKnockdown);
if (mKnockdownOneFrame)
esm.writeHNT ("KNC1", mKnockdownOneFrame);
if (mKnockdownOverOneFrame)
esm.writeHNT ("KNCO", mKnockdownOverOneFrame);
if (mHitRecovery)
esm.writeHNT ("HITR", mHitRecovery);
if (mBlock)
esm.writeHNT ("BLCK", mBlock);
if (mMovementFlags)
esm.writeHNT ("MOVE", mMovementFlags);
if (mAttackStrength)
esm.writeHNT ("ASTR", mAttackStrength);
if (mFallHeight)
esm.writeHNT ("FALL", mFallHeight);
if (!mLastHitObject.empty())
esm.writeHNString ("LHIT", mLastHitObject);
if (mRecalcDynamicStats)
esm.writeHNT ("CALC", mRecalcDynamicStats);
if (mDrawState)
esm.writeHNT ("DRAW", mDrawState);
if (mLevel != 1)
esm.writeHNT ("LEVL", mLevel);
mSpells.save(esm);
} }

View file

@ -7,18 +7,48 @@
#include "statstate.hpp" #include "statstate.hpp"
#include "defs.hpp"
#include "spellstate.hpp"
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
class ESMWriter; class ESMWriter;
// format 0, saved games only // format 0, saved games only
struct CreatureStats struct CreatureStats
{ {
StatState<int> mAttributes[8]; StatState<int> mAttributes[8];
StatState<float> mDynamic[3]; StatState<float> mDynamic[3];
ESM::TimeStamp mTradeTime;
int mGoldPool;
bool mDead;
bool mDied;
int mFriendlyHits;
bool mTalkedTo;
bool mAlarmed;
bool mAttacked;
bool mHostile;
bool mAttackingOrSpell;
bool mKnockdown;
bool mKnockdownOneFrame;
bool mKnockdownOverOneFrame;
bool mHitRecovery;
bool mBlock;
unsigned int mMovementFlags;
float mAttackStrength;
float mFallHeight;
std::string mLastHitObject;
bool mRecalcDynamicStats;
int mDrawState;
int mLevel;
SpellState mSpells;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;
}; };

View file

@ -6,6 +6,12 @@
namespace ESM namespace ESM
{ {
struct TimeStamp
{
float mHour;
int mDay;
};
// Pixel color value. Standard four-byte rr,gg,bb,aa format. // Pixel color value. Standard four-byte rr,gg,bb,aa format.
typedef int32_t Color; typedef int32_t Color;
@ -101,6 +107,7 @@ enum RecNameInts
REC_WTHR = 0x52485457, REC_WTHR = 0x52485457,
REC_KEYS = FourCC<'K','E','Y','S'>::value, REC_KEYS = FourCC<'K','E','Y','S'>::value,
REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value,
// format 1 // format 1
REC_FILT = 0x544C4946 REC_FILT = 0x544C4946

View file

@ -100,6 +100,9 @@ namespace ESM
void ESMWriter::startSubRecord(const std::string& name) void ESMWriter::startSubRecord(const std::string& name)
{ {
// Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.
assert (mRecords.size() <= 1);
writeName(name); writeName(name);
RecordData rec; RecordData rec;
rec.name = name; rec.name = name;

View file

@ -96,6 +96,7 @@ class ESMWriter
void startRecord(const std::string& name, uint32_t flags = 0); void startRecord(const std::string& name, uint32_t flags = 0);
void startRecord(uint32_t name, uint32_t flags = 0); void startRecord(uint32_t name, uint32_t flags = 0);
/// @note Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.
void startSubRecord(const std::string& name); void startSubRecord(const std::string& name);
void endRecord(const std::string& name); void endRecord(const std::string& name);
void endRecord(uint32_t name); void endRecord(uint32_t name);

View file

@ -36,6 +36,18 @@ void ESM::NpcStats::load (ESMReader &esm)
mSkills[i].mWerewolf.load (esm); mSkills[i].mWerewolf.load (esm);
} }
bool hasWerewolfAttributes = false;
esm.getHNOT (hasWerewolfAttributes, "HWAT");
if (hasWerewolfAttributes)
{
for (int i=0; i<8; ++i)
mWerewolfAttributes[i].load (esm);
}
mIsWerewolf = false;
esm.getHNOT (mIsWerewolf, "WOLF");
mBounty = 0; mBounty = 0;
esm.getHNOT (mBounty, "BOUN"); esm.getHNOT (mBounty, "BOUN");
@ -48,8 +60,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mProfit = 0; mProfit = 0;
esm.getHNOT (mProfit, "PROF"); esm.getHNOT (mProfit, "PROF");
mAttackStrength = 0; // No longer used. Now part of CreatureStats.
esm.getHNOT (mAttackStrength, "ASTR"); float attackStrength = 0;
esm.getHNOT (attackStrength, "ASTR");
mLevelProgress = 0; mLevelProgress = 0;
esm.getHNOT (mLevelProgress, "LPRO"); esm.getHNOT (mLevelProgress, "LPRO");
@ -101,6 +114,13 @@ void ESM::NpcStats::save (ESMWriter &esm) const
mSkills[i].mWerewolf.save (esm); mSkills[i].mWerewolf.save (esm);
} }
esm.writeHNT ("HWAT", true);
for (int i=0; i<8; ++i)
mWerewolfAttributes[i].save (esm);
if (mIsWerewolf)
esm.writeHNT ("WOLF", mIsWerewolf);
if (mBounty) if (mBounty)
esm.writeHNT ("BOUN", mBounty); esm.writeHNT ("BOUN", mBounty);
@ -113,9 +133,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mProfit) if (mProfit)
esm.writeHNT ("PROF", mProfit); esm.writeHNT ("PROF", mProfit);
if (mAttackStrength)
esm.writeHNT ("ASTR", mAttackStrength);
if (mLevelProgress) if (mLevelProgress)
esm.writeHNT ("LPRO", mLevelProgress); esm.writeHNT ("LPRO", mLevelProgress);

View file

@ -31,6 +31,9 @@ namespace ESM
Faction(); Faction();
}; };
StatState<int> mWerewolfAttributes[8];
bool mIsWerewolf;
std::map<std::string, Faction> mFactions; std::map<std::string, Faction> mFactions;
int mDisposition; int mDisposition;
Skill mSkills[27]; Skill mSkills[27];
@ -38,7 +41,6 @@ namespace ESM
int mReputation; int mReputation;
int mWerewolfKills; int mWerewolfKills;
int mProfit; int mProfit;
float mAttackStrength;
int mLevelProgress; int mLevelProgress;
int mSkillIncrease[8]; int mSkillIncrease[8];
std::vector<std::string> mUsedIds; std::vector<std::string> mUsedIds;

View file

@ -8,11 +8,13 @@ namespace ESM
void QuickKeys::load(ESMReader &esm) void QuickKeys::load(ESMReader &esm)
{ {
while (esm.isNextSub("KEY_")) if (esm.isNextSub("KEY_"))
esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader
while (esm.isNextSub("TYPE"))
{ {
esm.getSubHeader();
int keyType; int keyType;
esm.getHNT(keyType, "TYPE"); esm.getHT(keyType);
std::string id; std::string id;
id = esm.getHNString("ID__"); id = esm.getHNString("ID__");
@ -21,21 +23,18 @@ namespace ESM
key.mId = id; key.mId = id;
mKeys.push_back(key); mKeys.push_back(key);
if (esm.isNextSub("KEY_"))
esm.getSubHeader(); // no longer used, because sub-record hierachies do not work properly in esmreader
} }
} }
void QuickKeys::save(ESMWriter &esm) const void QuickKeys::save(ESMWriter &esm) const
{ {
const std::string recKey = "KEY_";
for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it) for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it)
{ {
esm.startSubRecord(recKey);
esm.writeHNT("TYPE", it->mType); esm.writeHNT("TYPE", it->mType);
esm.writeHNString("ID__", it->mId); esm.writeHNString("ID__", it->mId);
esm.endRecord(recKey);
} }
} }

View file

@ -0,0 +1,66 @@
#include "spellstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void SpellState::load(ESMReader &esm)
{
while (esm.isNextSub("SPEL"))
{
std::string id = esm.getHString();
std::map<const int, float> random;
while (esm.isNextSub("INDX"))
{
int index;
esm.getHT(index);
float magnitude;
esm.getHNT(magnitude, "RAND");
random[index] = magnitude;
}
mSpells[id] = random;
}
while (esm.isNextSub("USED"))
{
std::string id = esm.getHString();
TimeStamp time;
esm.getHNT(time, "TIME");
mUsedPowers[id] = time;
}
mSelectedSpell = esm.getHNOString("SLCT");
}
void SpellState::save(ESMWriter &esm) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
esm.writeHNString("SPEL", it->first);
const std::map<const int, float>& random = it->second;
for (std::map<const int, float>::const_iterator rIt = random.begin(); rIt != random.end(); ++rIt)
{
esm.writeHNT("INDX", rIt->first);
esm.writeHNT("RAND", rIt->second);
}
}
for (std::map<std::string, TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
{
esm.writeHNString("USED", it->first);
esm.writeHNT("TIME", it->second);
}
if (!mSelectedSpell.empty())
esm.writeHNString("SLCT", mSelectedSpell);
}
}

View file

@ -0,0 +1,29 @@
#ifndef OPENMW_ESM_SPELLSTATE_H
#define OPENMW_ESM_SPELLSTATE_H
#include <map>
#include <string>
#include "defs.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
struct SpellState
{
typedef std::map<std::string, std::map<const int, float> > TContainer;
TContainer mSpells;
std::map<std::string, TimeStamp> mUsedPowers;
std::string mSelectedSpell;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

View file

@ -41,15 +41,18 @@ namespace Physic
} }
} }
void PhysicActor::enableCollisions(bool collision) void PhysicActor::enableCollisionMode(bool collision)
{
mCollisionMode = collision;
}
void PhysicActor::enableCollisionBody(bool collision)
{ {
assert(mBody); assert(mBody);
if(collision && !mCollisionMode) enableCollisionBody(); if(collision && !mCollisionMode) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody(); if(!collision && mCollisionMode) disableCollisionBody();
mCollisionMode = collision;
} }
void PhysicActor::setPosition(const Ogre::Vector3 &pos) void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{ {
assert(mBody); assert(mBody);

View file

@ -99,7 +99,15 @@ namespace Physic
*/ */
void setRotation(const Ogre::Quaternion &quat); void setRotation(const Ogre::Quaternion &quat);
void enableCollisions(bool collision); /**
* Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry.
*/
void enableCollisionMode(bool collision);
/**
* Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor.
*/
void enableCollisionBody(bool collision);
bool getCollisionMode() const bool getCollisionMode() const
{ {

View file

@ -116,7 +116,7 @@ void ActorTracer::findGround(btCollisionObject *actor, const Ogre::Vector3 &star
mFraction = newTraceCallback.m_closestHitFraction; mFraction = newTraceCallback.m_closestHitFraction;
mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
mEndPos = (end-start)*mFraction + start; mEndPos = (end-start)*mFraction + start;
mEndPos[2] -= 1.0f; mEndPos[2] += 1.0f;
} }
else else
{ {