Merge remote-tracking branch 'upstream/master'

deque
Thomas 11 years ago
commit 9dbe53c722

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

@ -233,6 +233,9 @@ namespace MWGui
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::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();
float mouseX = cursorPosition.left / float(viewSize.width);

@ -541,9 +541,11 @@ namespace MWGui
int count = object.getRefData().getCount();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWBase::Environment::get().getWorld()->breakInvisibility(player);
// add to player inventory
// 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);
// remove from world
MWBase::Environment::get().getWorld()->deleteObject (object);

@ -465,7 +465,18 @@ namespace MWGui
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)

@ -97,14 +97,17 @@ namespace MWGui
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 setGlobalMapPlayerPosition (float worldX, float worldY);
virtual void open();
void onFrame(float dt) { NoDrop::onFrame(dt); }
void onFrame(float dt);
/// Clear all savegame-specific data
void clear();
@ -132,6 +135,10 @@ namespace MWGui
typedef std::pair<int, int> CellId;
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* mEventBoxLocal;

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

@ -920,9 +920,6 @@ namespace MWMechanics
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())
{
++mDeathCount[cls.getId(iter->first)];
@ -939,6 +936,8 @@ namespace MWMechanics
stats.setMagicEffects(MWMechanics::MagicEffects());
calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
if (cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
}

@ -17,8 +17,10 @@ namespace MWMechanics
mAttacked (false), mHostile (false),
mAttackingOrSpell(false),
mIsWerewolf(false),
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f)
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
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)
mAiSettings[i] = 0;
@ -348,20 +350,6 @@ namespace MWMechanics
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)
{
mFallHeight += height;
@ -481,20 +469,75 @@ namespace MWMechanics
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]);
for (int i=0; i<3; ++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)
{
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
mAttributes[i].readState (state.mAttributes[i]);
for (int i=0; i<3; ++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

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

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

@ -342,7 +342,9 @@ namespace MWMechanics
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
if (enchantItem != inv.end())
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();
}

@ -432,9 +432,9 @@ float MWMechanics::NpcStats::getTimeToStartDrowning() const
{
return mTimeToStartDrowning;
}
void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
{
assert(time>=0 && time<=20);
mTimeToStartDrowning=time;
}
@ -446,11 +446,16 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
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);
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;
@ -467,10 +472,9 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
state.mReputation = mReputation;
state.mWerewolfKills = mWerewolfKills;
state.mProfit = mProfit;
state.mAttackStrength = mAttackStrength;
state.mLevelProgress = mLevelProgress;
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
state.mSkillIncrease[i] = mSkillIncreases[i];
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;
for (int i=0; i<27; ++i)
for (int i=0; i<ESM::Skill::Length; ++i)
{
mSkill[i].readState (state.mSkills[i].mRegular);
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;
mBounty = state.mBounty;
mReputation = state.mReputation;
mWerewolfKills = state.mWerewolfKills;
mProfit = state.mProfit;
mAttackStrength = state.mAttackStrength;
mLevelProgress = state.mLevelProgress;
for (int i=0; i<8; ++i)
for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = state.mSkillIncrease[i];
for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());

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

@ -4,6 +4,7 @@
#include <cstdlib>
#include <components/esm/loadspel.hpp>
#include <components/esm/spellstate.hpp>
#include "../mwbase/environment.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);
std::vector<float> random;
random.resize(spell->mEffects.mList.size());
for (unsigned int i=0; i<random.size();++i)
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
std::map<const int, float> random;
// Determine the random magnitudes (unless this is a castable spell, in which case
// 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));
}
}
@ -67,7 +77,11 @@ namespace MWMechanics
int i=0;
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;
}
}
@ -192,9 +206,60 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
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);
}
}
}
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();
}
}

@ -7,12 +7,16 @@
#include <components/misc/stringops.hpp>
#include "../mwworld/ptr.hpp"
#include "../mwworld/timestamp.hpp"
#include "magiceffects.hpp"
namespace ESM
{
struct Spell;
struct SpellState;
}
namespace MWMechanics
@ -22,21 +26,29 @@ namespace MWMechanics
/// \brief Spell list
///
/// 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
{
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;
private:
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::map<std::string, MWWorld::TimeStamp> mUsedPowers;
public:
bool canUsePower (const std::string& power) const;
void usePower (const std::string& power);
void purgeCommonDisease();
void purgeBlightDisease();
void purgeCorprusDisease();
@ -72,6 +84,9 @@ namespace MWMechanics
bool hasBlightDisease() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
void readState (const ESM::SpellState& state);
void writeState (ESM::SpellState& state) const;
};
}

@ -168,7 +168,10 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax)
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())
{
@ -296,7 +299,8 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
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.
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];
loadFogOfWar(texturePrefix, esm);
@ -338,8 +342,6 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
PF_A8R8G8B8,
TU_DYNAMIC_WRITE_ONLY);
}
else
tex->unload();
return tex;
}
@ -351,12 +353,14 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
Ogre::Image image;
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";
Ogre::TexturePtr tex = createFogOfWarTexture(texName);
tex->unload();
tex->loadImage(image);
// 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,
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->setNearClipDistance(50);
@ -408,6 +412,11 @@ void LocalMap::render(const float x, const float y,
PF_R8G8B8);
tex->getBuffer()->blit(mRenderTexture->getBuffer());
}
else if (force)
{
mRenderTarget->update();
tex->getBuffer()->blit(mRenderTexture->getBuffer());
}
mRenderingManager->enableLights(true);
mLight->setVisible(false);

@ -106,10 +106,11 @@ namespace MWRender
float mAngle;
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,
const float zlow, const float zhigh,
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
void createFogOfWar(const std::string& texturePrefix);

@ -380,6 +380,10 @@ void RenderingManager::update (float duration, bool paused)
mCamera->update(duration, paused);
Ogre::SceneNode *node = data.getBaseNode();
Ogre::Quaternion orient = node->_getDerivedOrientation();
mLocalMap->updatePlayer(playerPos, orient);
if(paused)
return;
@ -393,10 +397,6 @@ void RenderingManager::update (float duration, bool paused)
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));

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

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

@ -199,7 +199,7 @@ void ESMStore::setUp()
if (!mRaces.find (player->mRace) ||
!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;

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

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

@ -85,7 +85,17 @@ namespace
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);
}
@ -197,8 +207,9 @@ namespace MWWorld
mRendering.updateTerrain();
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
mRendering.requestMap(*active);
// Delay the map update until scripts have been given a chance to run.
// 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);
}
@ -342,7 +353,7 @@ namespace MWWorld
//We need the ogre renderer and a scene node.
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)
{
}

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

@ -1,10 +1,10 @@
#include "timestamp.hpp"
#include <cmath>
#include <stdexcept>
#include <components/esm/defs.hpp>
namespace MWWorld
{
TimeStamp::TimeStamp (float hour, int day)
@ -105,4 +105,18 @@ namespace MWWorld
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;
}
}

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

@ -1496,6 +1496,9 @@ namespace MWWorld
{
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if (!ref.mData.isEnabled())
continue;
if (ref.mRef.mTeleport)
{
World::DoorMarker newMarker;
@ -1969,7 +1972,7 @@ namespace MWWorld
{
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)
@ -2176,8 +2179,8 @@ namespace MWWorld
// 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 (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
if (stats.getSpells().canUsePower(spell->mId))
stats.getSpells().usePower(spell->mId);
else
{
message = "#{sPowerAlreadyUsed}";

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
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
npcstats creaturestats weatherstate quickkeys fogstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate
)
add_component_dir (misc

@ -8,13 +8,143 @@ void ESM::CreatureStats::load (ESMReader &esm)
for (int i=0; i<3; ++i)
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
{
for (int i=0; i<8; ++i)
mAttributes[i].save (esm);
for (int i=0; i<3; ++i)
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);
}

@ -7,21 +7,51 @@
#include "statstate.hpp"
#include "defs.hpp"
#include "spellstate.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct CreatureStats
{
StatState<int> mAttributes[8];
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 save (ESMWriter &esm) const;
};
}
#endif
#endif

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

@ -100,6 +100,9 @@ namespace ESM
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);
RecordData rec;
rec.name = name;

@ -96,6 +96,7 @@ class ESMWriter
void startRecord(const std::string& 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 endRecord(const std::string& name);
void endRecord(uint32_t name);

@ -36,6 +36,18 @@ void ESM::NpcStats::load (ESMReader &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;
esm.getHNOT (mBounty, "BOUN");
@ -48,8 +60,9 @@ void ESM::NpcStats::load (ESMReader &esm)
mProfit = 0;
esm.getHNOT (mProfit, "PROF");
mAttackStrength = 0;
esm.getHNOT (mAttackStrength, "ASTR");
// No longer used. Now part of CreatureStats.
float attackStrength = 0;
esm.getHNOT (attackStrength, "ASTR");
mLevelProgress = 0;
esm.getHNOT (mLevelProgress, "LPRO");
@ -101,6 +114,13 @@ void ESM::NpcStats::save (ESMWriter &esm) const
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)
esm.writeHNT ("BOUN", mBounty);
@ -113,9 +133,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mProfit)
esm.writeHNT ("PROF", mProfit);
if (mAttackStrength)
esm.writeHNT ("ASTR", mAttackStrength);
if (mLevelProgress)
esm.writeHNT ("LPRO", mLevelProgress);
@ -136,4 +153,4 @@ void ESM::NpcStats::save (ESMWriter &esm) const
if (mCrimeId != -1)
esm.writeHNT ("CRID", mCrimeId);
}
}

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

@ -8,11 +8,13 @@ namespace 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;
esm.getHNT(keyType, "TYPE");
esm.getHT(keyType);
std::string id;
id = esm.getHNString("ID__");
@ -21,21 +23,18 @@ namespace ESM
key.mId = id;
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
{
const std::string recKey = "KEY_";
for (std::vector<QuickKey>::const_iterator it = mKeys.begin(); it != mKeys.end(); ++it)
{
esm.startSubRecord(recKey);
esm.writeHNT("TYPE", it->mType);
esm.writeHNString("ID__", it->mId);
esm.endRecord(recKey);
}
}

@ -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);
}
}

@ -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

@ -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);
if(collision && !mCollisionMode) enableCollisionBody();
if(!collision && mCollisionMode) disableCollisionBody();
mCollisionMode = collision;
}
void PhysicActor::setPosition(const Ogre::Vector3 &pos)
{
assert(mBody);

@ -99,7 +99,15 @@ namespace Physic
*/
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
{

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

Loading…
Cancel
Save