Merge remote-tracking branch 'scrawl/master'

This commit is contained in:
Marc Zinnschlag 2015-08-17 14:05:51 +02:00
commit 3992125b61
29 changed files with 249 additions and 116 deletions

View file

@ -281,9 +281,12 @@ namespace MWClass
ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative
} }
bool Container::canLock(const MWWorld::Ptr &ptr) const
{
return true;
}
MWWorld::Ptr MWWorld::Ptr Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
Container::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{ {
MWWorld::LiveCellRef<ESM::Container> *ref = MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>(); ptr.get<ESM::Container>();
@ -291,8 +294,7 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Container>().insert(*ref), &cell); return MWWorld::Ptr(&cell.get<ESM::Container>().insert(*ref), &cell);
} }
void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
const
{ {
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state); const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
@ -307,8 +309,7 @@ namespace MWClass
readState (state2.mInventory); readState (state2.mInventory);
} }
void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const
const
{ {
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state); ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);

View file

@ -57,6 +57,8 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const; virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object ///< Unlock object
virtual bool canLock(const MWWorld::Ptr &ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const; const;
///< Read additional state from \a state into \a ptr. ///< Read additional state from \a state into \a ptr.

View file

@ -207,6 +207,11 @@ namespace MWClass
ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative
} }
bool Door::canLock(const MWWorld::Ptr &ptr) const
{
return true;
}
std::string Door::getScript (const MWWorld::Ptr& ptr) const std::string Door::getScript (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Door> *ref = MWWorld::LiveCellRef<ESM::Door> *ref =

View file

@ -47,6 +47,8 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const; virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object ///< Unlock object
virtual bool canLock(const MWWorld::Ptr &ptr) const;
virtual std::string getScript (const MWWorld::Ptr& ptr) const; virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr

View file

@ -312,7 +312,7 @@ namespace MWGui
}; };
} }
void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) void CharacterCreation::selectPickedClass()
{ {
if (mPickClassDialog) if (mPickClassDialog)
{ {
@ -332,20 +332,18 @@ namespace MWGui
} }
updatePlayerHealth(); updatePlayerHealth();
}
void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow)
{
selectPickedClass();
handleDialogDone(CSE_ClassChosen, GM_Birth); handleDialogDone(CSE_ClassChosen, GM_Birth);
} }
void CharacterCreation::onPickClassDialogBack() void CharacterCreation::onPickClassDialogBack()
{ {
if (mPickClassDialog) selectPickedClass();
{
const std::string classId = mPickClassDialog->getClassId();
if (!classId.empty())
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(classId);
MWBase::Environment::get().getWindowManager()->removeDialog(mPickClassDialog);
mPickClassDialog = 0;
}
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
@ -390,29 +388,7 @@ namespace MWGui
handleDialogDone(CSE_NameChosen, GM_Race); handleDialogDone(CSE_NameChosen, GM_Race);
} }
void CharacterCreation::onRaceDialogBack() void CharacterCreation::selectRace()
{
if (mRaceDialog)
{
const ESM::NPC &data = mRaceDialog->getResult();
mPlayerRaceId = data.mRace;
if (!mPlayerRaceId.empty()) {
MWBase::Environment::get().getMechanicsManager()->setPlayerRace(
data.mRace,
data.isMale(),
data.mHead,
data.mHair
);
}
MWBase::Environment::get().getWindowManager()->removeDialog(mRaceDialog);
mRaceDialog = 0;
}
MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name);
}
void CharacterCreation::onRaceDialogDone(WindowBase* parWindow)
{ {
if (mRaceDialog) if (mRaceDialog)
{ {
@ -433,11 +409,24 @@ namespace MWGui
} }
updatePlayerHealth(); updatePlayerHealth();
}
void CharacterCreation::onRaceDialogBack()
{
selectRace();
MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Name);
}
void CharacterCreation::onRaceDialogDone(WindowBase* parWindow)
{
selectRace();
handleDialogDone(CSE_RaceChosen, GM_Class); handleDialogDone(CSE_RaceChosen, GM_Class);
} }
void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) void CharacterCreation::selectBirthSign()
{ {
if (mBirthSignDialog) if (mBirthSignDialog)
{ {
@ -449,24 +438,24 @@ namespace MWGui
} }
updatePlayerHealth(); updatePlayerHealth();
}
void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow)
{
selectBirthSign();
handleDialogDone(CSE_BirthSignChosen, GM_Review); handleDialogDone(CSE_BirthSignChosen, GM_Review);
} }
void CharacterCreation::onBirthSignDialogBack() void CharacterCreation::onBirthSignDialogBack()
{ {
if (mBirthSignDialog) selectBirthSign();
{
MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(mBirthSignDialog->getBirthId());
MWBase::Environment::get().getWindowManager()->removeDialog(mBirthSignDialog);
mBirthSignDialog = 0;
}
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
} }
void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) void CharacterCreation::selectCreatedClass()
{ {
if (mCreateClassDialog) if (mCreateClassDialog)
{ {
@ -495,19 +484,23 @@ namespace MWGui
mPlayerClass = klass; mPlayerClass = klass;
MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); MWBase::Environment::get().getWindowManager()->setPlayerClass(klass);
// Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later // Do not delete dialog, so that choices are remembered in case we want to go back and adjust them later
mCreateClassDialog->setVisible(false); mCreateClassDialog->setVisible(false);
} }
updatePlayerHealth(); updatePlayerHealth();
}
void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow)
{
selectCreatedClass();
handleDialogDone(CSE_ClassChosen, GM_Birth); handleDialogDone(CSE_ClassChosen, GM_Birth);
} }
void CharacterCreation::onCreateClassDialogBack() void CharacterCreation::onCreateClassDialogBack()
{ {
// Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later // not done in MW, but we do it for consistency with the other dialogs
mCreateClassDialog->setVisible(false); selectCreatedClass();
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
@ -631,18 +624,7 @@ namespace MWGui
MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound); MWBase::Environment::get().getSoundManager()->say(sGenerateClassSteps(mGenerateClassStep).mSound);
} }
void CharacterCreation::onGenerateClassBack() void CharacterCreation::selectGeneratedClass()
{
MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog);
mGenerateClassResultDialog = 0;
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(mGenerateClass);
MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
}
void CharacterCreation::onGenerateClassDone(WindowBase* parWindow)
{ {
MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog); MWBase::Environment::get().getWindowManager()->removeDialog(mGenerateClassResultDialog);
mGenerateClassResultDialog = 0; mGenerateClassResultDialog = 0;
@ -656,6 +638,19 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass);
updatePlayerHealth(); updatePlayerHealth();
}
void CharacterCreation::onGenerateClassBack()
{
selectGeneratedClass();
MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);
}
void CharacterCreation::onGenerateClassDone(WindowBase* parWindow)
{
selectGeneratedClass();
handleDialogDone(CSE_ClassChosen, GM_Birth); handleDialogDone(CSE_ClassChosen, GM_Birth);
} }

View file

@ -83,6 +83,7 @@ namespace MWGui
//Race dialog //Race dialog
void onRaceDialogDone(WindowBase* parWindow); void onRaceDialogDone(WindowBase* parWindow);
void onRaceDialogBack(); void onRaceDialogBack();
void selectRace();
//Class dialogs //Class dialogs
void onClassChoice(int _index); void onClassChoice(int _index);
@ -94,10 +95,14 @@ namespace MWGui
void onClassQuestionChosen(int _index); void onClassQuestionChosen(int _index);
void onGenerateClassBack(); void onGenerateClassBack();
void onGenerateClassDone(WindowBase* parWindow); void onGenerateClassDone(WindowBase* parWindow);
void selectGeneratedClass();
void selectCreatedClass();
void selectPickedClass();
//Birthsign dialog //Birthsign dialog
void onBirthSignDialogDone(WindowBase* parWindow); void onBirthSignDialogDone(WindowBase* parWindow);
void onBirthSignDialogBack(); void onBirthSignDialogBack();
void selectBirthSign();
//Review dialog //Review dialog
void onReviewDialogDone(WindowBase* parWindow); void onReviewDialogDone(WindowBase* parWindow);

View file

@ -119,7 +119,12 @@ namespace MWGui
{ {
mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); mPtr = MWBase::Environment::get().getWorld ()->getPlayerPtr();
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
mSortModel = new SortFilterItemModel(mTradeModel);
if (mSortModel) // reuse existing SortModel when possible to keep previous category/filter settings
mSortModel->setSourceModel(mTradeModel);
else
mSortModel = new SortFilterItemModel(mTradeModel);
mItemView->setModel(mSortModel); mItemView->setModel(mSortModel);
mPreview->updatePtr(mPtr); mPreview->updatePtr(mPtr);

View file

@ -120,6 +120,11 @@ namespace MWGui
} }
ProxyItemModel::ProxyItemModel()
: mSourceModel(NULL)
{
}
ProxyItemModel::~ProxyItemModel() ProxyItemModel::~ProxyItemModel()
{ {
delete mSourceModel; delete mSourceModel;
@ -164,4 +169,18 @@ namespace MWGui
return mSourceModel->getIndex(item); return mSourceModel->getIndex(item);
} }
void ProxyItemModel::setSourceModel(ItemModel *sourceModel)
{
if (mSourceModel == sourceModel)
return;
if (mSourceModel)
{
delete mSourceModel;
mSourceModel = NULL;
}
mSourceModel = sourceModel;
}
} }

View file

@ -80,11 +80,15 @@ namespace MWGui
class ProxyItemModel : public ItemModel class ProxyItemModel : public ItemModel
{ {
public: public:
ProxyItemModel();
virtual ~ProxyItemModel(); virtual ~ProxyItemModel();
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
/// @note Takes ownership of the passed pointer.
void setSourceModel(ItemModel* sourceModel);
ModelIndex mapToSource (ModelIndex index); ModelIndex mapToSource (ModelIndex index);
ModelIndex mapFromSource (ModelIndex index); ModelIndex mapFromSource (ModelIndex index);
protected: protected:

View file

@ -30,8 +30,12 @@ ItemView::~ItemView()
void ItemView::setModel(ItemModel *model) void ItemView::setModel(ItemModel *model)
{ {
if (mModel == model)
return;
delete mModel; delete mModel;
mModel = model; mModel = model;
update(); update();
} }

View file

@ -61,6 +61,7 @@ namespace
DisplayStateStack mStates; DisplayStateStack mStates;
Book mTopicIndexBook; Book mTopicIndexBook;
bool mQuestMode; bool mQuestMode;
bool mOptionsMode;
bool mAllQuests; bool mAllQuests;
template <typename T> template <typename T>
@ -182,6 +183,7 @@ namespace
mQuestMode = false; mQuestMode = false;
mAllQuests = false; mAllQuests = false;
mOptionsMode = false;
} }
void adjustButton (char const * name, bool optional = false) void adjustButton (char const * name, bool optional = false)
@ -244,6 +246,7 @@ namespace
void setBookMode () void setBookMode ()
{ {
mOptionsMode = false;
setVisible (OptionsBTN, true); setVisible (OptionsBTN, true);
setVisible (OptionsOverlay, false); setVisible (OptionsOverlay, false);
@ -253,6 +256,8 @@ namespace
void setOptionsMode () void setOptionsMode ()
{ {
mOptionsMode = true;
setVisible (OptionsBTN, false); setVisible (OptionsBTN, false);
setVisible (OptionsOverlay, true); setVisible (OptionsOverlay, true);
@ -508,6 +513,8 @@ namespace
void notifyNextPage(MyGUI::Widget* _sender) void notifyNextPage(MyGUI::Widget* _sender)
{ {
if (mOptionsMode)
return;
if (!mStates.empty ()) if (!mStates.empty ())
{ {
unsigned int & page = mStates.top ().mPage; unsigned int & page = mStates.top ().mPage;
@ -523,6 +530,8 @@ namespace
void notifyPrevPage(MyGUI::Widget* _sender) void notifyPrevPage(MyGUI::Widget* _sender)
{ {
if (mOptionsMode)
return;
if (!mStates.empty ()) if (!mStates.empty ())
{ {
unsigned int & page = mStates.top ().mPage; unsigned int & page = mStates.top ().mPage;

View file

@ -783,7 +783,7 @@ namespace MWGui
MyGUI::Widget* markerWidget = mGlobalMap->createWidget<MyGUI::Widget>("MarkerButton", MyGUI::Widget* markerWidget = mGlobalMap->createWidget<MyGUI::Widget>("MarkerButton",
widgetCoord, MyGUI::Align::Default); widgetCoord, MyGUI::Align::Default);
markerWidget->setUserString("Caption_TextOneLine", name); markerWidget->setUserString("Caption_TextOneLine", "#{sCell=" + name + "}");
setGlobalMapMarkerTooltip(markerWidget, x, y); setGlobalMapMarkerTooltip(markerWidget, x, y);

View file

@ -190,7 +190,8 @@ namespace MWGui
void renderGlobalMap(Loading::Listener* loadingListener); void renderGlobalMap(Loading::Listener* loadingListener);
// adds the marker to the global map /// adds the marker to the global map
/// @param name The ESM::Cell::mName
void addVisitedLocation(const std::string& name, int x, int y); void addVisitedLocation(const std::string& name, int x, int y);
// reveals this cell's map on the global map // reveals this cell's map on the global map

View file

@ -347,8 +347,8 @@ namespace MWGui
char buffer[size]; char buffer[size];
if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) if (std::strftime(buffer, size, "%x %X", timeinfo) > 0)
text << buffer << "\n"; text << buffer << "\n";
text << "Level " << mCurrentSlot->mProfile.mPlayerLevel << "\n"; text << "#{sLevel} " << mCurrentSlot->mProfile.mPlayerLevel << "\n";
text << mCurrentSlot->mProfile.mPlayerCell << "\n"; text << "#{sCell=" << mCurrentSlot->mProfile.mPlayerCell << "}\n";
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; // text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour); int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour);

View file

@ -71,7 +71,6 @@ namespace MWGui
SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel) SortFilterItemModel::SortFilterItemModel(ItemModel *sourceModel)
: mCategory(Category_All) : mCategory(Category_All)
, mFilter(0) , mFilter(0)
, mShowEquipped(true)
, mSortByType(true) , mSortByType(true)
{ {
mSourceModel = sourceModel; mSourceModel = sourceModel;
@ -91,9 +90,6 @@ namespace MWGui
{ {
MWWorld::Ptr base = item.mBase; MWWorld::Ptr base = item.mBase;
if (item.mType == ItemStack::Type_Equipped && !mShowEquipped)
return false;
int category = 0; int category = 0;
if (base.getTypeName() == typeid(ESM::Armor).name() if (base.getTypeName() == typeid(ESM::Armor).name()
|| base.getTypeName() == typeid(ESM::Clothing).name()) || base.getTypeName() == typeid(ESM::Clothing).name())

View file

@ -24,7 +24,6 @@ namespace MWGui
void setCategory (int category); void setCategory (int category);
void setFilter (int filter); void setFilter (int filter);
void setShowEquipped (bool show) { mShowEquipped = show; }
/// Use ItemStack::Type for sorting? /// Use ItemStack::Type for sorting?
void setSortByType(bool sort) { mSortByType = sort; } void setSortByType(bool sort) { mSortByType = sort; }
@ -49,7 +48,6 @@ namespace MWGui
int mCategory; int mCategory;
int mFilter; int mFilter;
bool mShowEquipped;
bool mSortByType; bool mSortByType;
}; };

View file

@ -157,7 +157,7 @@ namespace MWGui
// figure out if player will be woken while sleeping // figure out if player will be woken while sleeping
int x = Misc::Rng::rollDice(hoursToWait); int x = Misc::Rng::rollDice(hoursToWait);
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat(); float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat();
if (x < fSleepRandMod * hoursToWait) if (x < static_cast<int>(fSleepRandMod * hoursToWait))
{ {
float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat(); float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat();
mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait); mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait);

View file

@ -993,7 +993,7 @@ namespace MWGui
if (cell->getCell()->isExterior()) if (cell->getCell()->isExterior())
{ {
if (!cell->getCell()->mName.empty()) if (!cell->getCell()->mName.empty())
mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->getCell()->getGridX (), cell->getCell()->getGridY ()); mMap->addVisitedLocation (name, cell->getCell()->getGridX (), cell->getCell()->getGridY ());
mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY()); mMap->cellExplored (cell->getCell()->getGridX(), cell->getCell()->getGridY());
} }

View file

@ -247,15 +247,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock(); bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
if(mHitState == CharState_None) if(mHitState == CharState_None)
{ {
if (mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0 if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0) || mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0)
&& mAnimation->hasAnimation("knockout"))
{ {
mHitState = CharState_KnockOut; mHitState = CharState_KnockOut;
mCurrentHit = "knockout"; mCurrentHit = "knockout";
mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul); mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, false, 1, "start", "stop", 0.0f, ~0ul);
mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true); mPtr.getClass().getCreatureStats(mPtr).setKnockedDown(true);
} }
else if(knockdown) else if(knockdown && mAnimation->hasAnimation("knockdown"))
{ {
mHitState = CharState_KnockDown; mHitState = CharState_KnockDown;
mCurrentHit = "knockdown"; mCurrentHit = "knockdown";
@ -263,11 +264,15 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
} }
else if (recovery) else if (recovery)
{ {
mHitState = CharState_Hit; std::string anim = chooseRandomGroup("hit");
mCurrentHit = chooseRandomGroup("hit"); if (mAnimation->hasAnimation(anim))
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0); {
mHitState = CharState_Hit;
mCurrentHit = anim;
mAnimation->play(mCurrentHit, Priority_Hit, MWRender::Animation::BlendMask_All, true, 1, "start", "stop", 0.0f, 0);
}
} }
else if (block) else if (block && mAnimation->hasAnimation("shield"))
{ {
mHitState = CharState_Block; mHitState = CharState_Block;
mCurrentHit = "shield"; mCurrentHit = "shield";

View file

@ -31,7 +31,7 @@ namespace MWMechanics
void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick, void Security::pickLock(const MWWorld::Ptr &lock, const MWWorld::Ptr &lockpick,
std::string& resultMessage, std::string& resultSound) std::string& resultMessage, std::string& resultSound)
{ {
if (!(lock.getCellRef().getLockLevel() > 0)) //If it's unlocked back out immediately if (!(lock.getCellRef().getLockLevel() > 0) || !lock.getClass().canLock(lock)) //If it's unlocked back out immediately
return; return;
int lockStrength = lock.getCellRef().getLockLevel(); int lockStrength = lock.getCellRef().getLockLevel();

View file

@ -489,8 +489,8 @@ namespace MWMechanics
if (!wasDead && isDead) if (!wasDead && isDead)
MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster); MWBase::Environment::get().getMechanicsManager()->actorKilled(target, caster);
} }
else else if (!applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude))
applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude); continue;
} }
// Re-casting a summon effect will remove the creature from previous castings of that effect. // Re-casting a summon effect will remove the creature from previous castings of that effect.
@ -559,10 +559,10 @@ namespace MWMechanics
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
} }
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) bool CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude)
{ {
short effectId = effect.mId; short effectId = effect.mId;
if (!target.getClass().isActor()) if (target.getClass().canLock(target))
{ {
if (effectId == ESM::MagicEffect::Lock) if (effectId == ESM::MagicEffect::Lock)
{ {
@ -570,8 +570,9 @@ namespace MWMechanics
{ {
if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr()) if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
target.getCellRef().setLockLevel(static_cast<int>(magnitude)); target.getClass().lock(target, static_cast<int>(magnitude));
} }
return true;
} }
else if (effectId == ESM::MagicEffect::Open) else if (effectId == ESM::MagicEffect::Open)
{ {
@ -586,47 +587,59 @@ namespace MWMechanics
if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr()) if (caster == MWBase::Environment::get().getWorld()->getPlayerPtr())
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}");
} }
target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel())); target.getClass().unlock(target);
} }
else else
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f); MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f);
return true;
} }
} }
else else if (target.getClass().isActor())
{ {
if (effectId == ESM::MagicEffect::CurePoison) switch (effectId)
{
case ESM::MagicEffect::CurePoison:
target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison);
else if (effectId == ESM::MagicEffect::CureParalyzation) return true;
case ESM::MagicEffect::CureParalyzation:
target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze); target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze);
else if (effectId == ESM::MagicEffect::CureCommonDisease) return true;
case ESM::MagicEffect::CureCommonDisease:
target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease(); target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease();
else if (effectId == ESM::MagicEffect::CureBlightDisease) return true;
case ESM::MagicEffect::CureBlightDisease:
target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease(); target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease();
else if (effectId == ESM::MagicEffect::CureCorprusDisease) return true;
case ESM::MagicEffect::CureCorprusDisease:
target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease(); target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease();
else if (effectId == ESM::MagicEffect::Dispel) return true;
case ESM::MagicEffect::Dispel:
target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude); target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude);
else if (effectId == ESM::MagicEffect::RemoveCurse) return true;
case ESM::MagicEffect::RemoveCurse:
target.getClass().getCreatureStats(target).getSpells().purgeCurses(); target.getClass().getCreatureStats(target).getSpells().purgeCurses();
return true;
}
if (target != MWBase::Environment::get().getWorld()->getPlayerPtr()) if (target != MWBase::Environment::get().getWorld()->getPlayerPtr())
return; return false;
if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled())
return;
if (effectId == ESM::MagicEffect::DivineIntervention) if (effectId == ESM::MagicEffect::DivineIntervention)
{ {
MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker"); MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker");
return true;
} }
else if (effectId == ESM::MagicEffect::AlmsiviIntervention) else if (effectId == ESM::MagicEffect::AlmsiviIntervention)
{ {
MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker"); MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker");
return true;
} }
else if (effectId == ESM::MagicEffect::Mark) else if (effectId == ESM::MagicEffect::Mark)
{ {
MWBase::Environment::get().getWorld()->getPlayer().markPosition( MWBase::Environment::get().getWorld()->getPlayer().markPosition(
target.getCell(), target.getRefData().getPosition()); target.getCell(), target.getRefData().getPosition());
return true;
} }
else if (effectId == ESM::MagicEffect::Recall) else if (effectId == ESM::MagicEffect::Recall)
{ {
@ -640,8 +653,10 @@ namespace MWMechanics
markedPosition, false); markedPosition, false);
action.execute(target); action.execute(target);
} }
return true;
} }
} }
return false;
} }
@ -926,7 +941,8 @@ namespace MWMechanics
MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr); MWWorld::InventoryStore& inv = ptr.getClass().getInventoryStore(ptr);
MWWorld::ContainerStoreIterator item = MWWorld::ContainerStoreIterator item =
inv.getSlot(slot); inv.getSlot(slot);
if (item != inv.end())
if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor || item.getType() == MWWorld::ContainerStore::Type_Weapon))
{ {
if (!item->getClass().hasItemHealth(*item)) if (!item->getClass().hasItemHealth(*item))
return false; return false;

View file

@ -97,7 +97,8 @@ namespace MWMechanics
const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false);
/// @note \a caster can be any type of object, or even an empty object. /// @note \a caster can be any type of object, or even an empty object.
void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); /// @return was the target suitable for the effect?
bool applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);
}; };
} }

View file

@ -208,6 +208,38 @@ namespace
std::vector<osg::Node*> mToRemove; std::vector<osg::Node*> mToRemove;
}; };
class RemoveTriBipVisitor : public osg::NodeVisitor
{
public:
RemoveTriBipVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{
}
virtual void apply(osg::Geode &node)
{
const std::string toFind = "tri bip";
if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0)
{
// Not safe to remove in apply(), since the visitor is still iterating the child list
mToRemove.push_back(&node);
}
}
void remove()
{
for (std::vector<osg::Node*>::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
{
osg::Node* node = *it;
if (node->getNumParents())
node->getParent(0)->removeChild(node);
}
}
private:
std::vector<osg::Node*> mToRemove;
};
} }
namespace MWRender namespace MWRender
@ -898,7 +930,7 @@ namespace MWRender
return movement; return movement;
} }
void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly) void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature)
{ {
if (mObjectRoot) if (mObjectRoot)
{ {
@ -933,6 +965,13 @@ namespace MWRender
removeDrawableVisitor.remove(); removeDrawableVisitor.remove();
} }
if (isCreature)
{
RemoveTriBipVisitor removeTriBipVisitor;
mObjectRoot->accept(removeTriBipVisitor);
removeTriBipVisitor.remove();
}
NodeMapVisitor visitor; NodeMapVisitor visitor;
mObjectRoot->accept(visitor); mObjectRoot->accept(visitor);
mNodeMap = visitor.getNodeMap(); mNodeMap = visitor.getNodeMap();
@ -1308,7 +1347,7 @@ namespace MWRender
{ {
if (!model.empty()) if (!model.empty())
{ {
setObjectRoot(model, false, false); setObjectRoot(model, false, false, false);
if (animated) if (animated)
addAnimSource(model); addAnimSource(model);

View file

@ -273,7 +273,7 @@ protected:
* @param baseonly If true, then any meshes or particle systems in the model are ignored * @param baseonly If true, then any meshes or particle systems in the model are ignored
* (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files). * (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files).
*/ */
void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly); void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature);
/* Adds the keyframe controllers in the specified model as a new animation source. Note that /* Adds the keyframe controllers in the specified model as a new animation source. Note that
* the filename portion of the provided model name will be prepended with 'x', and the .nif * the filename portion of the provided model name will be prepended with 'x', and the .nif

View file

@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,
if(!model.empty()) if(!model.empty())
{ {
setObjectRoot(model, false, false); setObjectRoot(model, false, false, true);
if((ref->mBase->mFlags&ESM::Creature::Bipedal)) if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\xbase_anim.nif"); addAnimSource("meshes\\xbase_anim.nif");
@ -42,7 +42,7 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const
if(!model.empty()) if(!model.empty())
{ {
setObjectRoot(model, true, false); setObjectRoot(model, true, false, true);
if((ref->mBase->mFlags&ESM::Creature::Bipedal)) if((ref->mBase->mFlags&ESM::Creature::Bipedal))
addAnimSource("meshes\\xbase_anim.nif"); addAnimSource("meshes\\xbase_anim.nif");

View file

@ -378,7 +378,7 @@ void NpcAnimation::updateNpcBase()
: "meshes\\wolf\\skin.1st.nif"); : "meshes\\wolf\\skin.1st.nif");
smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS());
setObjectRoot(smodel, true, true); setObjectRoot(smodel, true, true, false);
if(mViewMode != VM_FirstPerson) if(mViewMode != VM_FirstPerson)
{ {

View file

@ -144,6 +144,11 @@ namespace MWWorld
throw std::runtime_error ("class does not support unlocking"); throw std::runtime_error ("class does not support unlocking");
} }
bool Class::canLock(const Ptr &ptr) const
{
return false;
}
void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const
{ {
throw std::runtime_error ("class does not support time-based uses"); throw std::runtime_error ("class does not support time-based uses");

View file

@ -161,6 +161,8 @@ namespace MWWorld
virtual void unlock (const Ptr& ptr) const; virtual void unlock (const Ptr& ptr) const;
///< Unlock object (default implementation: throw an exception) ///< Unlock object (default implementation: throw an exception)
virtual bool canLock (const Ptr& ptr) const;
virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const;
///< Sets the remaining duration of the object, such as an equippable light ///< Sets the remaining duration of the object, such as an equippable light
/// source. (default implementation: throw an exception) /// source. (default implementation: throw an exception)

View file

@ -228,6 +228,7 @@ namespace MWWorld
if (findExteriorPosition (mStartCell, pos)) if (findExteriorPosition (mStartCell, pos))
{ {
changeToExteriorCell (pos); changeToExteriorCell (pos);
fixPosition(getPlayerPtr());
} }
else else
{ {
@ -2623,12 +2624,10 @@ namespace MWWorld
{ {
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
// Get the target to use for "on touch" effects // Get the target to use for "on touch" effects, using the facing direction from Head node
MWWorld::Ptr target; MWWorld::Ptr target;
float distance = 192.f; // ?? float distance = 192.f; // ??
osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3(); osg::Vec3f hitPosition = actor.getRefData().getPosition().asVec3();
// For NPCs use facing direction from Head node
osg::Vec3f origin(actor.getRefData().getPosition().asVec3()); osg::Vec3f origin(actor.getRefData().getPosition().asVec3());
MWRender::Animation* anim = mRendering->getAnimation(actor); MWRender::Animation* anim = mRendering->getAnimation(actor);
@ -2651,13 +2650,33 @@ namespace MWWorld
osg::Vec3f direction = orient * osg::Vec3f(0,1,0); osg::Vec3f direction = orient * osg::Vec3f(0,1,0);
osg::Vec3f dest = origin + direction * distance; osg::Vec3f dest = origin + direction * distance;
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(origin, dest, actor); // For actor targets, we want to use bounding boxes (physics raycast).
target = result.mHitObject; // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise.
hitPosition = result.mHitPos; // For object targets, we want the detailed shapes (rendering raycast).
// If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf.
// don't allow casting on non-activatable objects MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, MWPhysics::CollisionType_Actor);
if (!target.isEmpty() && !target.getClass().isActor() && target.getClass().getName(target).empty())
target = MWWorld::Ptr(); MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true);
float dist1 = FLT_MAX;
float dist2 = FLT_MAX;
if (result1.mHit)
dist1 = (origin - result1.mHitPos).length();
if (result2.mHit)
dist2 = (origin - result2.mHitPointWorld).length();
if (dist1 <= dist2 && result1.mHit)
{
target = result1.mHitObject;
hitPosition = result1.mHitPos;
}
else if (result2.mHit)
{
target = result2.mHitObject;
hitPosition = result2.mHitPointWorld;
}
std::string selectedSpell = stats.getSpells().getSelectedSpell(); std::string selectedSpell = stats.getSpells().getSelectedSpell();