diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 97bccd01f..8443aaf30 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -91,6 +91,7 @@ namespace MWGui WindowBase("openmw_settings_window.layout") { getWidget(mOkButton, "OkButton"); + getWidget(mBestAttackButton, "BestAttackButton"); getWidget(mSubtitlesButton, "SubtitlesButton"); getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mResolutionList, "ResolutionList"); @@ -131,6 +132,7 @@ namespace MWGui mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); + mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); @@ -200,6 +202,7 @@ namespace MWGui mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}"); + mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}"); float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); @@ -407,6 +410,8 @@ namespace MWGui Settings::Manager::setBool("crosshair", "HUD", newState); else if (_sender == mSubtitlesButton) Settings::Manager::setBool("subtitles", "GUI", newState); + else if (_sender == mBestAttackButton) + Settings::Manager::setBool("best attack", "Game", newState); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 20e9907d9..42ed5bf6d 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -32,6 +32,7 @@ namespace MWGui MyGUI::ScrollBar* mToolTipDelaySlider; MyGUI::Button* mSubtitlesButton; MyGUI::Button* mCrosshairButton; + MyGUI::Button* mBestAttackButton; // graphics MyGUI::ListBox* mResolutionList; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 95d98e35f..0addc7daf 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -20,6 +20,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwgui/bookwindow.hpp" +#include "../mwmechanics/creaturestats.hpp" using namespace ICS; @@ -160,6 +161,26 @@ namespace MWInput resetIdleTime (); int action = channel->getNumber(); + + if (action == A_Use) + { + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(currentValue); + if (currentValue == 1) + { + int type = MWMechanics::CreatureStats::AT_Chop; + bool forward = (mInputBinder->getChannel(A_MoveForward)->getValue() > 0 + || mInputBinder->getChannel(A_MoveBackward)->getValue() > 0); + bool side = (mInputBinder->getChannel(A_MoveLeft)->getValue() > 0 + || mInputBinder->getChannel(A_MoveRight)->getValue() > 0); + if (side && !forward) + type = MWMechanics::CreatureStats::AT_Slash; + if (forward && !side) + type = MWMechanics::CreatureStats::AT_Thrust; + + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackType(type); + } + } + if (currentValue == 1) { // trigger action activated @@ -512,7 +533,6 @@ namespace MWInput return true; // MyGUI has no use for these events MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); - if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) { MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType(false); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f0db91bac..86407a32d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -35,6 +35,23 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +namespace +{ + +int getBestAttack (const ESM::Weapon* weapon) +{ + int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; + int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2; + int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2; + if (slash >= chop && slash >= thrust) + return MWMechanics::CreatureStats::AT_Slash; + else if (chop >= slash && chop >= thrust) + return MWMechanics::CreatureStats::AT_Chop; + else + return MWMechanics::CreatureStats::AT_Thrust; +} + +} namespace MWMechanics { @@ -222,6 +239,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mMovementState(CharState_None) , mMovementSpeed(0.0f) , mDeathState(CharState_None) + , mUpperBodyState(UpperCharState_Nothing) , mWeaponType(WeapType_None) , mSkipAnim(false) , mSecondsOfRunning(0) @@ -493,6 +511,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; } else { @@ -501,6 +520,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "equip start", "equip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_EquipingWeap; } mWeaponType = weaptype; @@ -518,6 +538,93 @@ void CharacterController::update(float duration, Movement &movement) } } + if(weaptype != WeapType_PickProbe && weaptype != WeapType_BowAndArrow + && weaptype != WeapType_Crossbow && weaptype != WeapType_ThowWeapon) + { + std::string weapgroup; + getWeaponGroup(mWeaponType, weapgroup); + float weapSpeed = 1; + if(weapon != inv.end()) weapSpeed = weapon->get()->mBase->mData.mSpeed; + std::string start; + std::string stop; + float complete; + float speedMult; + bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop); + + if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_WeapEquiped) + { + int attackType = cls.getCreatureStats(mPtr).getAttackType(); + if (Settings::Manager::getBool("best attack", "Game") && weapon != inv.end()) + attackType = getBestAttack(weapon->get()->mBase); + + if (attackType == MWMechanics::CreatureStats::AT_Chop) + mAttackType = "chop"; + else if (attackType == MWMechanics::CreatureStats::AT_Slash) + mAttackType = "slash"; + else + mAttackType = "thrust"; + + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } + } + else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + { + if(animPlaying) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1-complete, 0); + } + else + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 0, 0); + } + mUpperBodyState = UpperCharState_MaxAttackToMinHit; + } + + if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; + if(animPlaying) + { + if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0); + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + } + else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0); + mUpperBodyState = UpperCharState_MinHitToHit; + } + else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0); + mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; + } + else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1) + { + mUpperBodyState = UpperCharState_WeapEquiped; + } + } + } + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index ea354a6d7..ff9f3aa62 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -95,6 +95,22 @@ enum WeaponType { WeapType_Spell }; +enum UpperBodyCharacterState { + UpperCharState_Nothing, + UpperCharState_EquipingWeap, + UpperCharState_UnEquipingWeap, + UpperCharState_WeapEquiped, + UpperCharState_StartToMinAttack, + UpperCharState_MinAttackToMaxAttack, + UpperCharState_MaxAttackToMinHit, + UpperCharState_MinHitToHit, + UpperCharState_LargeFollowStartToLargeFollowStop, + UpperCharState_MediumFollowStartToMediumFollowStop, + UpperCharState_SmallFollowStartToSmallFollowStop, + UpperCharState_EquipingSpell, + UpperCharState_UnEquipingSpell +}; + class CharacterController { MWWorld::Ptr mPtr; @@ -113,6 +129,8 @@ class CharacterController CharacterState mDeathState; std::string mCurrentDeath; + UpperBodyCharacterState mUpperBodyState; + WeaponType mWeaponType; bool mSkipAnim; @@ -123,6 +141,8 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; + std::string mAttackType; // slash, chop or thrust + void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); static void getWeaponGroup(WeaponType weaptype, std::string &group); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 569ccd592..c43b80a25 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -12,7 +12,8 @@ namespace MWMechanics CreatureStats::CreatureStats() : mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), - mAttacked (false), mHostile (false) + mAttacked (false), mHostile (false), + mAttackingOrSpell(false) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -109,6 +110,11 @@ namespace MWMechanics return mMagicEffects; } + const bool &CreatureStats::getAttackingOrSpell() const + { + return mAttackingOrSpell; + } + int CreatureStats::getLevel() const { return mLevel; @@ -210,6 +216,11 @@ namespace MWMechanics mMagicEffects = effects; } + void CreatureStats::setAttackingOrSpell(const bool &attackingOrSpell) + { + mAttackingOrSpell = attackingOrSpell; + } + void CreatureStats::setAiSetting (int index, int value) { assert (index>=0 && index<4); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index da8b5842a..a9a3caa49 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -34,6 +34,9 @@ namespace MWMechanics bool mAlarmed; bool mAttacked; bool mHostile; + bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + + int mAttackType; public: CreatureStats(); @@ -54,6 +57,8 @@ namespace MWMechanics const MagicEffects & getMagicEffects() const; + const bool & getAttackingOrSpell() const; + int getLevel() const; int getAiSetting (int index) const; @@ -83,6 +88,17 @@ namespace MWMechanics void setMagicEffects(const MagicEffects &effects); + void setAttackingOrSpell(const bool &attackingOrSpell); + + enum AttackType + { + AT_Slash, + AT_Thrust, + AT_Chop + }; + void setAttackType(int attackType) { mAttackType = attackType; } + int getAttackType() { return mAttackType; } + void setLevel(int level); void setAiSetting (int index, int value); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index fb3e4a1b9..11a088e63 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -662,6 +662,7 @@ void Animation::resetActiveGroups() return; const Ogre::SharedPtr &animsrc = state->second.mSource; + const std::vector >&ctrls = animsrc->mControllers[0]; for(size_t i = 0;i < ctrls.size();i++) { diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ab91aed78..a54f3086e 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -44,6 +44,12 @@ + + + + + + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 613f12432..ac56604d1 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -163,3 +163,7 @@ ui sensitivity = 1.0 camera y multiplier = 1.0 ui y multiplier = 1.0 + +[Game] +# Always use the most powerful attack when striking with a weapon (chop, slash or thrust) +best attack = false