Do not allow player to change weapon/spell during attack or spellcasting (bug #2445)

pull/280/head
Andrei Kortunov 7 years ago
parent b73ed5ccac
commit fb45995a41

@ -221,6 +221,7 @@ namespace MWBase
virtual void keepPlayerAlive() = 0;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;

@ -212,7 +212,10 @@ namespace MWBase
virtual void setSpellVisibility(bool visible) = 0;
virtual void setSneakVisibility(bool visible) = 0;
virtual void activateQuickKey (int index) = 0;
/// activate selected quick key
virtual void activateQuickKey (int index) = 0;
/// update activated quick key state (if action executing was delayed for some reason)
virtual void updateActivatedQuickKey () = 0;
virtual std::string getSelectedSpell() = 0;
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;

@ -4,6 +4,7 @@
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -373,6 +374,9 @@ namespace MWClass
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
return std::make_pair(0, "#{sInventoryMessage1}");
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
return std::make_pair(0, "#{sCantEquipWeapWarning}");
std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr);
if (slots_.first.empty())

@ -29,6 +29,7 @@
#include "../mwrender/characterpreview.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "itemview.hpp"
#include "inventoryitemmodel.hpp"
@ -660,9 +661,18 @@ namespace MWGui
void InventoryWindow::cycle(bool next)
{
MWWorld::Ptr player = MWMechanics::getPlayer();
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
return;
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
ItemModel::ModelIndex selected = -1;
// not using mSortFilterModel as we only need sorting, not filtering
SortFilterItemModel model(new InventoryItemModel(MWMechanics::getPlayer()));
SortFilterItemModel model(new InventoryItemModel(player));
model.setSortByType(false);
model.update();
if (model.getItemCount() == 0)

@ -14,6 +14,7 @@
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -36,6 +37,7 @@ namespace MWGui
, mItemSelectionDialog(0)
, mMagicSelectionDialog(0)
, mSelectedIndex(-1)
, mActivatedIndex(-1)
{
getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel");
@ -69,6 +71,8 @@ namespace MWGui
void QuickKeysMenu::clear()
{
mActivatedIndex = -1;
for (int i=0; i<10; ++i)
{
unassign(mQuickKeyButtons[i], i);
@ -254,6 +258,15 @@ namespace MWGui
mMagicSelectionDialog->setVisible(false);
}
void QuickKeysMenu::updateActivatedQuickKey()
{
// there is no delayed action, nothing to do.
if (mActivatedIndex < 0)
return;
activateQuickKey(mActivatedIndex);
}
void QuickKeysMenu::activateQuickKey(int index)
{
assert (index-1 >= 0);
@ -263,6 +276,27 @@ namespace MWGui
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);
// Delay action executing,
// if player is busy for now (casting a spell, attacking someone, etc.)
bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player)
|| playerStats.getKnockedDown()
|| playerStats.getHitRecovery();
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
if (isReturnNeeded && type != Type_Item)
{
return;
}
if (isDelayNeeded && type != Type_Item)
{
mActivatedIndex = index;
return;
}
else
mActivatedIndex = -1;
if (type == Type_Item || type == Type_MagicItem)
{
@ -309,6 +343,21 @@ namespace MWGui
else if (type == Type_Item)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
// delay weapon switching if player is busy
if (isDelayNeeded && isWeapon)
{
mActivatedIndex = index;
return;
}
// disable weapon switching if player is dead or paralyzed
if (isReturnNeeded && isWeapon)
{
return;
}
MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand

@ -36,6 +36,7 @@ namespace MWGui
void onAssignMagicCancel ();
void activateQuickKey(int index);
void updateActivatedQuickKey();
/// @note This enum is serialized, so don't move the items around!
enum QuickKeyType
@ -64,7 +65,7 @@ namespace MWGui
MagicSelectionDialog* mMagicSelectionDialog;
int mSelectedIndex;
int mActivatedIndex;
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender);

@ -9,6 +9,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
@ -195,6 +196,15 @@ namespace MWGui
void SpellWindow::cycle(bool next)
{
MWWorld::Ptr player = MWMechanics::getPlayer();
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
return;
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
return;
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer()));
SpellModel::ModelIndex selected = 0;

@ -518,6 +518,8 @@ namespace MWGui
cleanupGarbage();
mHud->update();
updateActivatedQuickKey ();
}
void WindowManager::updateVisible()
@ -1528,6 +1530,11 @@ namespace MWGui
mHud->setCrosshairVisible (show && mCrosshairEnabled);
}
void WindowManager::updateActivatedQuickKey ()
{
mQuickKeysMenu->updateActivatedQuickKey();
}
void WindowManager::activateQuickKey (int index)
{
mQuickKeysMenu->activateQuickKey(index);

@ -241,7 +241,10 @@ namespace MWGui
virtual void setSpellVisibility(bool visible);
virtual void setSneakVisibility(bool visible);
virtual void activateQuickKey (int index);
/// activate selected quick key
virtual void activateQuickKey (int index);
/// update activated quick key state (if action executing was delayed for some reason)
virtual void updateActivatedQuickKey ();
virtual std::string getSelectedSpell() { return mSelectedSpell; }
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent);

@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/statemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
@ -929,6 +930,9 @@ namespace MWInput
inventory.getSelectedEnchantItem() == inventory.end())
return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Spell);
@ -944,6 +948,9 @@ namespace MWInput
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
return;
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
return;
MWMechanics::DrawState_ state = mPlayer->getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
mPlayer->setDrawState(MWMechanics::DrawState_Weapon);

@ -1784,6 +1784,16 @@ namespace MWMechanics
return it->second->getCharacterController()->isReadyToBlock();
}
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
{
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
CharacterController* ctrl = it->second->getCharacterController();
return ctrl->isAttackingOrSpell();
}
void Actors::fastForwardAi()
{
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())

@ -150,6 +150,7 @@ namespace MWMechanics
void clear(); // Clear death counter
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
private:
PtrActorMap mActors;

@ -2228,6 +2228,12 @@ bool CharacterController::isKnockedOut() const
return mHitState == CharState_KnockOut;
}
bool CharacterController::isAttackingOrSpell() const
{
return mUpperBodyState != UpperCharState_Nothing &&
mUpperBodyState != UpperCharState_WeapEquiped;
}
bool CharacterController::isSneaking() const
{
return mIdleState == CharState_IdleSneak ||

@ -267,6 +267,7 @@ public:
bool isKnockedOut() const;
bool isSneaking() const;
bool isRunning() const;
bool isAttackingOrSpell() const;
void setAttackingOrSpell(bool attackingOrSpell);
void setAIAttackType(const std::string& attackType);

@ -1581,6 +1581,11 @@ namespace MWMechanics
return mActors.isReadyToBlock(ptr);
}
bool MechanicsManager::isAttackingOrSpell(const MWWorld::Ptr &ptr) const
{
return mActors.isAttackingOrSpell(ptr);
}
void MechanicsManager::setWerewolf(const MWWorld::Ptr& actor, bool werewolf)
{
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor);

@ -188,6 +188,8 @@ namespace MWMechanics
virtual void keepPlayerAlive();
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const;
/// Is \a ptr casting spell or using weapon now?
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer);

Loading…
Cancel
Save