From 3947f44aa361fbf40279d21d7bda7ce683c85514 Mon Sep 17 00:00:00 2001 From: gus Date: Sat, 13 Jul 2013 22:24:52 +0100 Subject: [PATCH 01/92] Porting old work --- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++- apps/openmw/mwmechanics/character.cpp | 42 ++++++++++++++++++++++- apps/openmw/mwmechanics/character.hpp | 16 +++++++++ apps/openmw/mwmechanics/creaturestats.cpp | 24 ++++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 10 ++++++ apps/openmw/mwrender/animation.cpp | 7 ++++ 6 files changed, 102 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f19f634589..d46508157a 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; @@ -498,6 +499,8 @@ namespace MWInput MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(false); + return true; } @@ -509,7 +512,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); @@ -519,6 +521,8 @@ namespace MWInput } } + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(true); + return true; } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d3dbb93256..4b95999bfa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -175,6 +175,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim : mPtr(ptr) , mAnimation(anim) , mCharState(state) + , mUpperBodyState(UpperCharState_Nothing) , mWeaponType(WeapType_None) , mSkipAnim(false) , mSecondsOfRunning(0) @@ -237,6 +238,26 @@ void CharacterController::update(float duration, Movement &movement) } else if(!cls.getCreatureStats(mPtr).isDead()) { + std::list> lastKeys = cls.getCreatureStats(mPtr).getLastAnimKey(); + for(std::list>::iterator it = cls.getCreatureStats(mPtr).getLastAnimKey().begin(); + it!=cls.getCreatureStats(mPtr).getLastAnimKey().end();) + { + std::cout << "NE"; + std::pair lastKey = *it; + if(!lastKey.first.empty()) + { + std::cout << lastKey.first << " " << lastKey.second << " "; + //const std::string &evt = key->second; + size_t off = lastKey.second.size()+2; + size_t len = lastKey.first.size() - off; + + if(lastKey.first.compare(off, len, "equip stop") == 0) mUpperBodyState = UpperCharState_WeapEquiped; + if(lastKey.first.compare(off, len, "unequip stop") == 0) mUpperBodyState = UpperCharState_Nothing; + if(lastKey.first.compare(off, len, "chop large follow stop") == 0){ mUpperBodyState = UpperCharState_WeapEquiped;std::cout << "FINISHED";} + } + it = cls.getCreatureStats(mPtr).getLastAnimKey().erase(it); + } + MWBase::World *world = MWBase::Environment::get().getWorld(); bool onground = world->isOnGround(mPtr); @@ -333,7 +354,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimQueue.pop_front(); mAnimation->play(mAnimQueue.front().first, Priority_Default, - MWRender::Animation::Group_All, false, + MWRender::Animation::Group_All, true, "start", "stop", 0.0f, mAnimQueue.front().second); } } @@ -424,6 +445,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; } else { @@ -432,6 +454,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "equip start", "equip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_EquipingWeap; } mWeaponType = weaptype; @@ -450,6 +473,23 @@ void CharacterController::update(float duration, Movement &movement) } } + if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_WeapEquiped) + { + std::string weapgroup; + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "chop start", "chop large follow stop", 0.0f, 0); + mUpperBodyState = UpperCharState_ChopReadyingMouseHold; + } + } + else if(mUpperBodyState == UpperCharState_ChopReadyingMouseHold) + { + mUpperBodyState = UpperCharState_ChopReadying; + } + 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 cf5d6e8231..ab01038b8c 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -92,6 +92,21 @@ enum WeaponType { WeapType_Spell }; +enum UpperBodyCharacterState { + UpperCharState_Nothing, + UpperCharState_EquipingWeap, + UpperCharState_UnEquipingWeap, + UpperCharState_WeapEquiped, + UpperCharState_Slashing, + UpperCharState_ChopReadying, + UpperCharState_ChopReadyingMouseHold, //when you keep your mouse clicked to ready your weapon + UpperCharState_ChopReadyWaiting, //when your weapon is ready and you keep mouse hold + UpperCharState_Choping, + UpperCharState_Thrusting, + UpperCharState_EquipingSpell, + UpperCharState_UnEquipingSpell +}; + class CharacterController { MWWorld::Ptr mPtr; @@ -101,6 +116,7 @@ class CharacterController AnimationQueue mAnimQueue; CharacterState mCharState; + UpperBodyCharacterState mUpperBodyState; WeaponType mWeaponType; bool mSkipAnim; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 19d2080211..f4f208bcfc 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; @@ -90,6 +91,16 @@ namespace MWMechanics return mMagicEffects; } + const bool &CreatureStats::getAttackingOrSpell() const + { + return mAttackingOrSpell; + } + + std::list> &CreatureStats::getLastAnimKey() + { + return mLastAnimKeys; + } + int CreatureStats::getLevel() const { return mLevel; @@ -191,6 +202,17 @@ namespace MWMechanics mMagicEffects = effects; } + void CreatureStats::setAttackingOrSpell(const bool &attackingOrSpell) + { + mAttackingOrSpell = attackingOrSpell; + } + + /*void CreatureStats::setLastAnimKey(std::string key,std::string animGroup) + { + mLastAnimKey = key; + mLastAnimGroup = animGroup; + }*/ + 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 63de13d0d4..7bb1dc4391 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -34,6 +34,8 @@ 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. + std::list> mLastAnimKeys; public: CreatureStats(); @@ -54,6 +56,10 @@ namespace MWMechanics const MagicEffects & getMagicEffects() const; + const bool & getAttackingOrSpell() const; + + std::list> & getLastAnimKey(); + int getLevel() const; int getAiSetting (int index) const; @@ -83,6 +89,10 @@ namespace MWMechanics void setMagicEffects(const MagicEffects &effects); + void setAttackingOrSpell(const bool &attackingOrSpell); + + //void addLastAnimKey(std::string key,std::string animGroup); + 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 853ffc3752..db9736eb90 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -15,6 +15,11 @@ #include "../mwmechanics/character.hpp" + +//TODO: remove that and create a class interface +#include "../mwmechanics/creaturestats.hpp" +#include "../mwworld/class.hpp" + namespace MWRender { @@ -475,6 +480,8 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co return true; } + MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getLastAnimKey().push_back(std::pair(key->second,groupname)); + if(evt.compare(off, len, "equip attach") == 0) { showWeapons(true); From 076f98cbba263a572e10995192814ad3e7a37c80 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 15 Jul 2013 16:37:25 +0100 Subject: [PATCH 02/92] change way animations keys are handled (thanks KittyCat!) --- apps/openmw/mwmechanics/character.cpp | 15 +++++++++++++-- apps/openmw/mwmechanics/creaturestats.cpp | 11 ----------- apps/openmw/mwmechanics/creaturestats.hpp | 5 ----- apps/openmw/mwrender/animation.cpp | 7 ------- 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4b95999bfa..35f2ae207b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -238,7 +238,7 @@ void CharacterController::update(float duration, Movement &movement) } else if(!cls.getCreatureStats(mPtr).isDead()) { - std::list> lastKeys = cls.getCreatureStats(mPtr).getLastAnimKey(); + /*std::list> lastKeys = cls.getCreatureStats(mPtr).getLastAnimKey(); for(std::list>::iterator it = cls.getCreatureStats(mPtr).getLastAnimKey().begin(); it!=cls.getCreatureStats(mPtr).getLastAnimKey().end();) { @@ -256,7 +256,7 @@ void CharacterController::update(float duration, Movement &movement) if(lastKey.first.compare(off, len, "chop large follow stop") == 0){ mUpperBodyState = UpperCharState_WeapEquiped;std::cout << "FINISHED";} } it = cls.getCreatureStats(mPtr).getLastAnimKey().erase(it); - } + }*/ MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -478,6 +478,7 @@ void CharacterController::update(float duration, Movement &movement) if(mUpperBodyState == UpperCharState_WeapEquiped) { std::string weapgroup; + std::cout << "attaquing"; getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, @@ -490,6 +491,16 @@ void CharacterController::update(float duration, Movement &movement) mUpperBodyState = UpperCharState_ChopReadying; } + std::string weapgroup; + getWeaponGroup(mWeaponType, weapgroup); + std::string start; + std::string stop; + float complete; + bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&start,&stop); + if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; + if(mUpperBodyState == UpperCharState_ChopReadying && !animPlaying) 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/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index f4f208bcfc..8b4da03d2a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -96,11 +96,6 @@ namespace MWMechanics return mAttackingOrSpell; } - std::list> &CreatureStats::getLastAnimKey() - { - return mLastAnimKeys; - } - int CreatureStats::getLevel() const { return mLevel; @@ -207,12 +202,6 @@ namespace MWMechanics mAttackingOrSpell = attackingOrSpell; } - /*void CreatureStats::setLastAnimKey(std::string key,std::string animGroup) - { - mLastAnimKey = key; - mLastAnimGroup = animGroup; - }*/ - 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 7bb1dc4391..7423e76b2b 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -35,7 +35,6 @@ namespace MWMechanics bool mAttacked; bool mHostile; bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. - std::list> mLastAnimKeys; public: CreatureStats(); @@ -58,8 +57,6 @@ namespace MWMechanics const bool & getAttackingOrSpell() const; - std::list> & getLastAnimKey(); - int getLevel() const; int getAiSetting (int index) const; @@ -91,8 +88,6 @@ namespace MWMechanics void setAttackingOrSpell(const bool &attackingOrSpell); - //void addLastAnimKey(std::string key,std::string animGroup); - 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 db9736eb90..853ffc3752 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -15,11 +15,6 @@ #include "../mwmechanics/character.hpp" - -//TODO: remove that and create a class interface -#include "../mwmechanics/creaturestats.hpp" -#include "../mwworld/class.hpp" - namespace MWRender { @@ -480,8 +475,6 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co return true; } - MWWorld::Class::get(mPtr).getCreatureStats(mPtr).getLastAnimKey().push_back(std::pair(key->second,groupname)); - if(evt.compare(off, len, "equip attach") == 0) { showWeapons(true); From f7b711aabe4ce7022f28c691bf253bcce2d61c07 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 15 Jul 2013 16:37:32 +0100 Subject: [PATCH 03/92] bugfix --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index d46508157a..739176a8e2 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -499,8 +499,6 @@ namespace MWInput MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); - MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(false); - return true; } @@ -532,6 +530,8 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(false); + return true; } From f7f23ac5d8f3e6c6f541d5f56b66abc9977f4742 Mon Sep 17 00:00:00 2001 From: gus Date: Mon, 15 Jul 2013 17:18:16 +0100 Subject: [PATCH 04/92] improvement of the chop animation. Still some jittering. --- apps/openmw/mwmechanics/character.cpp | 50 +++++++++++++++++++++++---- apps/openmw/mwmechanics/character.hpp | 12 +++++-- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 35f2ae207b..9bc914ce34 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -478,17 +478,30 @@ void CharacterController::update(float duration, Movement &movement) if(mUpperBodyState == UpperCharState_WeapEquiped) { std::string weapgroup; - std::cout << "attaquing"; getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, - "chop start", "chop large follow stop", 0.0f, 0); - mUpperBodyState = UpperCharState_ChopReadyingMouseHold; + "chop start", "chop min attack", 0.0f, 0); + mUpperBodyState = UpperCharState_ChopStartToMinAttack; } } - else if(mUpperBodyState == UpperCharState_ChopReadyingMouseHold) + else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) { - mUpperBodyState = UpperCharState_ChopReadying; + std::string weapgroup; + getWeaponGroup(mWeaponType, weapgroup); + std::string start; + std::string stop; + float complete; + //mAnimation-> + if(mAnimation->getInfo(weapgroup,&complete,&start,&stop)) + { + std::cout << "BLABLABLA"; + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "chop max attack", "chop min hit", 1-complete, 0); + } + mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; } std::string weapgroup; @@ -499,7 +512,32 @@ void CharacterController::update(float duration, Movement &movement) bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&start,&stop); if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; - if(mUpperBodyState == UpperCharState_ChopReadying && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && !animPlaying) + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "chop min attack", "chop max attack",0, 0); + std::cout << "changing 1"; + mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; + } + if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && !animPlaying) + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "chop min hit", "chop hit",0, 0); + mUpperBodyState = UpperCharState_ChopMinHitToHit; + } + if(mUpperBodyState == UpperCharState_ChopMinHitToHit && !animPlaying) + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + "chop large follow start", "chop large follow stop",0, 0); + mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; + } + if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && !animPlaying) + { + 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 ab01038b8c..72768d7e77 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -98,9 +98,15 @@ enum UpperBodyCharacterState { UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, UpperCharState_Slashing, - UpperCharState_ChopReadying, - UpperCharState_ChopReadyingMouseHold, //when you keep your mouse clicked to ready your weapon - UpperCharState_ChopReadyWaiting, //when your weapon is ready and you keep mouse hold + UpperCharState_ChopStartToMinAttack, + UpperCharState_ChopMinAttackToMaxAttack, + UpperCharState_ChopMaxAttackToMinHit, + UpperCharState_ChopMinHitToHit, + UpperCharState_ChopLargeFollowStartToLargeFollowStop, + UpperCharState_ChopMediumFollowStartToMediumFollowStop, + UpperCharState_ChopSmallFollowStartToSmallFollowStop, + //UpperCharState_ChopReadyingMouseHold, //when you keep your mouse clicked to ready your weapon + //UpperCharState_ChopReadyWaiting, //when your weapon is ready and you keep mouse hold UpperCharState_Choping, UpperCharState_Thrusting, UpperCharState_EquipingSpell, From f6e3445414cdab707064a529b803902bb831b79d Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 16 Jul 2013 09:50:59 +0100 Subject: [PATCH 05/92] Jittering gone + bugfix: chop animation works! TODO: fix this unequip stuff --- apps/openmw/mwmechanics/character.cpp | 67 ++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 9bc914ce34..567201952c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -477,16 +477,18 @@ void CharacterController::update(float duration, Movement &movement) { if(mUpperBodyState == UpperCharState_WeapEquiped) { + std::cout << "ATTaquiomg \n"; std::string weapgroup; getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_UpperBody, false, "chop start", "chop min attack", 0.0f, 0); mUpperBodyState = UpperCharState_ChopStartToMinAttack; } } else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) { + std::cout << "OMG \n"; std::string weapgroup; getWeaponGroup(mWeaponType, weapgroup); std::string start; @@ -498,9 +500,15 @@ void CharacterController::update(float duration, Movement &movement) std::cout << "BLABLABLA"; mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, + MWRender::Animation::Group_UpperBody, false, "chop max attack", "chop min hit", 1-complete, 0); } + else + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + "chop max attack", "chop min hit", 0, 0); + } mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; } @@ -510,33 +518,40 @@ void CharacterController::update(float duration, Movement &movement) std::string stop; float complete; bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&start,&stop); + std::cout << "update"; + if(animPlaying) std::cout << "playing\n"; if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; - if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && !animPlaying) - { - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - "chop min attack", "chop max attack",0, 0); - std::cout << "changing 1"; - mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; - } - if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && !animPlaying) + if(animPlaying) { - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - "chop min hit", "chop hit",0, 0); - mUpperBodyState = UpperCharState_ChopMinHitToHit; - } - if(mUpperBodyState == UpperCharState_ChopMinHitToHit && !animPlaying) - { - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - "chop large follow start", "chop large follow stop",0, 0); - mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; - } - if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && !animPlaying) - { - mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + "chop min attack", "chop max attack",0, 0); + mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; + } + else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + "chop min hit", "chop hit",0, 0); + mUpperBodyState = UpperCharState_ChopMinHitToHit; + } + else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + "chop large follow start", "chop large follow stop",0, 0); + mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; + } + else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) + { + mUpperBodyState = UpperCharState_WeapEquiped; + } } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); From 8fc6e736498b8340fc4b48fd2db5c07ddce24014 Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 16 Jul 2013 11:51:18 +0100 Subject: [PATCH 06/92] post-merge fix --- apps/openmw/mwmechanics/character.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d8e32be487..60dc3ffedc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -549,8 +549,9 @@ void CharacterController::update(float duration, Movement &movement) std::string start; std::string stop; float complete; + float speedMult; //mAnimation-> - if(mAnimation->getInfo(weapgroup,&complete,&start,&stop)) + if(mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop)) { std::cout << "BLABLABLA"; mAnimation->disable(weapgroup); @@ -572,9 +573,8 @@ void CharacterController::update(float duration, Movement &movement) std::string start; std::string stop; float complete; - bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&start,&stop); - std::cout << "update"; - if(animPlaying) std::cout << "playing\n"; + float speedMult; + bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop); if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; if(animPlaying) @@ -584,7 +584,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - "chop min attack", "chop max attack",0, 0); + 1.0f,"chop min attack", "chop max attack",0, 0); mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; } else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) @@ -592,7 +592,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - "chop min hit", "chop hit",0, 0); + 1.0f,"chop min hit", "chop hit",0, 0); mUpperBodyState = UpperCharState_ChopMinHitToHit; } else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) @@ -600,7 +600,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - "chop large follow start", "chop large follow stop",0, 0); + 1.0f,"chop large follow start", "chop large follow stop",0, 0); mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; } else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) From 20341ae8b7a03fe418585af663a5f1e5f1d8127a Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 16 Jul 2013 14:10:14 +0100 Subject: [PATCH 07/92] simplification + taking into account weapon speed. Seems too fast, but i'm not sure --- apps/openmw/mwmechanics/character.cpp | 42 ++++++++++----------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 60dc3ffedc..eca71e15e7 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -528,53 +528,43 @@ void CharacterController::update(float duration, Movement &movement) } } + std::string weapgroup; + getWeaponGroup(mWeaponType, weapgroup); + float weapSpeed = 1; + if(weapon != inv.end()) {weapSpeed = weapon->get()->mBase->mData.mSpeed;std::cout << "setspeed "<< weapSpeed<< "\n";} + 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) { - std::cout << "ATTaquiomg \n"; - std::string weapgroup; - getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop start", "chop min attack", 0.0f, 0); + weapSpeed,"chop start", "chop min attack", 0.0f, 0); mUpperBodyState = UpperCharState_ChopStartToMinAttack; } } else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) { - std::cout << "OMG \n"; - std::string weapgroup; - getWeaponGroup(mWeaponType, weapgroup); - std::string start; - std::string stop; - float complete; - float speedMult; - //mAnimation-> - if(mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop)) + if(animPlaying) { - std::cout << "BLABLABLA"; mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop max attack", "chop min hit", 1-complete, 0); + weapSpeed,"chop max attack", "chop min hit", 1-complete, 0); } else { mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop max attack", "chop min hit", 0, 0); + weapSpeed,"chop max attack", "chop min hit", 0, 0); } mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; } - std::string weapgroup; - getWeaponGroup(mWeaponType, weapgroup); - std::string start; - std::string stop; - float complete; - float speedMult; - bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop); if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; if(animPlaying) @@ -584,7 +574,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop min attack", "chop max attack",0, 0); + weapSpeed,"chop min attack", "chop max attack",0, 0); mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; } else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) @@ -592,7 +582,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop min hit", "chop hit",0, 0); + weapSpeed,"chop min hit", "chop hit",0, 0); mUpperBodyState = UpperCharState_ChopMinHitToHit; } else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) @@ -600,7 +590,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - 1.0f,"chop large follow start", "chop large follow stop",0, 0); + weapSpeed,"chop large follow start", "chop large follow stop",0, 0); mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; } else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) From 701ff948878bb2c9d5742040a2fea2d15d3a8dcd Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 16 Jul 2013 14:46:44 +0100 Subject: [PATCH 08/92] bugfix for lockpick --- apps/openmw/mwmechanics/character.cpp | 121 +++++++++++++------------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index eca71e15e7..90236b2b4b 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -528,74 +528,77 @@ void CharacterController::update(float duration, Movement &movement) } } - std::string weapgroup; - getWeaponGroup(mWeaponType, weapgroup); - float weapSpeed = 1; - if(weapon != inv.end()) {weapSpeed = weapon->get()->mBase->mData.mSpeed;std::cout << "setspeed "<< weapSpeed<< "\n";} - 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(weaptype != WeapType_PickProbe) { - if(mUpperBodyState == UpperCharState_WeapEquiped) + 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()) { - mAnimation->play(weapgroup, Priority_Weapon, + if(mUpperBodyState == UpperCharState_WeapEquiped) + { + mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed,"chop start", "chop min attack", 0.0f, 0); - mUpperBodyState = UpperCharState_ChopStartToMinAttack; - } - } - else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) - { - if(animPlaying) - { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop max attack", "chop min hit", 1-complete, 0); + mUpperBodyState = UpperCharState_ChopStartToMinAttack; + } } - else + else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) { - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop max attack", "chop min hit", 0, 0); + if(animPlaying) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed,"chop max attack", "chop min hit", 1-complete, 0); + } + else + { + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed,"chop max attack", "chop min hit", 0, 0); + } + mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; } - mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; - } - if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; - if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; - if(animPlaying) - { - if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && complete == 1) - { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop min attack", "chop max attack",0, 0); - mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; - } - else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) - { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop min hit", "chop hit",0, 0); - mUpperBodyState = UpperCharState_ChopMinHitToHit; - } - else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) - { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop large follow start", "chop large follow stop",0, 0); - mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; - } - else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) + if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; + if(animPlaying) { - mUpperBodyState = UpperCharState_WeapEquiped; + if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed,"chop min attack", "chop max attack",0, 0); + mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; + } + else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed,"chop min hit", "chop hit",0, 0); + mUpperBodyState = UpperCharState_ChopMinHitToHit; + } + else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) + { + mAnimation->disable(weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed,"chop large follow start", "chop large follow stop",0, 0); + mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; + } + else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) + { + mUpperBodyState = UpperCharState_WeapEquiped; + } } } From 66e3eacace2d118ed1ec0709a25ef631e183ce31 Mon Sep 17 00:00:00 2001 From: gus Date: Tue, 16 Jul 2013 15:14:34 +0100 Subject: [PATCH 09/92] fix bows --- apps/openmw/mwmechanics/character.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 90236b2b4b..f4c975c41f 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -528,7 +528,8 @@ void CharacterController::update(float duration, Movement &movement) } } - if(weaptype != WeapType_PickProbe) + if(weaptype != WeapType_PickProbe && weaptype != WeapType_BowAndArrow + && weaptype != WeapType_Crossbow && weaptype != WeapType_ThowWeapon) { std::string weapgroup; getWeaponGroup(mWeaponType, weapgroup); From 9536b5050b9161863c40c417c02aa52708091f12 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Jul 2013 16:25:41 +0200 Subject: [PATCH 10/92] Fixes weapon input to use A_Use instead of hardcoding to mouse button --- apps/openmw/mwinput/inputmanagerimp.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b9f1a67d49..fd2359f5ce 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -161,6 +161,12 @@ namespace MWInput resetIdleTime (); int action = channel->getNumber(); + + if (action == A_Use) + { + MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(currentValue); + } + if (currentValue == 1) { // trigger action activated @@ -522,8 +528,6 @@ namespace MWInput } } - MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(true); - return true; } @@ -533,8 +537,6 @@ namespace MWInput MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); - MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(false); - return true; } From 91e95e1404ea33a83b311eddb54ebfb2ce10e195 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Jul 2013 23:32:41 +0200 Subject: [PATCH 11/92] Use the correct slash, chop or thrust animation --- apps/openmw/mwgui/settingswindow.cpp | 5 +++ apps/openmw/mwgui/settingswindow.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 14 ++++++++ apps/openmw/mwmechanics/character.cpp | 41 +++++++++++++++++++---- apps/openmw/mwmechanics/character.hpp | 2 ++ apps/openmw/mwmechanics/creaturestats.hpp | 11 ++++++ apps/openmw/mwrender/animation.cpp | 2 +- files/mygui/openmw_settings_window.layout | 6 ++++ files/settings-default.cfg | 4 +++ 9 files changed, 79 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 97bccd01fb..8443aaf30c 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 20e9907d95..42ed5bf6d9 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 fd2359f5ce..90acbae6a1 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -165,6 +165,20 @@ namespace MWInput 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) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f4c975c41f..04b3797577 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 { @@ -540,13 +557,25 @@ void CharacterController::update(float duration, Movement &movement) 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,"chop start", "chop min attack", 0.0f, 0); + weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); mUpperBodyState = UpperCharState_ChopStartToMinAttack; } } @@ -557,13 +586,13 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop max attack", "chop min hit", 1-complete, 0); + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1-complete, 0); } else { mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop max attack", "chop min hit", 0, 0); + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 0, 0); } mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; } @@ -577,7 +606,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop min attack", "chop max attack",0, 0); + weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0); mUpperBodyState = UpperCharState_ChopMinAttackToMaxAttack; } else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) @@ -585,7 +614,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop min hit", "chop hit",0, 0); + weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0); mUpperBodyState = UpperCharState_ChopMinHitToHit; } else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) @@ -593,7 +622,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, - weapSpeed,"chop large follow start", "chop large follow stop",0, 0); + weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0); mUpperBodyState = UpperCharState_ChopLargeFollowStartToLargeFollowStop; } else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 08dc67f7b9..d760f89577 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -146,6 +146,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.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7423e76b2b..2d979b9b22 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -36,6 +36,8 @@ namespace MWMechanics bool mHostile; bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. + int mAttackType; + public: CreatureStats(); @@ -88,6 +90,15 @@ namespace MWMechanics 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 147e7c90ed..3ced108b38 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -654,7 +654,7 @@ void Animation::resetActiveGroups() return; const Ogre::SharedPtr &animsrc = state->second.mSource; - const NifOgre::TextKeyMap &keys = animsrc->mTextKeys; + //const NifOgre::TextKeyMap &keys = animsrc->mTextKeys; 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 ab91aed788..a54f3086e9 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 613f124323..ac56604d11 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 From dfe912dcab8ee8a3683bbef0e28211e497243a30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 16 Jul 2013 23:38:55 +0200 Subject: [PATCH 12/92] Enum renaming --- apps/openmw/mwmechanics/character.cpp | 20 ++++++++++---------- apps/openmw/mwmechanics/character.hpp | 19 +++++++------------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 04b3797577..9b8eecb9e4 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -576,10 +576,10 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); - mUpperBodyState = UpperCharState_ChopStartToMinAttack; + mUpperBodyState = UpperCharState_StartToMinAttack; } } - else if(mUpperBodyState == UpperCharState_ChopMinAttackToMaxAttack) + else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) { if(animPlaying) { @@ -594,38 +594,38 @@ void CharacterController::update(float duration, Movement &movement) MWRender::Animation::Group_UpperBody, false, weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 0, 0); } - mUpperBodyState = UpperCharState_ChopMaxAttackToMinHit; + mUpperBodyState = UpperCharState_MaxAttackToMinHit; } if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; if(animPlaying) { - if(mUpperBodyState == UpperCharState_ChopStartToMinAttack && complete == 1) + 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_ChopMinAttackToMaxAttack; + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; } - else if(mUpperBodyState == UpperCharState_ChopMaxAttackToMinHit && complete == 1) + 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_ChopMinHitToHit; + mUpperBodyState = UpperCharState_MinHitToHit; } - else if(mUpperBodyState == UpperCharState_ChopMinHitToHit && complete == 1) + 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_ChopLargeFollowStartToLargeFollowStop; + mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; } - else if(mUpperBodyState == UpperCharState_ChopLargeFollowStartToLargeFollowStop && complete == 1) + else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1) { mUpperBodyState = UpperCharState_WeapEquiped; } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d760f89577..1369566248 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -100,18 +100,13 @@ enum UpperBodyCharacterState { UpperCharState_EquipingWeap, UpperCharState_UnEquipingWeap, UpperCharState_WeapEquiped, - UpperCharState_Slashing, - UpperCharState_ChopStartToMinAttack, - UpperCharState_ChopMinAttackToMaxAttack, - UpperCharState_ChopMaxAttackToMinHit, - UpperCharState_ChopMinHitToHit, - UpperCharState_ChopLargeFollowStartToLargeFollowStop, - UpperCharState_ChopMediumFollowStartToMediumFollowStop, - UpperCharState_ChopSmallFollowStartToSmallFollowStop, - //UpperCharState_ChopReadyingMouseHold, //when you keep your mouse clicked to ready your weapon - //UpperCharState_ChopReadyWaiting, //when your weapon is ready and you keep mouse hold - UpperCharState_Choping, - UpperCharState_Thrusting, + UpperCharState_StartToMinAttack, + UpperCharState_MinAttackToMaxAttack, + UpperCharState_MaxAttackToMinHit, + UpperCharState_MinHitToHit, + UpperCharState_LargeFollowStartToLargeFollowStop, + UpperCharState_MediumFollowStartToMediumFollowStop, + UpperCharState_SmallFollowStartToSmallFollowStop, UpperCharState_EquipingSpell, UpperCharState_UnEquipingSpell }; From bf332b633e46978dac54afd4cac5af5bb3ab41b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 00:03:19 +0200 Subject: [PATCH 13/92] Make VideoPlayer::playVideo block until the video finishes --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 13 ++++++++++++- apps/openmw/mwrender/videoplayer.hpp | 4 +++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 41b9a854a4..e0b84600dc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -170,7 +170,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); - mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mVideoPlayer = new VideoPlayer(mRendering.getScene (), mRendering.getWindow()); mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video")); mSun = 0; diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 08642c5a30..bc9bef5311 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -16,6 +17,7 @@ #include "../mwbase/soundmanager.hpp" #include "../mwsound/sound_decoder.hpp" #include "../mwsound/sound.hpp" +#include "../mwbase/inputmanager.hpp" #include "renderconst.hpp" @@ -1032,13 +1034,14 @@ public: #endif // defined OPENMW_USE_FFMPEG -VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) +VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window) : mState(NULL) , mSceneMgr(sceneMgr) , mVideoMaterial(NULL) , mRectangle(NULL) , mNode(NULL) , mAllowSkipping(false) + , mWindow(window) { mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); if (mVideoMaterial.isNull ()) @@ -1129,6 +1132,14 @@ void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) try { mState = new VideoState; mState->init(resourceName); + + while (isPlaying()) + { + MWBase::Environment::get().getInputManager()->update(0, false); + update(); + mWindow->update(); + } + } catch(std::exception& e) { std::cerr<< "Failed to play video: "< Date: Tue, 23 Jul 2013 01:37:41 -0700 Subject: [PATCH 14/92] Remove some unused methods --- apps/openmw/mwrender/camera.cpp | 29 ++++------------------- apps/openmw/mwrender/camera.hpp | 10 ++------ apps/openmw/mwrender/renderingmanager.cpp | 14 +++++------ 3 files changed, 13 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index f8f954bf3f..59b320b65e 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -302,34 +302,13 @@ namespace MWRender } } - float Camera::getHeight() - { - if(mCamera->isParentTagPoint()) - { - Ogre::TagPoint *tag = static_cast(mCamera->getParentNode()); - return tag->_getFullLocalTransform().getTrans().z; - } - return mCamera->getParentNode()->getPosition().z; - } - - bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) + void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera) { mCamera->getParentSceneNode()->needUpdate(true); - camera = mCamera->getRealPosition(); - player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); - return mFirstPersonView && !mVanity.enabled && !mPreviewMode; - } - - Ogre::Vector3 Camera::getPosition() - { - return mTrackingPtr.getRefData().getBaseNode()->getPosition(); - } - - void Camera::getSightAngles(float &pitch, float &yaw) - { - pitch = mMainCam.pitch; - yaw = mMainCam.yaw; + camera = mCamera->getRealPosition(); + focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() * + Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr()); } void Camera::togglePlayerLooking(bool enable) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 3418efcc91..2993bad052 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -93,14 +93,8 @@ namespace MWRender void setAnimation(NpcAnimation *anim); - float getHeight(); - - /// Stores player and camera world positions in passed arguments - /// \return true if camera at the eye-place - bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera); - Ogre::Vector3 getPosition(); - - void getSightAngles(float &pitch, float &yaw); + /// Stores focal and camera world positions in passed arguments + void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera); void togglePlayerLooking(bool enable); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 19518c2c45..6cfe87ac2c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -323,18 +323,18 @@ void RenderingManager::update (float duration, bool paused) // player position MWWorld::RefData &data = player.getRefData(); - float *_playerPos = data.getPosition().pos; - Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]); + Ogre::Vector3 playerPos(data.getPosition().pos); - Ogre::Vector3 orig, dest; - if(!mCamera->getPosition(orig, dest)) + mCamera->setCameraDistance(); + if(!mCamera->isFirstPerson()) { - orig.z += mCamera->getHeight() * mRootNode->getScale().z; + Ogre::Vector3 orig, dest; + mCamera->getPosition(orig, dest); btVector3 btOrig(orig.x, orig.y, orig.z); btVector3 btDest(dest.x, dest.y, dest.z); - std::pair test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest); - if (test.first) + std::pair test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest); + if(test.first) mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); } From 0240efa6cf1df788f65415910e60c6fd36447593 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 02:28:58 -0700 Subject: [PATCH 15/92] Improve weapon attack logic to better handle picks, probes, and h2h --- apps/openmw/mwmechanics/character.cpp | 38 +++++++++++++++------------ 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 86407a32d3..d2f2d58837 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -538,25 +538,26 @@ void CharacterController::update(float duration, Movement &movement) } } - if(weaptype != WeapType_PickProbe && weaptype != WeapType_BowAndArrow - && weaptype != WeapType_Crossbow && weaptype != WeapType_ThowWeapon) + if(1) { + bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); + float weapSpeed = 1.0f; + if(isWeapon) + weapSpeed = weapon->get()->mBase->mData.mSpeed; + 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); + bool animPlaying = mAnimation->getInfo(weapgroup, &complete); if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) { - if(mUpperBodyState == UpperCharState_WeapEquiped) + if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_Crossbow && + mWeaponType != WeapType_BowAndArrow && mWeaponType != WeapType_ThowWeapon && + mWeaponType != WeapType_PickProbe) { int attackType = cls.getCreatureStats(mPtr).getAttackType(); - if (Settings::Manager::getBool("best attack", "Game") && weapon != inv.end()) + if(isWeapon && Settings::Manager::getBool("best attack", "Game")) attackType = getBestAttack(weapon->get()->mBase); if (attackType == MWMechanics::CreatureStats::AT_Chop) @@ -590,11 +591,13 @@ void CharacterController::update(float duration, Movement &movement) mUpperBodyState = UpperCharState_MaxAttackToMinHit; } - if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped; - if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing; - if(animPlaying) + if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) + mUpperBodyState = UpperCharState_WeapEquiped; + else if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) + mUpperBodyState = UpperCharState_Nothing; + else if(animPlaying) { - if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1) + if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1.0f) { mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, @@ -602,7 +605,7 @@ void CharacterController::update(float duration, Movement &movement) weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0); mUpperBodyState = UpperCharState_MinAttackToMaxAttack; } - else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1) + else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1.0f) { mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, @@ -610,7 +613,7 @@ void CharacterController::update(float duration, Movement &movement) weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0); mUpperBodyState = UpperCharState_MinHitToHit; } - else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1) + else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1.0f) { mAnimation->disable(weapgroup); mAnimation->play(weapgroup, Priority_Weapon, @@ -618,8 +621,9 @@ void CharacterController::update(float duration, Movement &movement) weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0); mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; } - else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1) + else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1.0f) { + mAnimation->disable(weapgroup); mUpperBodyState = UpperCharState_WeapEquiped; } } From 22e07968affe704f2edcb737a4eb265357eca49f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 02:50:52 -0700 Subject: [PATCH 16/92] Store the current weapon group name --- apps/openmw/mwmechanics/character.cpp | 60 +++++++++++++-------------- apps/openmw/mwmechanics/character.hpp | 2 + 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d2f2d58837..7aac81da31 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -497,6 +497,7 @@ void CharacterController::update(float duration, Movement &movement) { forcestateupdate = (mWeaponType != weaptype); mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); mUpdateWeapon = false; } @@ -524,6 +525,7 @@ void CharacterController::update(float duration, Movement &movement) } mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); if(weapon != inv.end()) { @@ -545,10 +547,8 @@ void CharacterController::update(float duration, Movement &movement) if(isWeapon) weapSpeed = weapon->get()->mBase->mData.mSpeed; - std::string weapgroup; - getWeaponGroup(mWeaponType, weapgroup); float complete; - bool animPlaying = mAnimation->getInfo(weapgroup, &complete); + bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) { @@ -567,27 +567,20 @@ void CharacterController::update(float duration, Movement &movement) else mAttackType = "thrust"; - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0); + mAnimation->play(mCurrentWeapon, 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); - } + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", + 1.0f-complete, 0); mUpperBodyState = UpperCharState_MaxAttackToMinHit; } @@ -599,31 +592,34 @@ void CharacterController::update(float duration, Movement &movement) { if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1.0f) { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0); + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min attack", mAttackType+" max attack", + 0.0f, 0); mUpperBodyState = UpperCharState_MinAttackToMaxAttack; } else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1.0f) { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0); + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" hit", + 0.0f, 0); mUpperBodyState = UpperCharState_MinHitToHit; } else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1.0f) { - mAnimation->disable(weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0); + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", + 0.0f, 0); mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; } else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1.0f) { - mAnimation->disable(weapgroup); + mAnimation->disable(mCurrentWeapon); mUpperBodyState = UpperCharState_WeapEquiped; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index ff9f3aa62f..5ee9f9c21e 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -132,6 +132,8 @@ class CharacterController UpperBodyCharacterState mUpperBodyState; WeaponType mWeaponType; + std::string mCurrentWeapon; + bool mSkipAnim; // Workaround for playing weapon draw animation and sound when going to new cell From 24567294ff80548910344ee47d447cf2c1347f0b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 03:26:24 -0700 Subject: [PATCH 17/92] Move NPC state update handling into a separate method --- apps/openmw/mwmechanics/character.cpp | 410 +++++++++++++------------- apps/openmw/mwmechanics/character.hpp | 2 + 2 files changed, 212 insertions(+), 200 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7aac81da31..7816860c63 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -295,6 +295,215 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) } +bool CharacterController::updateNpcState() +{ + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + NpcStats &stats = cls.getNpcStats(mPtr); + WeaponType weaptype = WeapType_None; + MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + MWWorld::ContainerStoreIterator weapon = inv.end(); + + if(stats.getDrawState() == DrawState_Spell) + weaptype = WeapType_Spell; + else if(stats.getDrawState() == MWMechanics::DrawState_Weapon) + { + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end()) + weaptype = WeapType_HandToHand; + else + { + const std::string &type = weapon->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + weaptype = WeapType_PickProbe; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + switch(type) + { + case ESM::Weapon::ShortBladeOneHand: + case ESM::Weapon::LongBladeOneHand: + case ESM::Weapon::BluntOneHand: + case ESM::Weapon::AxeOneHand: + case ESM::Weapon::Arrow: + case ESM::Weapon::Bolt: + weaptype = WeapType_OneHand; + break; + case ESM::Weapon::LongBladeTwoHand: + case ESM::Weapon::BluntTwoClose: + case ESM::Weapon::AxeTwoHand: + weaptype = WeapType_TwoHand; + break; + case ESM::Weapon::BluntTwoWide: + case ESM::Weapon::SpearTwoWide: + weaptype = WeapType_TwoWide; + break; + case ESM::Weapon::MarksmanBow: + weaptype = WeapType_BowAndArrow; + break; + case ESM::Weapon::MarksmanCrossbow: + weaptype = WeapType_Crossbow; + break; + case ESM::Weapon::MarksmanThrown: + weaptype = WeapType_ThowWeapon; + break; + } + } + } + } + else + weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + bool forcestateupdate = false; + if(mUpdateWeapon) + { + forcestateupdate = (mWeaponType != weaptype); + mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); + mUpdateWeapon = false; + } + + if(weaptype != mWeaponType) + { + forcestateupdate = true; + + std::string weapgroup; + if(weaptype == WeapType_None) + { + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; + } + else + { + getWeaponGroup(weaptype, weapgroup); + mAnimation->showWeapons(false); + mAnimation->play(weapgroup, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1.0f, "equip start", "equip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_EquipingWeap; + } + + mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); + + if(weapon != inv.end()) + { + std::string soundid = (mWeaponType == WeapType_None) ? + MWWorld::Class::get(*weapon).getDownSoundId(*weapon) : + MWWorld::Class::get(*weapon).getUpSoundId(*weapon); + if(!soundid.empty()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); + } + } + } + + + bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); + float weapSpeed = 1.0f; + if(isWeapon) + weapSpeed = weapon->get()->mBase->mData.mSpeed; + + float complete; + bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); + + if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_Crossbow && + mWeaponType != WeapType_BowAndArrow && mWeaponType != WeapType_ThowWeapon && + mWeaponType != WeapType_PickProbe) + { + int attackType = cls.getCreatureStats(mPtr).getAttackType(); + if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + 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(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" min attack", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } + } + else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", + 1.0f-complete, 0); + mUpperBodyState = UpperCharState_MaxAttackToMinHit; + } + + if(!animPlaying) + { + if(mUpperBodyState == UpperCharState_EquipingWeap || + mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop) + mUpperBodyState = UpperCharState_WeapEquiped; + else if(mUpperBodyState == UpperCharState_UnEquipingWeap) + mUpperBodyState = UpperCharState_Nothing; + } + else if(complete >= 1.0f) + { + if(mUpperBodyState == UpperCharState_StartToMinAttack) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min attack", mAttackType+" max attack", + 0.0f, 0); + mUpperBodyState = UpperCharState_MinAttackToMaxAttack; + } + else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" hit", + 0.0f, 0); + mUpperBodyState = UpperCharState_MinHitToHit; + } + else if(mUpperBodyState == UpperCharState_MinHitToHit) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", + 0.0f, 0); + mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; + } + else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop) + { + mAnimation->disable(mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + } + + + MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) + { + if(!mAnimation->isPlaying("torch")) + mAnimation->play("torch", Priority_Torch, + MWRender::Animation::Group_LeftArm, false, + 1.0f, "start", "stop", 0.0f, (~(size_t)0)); + } + else if(mAnimation->isPlaying("torch")) + mAnimation->disable("torch"); + + return forcestateupdate; +} + void CharacterController::update(float duration, Movement &movement) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); @@ -436,206 +645,7 @@ void CharacterController::update(float duration, Movement &movement) movement.mRotation[2] += rot.z; if(mPtr.getTypeName() == typeid(ESM::NPC).name()) - { - NpcStats &stats = cls.getNpcStats(mPtr); - WeaponType weaptype = WeapType_None; - MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weapon = inv.end(); - - if(stats.getDrawState() == DrawState_Spell) - weaptype = WeapType_Spell; - else if(stats.getDrawState() == MWMechanics::DrawState_Weapon) - { - weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if(weapon == inv.end()) - weaptype = WeapType_HandToHand; - else - { - const std::string &type = weapon->getTypeName(); - if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) - weaptype = WeapType_PickProbe; - else if(type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef *ref = weapon->get(); - ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; - switch(type) - { - case ESM::Weapon::ShortBladeOneHand: - case ESM::Weapon::LongBladeOneHand: - case ESM::Weapon::BluntOneHand: - case ESM::Weapon::AxeOneHand: - case ESM::Weapon::Arrow: - case ESM::Weapon::Bolt: - weaptype = WeapType_OneHand; - break; - case ESM::Weapon::LongBladeTwoHand: - case ESM::Weapon::BluntTwoClose: - case ESM::Weapon::AxeTwoHand: - weaptype = WeapType_TwoHand; - break; - case ESM::Weapon::BluntTwoWide: - case ESM::Weapon::SpearTwoWide: - weaptype = WeapType_TwoWide; - break; - case ESM::Weapon::MarksmanBow: - weaptype = WeapType_BowAndArrow; - break; - case ESM::Weapon::MarksmanCrossbow: - weaptype = WeapType_Crossbow; - break; - case ESM::Weapon::MarksmanThrown: - weaptype = WeapType_ThowWeapon; - break; - } - } - } - } - else - weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - - if(mUpdateWeapon) - { - forcestateupdate = (mWeaponType != weaptype); - mWeaponType = weaptype; - getWeaponGroup(mWeaponType, mCurrentWeapon); - mUpdateWeapon = false; - } - - if(weaptype != mWeaponType) - { - forcestateupdate = true; - - std::string weapgroup; - if(weaptype == WeapType_None) - { - getWeaponGroup(mWeaponType, weapgroup); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - 1.0f, "unequip start", "unequip stop", 0.0f, 0); - mUpperBodyState = UpperCharState_UnEquipingWeap; - } - else - { - getWeaponGroup(weaptype, weapgroup); - mAnimation->showWeapons(false); - mAnimation->play(weapgroup, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - 1.0f, "equip start", "equip stop", 0.0f, 0); - mUpperBodyState = UpperCharState_EquipingWeap; - } - - mWeaponType = weaptype; - getWeaponGroup(mWeaponType, mCurrentWeapon); - - if(weapon != inv.end()) - { - std::string soundid = (mWeaponType == WeapType_None) ? - MWWorld::Class::get(*weapon).getDownSoundId(*weapon) : - MWWorld::Class::get(*weapon).getUpSoundId(*weapon); - if(!soundid.empty()) - { - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); - } - } - } - - if(1) - { - bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name()); - float weapSpeed = 1.0f; - if(isWeapon) - weapSpeed = weapon->get()->mBase->mData.mSpeed; - - float complete; - bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - - if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) - { - if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_Crossbow && - mWeaponType != WeapType_BowAndArrow && mWeaponType != WeapType_ThowWeapon && - mWeaponType != WeapType_PickProbe) - { - int attackType = cls.getCreatureStats(mPtr).getAttackType(); - if(isWeapon && Settings::Manager::getBool("best attack", "Game")) - 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(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" start", mAttackType+" min attack", - 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; - } - } - else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) - { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" max attack", mAttackType+" min hit", - 1.0f-complete, 0); - mUpperBodyState = UpperCharState_MaxAttackToMinHit; - } - - if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) - mUpperBodyState = UpperCharState_WeapEquiped; - else if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) - mUpperBodyState = UpperCharState_Nothing; - else if(animPlaying) - { - if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1.0f) - { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min attack", mAttackType+" max attack", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinAttackToMaxAttack; - } - else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1.0f) - { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" hit", - 0.0f, 0); - mUpperBodyState = UpperCharState_MinHitToHit; - } - else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1.0f) - { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", - 0.0f, 0); - mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; - } - else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1.0f) - { - mAnimation->disable(mCurrentWeapon); - mUpperBodyState = UpperCharState_WeapEquiped; - } - } - } - - MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); - if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) - { - if(!mAnimation->isPlaying("torch")) - mAnimation->play("torch", Priority_Torch, - MWRender::Animation::Group_LeftArm, false, - 1.0f, "start", "stop", 0.0f, (~(size_t)0)); - } - else if(mAnimation->isPlaying("torch")) - mAnimation->disable("torch"); - } + forcestateupdate = updateNpcState(); refreshCurrentAnims(idlestate, movestate, forcestateupdate); } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5ee9f9c21e..d30328682b 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -151,6 +151,8 @@ class CharacterController void clearAnimQueue(); + bool updateNpcState(); + public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); From ad6d663e0991c785496699e9d84732e396028ab2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 03:44:52 -0700 Subject: [PATCH 18/92] Remove some unnecessary debug messages --- apps/openmw/mwrender/animation.cpp | 24 +++--------------------- apps/openmw/mwsound/soundmanagerimp.cpp | 14 +++++--------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 11a088e63e..a8328b7cbf 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -534,38 +534,20 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co size_t len = evt.size() - off; if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) - { state.mLoopStartKey = key; - return true; - } - - if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) + else if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) { if(doLoop(state)) { if(state.mTime >= time) return false; } - return true; } - - if(evt.compare(off, len, "equip attach") == 0) - { + else if(evt.compare(off, len, "equip attach") == 0) showWeapons(true); - return true; - } - if(evt.compare(off, len, "unequip detach") == 0) - { + else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); - return true; - } - - /* Nothing to do for these */ - if(evt.compare(off, len, "equip start") == 0 || evt.compare(off, len, "equip stop") == 0 || - evt.compare(off, len, "unequip start") == 0 || evt.compare(off, len, "unequip stop") == 0) - return true; - std::cerr<< "Unhandled animation textkey: "<mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { - const std::string go = soundIter->mSound.toString(); - int chance = (int) soundIter->mChance; - //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - soundIter++; - if(r - pos < chance) + if(r - pos < soundIter->mChance) { - //play sound - std::cout << "Sound: " << go <<" Chance:" << chance << "\n"; - playSound(go, 1.0f, 1.0f); + playSound(soundIter->mSound.toString(), 1.0f, 1.0f); break; } - pos += chance; + pos += soundIter->mChance; + + soundIter++; } } From 537d5c4934451f948acc13887e17f9047dd6d3e5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 04:36:25 -0700 Subject: [PATCH 19/92] Avoid trying to play animations without any animation sources --- apps/openmw/mwrender/animation.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a8328b7cbf..956bc6f7dd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -554,7 +554,7 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) { - if(!mSkelBase) + if(!mSkelBase || mAnimSources.size() == 0) return; if(groupname.empty()) @@ -602,7 +602,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo } } if(iter == mAnimSources.rend()) - std::cerr<< "Failed to find animation "< &animsrc = state->second.mSource; - const std::vector >&ctrls = animsrc->mControllers[0]; for(size_t i = 0;i < ctrls.size();i++) { From d5d832846e7440a47e5e212d6c7ae44361daec33 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 04:56:49 -0700 Subject: [PATCH 20/92] Rename the follow-up upper character state enum --- apps/openmw/mwmechanics/character.cpp | 9 ++------- apps/openmw/mwmechanics/character.hpp | 6 +----- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 7816860c63..d7cb7cf6c3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -448,7 +448,7 @@ bool CharacterController::updateNpcState() if(!animPlaying) { if(mUpperBodyState == UpperCharState_EquipingWeap || - mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop) + mUpperBodyState == UpperCharState_FollowStartToFollowStop) mUpperBodyState = UpperCharState_WeapEquiped; else if(mUpperBodyState == UpperCharState_UnEquipingWeap) mUpperBodyState = UpperCharState_Nothing; @@ -480,12 +480,7 @@ bool CharacterController::updateNpcState() MWRender::Animation::Group_UpperBody, true, weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", 0.0f, 0); - mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop; - } - else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop) - { - mAnimation->disable(mCurrentWeapon); - mUpperBodyState = UpperCharState_WeapEquiped; + mUpperBodyState = UpperCharState_FollowStartToFollowStop; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index d30328682b..6af6c16cc9 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -104,11 +104,7 @@ enum UpperBodyCharacterState { UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, - UpperCharState_LargeFollowStartToLargeFollowStop, - UpperCharState_MediumFollowStartToMediumFollowStop, - UpperCharState_SmallFollowStartToSmallFollowStop, - UpperCharState_EquipingSpell, - UpperCharState_UnEquipingSpell + UpperCharState_FollowStartToFollowStop }; class CharacterController From fecb8c1cc4258a0d012a69cc93828e2eb4938b77 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 05:30:37 -0700 Subject: [PATCH 21/92] Fix the completion amount for 0-length anims --- apps/openmw/mwrender/animation.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 956bc6f7dd..997a59dd04 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -670,8 +670,14 @@ bool Animation::getInfo(const std::string &groupname, float *complete, float *sp return false; } - if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) / - (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(complete) + { + if(iter->second.mStopKey->first > iter->second.mStartKey->first) + *complete = (iter->second.mTime - iter->second.mStartKey->first) / + (iter->second.mStopKey->first - iter->second.mStartKey->first); + else + *complete = (iter->second.mPlaying ? 0.0f : 1.0f); + } if(speedmult) *speedmult = iter->second.mSpeedMult; if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); From 4d157cb19a0cd7dddb28d0e2bd13490b6535878c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 06:13:08 -0700 Subject: [PATCH 22/92] Handle marksmen weapon attack sequences --- apps/openmw/mwmechanics/character.cpp | 56 +++++++++++++++++---------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d7cb7cf6c3..067bbc074e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -413,20 +413,24 @@ bool CharacterController::updateNpcState() if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) { - if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_Crossbow && - mWeaponType != WeapType_BowAndArrow && mWeaponType != WeapType_ThowWeapon && - mWeaponType != WeapType_PickProbe) + if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_PickProbe) { - int attackType = cls.getCreatureStats(mPtr).getAttackType(); - if(isWeapon && Settings::Manager::getBool("best attack", "Game")) - attackType = getBestAttack(weapon->get()->mBase); - - if (attackType == MWMechanics::CreatureStats::AT_Chop) - mAttackType = "chop"; - else if (attackType == MWMechanics::CreatureStats::AT_Slash) - mAttackType = "slash"; + if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_ThowWeapon) + mAttackType = "shoot"; else - mAttackType = "thrust"; + { + int attackType = cls.getCreatureStats(mPtr).getAttackType(); + if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + 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(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, @@ -467,19 +471,31 @@ bool CharacterController::updateNpcState() else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit) { mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" min hit", mAttackType+" hit", - 0.0f, 0); + if(mAttackType == "shoot") + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" follow start", + 0.0f, 0); + else + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" min hit", mAttackType+" hit", + 0.0f, 0); mUpperBodyState = UpperCharState_MinHitToHit; } else if(mUpperBodyState == UpperCharState_MinHitToHit) { mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, true, - weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", - 0.0f, 0); + if(mAttackType == "shoot") + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" follow start", mAttackType+" follow stop", + 0.0f, 0); + else + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop", + 0.0f, 0); mUpperBodyState = UpperCharState_FollowStartToFollowStop; } } From 17282c69b11cfca158c11755ae7e1038027ecd50 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 07:30:54 -0700 Subject: [PATCH 23/92] Handle spell-casting animation sequences --- apps/openmw/mwmechanics/character.cpp | 72 +++++++++++++++++++-------- apps/openmw/mwmechanics/character.hpp | 3 +- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 067bbc074e..323fda03f5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -298,6 +298,7 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) bool CharacterController::updateNpcState() { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + CreatureStats &crstats = cls.getCreatureStats(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -411,32 +412,58 @@ bool CharacterController::updateNpcState() float complete; bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - if(cls.getCreatureStats(mPtr).getAttackingOrSpell()) + if(crstats.getAttackingOrSpell()) { - if(mUpperBodyState == UpperCharState_WeapEquiped && mWeaponType != WeapType_PickProbe) + if(mUpperBodyState == UpperCharState_WeapEquiped) { - if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || - mWeaponType == WeapType_ThowWeapon) - mAttackType = "shoot"; - else + if(mWeaponType == WeapType_Spell) { - int attackType = cls.getCreatureStats(mPtr).getAttackType(); - if(isWeapon && Settings::Manager::getBool("best attack", "Game")) - 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"; + const std::string spellid = crstats.getSpells().getSelectedSpell(); + if(!spellid.empty()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Spell *spell = store.get().find(spellid); + + switch(spell->mEffects.mList.at(0).mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + default: mAttackType = ""; break; + } + + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + weapSpeed, mAttackType+" start", mAttackType+" stop", + 0.0f, 0); + mUpperBodyState = UpperCharState_CastingSpell; + } } + else if(mWeaponType != WeapType_PickProbe) + { + if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow || + mWeaponType == WeapType_ThowWeapon) + mAttackType = "shoot"; + else + { + int attackType = crstats.getAttackType(); + if(isWeapon && Settings::Manager::getBool("best attack", "Game")) + 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(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" start", mAttackType+" min attack", - 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" start", mAttackType+" min attack", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } } } else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) @@ -452,7 +479,8 @@ bool CharacterController::updateNpcState() if(!animPlaying) { if(mUpperBodyState == UpperCharState_EquipingWeap || - mUpperBodyState == UpperCharState_FollowStartToFollowStop) + mUpperBodyState == UpperCharState_FollowStartToFollowStop || + mUpperBodyState == UpperCharState_CastingSpell) mUpperBodyState = UpperCharState_WeapEquiped; else if(mUpperBodyState == UpperCharState_UnEquipingWeap) mUpperBodyState = UpperCharState_Nothing; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 6af6c16cc9..9b5178b583 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -104,7 +104,8 @@ enum UpperBodyCharacterState { UpperCharState_MinAttackToMaxAttack, UpperCharState_MaxAttackToMinHit, UpperCharState_MinHitToHit, - UpperCharState_FollowStartToFollowStop + UpperCharState_FollowStartToFollowStop, + UpperCharState_CastingSpell }; class CharacterController From 70a5a83419fc0cf84ca8f350c701851e83e42724 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 23 Jul 2013 16:48:54 +0200 Subject: [PATCH 24/92] Change the formula used to calculate initial NPC health --- apps/openmw/mwclass/npc.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b6af9f0e57..9757374ec1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -113,7 +113,19 @@ namespace // initial health int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); - creatureStats.setHealth(static_cast (0.5 * (strength + endurance)) + 4 * (creatureStats.getLevel() - 1)); + + int multiplier = 3; + + if (class_->mData.mSpecialization == ESM::Class::Combat) + multiplier += 2; + else if (class_->mData.mSpecialization == ESM::Class::Stealth) + multiplier += 1; + + if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance + || class_->mData.mAttribute[1] == ESM::Attribute::Endurance) + multiplier += 1; + + creatureStats.setHealth(static_cast (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1)); } } From 11b094559f79f17af4c9c51eccae75c32ce9780b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 11:12:19 -0700 Subject: [PATCH 25/92] Play the appropriate casting animation sound --- apps/openmw/mwmechanics/character.cpp | 52 ++++++++++++++++++++------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 323fda03f5..b4b1143c28 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -410,26 +410,47 @@ bool CharacterController::updateNpcState() weapSpeed = weapon->get()->mBase->mData.mSpeed; float complete; - bool animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); - + bool animPlaying; if(crstats.getAttackingOrSpell()) { if(mUpperBodyState == UpperCharState_WeapEquiped) { + mAttackType.clear(); if(mWeaponType == WeapType_Spell) { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::EffectList *effects = NULL; + const std::string spellid = crstats.getSpells().getSelectedSpell(); if(!spellid.empty()) + effects = &store.get().find(spellid)->mEffects; + else if(inv.getSelectedEnchantItem() != inv.end()) + { + MWWorld::Ptr item = *inv.getSelectedEnchantItem(); + const std::string enchid = MWWorld::Class::get(item).getEnchantment(item); + if(!enchid.empty()) + effects = &store.get().find(enchid)->mEffects; + } + + if(effects) { - const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::Spell *spell = store.get().find(spellid); + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + const ESM::MagicEffect *effect; + effect = store.get().find(effects->mList.at(0).mEffectID); + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mCastSound.empty()) + sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); - switch(spell->mEffects.mList.at(0).mRange) + switch(effects->mList[0].mRange) { case 0: mAttackType = "self"; break; case 1: mAttackType = "touch"; break; case 2: mAttackType = "target"; break; - default: mAttackType = ""; break; } mAnimation->play(mCurrentWeapon, Priority_Weapon, @@ -465,15 +486,20 @@ bool CharacterController::updateNpcState() mUpperBodyState = UpperCharState_StartToMinAttack; } } + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); } - else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + else { - mAnimation->disable(mCurrentWeapon); - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_UpperBody, false, - weapSpeed, mAttackType+" max attack", mAttackType+" min hit", - 1.0f-complete, 0); - mUpperBodyState = UpperCharState_MaxAttackToMinHit; + animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); + if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) + { + mAnimation->disable(mCurrentWeapon); + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, false, + weapSpeed, mAttackType+" max attack", mAttackType+" min hit", + 1.0f-complete, 0); + mUpperBodyState = UpperCharState_MaxAttackToMinHit; + } } if(!animPlaying) From a80e80dd940bdc855e0bd7a0b456af5e40c8a692 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 23 Jul 2013 21:59:02 +0200 Subject: [PATCH 26/92] added layout to table sub view --- apps/opencs/view/doc/subview.cpp | 5 ----- apps/opencs/view/world/tablesubview.cpp | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 731adabb35..153700d321 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -1,15 +1,10 @@ #include "subview.hpp" - CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view setWindowTitle (mUniversalId.toString().c_str()); - - /// \todo remove (for testing only) - setMinimumWidth (100); - setMinimumHeight (60); } CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 65cba4b02e..502dcc2928 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,6 +1,8 @@ #include "tablesubview.hpp" +#include + #include "../../model/doc/document.hpp" #include "table.hpp" @@ -9,7 +11,18 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D bool createAndDelete) : SubView (id) { - setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget ( + mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete), 2); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); } From 45302f9e25444f5a689afd915c991edf0aac9ef4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 23 Jul 2013 22:05:03 -0700 Subject: [PATCH 27/92] Handle controllers in the NPC's bound object parts --- apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/animation.hpp | 10 ++++++++++ apps/openmw/mwrender/npcanimation.cpp | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 997a59dd04..db20807e33 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -53,6 +53,7 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mNonAccumCtrl(NULL) , mAccumulate(0.0f) + , mNullAnimationValuePtr(OGRE_NEW NullAnimationValue) { for(size_t i = 0;i < sNumGroups;i++) mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index bc7fd3d22f..cbedfe5054 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -52,6 +52,15 @@ protected: virtual void setValue(Ogre::Real value); }; + class NullAnimationValue : public Ogre::ControllerValue + { + public: + virtual Ogre::Real getValue() const + { return 0.0f; } + virtual void setValue(Ogre::Real value) + { } + }; + struct AnimSource : public Ogre::AnimationAlloc { NifOgre::TextKeyMap mTextKeys; std::vector > mControllers[sNumGroups]; @@ -98,6 +107,7 @@ protected: AnimStateMap mStates; Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; + Ogre::SharedPtr mNullAnimationValuePtr; ObjectAttachMap mAttachedObjects; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index f2df5ccd5f..df95e9f472 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -424,6 +424,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) for(size_t i = 0;i < sPartListSize;i++) { + std::vector >::iterator ctrl(mObjectParts[i].mControllers.begin()); + for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) + ctrl->update(); + Ogre::Entity *ent = mObjectParts[i].mSkelBase; if(!ent) continue; updateSkeletonInstance(baseinst, ent->getSkeleton()); @@ -481,6 +485,19 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, if(type == sPartList[i].type) { mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); + + // TODO: + // type == ESM::PRT_Head should get an animation source based on the current output of + // the actor's 'say' sound (0 = silent, 1 = loud?). + // type == ESM::PRT_Weapon should get an animation source based on the current offset + // of the weapon attack animation (from its beginning, or start marker?) + std::vector >::iterator ctrl(mObjectParts[i].mControllers.begin()); + for(;ctrl != mObjectParts[i].mControllers.end();ctrl++) + { + if(ctrl->getSource().isNull()) + ctrl->setSource(mNullAnimationValuePtr); + } + break; } } From 0c8d4d9be210e1d507741e6da4aba5cf993ed9de Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 02:51:42 -0700 Subject: [PATCH 28/92] Add beginnings of melee hits --- apps/openmw/mwclass/creature.cpp | 33 +++++++++++++++ apps/openmw/mwclass/creature.hpp | 4 ++ apps/openmw/mwclass/npc.cpp | 68 ++++++++++++++++++++++++++++++ apps/openmw/mwclass/npc.hpp | 4 ++ apps/openmw/mwrender/animation.cpp | 9 ++++ apps/openmw/mwworld/class.cpp | 10 +++++ apps/openmw/mwworld/class.hpp | 13 ++++++ 7 files changed, 141 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index f5bd434b13..5a5ae6f416 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -148,6 +148,39 @@ namespace MWClass return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } + + void Creature::attack(const MWWorld::Ptr& ptr, int type) const + { + } + + void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + float diff = health - crstats.getHealth().getCurrent(); + + if(diff < 0.0f) + { + // actor is losing health. Alert the character controller, scripts, etc. + // NOTE: 'attacker' may be empty. + } + + bool wasDead = crstats.isDead(); + + MWMechanics::DynamicStat stat(crstats.getHealth()); + stat.setCurrent(health); + crstats.setHealth(stat); + + if(!wasDead && crstats.isDead()) + { + // actor was just killed + } + else if(wasDead && !crstats.isDead()) + { + // actor was just resurrected + } + } + + boost::shared_ptr Creature::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 356ecfb645..b90b07f3f5 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -42,6 +42,10 @@ namespace MWClass virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; ///< Return creature stats + virtual void attack(const MWWorld::Ptr& ptr, int type) const; + + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b6af9f0e57..0ba633df23 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -288,6 +288,74 @@ namespace MWClass return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } + + void Npc::attack(const MWWorld::Ptr& ptr, int type) const + { + // FIXME: Detect what was hit + MWWorld::Ptr victim; + if(victim.isEmpty()) // Didn't hit anything + return; + + const MWWorld::Class &othercls = MWWorld::Class::get(victim); + if(!othercls.isActor() || othercls.getCreatureStats(victim).isDead()) + { + // Can't hit non-actors, or dead actors + return; + } + + // Get the weapon used + MWWorld::LiveCellRef *weapon = NULL; + MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); + MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(iter != inv.end() && iter->getTypeName() == typeid(ESM::Weapon).name()) + weapon = iter->get(); + + // TODO: Check weapon skill against victim's armor skill (if !weapon, attacker is using + // hand-to-hand, which damages fatique unless in werewolf form). + if(weapon) + { + float health = othercls.getCreatureStats(victim).getHealth().getCurrent(); + // FIXME: Modify damage based on strength? + if(type == MWMechanics::CreatureStats::AT_Chop) + health -= weapon->mBase->mData.mChop[1]; + else if(type == MWMechanics::CreatureStats::AT_Slash) + health -= weapon->mBase->mData.mSlash[1]; + else if(type == MWMechanics::CreatureStats::AT_Thrust) + health -= weapon->mBase->mData.mThrust[1]; + + othercls.setActorHealth(victim, std::max(health, 0.0f), ptr); + } + } + + void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + float diff = health - crstats.getHealth().getCurrent(); + + if(diff < 0.0f) + { + // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying + // something, alert the character controller, scripts, etc. + // NOTE: 'attacker' may be empty. + } + + bool wasDead = crstats.isDead(); + + MWMechanics::DynamicStat stat(crstats.getHealth()); + stat.setCurrent(health); + crstats.setHealth(stat); + + if(!wasDead && crstats.isDead()) + { + // actor was just killed + } + else if(wasDead && !crstats.isDead()) + { + // actor was just resurrected + } + } + + boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const { diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 2a73df5934..15f715f14e 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -68,6 +68,10 @@ namespace MWClass virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store + virtual void attack(const MWWorld::Ptr& ptr, int type) const; + + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; ///< Generate action for activation diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index db20807e33..07c1c1fe70 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -14,6 +14,7 @@ #include "../mwbase/world.hpp" #include "../mwmechanics/character.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "../mwworld/class.hpp" namespace MWRender @@ -548,6 +549,14 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co showWeapons(true); else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); + else if(evt.compare(off, len, "chop hit") == 0) + MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Chop); + else if(evt.compare(off, len, "slash hit") == 0) + MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Slash); + else if(evt.compare(off, len, "thrust hit") == 0) + MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust); + else if(evt.compare(off, len, "hit") == 0) + MWWorld::Class::get(mPtr).attack(mPtr, -1); return true; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 6f420942b0..b64b5119a5 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -77,6 +77,16 @@ namespace MWWorld throw std::runtime_error ("class does not have item health"); } + void Class::attack(const Ptr& ptr, int type) const + { + throw std::runtime_error("class cannot attack"); + } + + void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const + { + throw std::runtime_error("class does not have actor health"); + } + boost::shared_ptr Class::activate (const Ptr& ptr, const Ptr& actor) const { return boost::shared_ptr (new NullAction); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index eed100024d..26ce65d0eb 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -105,6 +105,19 @@ namespace MWWorld ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exceoption) + virtual void attack(const Ptr& ptr, int type) const; + ///< Execute a melee hit, using the current weapon. This will check the relevant skills + /// of the given attacker, and whoever is hit. + /// \a type - type of attack, one of the MWMechanics::CreatureStats::AttackType enums. + /// ignored for creature attacks. + /// (default implementation: throw an exceoption) + + virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; + ///< Sets a new current health value for the actor, optionally specifying the object causing + /// the change. Use this instead of using CreatureStats directly as this will make sure the + /// correct dialog and actor states are properly handled when being hurt or healed. + /// (default implementation: throw an exceoption) + virtual boost::shared_ptr activate (const Ptr& ptr, const Ptr& actor) const; ///< Generate action for activation (default implementation: return a null action). From cc8e8c12720ecfd5e8e8992c1be003fae24695a7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 03:18:53 -0700 Subject: [PATCH 29/92] Use the attack strength as determined by how long the attack was held --- apps/openmw/mwclass/npc.cpp | 18 ++++++++++++------ apps/openmw/mwmechanics/character.cpp | 2 ++ apps/openmw/mwmechanics/npcstats.cpp | 11 +++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 6 +++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0ba633df23..7a033d78f5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -314,16 +314,22 @@ namespace MWClass // hand-to-hand, which damages fatique unless in werewolf form). if(weapon) { - float health = othercls.getCreatureStats(victim).getHealth().getCurrent(); - // FIXME: Modify damage based on strength? + const unsigned char *att = NULL; if(type == MWMechanics::CreatureStats::AT_Chop) - health -= weapon->mBase->mData.mChop[1]; + att = weapon->mBase->mData.mChop; else if(type == MWMechanics::CreatureStats::AT_Slash) - health -= weapon->mBase->mData.mSlash[1]; + att = weapon->mBase->mData.mSlash; else if(type == MWMechanics::CreatureStats::AT_Thrust) - health -= weapon->mBase->mData.mThrust[1]; + att = weapon->mBase->mData.mThrust; - othercls.setActorHealth(victim, std::max(health, 0.0f), ptr); + if(att) + { + float health = othercls.getCreatureStats(victim).getHealth().getCurrent(); + // FIXME: Modify damage based on strength attribute? + health -= att[0] + ((att[1]-att[0])*Npc::getNpcStats(ptr).getAttackStrength()); + + othercls.setActorHealth(victim, std::max(health, 0.0f), ptr); + } } } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b4b1143c28..404d748408 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -493,6 +493,8 @@ bool CharacterController::updateNpcState() animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) { + stats.setAttackStrength(complete); + mAnimation->disable(mCurrentWeapon); mAnimation->play(mCurrentWeapon, Priority_Weapon, MWRender::Animation::Group_UpperBody, false, diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index def91a6c54..50c1278567 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -31,6 +31,7 @@ MWMechanics::NpcStats::NpcStats() , mWerewolf (false) , mWerewolfKills (0) , mProfit(0) +, mAttackStrength(0.0f) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; i Date: Wed, 24 Jul 2013 05:39:15 -0700 Subject: [PATCH 30/92] Don't play casting animations for enchantments --- apps/openmw/mwmechanics/character.cpp | 31 +++++++++++---------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 404d748408..6f9228a0ed 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -419,34 +419,21 @@ bool CharacterController::updateNpcState() if(mWeaponType == WeapType_Spell) { const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - const ESM::EffectList *effects = NULL; const std::string spellid = crstats.getSpells().getSelectedSpell(); if(!spellid.empty()) - effects = &store.get().find(spellid)->mEffects; - else if(inv.getSelectedEnchantItem() != inv.end()) - { - MWWorld::Ptr item = *inv.getSelectedEnchantItem(); - const std::string enchid = MWWorld::Class::get(item).getEnchantment(item); - if(!enchid.empty()) - effects = &store.get().find(enchid)->mEffects; - } - - if(effects) { static const std::string schools[] = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; - const ESM::MagicEffect *effect; - effect = store.get().find(effects->mList.at(0).mEffectID); - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - if(!effect->mCastSound.empty()) - sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f); - else - sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); + const ESM::Spell *spell = store.get().find(spellid); + const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); - switch(effects->mList[0].mRange) + const ESM::MagicEffect *effect; + effect = store.get().find(effectentry.mEffectID); + + switch(effectentry.mRange) { case 0: mAttackType = "self"; break; case 1: mAttackType = "touch"; break; @@ -458,6 +445,12 @@ bool CharacterController::updateNpcState() weapSpeed, mAttackType+" start", mAttackType+" stop", 0.0f, 0); mUpperBodyState = UpperCharState_CastingSpell; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mCastSound.empty()) + sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); } } else if(mWeaponType != WeapType_PickProbe) From 59f1bc7542b45e883122aae0b3c4eea08bea72ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 08:38:36 -0700 Subject: [PATCH 31/92] Use time values for the aniamtion's start, stop, and loop points This seems to be closer to the expected behavior. This more cleanly handles 0- length animations, especially where the start marker comes after the stop marker while still being on the same time key. --- apps/openmw/mwrender/animation.cpp | 155 ++++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 19 ++-- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 07c1c1fe70..54e199059a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -444,64 +444,58 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - std::string tag = groupname+": "+start; - NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); - while(startkey != keys.end() && startkey->second != tag) + const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname); + + std::string starttag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(groupstart); + while(startkey != keys.end() && startkey->second != starttag) startkey++; if(startkey == keys.end() && start == "loop start") { - tag = groupname+": start"; - startkey = keys.begin(); - while(startkey != keys.end() && startkey->second != tag) + starttag = groupname+": start"; + startkey = groupstart; + while(startkey != keys.end() && startkey->second != starttag) startkey++; } if(startkey == keys.end()) return false; - tag = groupname+": "+stop; - NifOgre::TextKeyMap::const_iterator stopkey(startkey); - while(stopkey != keys.end() && stopkey->second != tag) + const std::string stoptag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(groupstart); + while(stopkey != keys.end() && stopkey->second != stoptag) stopkey++; if(stopkey == keys.end()) return false; - if(startkey == stopkey) + if(startkey->first > stopkey->first) return false; - state.mStartKey = startkey; - state.mLoopStartKey = startkey; - state.mStopKey = stopkey; - state.mNextKey = startkey; - - state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint); + state.mStartTime = startkey->first; + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = stopkey->first; + state.mStopTime = stopkey->first; - tag = groupname+": loop start"; - while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey) + state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); + if(state.mTime > state.mStartTime) { - if(state.mNextKey->second == tag) - state.mLoopStartKey = state.mNextKey; - state.mNextKey++; + const std::string loopstarttag = groupname+": loop start"; + const std::string loopstoptag = groupname+": loop stop"; + NifOgre::TextKeyMap::const_iterator key(groupstart); + while(key->first <= state.mTime && key != stopkey) + { + if(key->second == loopstarttag) + state.mLoopStartTime = key->first; + else if(key->second == loopstoptag) + state.mLoopStopTime = key->first; + key++; + } } return true; } -bool Animation::doLoop(AnimState &state) -{ - if(state.mLoopCount == 0) - return false; - state.mLoopCount--; - - state.mTime = state.mLoopStartKey->first; - state.mNextKey = state.mLoopStartKey; - state.mNextKey++; - state.mPlaying = true; - - return true; -} - -bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) +void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) { float time = key->first; const std::string &evt = key->second; @@ -510,7 +504,7 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); - return true; + return; } if(evt.compare(0, 10, "soundgen: ") == 0) { @@ -523,28 +517,22 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co type = MWBase::SoundManager::Play_TypeFoot; sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type); } - return true; + return; } if(evt.compare(0, groupname.size(), groupname) != 0 || evt.compare(groupname.size(), 2, ": ") != 0) { // Not ours, skip it - return true; + return; } size_t off = groupname.size()+2; size_t len = evt.size() - off; - if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) - state.mLoopStartKey = key; - else if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) - { - if(doLoop(state)) - { - if(state.mTime >= time) - return false; - } - } + if(evt.compare(off, len, "loop start") == 0) + state.mLoopStartTime = key->first; + else if(evt.compare(off, len, "loop stop") == 0) + state.mLoopStopTime = key->first; else if(evt.compare(off, len, "equip attach") == 0) showWeapons(true); else if(evt.compare(off, len, "unequip detach") == 0) @@ -557,8 +545,6 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) MWWorld::Class::get(mPtr).attack(mPtr, -1); - - return true; } @@ -596,8 +582,9 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); for(;iter != mAnimSources.rend();iter++) { + const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; AnimState state; - if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) + if(reset(state, textkeys, groupname, start, stop, startpoint)) { state.mSource = *iter; state.mSpeedMult = speedmult; @@ -608,6 +595,13 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo state.mAutoDisable = autodisable; mStates[groupname] = state; + NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey); + textkey++; + } + break; } } @@ -668,29 +662,25 @@ void Animation::resetActiveGroups() } -bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult, std::string *start, std::string *stop) const +bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const { AnimStateMap::const_iterator iter = mStates.find(groupname); if(iter == mStates.end()) { if(complete) *complete = 0.0f; if(speedmult) *speedmult = 0.0f; - if(start) *start = ""; - if(stop) *stop = ""; return false; } if(complete) { - if(iter->second.mStopKey->first > iter->second.mStartKey->first) - *complete = (iter->second.mTime - iter->second.mStartKey->first) / - (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(iter->second.mStopTime > iter->second.mStartTime) + *complete = (iter->second.mTime - iter->second.mStartTime) / + (iter->second.mStopTime - iter->second.mStartTime); else *complete = (iter->second.mPlaying ? 0.0f : 1.0f); } if(speedmult) *speedmult = iter->second.mSpeedMult; - if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); - if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); return true; } @@ -712,27 +702,52 @@ Ogre::Vector3 Animation::runAnimation(float duration) while(stateiter != mStates.end()) { AnimState &state = stateiter->second; + const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys; + NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime)); + float timepassed = duration * state.mSpeedMult; while(state.mPlaying) { float targetTime = state.mTime + timepassed; - if(state.mNextKey->first > targetTime) + if(textkey == textkeys.end() || textkey->first > targetTime) { if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) updatePosition(state.mTime, targetTime, movement); - state.mTime = targetTime; - break; + state.mTime = std::min(targetTime, state.mStopTime); + } + else + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, textkey->first, movement); + state.mTime = textkey->first; } - NifOgre::TextKeyMap::const_iterator key(state.mNextKey++); - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) - updatePosition(state.mTime, key->first, movement); - state.mTime = key->first; - - state.mPlaying = (key != state.mStopKey); + state.mPlaying = (state.mTime < state.mStopTime); timepassed = targetTime - state.mTime; - if(!handleTextKey(state, stateiter->first, key)) + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + if(state.mTime >= state.mLoopStopTime) + break; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + } + + if(timepassed <= 0.0f) break; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index cbedfe5054..0b8f3a02fb 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -69,10 +69,10 @@ protected: struct AnimState { Ogre::SharedPtr mSource; - NifOgre::TextKeyMap::const_iterator mStartKey; - NifOgre::TextKeyMap::const_iterator mLoopStartKey; - NifOgre::TextKeyMap::const_iterator mStopKey; - NifOgre::TextKeyMap::const_iterator mNextKey; + float mStartTime; + float mLoopStartTime; + float mLoopStopTime; + float mStopTime; float mTime; float mSpeedMult; @@ -84,7 +84,8 @@ protected: int mGroups; bool mAutoDisable; - AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), + AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), + mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), mPriority(0), mGroups(0), mAutoDisable(true) { } }; @@ -141,9 +142,7 @@ protected: const std::string &groupname, const std::string &start, const std::string &stop, float startpoint); - bool doLoop(AnimState &state); - - bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); + void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for @@ -209,11 +208,9 @@ public: * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. * \param speedmult Stores the animation speed multiplier - * \param start Stores the start key - * \param stop Stores the stop key * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const; + bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /** Disables the specified animation group; * \param groupname Animation group to disable. From d8ec8139399ab6677cb667892fb461c3999ddde2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 10:02:50 -0700 Subject: [PATCH 32/92] Say something from the "hit" dialog topic when losing health --- apps/openmw/mwbase/dialoguemanager.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 3 +++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 25 +++++++++++++++++++ apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 2 ++ 4 files changed, 32 insertions(+) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 12996cc309..58731d1c78 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -40,6 +40,8 @@ namespace MWBase virtual MWWorld::Ptr getActor() const = 0; ///< Return the actor the player is currently talking to. + virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0; + //calbacks for the GUI virtual void keywordSelected (const std::string& keyword) = 0; virtual void goodbyeSelected() = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7a033d78f5..8cd40ccd4f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -343,6 +344,8 @@ namespace MWClass // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. // NOTE: 'attacker' may be empty. + + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } bool wasDead = crstats.isDead(); diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 7093ff91e0..06a6e335dc 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -25,6 +25,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" @@ -557,6 +558,30 @@ namespace MWDialogue return false; } + void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!sndMgr->sayDone(actor)) + { + // Actor is already saying something. + return; + } + + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Dialogue *dial = store.get().find(topic); + + Filter filter(actor, 0, false); + const ESM::DialInfo *info = filter.search(*dial, false); + if(info != NULL) + { + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + if(winMgr->getSubtitlesEnabled()) + winMgr->messageBox(info->mResponse); + sndMgr->say(actor, info->mSound); + } + } + + std::vector ParseHyperText(const std::string& text) { std::vector result; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index de1ac77c6f..13bab4dae1 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -68,6 +68,8 @@ namespace MWDialogue virtual bool checkServiceRefused (); + virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const; + //calbacks for the GUI virtual void keywordSelected (const std::string& keyword); virtual void goodbyeSelected(); From 705498ec24cc5e91c07bdac3f616fbfda08e288b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 10:34:53 -0700 Subject: [PATCH 33/92] Play a swish sound when attacking --- apps/openmw/mwmechanics/character.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 6f9228a0ed..71be10fac0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -486,6 +486,13 @@ bool CharacterController::updateNpcState() animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack) { + if(mAttackType != "shoot") + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + // NOTE: SwishL, SwishM, SwishS - large, medium, small. + // Based on weapon weight, speed, or attack strength? + sndMgr->playSound3D(mPtr, "SwishL", 1.0f, 1.0f); + } stats.setAttackStrength(complete); mAnimation->disable(mCurrentWeapon); From d9a9c3d6bd83b75fc8419b366c69529931b10058 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 10:36:04 -0700 Subject: [PATCH 34/92] Handle the "land" soundgen type --- apps/openmw/mwclass/creature.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5a5ae6f416..30d84abc2b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -388,6 +388,8 @@ namespace MWClass return 5; if(name == "scream") return 6; + if(name == "land") + return 7; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8cd40ccd4f..229954dace 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -807,6 +807,8 @@ namespace MWClass return ""; if(name == "scream") return ""; + if(name == "land") + return ""; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } From 0db02af80731fddd2e58bd5de470110304606bd5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 21:08:16 -0700 Subject: [PATCH 35/92] Set up the weapon state in the CharacterController constructor --- apps/openmw/mwmechanics/character.cpp | 138 ++++++++++++++------------ apps/openmw/mwmechanics/character.hpp | 14 ++- 2 files changed, 86 insertions(+), 66 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 71be10fac0..f07e07482d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -232,6 +232,67 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } +MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype) +{ + if(stats.getDrawState() == DrawState_Spell) + { + *weaptype = WeapType_Spell; + return inv.end(); + } + + if(stats.getDrawState() == MWMechanics::DrawState_Weapon) + { + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon == inv.end()) + *weaptype = WeapType_HandToHand; + else + { + const std::string &type = weapon->getTypeName(); + if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) + *weaptype = WeapType_PickProbe; + else if(type == typeid(ESM::Weapon).name()) + { + MWWorld::LiveCellRef *ref = weapon->get(); + ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; + switch(type) + { + case ESM::Weapon::ShortBladeOneHand: + case ESM::Weapon::LongBladeOneHand: + case ESM::Weapon::BluntOneHand: + case ESM::Weapon::AxeOneHand: + case ESM::Weapon::Arrow: + case ESM::Weapon::Bolt: + *weaptype = WeapType_OneHand; + break; + case ESM::Weapon::LongBladeTwoHand: + case ESM::Weapon::BluntTwoClose: + case ESM::Weapon::AxeTwoHand: + *weaptype = WeapType_TwoHand; + break; + case ESM::Weapon::BluntTwoWide: + case ESM::Weapon::SpearTwoWide: + *weaptype = WeapType_TwoWide; + break; + case ESM::Weapon::MarksmanBow: + *weaptype = WeapType_BowAndArrow; + break; + case ESM::Weapon::MarksmanCrossbow: + *weaptype = WeapType_Crossbow; + break; + case ESM::Weapon::MarksmanThrown: + *weaptype = WeapType_ThowWeapon; + break; + } + } + } + + return weapon; + } + + return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); +} + + CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mAnimation(anim) @@ -244,18 +305,28 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mSkipAnim(false) , mSecondsOfRunning(0) , mSecondsOfSwimming(0) - , mUpdateWeapon(true) { if(!mAnimation) return; - if(MWWorld::Class::get(mPtr).isActor()) + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + if(cls.isActor()) { /* Accumulate along X/Y only for now, until we can figure out how we should * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); - if(!MWWorld::Class::get(mPtr).getCreatureStats(mPtr).isDead()) + if(mPtr.getTypeName() == typeid(ESM::NPC).name()) + { + getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType); + if(mWeaponType != WeapType_None) + { + getWeaponGroup(mWeaponType, mCurrentWeapon); + mUpperBodyState = UpperCharState_WeapEquiped; + } + } + + if(!cls.getCreatureStats(mPtr).isDead()) mIdleState = CharState_Idle; else { @@ -302,68 +373,9 @@ bool CharacterController::updateNpcState() NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weapon = inv.end(); - - if(stats.getDrawState() == DrawState_Spell) - weaptype = WeapType_Spell; - else if(stats.getDrawState() == MWMechanics::DrawState_Weapon) - { - weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if(weapon == inv.end()) - weaptype = WeapType_HandToHand; - else - { - const std::string &type = weapon->getTypeName(); - if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name()) - weaptype = WeapType_PickProbe; - else if(type == typeid(ESM::Weapon).name()) - { - MWWorld::LiveCellRef *ref = weapon->get(); - ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType; - switch(type) - { - case ESM::Weapon::ShortBladeOneHand: - case ESM::Weapon::LongBladeOneHand: - case ESM::Weapon::BluntOneHand: - case ESM::Weapon::AxeOneHand: - case ESM::Weapon::Arrow: - case ESM::Weapon::Bolt: - weaptype = WeapType_OneHand; - break; - case ESM::Weapon::LongBladeTwoHand: - case ESM::Weapon::BluntTwoClose: - case ESM::Weapon::AxeTwoHand: - weaptype = WeapType_TwoHand; - break; - case ESM::Weapon::BluntTwoWide: - case ESM::Weapon::SpearTwoWide: - weaptype = WeapType_TwoWide; - break; - case ESM::Weapon::MarksmanBow: - weaptype = WeapType_BowAndArrow; - break; - case ESM::Weapon::MarksmanCrossbow: - weaptype = WeapType_Crossbow; - break; - case ESM::Weapon::MarksmanThrown: - weaptype = WeapType_ThowWeapon; - break; - } - } - } - } - else - weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype); bool forcestateupdate = false; - if(mUpdateWeapon) - { - forcestateupdate = (mWeaponType != weaptype); - mWeaponType = weaptype; - getWeaponGroup(mWeaponType, mCurrentWeapon); - mUpdateWeapon = false; - } - if(weaptype != mWeaponType) { forcestateupdate = true; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 9b5178b583..3cd23b57b4 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -5,6 +5,12 @@ #include "../mwworld/ptr.hpp" +namespace MWWorld +{ + class ContainerStoreIterator; + class InventoryStore; +} + namespace MWRender { class Animation; @@ -14,6 +20,7 @@ namespace MWMechanics { class Movement; +class NpcStats; enum Priority { Priority_Default, @@ -133,9 +140,6 @@ class CharacterController bool mSkipAnim; - // Workaround for playing weapon draw animation and sound when going to new cell - bool mUpdateWeapon; - // counted for skill increase float mSecondsOfSwimming; float mSecondsOfRunning; @@ -146,6 +150,10 @@ class CharacterController static void getWeaponGroup(WeaponType weaptype, std::string &group); + static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats, + MWWorld::InventoryStore &inv, + WeaponType *weaptype); + void clearAnimQueue(); bool updateNpcState(); From 8e24cab935086024d99120b881b3513986ebd6ab Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 23:21:19 -0700 Subject: [PATCH 36/92] Strip trailing whitespace from textkeys --- components/nifogre/ogrenifloader.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index f9344caa4d..9c079bc98c 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -688,6 +688,21 @@ class NIFObjectLoader } std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); + if(nextpos != std::string::npos) + { + do { + nextpos--; + } while(nextpos > pos && ::isspace(str[nextpos])); + nextpos++; + } + else if(::isspace(*str.rbegin())) + { + std::string::const_iterator last = str.end(); + do { + last--; + } while(last != str.begin() && ::isspace(*last)); + nextpos = std::distance(str.begin(), ++last); + } std::string result = str.substr(pos, nextpos-pos); textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); From fb0ee7f2fc01b4e5720e4f8420175004a79426be Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 23:58:35 -0700 Subject: [PATCH 37/92] Improve constness of the Nif RecordPtr structs --- components/nif/recordptr.hpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/components/nif/recordptr.hpp b/components/nif/recordptr.hpp index 2ecde7f60e..1a4fc235bc 100644 --- a/components/nif/recordptr.hpp +++ b/components/nif/recordptr.hpp @@ -71,16 +71,26 @@ public: } /// Look up the actual object from the index - X* getPtr() const + const X* getPtr() const { assert(ptr != NULL); return ptr; } - X& get() const + X* getPtr() + { + assert(ptr != NULL); + return ptr; + } + + const X& get() const + { return *getPtr(); } + X& get() { return *getPtr(); } /// Syntactic sugar - X* operator->() const + const X* operator->() const + { return getPtr(); } + X* operator->() { return getPtr(); } /// Pointers are allowed to be empty @@ -116,6 +126,8 @@ public: const Ptr& operator[](size_t index) const { return list.at(index); } + Ptr& operator[](size_t index) + { return list.at(index); } size_t length() const { return list.size(); } From 92cc566fdc16c344564884f0eaf1666b1ca76762 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Jul 2013 00:30:01 -0700 Subject: [PATCH 38/92] Rename the Class::attack method to Class::hit --- apps/openmw/mwclass/creature.cpp | 2 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwrender/animation.cpp | 8 ++++---- apps/openmw/mwworld/class.cpp | 4 ++-- apps/openmw/mwworld/class.hpp | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 30d84abc2b..003eb1d148 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -149,7 +149,7 @@ namespace MWClass } - void Creature::attack(const MWWorld::Ptr& ptr, int type) const + void Creature::hit(const MWWorld::Ptr& ptr, int type) const { } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index b90b07f3f5..a15128ac72 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -42,7 +42,7 @@ namespace MWClass virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; ///< Return creature stats - virtual void attack(const MWWorld::Ptr& ptr, int type) const; + virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 229954dace..b19544f868 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -290,7 +290,7 @@ namespace MWClass } - void Npc::attack(const MWWorld::Ptr& ptr, int type) const + void Npc::hit(const MWWorld::Ptr& ptr, int type) const { // FIXME: Detect what was hit MWWorld::Ptr victim; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 15f715f14e..05c4b1e8f0 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -68,7 +68,7 @@ namespace MWClass virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; ///< Return inventory store - virtual void attack(const MWWorld::Ptr& ptr, int type) const; + virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 54e199059a..8d6a47e5ec 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -538,13 +538,13 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) - MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Chop); + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) - MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Slash); + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) - MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust); + MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) - MWWorld::Class::get(mPtr).attack(mPtr, -1); + MWWorld::Class::get(mPtr).hit(mPtr); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index b64b5119a5..c30fb51c17 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -77,9 +77,9 @@ namespace MWWorld throw std::runtime_error ("class does not have item health"); } - void Class::attack(const Ptr& ptr, int type) const + void Class::hit(const Ptr& ptr, int type) const { - throw std::runtime_error("class cannot attack"); + throw std::runtime_error("class cannot hit"); } void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 26ce65d0eb..2f5ac3ec84 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -105,11 +105,11 @@ namespace MWWorld ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exceoption) - virtual void attack(const Ptr& ptr, int type) const; + virtual void hit(const Ptr& ptr, int type=-1) const; ///< Execute a melee hit, using the current weapon. This will check the relevant skills /// of the given attacker, and whoever is hit. - /// \a type - type of attack, one of the MWMechanics::CreatureStats::AttackType enums. - /// ignored for creature attacks. + /// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType + /// enums. ignored for creature attacks. /// (default implementation: throw an exceoption) virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; From 4ad28ed36913b9073a86c6d86dfab45f1a634723 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Jul 2013 00:46:20 -0700 Subject: [PATCH 39/92] Don't play a weapon-down sound when unreadying a spell --- apps/openmw/mwmechanics/character.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f07e07482d..5bfdcf0a71 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -399,12 +399,9 @@ bool CharacterController::updateNpcState() mUpperBodyState = UpperCharState_EquipingWeap; } - mWeaponType = weaptype; - getWeaponGroup(mWeaponType, mCurrentWeapon); - - if(weapon != inv.end()) + if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell)) { - std::string soundid = (mWeaponType == WeapType_None) ? + std::string soundid = (weaptype == WeapType_None) ? MWWorld::Class::get(*weapon).getDownSoundId(*weapon) : MWWorld::Class::get(*weapon).getUpSoundId(*weapon); if(!soundid.empty()) @@ -413,6 +410,9 @@ bool CharacterController::updateNpcState() sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f); } } + + mWeaponType = weaptype; + getWeaponGroup(mWeaponType, mCurrentWeapon); } From 55e7e71c1176c090f51abc633d070240f1ccc3bb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 25 Jul 2013 14:29:56 +0200 Subject: [PATCH 40/92] added optional per-subview status bar --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/doc/subview.cpp | 2 + apps/opencs/view/doc/subview.hpp | 3 + apps/opencs/view/doc/view.cpp | 17 ++++ apps/opencs/view/doc/view.hpp | 3 + apps/opencs/view/world/table.cpp | 48 ++++++++++ apps/opencs/view/world/table.hpp | 13 +++ apps/opencs/view/world/tablebottombox.cpp | 111 ++++++++++++++++++++++ apps/opencs/view/world/tablebottombox.hpp | 39 ++++++++ apps/opencs/view/world/tablesubview.cpp | 16 ++++ apps/opencs/view/world/tablesubview.hpp | 8 +- 11 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 apps/opencs/view/world/tablebottombox.cpp create mode 100644 apps/opencs/view/world/tablebottombox.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 66c446baef..d2568ce022 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -57,7 +57,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview scriptsubview util regionmapsubview + table tablesubview scriptsubview util regionmapsubview tablebottombox ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/doc/subview.cpp b/apps/opencs/view/doc/subview.cpp index 153700d321..09361a1c03 100644 --- a/apps/opencs/view/doc/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -15,3 +15,5 @@ CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QString &settingValue) { } + +void CSVDoc::SubView::setStatusBar (bool show) {} \ No newline at end of file diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 280a2a4879..aa073f81dd 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -37,6 +37,9 @@ namespace CSVDoc virtual void setEditLock (bool locked) = 0; virtual void updateEditorSetting (const QString &, const QString &); + virtual void setStatusBar (bool show); + ///< Default implementation: ignored + signals: void focusId (const CSMWorld::UniversalId& universalId); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 63fab56dbf..b3374c4a50 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -78,6 +78,11 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); + + mShowStatusBar = new QAction (tr ("Show Status Bar"), this); + mShowStatusBar->setCheckable (true); + connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); + view->addAction (mShowStatusBar); } void CSVDoc::View::setupWorldMenu() @@ -282,6 +287,9 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); + + view->setStatusBar (mShowStatusBar->isChecked()); + mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, @@ -436,3 +444,12 @@ void CSVDoc::View::updateEditorSetting (const QString &settingName, const QStrin else if (settingName == "Height") resizeViewHeight (settingValue.toInt()); } + +void CSVDoc::View::toggleShowStatusBar (bool show) +{ + foreach (QObject *view, mSubViewWindow.children()) + { + if (CSVDoc::SubView *subView = dynamic_cast (view)) + subView->setStatusBar (show); + } +} diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 4e6b216a1d..c0163ce615 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -38,6 +38,7 @@ namespace CSVDoc QAction *mRedo; QAction *mSave; QAction *mVerify; + QAction *mShowStatusBar; std::vector mEditingActions; Operations *mOperations; SubViewFactoryManager mSubViewFactory; @@ -158,6 +159,8 @@ namespace CSVDoc void addRegionMapSubView(); void showUserSettings(); + + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 90ce42b345..9085592df6 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -44,6 +44,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) std::vector CSVWorld::Table::listRevertableSelectedIds() const { + /// \todo columns filtering fixes QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector revertableIds; @@ -64,6 +65,7 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const std::vector CSVWorld::Table::listDeletableSelectedIds() const { + /// \todo columns filtering fixes QModelIndexList selectedRows = selectionModel()->selectedRows(); std::vector deletableIds; @@ -137,6 +139,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mDeleteAction = new QAction (tr ("Delete Record"), this); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); addAction (mDeleteAction); + + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (tableSizeUpdate())); + + /// \note This signal could instead be connected to a slot that filters out changes not affecting + /// the records status column (for permanence reasons) + connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (tableSizeUpdate())); + + connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), + this, SLOT (selectionSizeUpdate ())); } void CSVWorld::Table::setEditLock (bool locked) @@ -231,3 +244,38 @@ void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QSt updateEditorSetting (settingName, settingValue)) emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); } + +void CSVWorld::Table::tableSizeUpdate() +{ + int size = 0; + int deleted = 0; + int modified = 0; + + if (mModel->columnCount()>0) + { + int rows = mModel->rowCount(); + + for (int i=0; imapToSource (mProxyModel->index (i, 0)); + + /// \todo Do not use hardcoded column numbers + int state = mModel->data (mModel->index (index.row(), 1)).toInt(); + + switch (state) + { + case CSMWorld::RecordBase::State_BaseOnly: ++size; break; + case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break; + case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break; + case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break; + } + } + } + + tableSizeChanged (size, deleted, modified); +} + +void CSVWorld::Table::selectionSizeUpdate() +{ + selectionSizeChanged (selectionModel()->selectedRows().size()); +} \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index eedfeb8a1d..dd8e82ed71 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -60,6 +60,13 @@ namespace CSVWorld void editRequest (int row); + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + private slots: void createRecord(); @@ -69,6 +76,12 @@ namespace CSVWorld void deleteRecord(); void editRecord(); + + public slots: + + void tableSizeUpdate(); + + void selectionSizeUpdate(); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp new file mode 100644 index 0000000000..c8ddb662c9 --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -0,0 +1,111 @@ + +#include "tablebottombox.hpp" + +#include + +#include +#include +#include + +void CSVWorld::TableBottomBox::updateStatus() +{ + if (mShowStatusBar) + { + static const char *sLabels[4] = { "record", "deleted", "touched", "selected" }; + static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" }; + + std::ostringstream stream; + + bool first = true; + + for (int i=0; i<4; ++i) + { + if (mStatusCount[i]>0) + { + if (first) + first = false; + else + stream << ", "; + + stream + << mStatusCount[i] << ' ' + << (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]); + } + } + + mStatus->setText (QString::fromUtf8 (stream.str().c_str())); + } +} + +CSVWorld::TableBottomBox::TableBottomBox (QWidget *parent) +: QWidget (parent), mShowStatusBar (false) +{ + for (int i=0; i<4; ++i) + mStatusCount[i] = 0; + + setVisible (false); + + QStackedLayout *layout = new QStackedLayout; + + mStatus = new QLabel; + + QStatusBar *statusBar = new QStatusBar; + + statusBar->addWidget (mStatus); + + layout->addWidget (statusBar); + + QStatusBar *statusBar2 = new QStatusBar; + + layout->addWidget (statusBar2); + + setLayout (layout); +} + +void CSVWorld::TableBottomBox::setStatusBar (bool show) +{ + if (show!=mShowStatusBar) + { + setVisible (show); + + mShowStatusBar = show; + + if (show) + updateStatus(); + } +} + +void CSVWorld::TableBottomBox::selectionSizeChanged (int size) +{ + if (mStatusCount[3]!=size) + { + mStatusCount[3] = size; + updateStatus(); + } +} + +void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified) +{ + bool changed = false; + + if (mStatusCount[0]!=size) + { + mStatusCount[0] = size; + changed = true; + } + + if (mStatusCount[1]!=deleted) + { + mStatusCount[1] = deleted; + changed = true; + } + + if (mStatusCount[2]!=modified) + { + mStatusCount[2] = modified; + changed = true; + } + + if (changed) + updateStatus(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp new file mode 100644 index 0000000000..2663da8555 --- /dev/null +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -0,0 +1,39 @@ +#ifndef CSV_WORLD_BOTTOMBOX_H +#define CSV_WORLD_BOTTOMBOX_H + +#include + +class QLabel; + +namespace CSVWorld +{ + class TableBottomBox : public QWidget + { + Q_OBJECT + + bool mShowStatusBar; + QLabel *mStatus; + int mStatusCount[4]; + + private: + + void updateStatus(); + + public: + + TableBottomBox (QWidget *parent = 0); + + void setStatusBar (bool show); + + public slots: + + void selectionSizeChanged (int size); + + void tableSizeChanged (int size, int deleted, int modified); + ///< \param size Number of not deleted records + /// \param deleted Number of deleted records + /// \param modified Number of added and modified records + }; +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 502dcc2928..7f9c671c09 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -6,6 +6,7 @@ #include "../../model/doc/document.hpp" #include "table.hpp" +#include "tablebottombox.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) @@ -18,6 +19,8 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->addWidget ( mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete), 2); + layout->addWidget (mBottom = new TableBottomBox (this), 0); + QWidget *widget = new QWidget; widget->setLayout (layout); @@ -25,6 +28,14 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D setWidget (widget); connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int))); + + connect (mTable, SIGNAL (selectionSizeChanged (int)), + mBottom, SLOT (selectionSizeChanged (int))); + connect (mTable, SIGNAL (tableSizeChanged (int, int, int)), + mBottom, SLOT (tableSizeChanged (int, int, int))); + + mTable->tableSizeUpdate(); + mTable->selectionSizeUpdate(); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -41,3 +52,8 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con { mTable->updateEditorSetting(settingName, settingValue); } + +void CSVWorld::TableSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index ccee50dbc4..79450eccec 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -13,18 +13,24 @@ namespace CSMDoc namespace CSVWorld { class Table; + class TableBottomBox; class TableSubView : public CSVDoc::SubView { Q_OBJECT Table *mTable; + TableBottomBox *mBottom; public: TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + virtual void setEditLock (bool locked); - void updateEditorSetting (const QString &, const QString &); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); private slots: From 4327b81bc3a4decb476dca3a2ec46e9dc63c51bc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 25 Jul 2013 14:38:53 +0200 Subject: [PATCH 41/92] fixed column handling in table (had problems with filtered out columns before) --- apps/opencs/view/world/table.cpp | 56 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 9085592df6..d8a840be4d 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -44,20 +44,30 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) std::vector CSVWorld::Table::listRevertableSelectedIds() const { - /// \todo columns filtering fixes - QModelIndexList selectedRows = selectionModel()->selectedRows(); - + /// \todo Do not use hardcoded column numbers std::vector revertableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_BaseOnly) - revertableIds.push_back (id); + if (state!=CSMWorld::RecordBase::State_BaseOnly) + { + std::string id = mModel->data (mModel->index (index.row(), 0)). + toString().toUtf8().constData(); + + revertableIds.push_back (id); + } + } } return revertableIds; @@ -65,20 +75,30 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const std::vector CSVWorld::Table::listDeletableSelectedIds() const { - /// \todo columns filtering fixes - QModelIndexList selectedRows = selectionModel()->selectedRows(); - + /// \todo Do not use hardcoded column numbers std::vector deletableIds; - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + if (mProxyModel->columnCount()>0) { - std::string id = mProxyModel->data (*iter).toString().toStdString(); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + CSMWorld::RecordBase::State state = + static_cast ( + mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) - deletableIds.push_back (id); + if (state!=CSMWorld::RecordBase::State_Deleted) + { + std::string id = mModel->data (mModel->index (index.row(), 0)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); + } + } } return deletableIds; From b3a057d679a01d2189108f527fa8e9e67405cc2b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Jul 2013 08:15:42 -0700 Subject: [PATCH 42/92] Handle hit chance and damage calculation Math is based on what the UESP describes, with some tweaks (using fatigue term, and the fCombatCriticalStrikeMult GMST): http://www.uesp.net/wiki/Morrowind:Combat --- apps/openmw/mwclass/npc.cpp | 69 +++++++++++++++++++++++++---------- apps/openmw/mwworld/class.cpp | 14 +++++++ apps/openmw/mwworld/class.hpp | 3 ++ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e2fdb037bb..5cc09624b5 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -14,6 +14,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" @@ -304,6 +305,12 @@ namespace MWClass void Npc::hit(const MWWorld::Ptr& ptr, int type) const { + // Get the weapon used (if hand-to-hand, weapon = inv.end()) + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + if(weapon != inv.end() && weapon->getTypeName() != typeid(ESM::Weapon).name()) + weapon = inv.end(); + // FIXME: Detect what was hit MWWorld::Ptr victim; if(victim.isEmpty()) // Didn't hit anything @@ -316,34 +323,58 @@ namespace MWClass return; } - // Get the weapon used - MWWorld::LiveCellRef *weapon = NULL; - MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr); - MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if(iter != inv.end() && iter->getTypeName() == typeid(ESM::Weapon).name()) - weapon = iter->get(); + int weapskill = ESM::Skill::HandToHand; + if(weapon != inv.end()) + weapskill = MWWorld::Class::get(*weapon).getEquipmentSkill(*weapon); + + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + MWMechanics::NpcStats &npcstats = getNpcStats(ptr); + const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects(); + float hitchance = npcstats.getSkill(weapskill).getModified() + + (crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + hitchance *= crstats.getFatigueTerm(); + hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude - + mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; + hitchance -= othercls.getEvasion(victim); + + if((::rand()/(RAND_MAX+1.0)) > hitchance) + { + // Missed + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(victim, "miss", 1.0f, 1.0f); + return; + } - // TODO: Check weapon skill against victim's armor skill (if !weapon, attacker is using - // hand-to-hand, which damages fatique unless in werewolf form). - if(weapon) + if(weapon != inv.end()) { - const unsigned char *att = NULL; + const unsigned char *attack = NULL; if(type == MWMechanics::CreatureStats::AT_Chop) - att = weapon->mBase->mData.mChop; + attack = weapon->get()->mBase->mData.mChop; else if(type == MWMechanics::CreatureStats::AT_Slash) - att = weapon->mBase->mData.mSlash; + attack = weapon->get()->mBase->mData.mSlash; else if(type == MWMechanics::CreatureStats::AT_Thrust) - att = weapon->mBase->mData.mThrust; - - if(att) + attack = weapon->get()->mBase->mData.mThrust; + if(attack) { - float health = othercls.getCreatureStats(victim).getHealth().getCurrent(); - // FIXME: Modify damage based on strength attribute? - health -= att[0] + ((att[1]-att[0])*Npc::getNpcStats(ptr).getAttackStrength()); + float damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength()); + damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); + //damage *= weapon_current_health / weapon_max_health; + if(!othercls.hasDetected(victim, ptr)) + { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + } + damage /= std::min(1.0f + othercls.getArmorRating(victim) / std::max(1.0f, damage), 4.0f); - othercls.setActorHealth(victim, std::max(health, 0.0f), ptr); + float health = othercls.getCreatureStats(victim).getHealth().getCurrent() - damage; + othercls.setActorHealth(victim, health, ptr); } } + + skillUsageSucceeded(ptr, weapskill, 0); } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c30fb51c17..8a2b87b803 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -13,6 +13,8 @@ #include "containerstore.hpp" #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/magiceffects.hpp" namespace MWWorld { @@ -77,6 +79,18 @@ namespace MWWorld throw std::runtime_error ("class does not have item health"); } + float Class::getEvasion(const Ptr& ptr) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); + const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects(); + float evasion = (crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + evasion *= crstats.getFatigueTerm(); + evasion += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude; + + return evasion; + } + void Class::hit(const Ptr& ptr, int type) const { throw std::runtime_error("class cannot hit"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 2f5ac3ec84..89d914746b 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -105,6 +105,9 @@ namespace MWWorld ///< Return item max health or throw an exception, if class does not have item health /// (default implementation: throw an exceoption) + virtual float getEvasion(const Ptr& ptr) const; + ///< Gets the chance the given object can evade an attack + virtual void hit(const Ptr& ptr, int type=-1) const; ///< Execute a melee hit, using the current weapon. This will check the relevant skills /// of the given attacker, and whoever is hit. From 6195062d72f210829477891282001bf47a3d4822 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Jul 2013 12:58:43 -0700 Subject: [PATCH 43/92] Try to figure out what gets hit This isn't the greatest. The distance multiplier is a guess, and a sphere cast may be better. --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwclass/npc.cpp | 11 +++++++---- apps/openmw/mwworld/physicssystem.cpp | 13 +++++++++++++ apps/openmw/mwworld/physicssystem.hpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 23 +++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 4 ++++ 6 files changed, 54 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a67ac666d2..b9033daac4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -218,6 +218,10 @@ namespace MWBase virtual MWWorld::Ptr getFacedObject() = 0; ///< Return pointer to the object the player is looking at, if it is within activation range + /// Returns a pointer to the object the provided object is facing (if within the + /// specified distance). This will attempt to use the "Bip01 Head" node as a basis. + virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance) = 0; + virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; ///< Adjust position after load to be on ground. Must be called after model load. diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5cc09624b5..de239d6346 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -305,14 +305,19 @@ namespace MWClass void Npc::hit(const MWWorld::Ptr& ptr, int type) const { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); + // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); if(weapon != inv.end() && weapon->getTypeName() != typeid(ESM::Weapon).name()) weapon = inv.end(); - // FIXME: Detect what was hit - MWWorld::Ptr victim; + float dist = 100.0f * ((weapon != inv.end()) ? + weapon->get()->mBase->mData.mReach : + gmst.find("fHandToHandReach")->getFloat()); + MWWorld::Ptr victim = world->getFacedObject(ptr, dist); if(victim.isEmpty()) // Didn't hit anything return; @@ -362,8 +367,6 @@ namespace MWClass //damage *= weapon_current_health / weapon_max_health; if(!othercls.hasDetected(victim, ptr)) { - const MWBase::World *world = MWBase::Environment::get().getWorld(); - const MWWorld::Store &gmst = world->getStore().get(); damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 3881523769..9cf944656c 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -304,6 +304,19 @@ namespace MWWorld return results; } + std::pair PhysicsSystem::getFacedHandle(const Ogre::Vector3 &origin_, const Ogre::Quaternion &orient_, float queryDistance) + { + Ogre::Vector3 dest_ = origin_ + orient_.yAxis()*queryDistance; + + btVector3 origin(origin_.x, origin_.y, origin_.z); + btVector3 dest(dest_.x, dest_.y, dest_.z); + + std::pair result = mEngine->rayTest(origin, dest); + result.second *= queryDistance; + return result; + } + + void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) { // TODO: store and use diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index d7e8532601..2b8cb00afc 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -55,6 +55,9 @@ namespace MWWorld Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr); std::pair getFacedHandle (MWWorld::World& world, float queryDistance); + std::pair getFacedHandle(const Ogre::Vector3 &origin, + const Ogre::Quaternion &orientation, + float queryDistance); std::vector < std::pair > getFacedHandles (float queryDistance); std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index d839a051a1..cf651a2e71 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -18,6 +18,7 @@ #include "../mwmechanics/movement.hpp" #include "../mwrender/sky.hpp" +#include "../mwrender/animation.hpp" #include "../mwclass/door.hpp" @@ -776,6 +777,28 @@ namespace MWWorld return object; } + MWWorld::Ptr World::getFacedObject(const MWWorld::Ptr &ptr, float distance) + { + const ESM::Position &posdata = ptr.getRefData().getPosition(); + Ogre::Vector3 pos(posdata.pos); + Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * + Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X); + + MWRender::Animation *anim = mRendering->getAnimation(ptr); + if(anim != NULL) + { + Ogre::Node *node = anim->getNode("Head"); + if(node != NULL) + pos += node->_getDerivedPosition(); + } + + std::pair result = mPhysics->getFacedHandle(pos, rot, distance); + if(result.first.empty()) + return MWWorld::Ptr(); + + return searchPtrViaHandle(result.first); + } + void World::deleteObject (const Ptr& ptr) { if (ptr.getRefData().getCount()>0) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1b436373ba..45cb1f61e8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -252,6 +252,10 @@ namespace MWWorld virtual MWWorld::Ptr getFacedObject(); ///< Return pointer to the object the player is looking at, if it is within activation range + /// Returns a pointer to the object the provided object is facing (if within the + /// specified distance). This will attempt to use the "Bip01 Head" node as a basis. + virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance); + virtual void deleteObject (const Ptr& ptr); virtual void moveObject (const Ptr& ptr, float x, float y, float z); From a569ae367ec659a7abbb78e49b80ca8ce9dbd98d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Jul 2013 13:09:26 -0700 Subject: [PATCH 44/92] Fix hit chance calculation --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index de239d6346..e5fede1a61 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -343,7 +343,7 @@ namespace MWClass mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude; hitchance -= othercls.getEvasion(victim); - if((::rand()/(RAND_MAX+1.0)) > hitchance) + if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { // Missed MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); From e7459a04b1d7b6061ab1f05318b14aa0f84ef8ad Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 24 Jul 2013 02:16:23 +0200 Subject: [PATCH 45/92] Fix always false comparison SDL_GetWindowWMInfo result of type SDL_bool (SDL_TRUE = 1 or SDL_FALSE = 0) should not be compared to -1. --- libs/openengine/ogre/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index dbfdfaaa56..de090a0f1d 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -293,7 +293,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& struct SDL_SysWMinfo wmInfo; SDL_VERSION(&wmInfo.version); - if(-1 == SDL_GetWindowWMInfo(mSDLWindow, &wmInfo)) + if(SDL_FALSE == SDL_GetWindowWMInfo(mSDLWindow, &wmInfo)) throw std::runtime_error("Couldn't get WM Info!"); Ogre::String winHandle; From aa14656ff239c95f17edd9b2b5d6618123e3bef0 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 24 Jul 2013 03:55:58 +0200 Subject: [PATCH 46/92] esmtool: fix swapped comparison operators --- apps/esmtool/record.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 8e2de6494e..68e0dcc09d 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -106,10 +106,10 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss) { case '0': oper_str = "=="; break; case '1': oper_str = "!="; break; - case '2': oper_str = "< "; break; - case '3': oper_str = "<="; break; - case '4': oper_str = "> "; break; - case '5': oper_str = ">="; break; + case '2': oper_str = "> "; break; + case '3': oper_str = ">="; break; + case '4': oper_str = "< "; break; + case '5': oper_str = "<="; break; } std::ostringstream stream; From ba5ca5beed93cad436f73f4bdad6a631619417bc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 12:34:30 +0200 Subject: [PATCH 47/92] replaced createAndDelete flag with a new class hierarhy (Creator) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/doc/subviewfactoryimp.hpp | 22 +++----- apps/opencs/view/world/creator.cpp | 12 +++++ apps/opencs/view/world/creator.hpp | 58 ++++++++++++++++++++++ apps/opencs/view/world/genericcreator.cpp | 2 + apps/opencs/view/world/genericcreator.hpp | 14 ++++++ apps/opencs/view/world/subviews.cpp | 11 ++-- apps/opencs/view/world/tablesubview.cpp | 6 ++- apps/opencs/view/world/tablesubview.hpp | 5 +- 9 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 apps/opencs/view/world/creator.cpp create mode 100644 apps/opencs/view/world/creator.hpp create mode 100644 apps/opencs/view/world/genericcreator.cpp create mode 100644 apps/opencs/view/world/genericcreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d2568ce022..6102671ff7 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -63,7 +63,7 @@ opencs_units (view/world opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate - scripthighlighter + scripthighlighter creator genericcreator ) diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index d16e0b2b7b..1278c8b275 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -22,28 +22,20 @@ namespace CSVDoc return new SubViewT (id, document); } - template - class SubViewFactoryWithCreateFlag : public SubViewFactoryBase - { - bool mCreateAndDelete; + template + class SubViewFactoryWithCreator : public SubViewFactoryBase + { public: - SubViewFactoryWithCreateFlag (bool createAndDelete); - virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; - template - SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) - : mCreateAndDelete (createAndDelete) - {} - - template - CSVDoc::SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document) + template + CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( + const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, mCreateAndDelete); + return new SubViewT (id, document, CreatorFactoryT().makeCreator()); } } diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp new file mode 100644 index 0000000000..7d57c2ec73 --- /dev/null +++ b/apps/opencs/view/world/creator.cpp @@ -0,0 +1,12 @@ + +#include "creator.hpp" + +CSVWorld::Creator:: ~Creator() {} + +CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} + + +CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator() const +{ + return 0; +} \ No newline at end of file diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp new file mode 100644 index 0000000000..b9dc5629f4 --- /dev/null +++ b/apps/opencs/view/world/creator.hpp @@ -0,0 +1,58 @@ +#ifndef CSV_WORLD_CREATOR_H +#define CSV_WORLD_CREATOR_H + +namespace CSVWorld +{ + /// \brief Record creator UI base class + class Creator + { + public: + + virtual ~Creator(); + }; + + /// \brief Base class for Creator factory + class CreatorFactoryBase + { + public: + + virtual ~CreatorFactoryBase(); + + virtual Creator *makeCreator() const = 0; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + /// \brief Creator factory that does not produces any creator + class NullCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator() const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function always returns 0. + }; + + template + class CreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator() const; + ///< The ownership of the returned Creator is transferred to the caller. + /// + /// \note The function can return a 0-pointer, which means no UI for creating/deleting + /// records should be provided. + }; + + template + Creator *CreatorFactory::makeCreator() const + { + return new CreatorT; + } +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp new file mode 100644 index 0000000000..4685df339c --- /dev/null +++ b/apps/opencs/view/world/genericcreator.cpp @@ -0,0 +1,2 @@ + +#include "genericcreator.hpp" \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp new file mode 100644 index 0000000000..28024041ff --- /dev/null +++ b/apps/opencs/view/world/genericcreator.hpp @@ -0,0 +1,14 @@ +#ifndef CSV_WORLD_GENERICCREATOR_H +#define CSV_WORLD_GENERICCREATOR_H + +#include "creator.hpp" + +namespace CSVWorld +{ + class GenericCreator : public Creator + { + public: + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 1c06ea2f60..3ad4b36c6d 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -7,14 +7,15 @@ #include "dialoguesubview.hpp" #include "scriptsubview.hpp" #include "regionmapsubview.hpp" +#include "genericcreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { manager.add (CSMWorld::UniversalId::Type_Gmsts, - new CSVDoc::SubViewFactoryWithCreateFlag (false)); + new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Skills, - new CSVDoc::SubViewFactoryWithCreateFlag (false)); + new CSVDoc::SubViewFactoryWithCreator); static const CSMWorld::UniversalId::Type sTableTypes[] = { @@ -35,12 +36,10 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) }; for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) - manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag (true)); + manager.add (sTableTypes[i], + new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); - -// manager.add (CSMWorld::UniversalId::Type_Global, -// new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 7f9c671c09..03f0822d12 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -7,11 +7,15 @@ #include "table.hpp" #include "tablebottombox.hpp" +#include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - bool createAndDelete) + Creator *creator) : SubView (id) { + bool createAndDelete = creator!=0; + delete creator; + QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins (QMargins (0, 0, 0, 0)); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 79450eccec..1148be9e03 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -14,6 +14,7 @@ namespace CSVWorld { class Table; class TableBottomBox; + class Creator; class TableSubView : public CSVDoc::SubView { @@ -24,7 +25,9 @@ namespace CSVWorld public: - TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + Creator *creator = 0); + ///< The ownership of \a creator is transferred to this. virtual void setEditLock (bool locked); From 6c4bdc010181f832935572ebca8b8abf3c9b518b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 12:42:15 +0200 Subject: [PATCH 48/92] minor cleanup; less pointer usage --- apps/opencs/view/doc/subviewfactoryimp.hpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 8 +++++--- apps/opencs/view/world/tablesubview.hpp | 5 ++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp index 1278c8b275..2c4158f854 100644 --- a/apps/opencs/view/doc/subviewfactoryimp.hpp +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -35,7 +35,7 @@ namespace CSVDoc CSVDoc::SubView *SubViewFactoryWithCreator::makeSubView ( const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, document, CreatorFactoryT().makeCreator()); + return new SubViewT (id, document, CreatorFactoryT()); } } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 03f0822d12..f236a079af 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -10,16 +10,18 @@ #include "creator.hpp" CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - Creator *creator) + const CreatorFactoryBase& creatorFactory) : SubView (id) { - bool createAndDelete = creator!=0; - delete creator; QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins (QMargins (0, 0, 0, 0)); + Creator *creator = creatorFactory.makeCreator(); + bool createAndDelete = creator!=0; + delete creator; + layout->addWidget ( mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete), 2); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 1148be9e03..d61c789356 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -14,7 +14,7 @@ namespace CSVWorld { class Table; class TableBottomBox; - class Creator; + class CreatorFactoryBase; class TableSubView : public CSVDoc::SubView { @@ -26,8 +26,7 @@ namespace CSVWorld public: TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, - Creator *creator = 0); - ///< The ownership of \a creator is transferred to this. + const CreatorFactoryBase& creatorFactory); virtual void setEditLock (bool locked); From ed83e2e70a794157254c0cbdbe05411562f45ca7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 12:51:45 +0200 Subject: [PATCH 49/92] added creator to bottom box; more cleanup --- apps/opencs/view/world/tablebottombox.cpp | 16 +++++++++++++++- apps/opencs/view/world/tablebottombox.hpp | 17 ++++++++++++++++- apps/opencs/view/world/tablesubview.cpp | 10 +++------- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index c8ddb662c9..dbd518c60d 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -7,6 +7,8 @@ #include #include +#include "creator.hpp" + void CSVWorld::TableBottomBox::updateStatus() { if (mShowStatusBar) @@ -37,7 +39,7 @@ void CSVWorld::TableBottomBox::updateStatus() } } -CSVWorld::TableBottomBox::TableBottomBox (QWidget *parent) +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, QWidget *parent) : QWidget (parent), mShowStatusBar (false) { for (int i=0; i<4; ++i) @@ -60,6 +62,13 @@ CSVWorld::TableBottomBox::TableBottomBox (QWidget *parent) layout->addWidget (statusBar2); setLayout (layout); + + mCreator = creatorFactory.makeCreator(); +} + +CSVWorld::TableBottomBox::~TableBottomBox() +{ + delete mCreator; } void CSVWorld::TableBottomBox::setStatusBar (bool show) @@ -75,6 +84,11 @@ void CSVWorld::TableBottomBox::setStatusBar (bool show) } } +bool CSVWorld::TableBottomBox::canCreateAndDelete() const +{ + return mCreator; +} + void CSVWorld::TableBottomBox::selectionSizeChanged (int size) { if (mStatusCount[3]!=size) diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 2663da8555..0f25f787b2 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -7,6 +7,9 @@ class QLabel; namespace CSVWorld { + class CreatorFactoryBase; + class Creator; + class TableBottomBox : public QWidget { Q_OBJECT @@ -14,17 +17,29 @@ namespace CSVWorld bool mShowStatusBar; QLabel *mStatus; int mStatusCount[4]; + Creator *mCreator; private: + // not implemented + TableBottomBox (const TableBottomBox&); + TableBottomBox& operator= (const TableBottomBox&); + void updateStatus(); public: - TableBottomBox (QWidget *parent = 0); + TableBottomBox (const CreatorFactoryBase& creatorFactory, QWidget *parent = 0); + + virtual ~TableBottomBox(); void setStatusBar (bool show); + bool canCreateAndDelete() const; + ///< Is record creation and deletion supported? + /// + /// \note The BotomBox does not partake in the deletion of records. + public slots: void selectionSizeChanged (int size); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index f236a079af..2173c0a483 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -18,14 +18,10 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->setContentsMargins (QMargins (0, 0, 0, 0)); - Creator *creator = creatorFactory.makeCreator(); - bool createAndDelete = creator!=0; - delete creator; + layout->addWidget (mBottom = new TableBottomBox (creatorFactory, this), 0); - layout->addWidget ( - mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete), 2); - - layout->addWidget (mBottom = new TableBottomBox (this), 0); + layout->insertWidget (0, mTable = + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); QWidget *widget = new QWidget; From d899cbb4495a988260a1821d3fe80c91e9231a41 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 18:14:34 +0200 Subject: [PATCH 50/92] removed some junk --- apps/opencs/view/world/tablebottombox.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index dbd518c60d..bc90ebbc23 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -57,10 +57,6 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto layout->addWidget (statusBar); - QStatusBar *statusBar2 = new QStatusBar; - - layout->addWidget (statusBar2); - setLayout (layout); mCreator = creatorFactory.makeCreator(); From b8ac45defedc3f102cb34730e51613f95f956dd6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 18:22:31 +0200 Subject: [PATCH 51/92] added creator to bottom box layout and re-routed add record event to bottom box --- apps/opencs/view/world/creator.hpp | 4 +++- apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/table.hpp | 3 +++ apps/opencs/view/world/tablebottombox.cpp | 8 ++++++++ apps/opencs/view/world/tablebottombox.hpp | 2 ++ apps/opencs/view/world/tablesubview.cpp | 3 +++ 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index b9dc5629f4..54342ccdc7 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -1,10 +1,12 @@ #ifndef CSV_WORLD_CREATOR_H #define CSV_WORLD_CREATOR_H +#include + namespace CSVWorld { /// \brief Record creator UI base class - class Creator + class Creator : public QWidget { public: diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index d8a840be4d..441828e69c 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -148,7 +148,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q if (createAndDelete) { mCreateAction = new QAction (tr ("Add Record"), this); - connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); + connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); addAction (mCreateAction); } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index dd8e82ed71..913f048894 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -67,6 +67,8 @@ namespace CSVWorld /// \param deleted Number of deleted records /// \param modified Number of added and modified records + void createRequest(); + private slots: void createRecord(); @@ -82,6 +84,7 @@ namespace CSVWorld void tableSizeUpdate(); void selectionSizeUpdate(); + }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index bc90ebbc23..a48325575e 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -60,6 +60,8 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto setLayout (layout); mCreator = creatorFactory.makeCreator(); + + layout->addWidget (mCreator); } CSVWorld::TableBottomBox::~TableBottomBox() @@ -118,4 +120,10 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi if (changed) updateStatus(); +} + +#include +void CSVWorld::TableBottomBox::createRequest() +{ + std::cout<<"create"<tableSizeUpdate(); mTable->selectionSizeUpdate(); + + if (mBottom->canCreateAndDelete()) + connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); } void CSVWorld::TableSubView::setEditLock (bool locked) From dd6edd21f8ee1f00efce9010fc646db3ffed0950 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 26 Jul 2013 03:21:54 -0700 Subject: [PATCH 52/92] Add a separate on-hit method to handle objects being hit --- apps/openmw/mwclass/npc.cpp | 64 +++++++++++++++++++++-------------- apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwworld/class.cpp | 5 +++ apps/openmw/mwworld/class.hpp | 5 +++ 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e5fede1a61..5abefd9d47 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -310,12 +310,13 @@ namespace MWClass // Get the weapon used (if hand-to-hand, weapon = inv.end()) MWWorld::InventoryStore &inv = getInventoryStore(ptr); - MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if(weapon != inv.end() && weapon->getTypeName() != typeid(ESM::Weapon).name()) - weapon = inv.end(); + MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr()); + if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) + weapon = MWWorld::Ptr(); - float dist = 100.0f * ((weapon != inv.end()) ? - weapon->get()->mBase->mData.mReach : + float dist = 100.0f * (!weapon.isEmpty() ? + weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); MWWorld::Ptr victim = world->getFacedObject(ptr, dist); if(victim.isEmpty()) // Didn't hit anything @@ -329,8 +330,8 @@ namespace MWClass } int weapskill = ESM::Skill::HandToHand; - if(weapon != inv.end()) - weapskill = MWWorld::Class::get(*weapon).getEquipmentSkill(*weapon); + if(!weapon.isEmpty()) + weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon); MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); MWMechanics::NpcStats &npcstats = getNpcStats(ptr); @@ -345,24 +346,23 @@ namespace MWClass if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { - // Missed - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); - sndMgr->playSound3D(victim, "miss", 1.0f, 1.0f); + othercls.onHit(victim, 0.0f, weapon, ptr, false); return; } - if(weapon != inv.end()) + float damage = 0.0f; + if(!weapon.isEmpty()) { const unsigned char *attack = NULL; if(type == MWMechanics::CreatureStats::AT_Chop) - attack = weapon->get()->mBase->mData.mChop; + attack = weapon.get()->mBase->mData.mChop; else if(type == MWMechanics::CreatureStats::AT_Slash) - attack = weapon->get()->mBase->mData.mSlash; + attack = weapon.get()->mBase->mData.mSlash; else if(type == MWMechanics::CreatureStats::AT_Thrust) - attack = weapon->get()->mBase->mData.mThrust; + attack = weapon.get()->mBase->mData.mThrust; if(attack) { - float damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength()); + damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength()); damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); //damage *= weapon_current_health / weapon_max_health; if(!othercls.hasDetected(victim, ptr)) @@ -370,30 +370,44 @@ namespace MWClass damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); } - damage /= std::min(1.0f + othercls.getArmorRating(victim) / std::max(1.0f, damage), 4.0f); - - float health = othercls.getCreatureStats(victim).getHealth().getCurrent() - damage; - othercls.setActorHealth(victim, health, ptr); + damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } } - skillUsageSucceeded(ptr, weapskill, 0); + + othercls.onHit(victim, damage, weapon, ptr, true); } - void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + void Npc::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { - MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); - float diff = health - crstats.getHealth().getCurrent(); + // NOTE: 'object' and/or 'attacker' may be empty. + + if(!successful) + { + // TODO: Handle HitAttemptOnMe script function + + // Missed + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); + return; + } - if(diff < 0.0f) + // TODO: Handle HitOnMe script function and OnPCHitMe script variable. + + if(damage > 0.0f) { // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // something, alert the character controller, scripts, etc. - // NOTE: 'attacker' may be empty. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); } + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + + void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); bool wasDead = crstats.isDead(); MWMechanics::DynamicStat stat(crstats.getHealth()); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 05c4b1e8f0..a527740dee 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -70,6 +70,8 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; + virtual void onHit(const MWWorld::Ptr& ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 8a2b87b803..9bcfaa026e 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -96,6 +96,11 @@ namespace MWWorld throw std::runtime_error("class cannot hit"); } + void Class::onHit(const Ptr& ptr, float damage, const Ptr& object, const Ptr& attacker, bool successful) const + { + throw std::runtime_error("class cannot be hit"); + } + void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const { throw std::runtime_error("class does not have actor health"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 89d914746b..9755686c9c 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -115,6 +115,11 @@ namespace MWWorld /// enums. ignored for creature attacks. /// (default implementation: throw an exceoption) + virtual void onHit(const Ptr& ptr, float damage, const Ptr &object, const Ptr &attacker, bool successful) const; + ///< Alerts \a ptr that it's being hit for \a damage health by \a object (sword, arrow, + /// etc). \a attacker specifies the actor responsible for the attack, and \a successful + /// specifies if the hit is successful or not. + virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; ///< Sets a new current health value for the actor, optionally specifying the object causing /// the change. Use this instead of using CreatureStats directly as this will make sure the From bec420c69b988b75254e848ab745080963ed32c7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 26 Jul 2013 05:24:56 -0700 Subject: [PATCH 53/92] Add Class::onHit for creatures --- apps/openmw/mwclass/creature.cpp | 24 ++++++++++++++++++------ apps/openmw/mwclass/creature.hpp | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 003eb1d148..0f719b3320 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -11,6 +11,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" @@ -153,17 +154,28 @@ namespace MWClass { } - void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + void Creature::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { - MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); - float diff = health - crstats.getHealth().getCurrent(); + // NOTE: 'object' and/or 'attacker' may be empty. - if(diff < 0.0f) + if(!successful) { - // actor is losing health. Alert the character controller, scripts, etc. - // NOTE: 'attacker' may be empty. + // TODO: Handle HitAttemptOnMe script function + + // Missed + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); + return; } + // TODO: Handle HitOnMe script function and OnPCHitMe script variable. + + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + + void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const + { + MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); bool wasDead = crstats.isDead(); MWMechanics::DynamicStat stat(crstats.getHealth()); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index a15128ac72..452a0054fe 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -44,6 +44,8 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, From f215d2cc30b25409ba2c308f20c31a336bb83bb8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 26 Jul 2013 05:26:01 -0700 Subject: [PATCH 54/92] Only raise weapon skills for the player --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5abefd9d47..bc6d2b0561 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -373,7 +373,8 @@ namespace MWClass damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } } - skillUsageSucceeded(ptr, weapskill, 0); + if(ptr.getRefData().getHandle() == "player") + skillUsageSucceeded(ptr, weapskill, 0); othercls.onHit(victim, damage, weapon, ptr, true); } From 5379e607cbd6d800d5d2cedc6356e1f5a5d3a687 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 26 Jul 2013 07:04:36 -0700 Subject: [PATCH 55/92] Handle the OnPCHitMe script variable --- apps/openmw/mwclass/creature.cpp | 10 +++++++++- apps/openmw/mwclass/npc.cpp | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0f719b3320..36d1ae4f4b 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -167,7 +167,15 @@ namespace MWClass return; } - // TODO: Handle HitOnMe script function and OnPCHitMe script variable. + // TODO: Handle HitOnMe script function + + if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") + { + const std::string &script = ptr.get()->mBase->mScript; + /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ + if(!script.empty()) + ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); + } float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; setActorHealth(ptr, health, attacker); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index bc6d2b0561..12144bedbd 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -392,7 +392,15 @@ namespace MWClass return; } - // TODO: Handle HitOnMe script function and OnPCHitMe script variable. + // TODO: Handle HitOnMe script function + + if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") + { + const std::string &script = ptr.get()->mBase->mScript; + /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ + if(!script.empty()) + ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); + } if(damage > 0.0f) { From 3298eb1b37904454ceecc548018da3494110f23f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 26 Jul 2013 08:08:52 -0700 Subject: [PATCH 56/92] Implement HitOnMe script function --- apps/openmw/mwclass/creature.cpp | 3 ++- apps/openmw/mwclass/npc.cpp | 3 ++- apps/openmw/mwclass/weapon.cpp | 7 ++++++ apps/openmw/mwclass/weapon.hpp | 3 +++ apps/openmw/mwmechanics/actors.cpp | 26 ++++++++++++----------- apps/openmw/mwmechanics/creaturestats.cpp | 10 +++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 5 +++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 23 ++++++++++++++++++++ 9 files changed, 69 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 36d1ae4f4b..a80575985a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -167,7 +167,8 @@ namespace MWClass return; } - // TODO: Handle HitOnMe script function + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object)); if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") { diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 12144bedbd..3ed45ac722 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -392,7 +392,8 @@ namespace MWClass return; } - // TODO: Handle HitOnMe script function + if(!object.isEmpty()) + getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object)); if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") { diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index cb394d089f..db07fb553d 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -22,6 +22,13 @@ namespace MWClass { + std::string Weapon::getId (const MWWorld::Ptr& ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + return ref->mBase->mId; + } + void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 3902ef6128..181c637db6 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -12,6 +12,9 @@ namespace MWClass public: + virtual std::string getId (const MWWorld::Ptr& ptr) const; + ///< Return ID of \a ptr + virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index baa264aa4e..fb273c7c13 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -221,7 +221,11 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) { - if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + const MWWorld::Class &cls = MWWorld::Class::get(iter->first); + CreatureStats &stats = cls.getCreatureStats(iter->first); + + stats.setLastHitObject(std::string()); + if(!stats.isDead()) { if(iter->second->isDead()) iter->second->resurrect(); @@ -230,7 +234,7 @@ namespace MWMechanics if(iter->first.getTypeName() == typeid(ESM::NPC).name()) updateNpc(iter->first, totalDuration, paused); - if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) + if(!stats.isDead()) continue; } @@ -238,16 +242,15 @@ namespace MWMechanics // \todo remove workaround, once player death can be handled if(iter->first.getRefData().getHandle()=="player") { - MWMechanics::DynamicStat stat ( - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth()); + MWMechanics::DynamicStat stat(stats.getHealth()); - if (stat.getModified()<1) + if(stat.getModified()<1) { - stat.setModified (1, 0); - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); + stat.setModified(1, 0); + stats.setHealth(stat); } - MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); + stats.resurrect(); continue; } @@ -256,11 +259,10 @@ namespace MWMechanics iter->second->kill(); - ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; + ++mDeathCount[cls.getId(iter->first)]; - if(MWWorld::Class::get(iter->first).isEssential(iter->first)) - MWBase::Environment::get().getWindowManager()->messageBox( - "#{sKilledEssential}"); + if(cls.isEssential(iter->first)) + MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}"); } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index c43b80a253..5b4621905d 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -318,4 +318,14 @@ namespace MWMechanics { return false; } + + void CreatureStats::setLastHitObject(const std::string& objectid) + { + mLastHitObject = objectid; + } + + const std::string &CreatureStats::getLastHitObject() const + { + return mLastHitObject; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a9a3caa497..fe46ed072a 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -38,6 +38,8 @@ namespace MWMechanics int mAttackType; + std::string mLastHitObject; // The last object to hit this actor + public: CreatureStats(); @@ -155,6 +157,9 @@ namespace MWMechanics void setHostile (bool hostile); bool getCreatureTargetted() const; + + void setLastHitObject(const std::string &objectid); + const std::string &getLastHitObject() const; }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 85d8deaecd..1b8682542c 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -338,5 +338,7 @@ op 0x200020f: GetStandingActor, explicit op 0x2000210: GetStartingAngle op 0x2000211: GetStartingAngle, explicit op 0x2000212: GetWindSpeed +op 0x2000213: HitOnMe +op 0x2000214: HitOnMe, explicit -opcodes 0x2000213-0x3ffffff unused +opcodes 0x2000215-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 5ed26119c0..453f6fbbab 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -599,6 +599,23 @@ namespace MWScript } }; + template + class OpHitOnMe : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWMechanics::CreatureStats &stats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject())); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -650,6 +667,9 @@ namespace MWScript const int opcodePlayBink = 0x20001f7; + const int opcodeHitOnMe = 0x2000213; + const int opcodeHitOnMeExplicit = 0x2000214; + void registerExtensions (Compiler::Extensions& extensions) { extensions.registerFunction ("xbox", 'l', "", opcodeXBox); @@ -692,6 +712,7 @@ namespace MWScript extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); + extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -745,6 +766,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor); interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor); interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed); + interpreter.installSegment5 (opcodeHitOnMe, new OpHitOnMe); + interpreter.installSegment5 (opcodeHitOnMeExplicit, new OpHitOnMe); } } } From fee748d4b5931e0d6c97b0689e0a0884beb03504 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 26 Jul 2013 21:09:23 +0200 Subject: [PATCH 57/92] added creator bar UI and cancel button --- apps/opencs/CMakeLists.txt | 4 +-- apps/opencs/view/world/creator.hpp | 8 ++++++ apps/opencs/view/world/genericcreator.cpp | 29 ++++++++++++++++++- apps/opencs/view/world/genericcreator.hpp | 6 ++++ apps/opencs/view/world/tablebottombox.cpp | 35 ++++++++++++++++------- apps/opencs/view/world/tablebottombox.hpp | 10 +++++++ 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6102671ff7..bca37755c5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -57,13 +57,13 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview scriptsubview util regionmapsubview tablebottombox + table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator ) opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate - scripthighlighter creator genericcreator + scripthighlighter ) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 54342ccdc7..f5485534c7 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -8,9 +8,17 @@ namespace CSVWorld /// \brief Record creator UI base class class Creator : public QWidget { + Q_OBJECT + public: virtual ~Creator(); + + virtual void reset() = 0; + + signals: + + void done(); }; /// \brief Base class for Creator factory diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 4685df339c..6dbd847115 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -1,2 +1,29 @@ -#include "genericcreator.hpp" \ No newline at end of file +#include "genericcreator.hpp" + +#include +#include +#include + +CSVWorld::GenericCreator::GenericCreator() +{ + QHBoxLayout *layout = new QHBoxLayout; + + QLineEdit *name = new QLineEdit; + layout->addWidget (name, 1); + + QPushButton *createButton = new QPushButton ("Create"); + layout->addWidget (createButton); + + QPushButton *cancelButton = new QPushButton ("Cancel"); + layout->addWidget (cancelButton); + + connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); + + setLayout (layout); +} + +void CSVWorld::GenericCreator::reset() +{ + +} \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 28024041ff..7ab125dfc3 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -7,7 +7,13 @@ namespace CSVWorld { class GenericCreator : public Creator { + Q_OBJECT + public: + + GenericCreator(); + + virtual void reset(); }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index a48325575e..d84e66390d 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -40,28 +40,30 @@ void CSVWorld::TableBottomBox::updateStatus() } CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, QWidget *parent) -: QWidget (parent), mShowStatusBar (false) +: QWidget (parent), mShowStatusBar (false), mCreating (false) { for (int i=0; i<4; ++i) mStatusCount[i] = 0; setVisible (false); - QStackedLayout *layout = new QStackedLayout; + mLayout = new QStackedLayout; mStatus = new QLabel; - QStatusBar *statusBar = new QStatusBar; + mStatusBar = new QStatusBar; - statusBar->addWidget (mStatus); + mStatusBar->addWidget (mStatus); - layout->addWidget (statusBar); + mLayout->addWidget (mStatusBar); - setLayout (layout); + setLayout (mLayout); mCreator = creatorFactory.makeCreator(); - layout->addWidget (mCreator); + mLayout->addWidget (mCreator); + + connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); } CSVWorld::TableBottomBox::~TableBottomBox() @@ -73,7 +75,7 @@ void CSVWorld::TableBottomBox::setStatusBar (bool show) { if (show!=mShowStatusBar) { - setVisible (show); + setVisible (show || mCreating); mShowStatusBar = show; @@ -87,6 +89,18 @@ bool CSVWorld::TableBottomBox::canCreateAndDelete() const return mCreator; } +void CSVWorld::TableBottomBox::createRequestDone() +{ + if (!mShowStatusBar) + setVisible (false); + else + updateStatus(); + + mLayout->setCurrentWidget (mStatusBar); + + mCreating = false; +} + void CSVWorld::TableBottomBox::selectionSizeChanged (int size) { if (mStatusCount[3]!=size) @@ -122,8 +136,9 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi updateStatus(); } -#include void CSVWorld::TableBottomBox::createRequest() { - std::cout<<"create"<setCurrentWidget (mCreator); + setVisible (true); + mCreating = true; } \ No newline at end of file diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index dd00ea27ce..8f2ceda112 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -4,6 +4,8 @@ #include class QLabel; +class QStackedLayout; +class QStatusBar; namespace CSVWorld { @@ -16,8 +18,11 @@ namespace CSVWorld bool mShowStatusBar; QLabel *mStatus; + QStatusBar *mStatusBar; int mStatusCount[4]; Creator *mCreator; + bool mCreating; + QStackedLayout *mLayout; private: @@ -40,6 +45,11 @@ namespace CSVWorld /// /// \note The BotomBox does not partake in the deletion of records. + private slots: + + void createRequestDone(); + ///< \note This slot being called does not imply success. + public slots: void selectionSizeChanged (int size); From 1174b85ac8de0cb15e7f2d9c8079dc80494d6974 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 27 Jul 2013 00:14:55 -0700 Subject: [PATCH 58/92] Implement Disable/EnableTeleporting script functions --- apps/openmw/mwbase/world.hpp | 6 ++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 19 +++++++++++++++++++ apps/openmw/mwworld/worldimp.cpp | 13 ++++++++++++- apps/openmw/mwworld/worldimp.hpp | 8 ++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index b9033daac4..dd069bfa93 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -376,6 +376,12 @@ namespace MWBase /// Find default position inside interior cell specified by name /// \return false if interior with given name not exists, true otherwise virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0; + + /// Enables or disables use of teleport spell effects (recall, intervention, etc). + virtual void enableTeleporting(bool enable) = 0; + + /// Returns true if teleport spell effects are allowed. + virtual bool isTeleportingEnabled() const = 0; }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 1b8682542c..772069d05e 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -340,5 +340,7 @@ op 0x2000211: GetStartingAngle, explicit op 0x2000212: GetWindSpeed op 0x2000213: HitOnMe op 0x2000214: HitOnMe, explicit +op 0x2000215: DisableTeleporting +op 0x2000216: EnableTeleporting -opcodes 0x2000215-0x3ffffff unused +opcodes 0x2000217-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 453f6fbbab..6b1adc9387 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -616,6 +616,18 @@ namespace MWScript } }; + template + class OpEnableTeleporting : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->enableTeleporting(Enable); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -670,6 +682,9 @@ namespace MWScript const int opcodeHitOnMe = 0x2000213; const int opcodeHitOnMeExplicit = 0x2000214; + const int opcodeDisableTeleporting = 0x2000215; + const int opcodeEnableTeleporting = 0x2000216; + void registerExtensions (Compiler::Extensions& extensions) { extensions.registerFunction ("xbox", 'l', "", opcodeXBox); @@ -713,6 +728,8 @@ namespace MWScript extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit); + extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting); + extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -768,6 +785,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed); interpreter.installSegment5 (opcodeHitOnMe, new OpHitOnMe); interpreter.installSegment5 (opcodeHitOnMeExplicit, new OpHitOnMe); + interpreter.installSegment5 (opcodeDisableTeleporting, new OpEnableTeleporting); + interpreter.installSegment5 (opcodeEnableTeleporting, new OpEnableTeleporting); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index cf651a2e71..70eda24290 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -166,7 +166,7 @@ namespace MWWorld : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride), - mFallback(fallbackMap), mPlayIntro(0) + mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -1844,4 +1844,15 @@ namespace MWWorld } return false; } + + void World::enableTeleporting(bool enable) + { + mTeleportEnabled = enable; + } + + bool World::isTeleportingEnabled() const + { + return mTeleportEnabled; + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 45cb1f61e8..8a30e970d7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -117,6 +117,8 @@ namespace MWWorld int mPlayIntro; + bool mTeleportEnabled; + public: World (OEngine::Render::OgreRenderer& renderer, @@ -425,6 +427,12 @@ namespace MWWorld /// Find position in interior cell near door entrance /// \return false if interior with given name not exists, true otherwise virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos); + + /// Enables or disables use of teleport spell effects (recall, intervention, etc). + virtual void enableTeleporting(bool enable); + + /// Returns true if teleport spell effects are allowed. + virtual bool isTeleportingEnabled() const; }; } From f93af52486c8e84324bb41a5c90cf22ea6a66b85 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 27 Jul 2013 13:28:12 +0200 Subject: [PATCH 59/92] handling down a reference to Data and the undo stack to the creators --- apps/opencs/view/world/creator.cpp | 3 ++- apps/opencs/view/world/creator.hpp | 17 ++++++++++++----- apps/opencs/view/world/genericcreator.cpp | 2 +- apps/opencs/view/world/genericcreator.hpp | 2 +- apps/opencs/view/world/tablebottombox.cpp | 5 +++-- apps/opencs/view/world/tablebottombox.hpp | 9 ++++++++- apps/opencs/view/world/tablesubview.cpp | 4 ++-- 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp index 7d57c2ec73..c0378151ed 100644 --- a/apps/opencs/view/world/creator.cpp +++ b/apps/opencs/view/world/creator.cpp @@ -6,7 +6,8 @@ CSVWorld::Creator:: ~Creator() {} CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} -CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator() const +CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack) const { return 0; } \ No newline at end of file diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index f5485534c7..9f586bcba6 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -3,6 +3,13 @@ #include +class QUndoStack; + +namespace CSMWorld +{ + class Data; +} + namespace CSVWorld { /// \brief Record creator UI base class @@ -28,7 +35,7 @@ namespace CSVWorld virtual ~CreatorFactoryBase(); - virtual Creator *makeCreator() const = 0; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const = 0; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -40,7 +47,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator() const; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function always returns 0. @@ -51,7 +58,7 @@ namespace CSVWorld { public: - virtual Creator *makeCreator() const; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStac) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -59,9 +66,9 @@ namespace CSVWorld }; template - Creator *CreatorFactory::makeCreator() const + Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const { - return new CreatorT; + return new CreatorT (data, undoStack); } } diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 6dbd847115..9a855fc4b9 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -5,7 +5,7 @@ #include #include -CSVWorld::GenericCreator::GenericCreator() +CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack) { QHBoxLayout *layout = new QHBoxLayout; diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 7ab125dfc3..553315ae34 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -11,7 +11,7 @@ namespace CSVWorld public: - GenericCreator(); + GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack); virtual void reset(); }; diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index d84e66390d..ae2b3d9208 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -39,7 +39,8 @@ void CSVWorld::TableBottomBox::updateStatus() } } -CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, QWidget *parent) +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMWorld::Data& data, QUndoStack& undoStack, QWidget *parent) : QWidget (parent), mShowStatusBar (false), mCreating (false) { for (int i=0; i<4; ++i) @@ -59,7 +60,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto setLayout (mLayout); - mCreator = creatorFactory.makeCreator(); + mCreator = creatorFactory.makeCreator (data, undoStack); mLayout->addWidget (mCreator); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 8f2ceda112..50741167c1 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -6,6 +6,12 @@ class QLabel; class QStackedLayout; class QStatusBar; +class QUndoStack; + +namespace CSMWorld +{ + class Data; +} namespace CSVWorld { @@ -34,7 +40,8 @@ namespace CSVWorld public: - TableBottomBox (const CreatorFactoryBase& creatorFactory, QWidget *parent = 0); + TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data, + QUndoStack& undoStack, QWidget *parent = 0); virtual ~TableBottomBox(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 61a731289c..a76042842d 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -13,12 +13,12 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D const CreatorFactoryBase& creatorFactory) : SubView (id) { - QVBoxLayout *layout = new QVBoxLayout; layout->setContentsMargins (QMargins (0, 0, 0, 0)); - layout->addWidget (mBottom = new TableBottomBox (creatorFactory, this), 0); + layout->addWidget (mBottom = + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), this), 0); layout->insertWidget (0, mTable = new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); From c94653dc49156421c0b1b63e7d3dd3d8f317b4c1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 27 Jul 2013 07:10:18 -0700 Subject: [PATCH 60/92] Implement the ModRegion script function --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwscript/docs/vmformat.txt | 3 +- apps/openmw/mwscript/skyextensions.cpp | 25 ++++++++++++++ apps/openmw/mwworld/weather.cpp | 48 ++++++++++++++++++-------- apps/openmw/mwworld/weather.hpp | 5 +++ apps/openmw/mwworld/worldimp.cpp | 5 +++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 7 files changed, 74 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index dd069bfa93..480bcf9cf4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -201,6 +201,8 @@ namespace MWBase virtual void setMoonColour (bool red) = 0; + virtual void modRegion(const std::string ®ionid, const std::vector &chances) = 0; + virtual float getTimeScaleFactor() const = 0; virtual void changeToInteriorCell (const std::string& cellName, diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 772069d05e..59d988410b 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -51,7 +51,8 @@ op 0x20022: AiFollow op 0x20023: AiFollow, explicit reference op 0x20024: AiFollowCell op 0x20025: AiFollowCell, explicit reference -op s 0x20026-0x3ffff unused +op 0x20026: ModRegion +opcodes 0x20027-0x3ffff unused Segment 4: (not implemented yet) diff --git a/apps/openmw/mwscript/skyextensions.cpp b/apps/openmw/mwscript/skyextensions.cpp index e16c2ec233..393c9c55b8 100644 --- a/apps/openmw/mwscript/skyextensions.cpp +++ b/apps/openmw/mwscript/skyextensions.cpp @@ -96,6 +96,28 @@ namespace MWScript } }; + class OpModRegion : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + std::string region = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::vector chances; + chances.reserve(10); + while(arg0 > 0) + { + chances.push_back(std::max(0, std::min(127, runtime[0].mInteger))); + runtime.pop(); + arg0--; + } + + MWBase::Environment::get().getWorld()->modRegion(region, chances); + } + }; + const int opcodeToggleSky = 0x2000021; const int opcodeTurnMoonWhite = 0x2000022; const int opcodeTurnMoonRed = 0x2000023; @@ -103,6 +125,7 @@ namespace MWScript const int opcodeGetSecundaPhase = 0x2000025; const int opcodeGetCurrentWeather = 0x200013f; const int opcodeChangeWeather = 0x2000140; + const int opcodeModRegion = 0x20026; void registerExtensions (Compiler::Extensions& extensions) { @@ -114,6 +137,7 @@ namespace MWScript extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase); extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase); extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather); + extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -125,6 +149,7 @@ namespace MWScript interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase); interpreter.installSegment5 (opcodeGetCurrentWeather, new OpGetCurrentWeather); interpreter.installSegment5 (opcodeChangeWeather, new OpChangeWeather); + interpreter.installSegment3 (opcodeModRegion, new OpModRegion); } } } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 6cdb131471..c8283fe0db 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -537,8 +537,8 @@ void WeatherManager::stopSounds(bool stopAll) std::vector::iterator it = mSoundsPlaying.begin(); while (it!=mSoundsPlaying.end()) { - if (stopAll || \ - !((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) || \ + if (stopAll || + !((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) || (*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID))) { MWBase::Environment::get().getSoundManager()->stopSound(*it); @@ -551,29 +551,38 @@ void WeatherManager::stopSounds(bool stopAll) Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const { + const MWBase::World *world = MWBase::Environment::get().getWorld(); + std::vector probability; + + RegionModMap::const_iterator iter = mRegionMods.find(Misc::StringUtils::lowerCase(region->mId)); + if(iter != mRegionMods.end()) + probability = iter->second; + else + { + probability.reserve(10); + probability.push_back(region->mData.mClear); + probability.push_back(region->mData.mCloudy); + probability.push_back(region->mData.mFoggy); + probability.push_back(region->mData.mOvercast); + probability.push_back(region->mData.mRain); + probability.push_back(region->mData.mThunder); + probability.push_back(region->mData.mAsh); + probability.push_back(region->mData.mBlight); + probability.push_back(region->mData.mA); + probability.push_back(region->mData.mB); + } + /* * All probabilities must add to 100 (responsibility of the user). * If chances A and B has values 30 and 70 then by generating * 100 numbers 1..100, 30% will be lesser or equal 30 and * 70% will be greater than 30 (in theory). */ - const int probability[] = { - region->mData.mClear, - region->mData.mCloudy, - region->mData.mFoggy, - region->mData.mOvercast, - region->mData.mRain, - region->mData.mThunder, - region->mData.mAsh, - region->mData.mBlight, - region->mData.mA, - region->mData.mB - }; // 10 elements int chance = (rand() % 100) + 1; // 1..100 int sum = 0; int i = 0; - for (; i < 10; ++i) + for (; i < probability.size(); ++i) { sum += probability[i]; if (chance < sum) @@ -681,6 +690,15 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int setWeather(weather); } +void WeatherManager::modRegion(const std::string ®ionid, const std::vector &chances) +{ + mRegionMods[Misc::StringUtils::lowerCase(regionid)] = chances; + // Start transitioning right away if the region no longer supports the current weather type + unsigned int current = getWeatherID(); + if(current >= chances.size() || chances[current] == 0) + mWeatherUpdateTime = 0.0f; +} + float WeatherManager::getWindSpeed() const { return mWindSpeed; diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 1a787aae8f..a2a07cec79 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -149,6 +149,8 @@ namespace MWWorld unsigned int getWeatherID() const; + void modRegion(const std::string ®ionid, const std::vector &chances); + private: float mHour; int mDay, mMonth; @@ -188,6 +190,9 @@ namespace MWWorld Ogre::String nextWeather(const ESM::Region* region) const; WeatherResult mResult; + typedef std::map > RegionModMap; + RegionModMap mRegionMods; + float mSunriseTime; float mSunsetTime; float mSunriseDuration; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 70eda24290..33d4719c09 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1374,6 +1374,11 @@ namespace MWWorld mWeatherManager->changeWeather(region, id); } + void World::modRegion(const std::string ®ionid, const std::vector &chances) + { + mWeatherManager->modRegion(regionid, chances); + } + OEngine::Render::Fader* World::getFader() { return mRendering->getFader(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8a30e970d7..252248e406 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -237,6 +237,8 @@ namespace MWWorld virtual void setMoonColour (bool red); + virtual void modRegion(const std::string ®ionid, const std::vector &chances); + virtual float getTimeScaleFactor() const; virtual void changeToInteriorCell (const std::string& cellName, From f73008546f828debd607600833913a441c569513 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 27 Jul 2013 07:24:18 -0700 Subject: [PATCH 61/92] Keep a sound updated with its object's position --- apps/openmw/mwsound/soundmanagerimp.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2b4978158e..1e431d54a1 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -546,6 +546,13 @@ namespace MWSound mActiveSounds.erase(snditer++); else { + const MWWorld::Ptr &ptr = snditer->second.first; + if(!ptr.isEmpty()) + { + const ESM::Position &pos = ptr.getRefData().getPosition(); + const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); + snditer->first->setPosition(objpos); + } snditer->first->update(); snditer++; } From 1a40d01afac26379c6438821e00d8f2b0ab492d2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 27 Jul 2013 09:31:16 -0700 Subject: [PATCH 62/92] Fix NPC armor rating calculation and reduce redundant lookups --- apps/openmw/mwclass/npc.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 3ed45ac722..92ede8680a 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -739,18 +739,19 @@ namespace MWClass float Npc::getArmorRating (const MWWorld::Ptr& ptr) const { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); - const MWWorld::Store &gmst = - MWBase::Environment::get().getWorld()->getStore().get(); + const MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store &gmst = world->getStore().get(); - int ratings[MWWorld::InventoryStore::Slots]; + MWMechanics::NpcStats &stats = getNpcStats(ptr); + MWWorld::InventoryStore &invStore = getInventoryStore(ptr); int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); - int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified(); + int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); - for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i) + int ratings[MWWorld::InventoryStore::Slots]; + for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) { MWWorld::ContainerStoreIterator it = invStore.getSlot(i); if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) @@ -760,28 +761,27 @@ namespace MWClass } else { - MWWorld::LiveCellRef *ref = - it->get(); + MWWorld::LiveCellRef *ref = it->get(); int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it); - int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified(); + int armorSkill = stats.getSkill(armorSkillType).getModified(); - if (ref->mBase->mData.mWeight == 0) + if(ref->mBase->mData.mWeight == 0) ratings[i] = ref->mBase->mData.mArmor; else ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; } } - float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; + float shield = getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; - return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3 + return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet] + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] - ) * 0.1 - + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet) - * 0.05 + ) * 0.1f + + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet]) + * 0.05f + shield; } From db4f34b332f077409b9e22eedae20fe1b4d6d7b6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 27 Jul 2013 11:20:39 -0700 Subject: [PATCH 63/92] Play hit sounds when hitting creatures and NPCs --- apps/openmw/mwclass/creature.cpp | 5 ++++ apps/openmw/mwclass/npc.cpp | 43 +++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a80575985a..43d6c61590 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -178,6 +178,11 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + if(damage > 0.0f) + { + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + } + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; setActorHealth(ptr, health, attacker); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 92ede8680a..42b1176c74 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -369,6 +369,7 @@ namespace MWClass { damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } @@ -393,7 +394,7 @@ namespace MWClass } if(!object.isEmpty()) - getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object)); + getCreatureStats(ptr).setLastHitObject(get(object).getId(object)); if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") { @@ -409,6 +410,46 @@ namespace MWClass // something, alert the character controller, scripts, etc. MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); + + // Hit percentages: + // cuirass = 30% + // shield, helmet, greaves, boots, pauldrons = 10% each + // guantlets = 5% each + static const int hitslots[20] = { + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots, + MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet + }; + int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); + MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); + if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) + { + switch(get(armor).getEquipmentSkill(armor)) + { + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + } + } + + sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); } float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; From a34f0de3e1fa75840b960d27adc22a17be908a41 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 04:17:36 -0700 Subject: [PATCH 64/92] Play the correct medium armor foot sounds --- apps/openmw/mwclass/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 42b1176c74..e1e4e78566 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -877,7 +877,7 @@ namespace MWClass case ESM::Skill::LightArmor: return "FootLightLeft"; case ESM::Skill::MediumArmor: - return "FootMediumLeft"; + return "FootMedLeft"; case ESM::Skill::HeavyArmor: return "FootHeavyLeft"; } @@ -902,7 +902,7 @@ namespace MWClass case ESM::Skill::LightArmor: return "FootLightRight"; case ESM::Skill::MediumArmor: - return "FootMediumRight"; + return "FootMedRight"; case ESM::Skill::HeavyArmor: return "FootHeavyRight"; } From b7e81dbc5bcb7c25cb89fd03776156100542322d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 04:20:33 -0700 Subject: [PATCH 65/92] Ignore controllers on the root NiNode I wonder if we should ignore the root NiNode completely. --- components/nifogre/ogrenifloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 9c079bc98c..3bb9ea2309 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -758,7 +758,7 @@ class NIFObjectLoader e = e->extra; } - if(!node->controller.empty()) + if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode)) createNodeControllers(name, node->controller, objectlist, animflags); if(node->recType == Nif::RC_NiCamera) From 2d46a1db2fa7ca235dde8eb9a98a4dbbc55b6017 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 13:39:33 +0200 Subject: [PATCH 66/92] changed CreateCommand to work directly on the model instead of the proxy --- apps/opencs/model/world/commands.cpp | 4 ++-- apps/opencs/model/world/commands.hpp | 6 +++--- apps/opencs/model/world/idtableproxymodel.cpp | 5 ----- apps/opencs/model/world/idtableproxymodel.hpp | 2 -- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index eaded5b702..3bcdf8b050 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,7 +3,7 @@ #include -#include "idtableproxymodel.hpp" +#include "idtable.hpp" #include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, @@ -25,7 +25,7 @@ void CSMWorld::ModifyCommand::undo() mModel.setData (mIndex, mOld); } -CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) +CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mId (id) { setText (("Create record " + id).c_str()); diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index af419215dc..1fa124277f 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -14,7 +14,7 @@ class QAbstractItemModel; namespace CSMWorld { - class IdTableProxyModel; + class IdTable; class IdTable; class RecordBase; @@ -37,12 +37,12 @@ namespace CSMWorld class CreateCommand : public QUndoCommand { - IdTableProxyModel& mModel; + IdTable& mModel; std::string mId; public: - CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0); + CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); virtual void redo(); diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 78995f60bc..e99e1575ce 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -7,11 +7,6 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent) {} -void CSMWorld::IdTableProxyModel::addRecord (const std::string& id) -{ - dynamic_cast (*sourceModel()).addRecord (id); -} - QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp index 3f1537cce6..200b99fe20 100644 --- a/apps/opencs/model/world/idtableproxymodel.hpp +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -15,8 +15,6 @@ namespace CSMWorld IdTableProxyModel (QObject *parent = 0); - virtual void addRecord (const std::string& id); - virtual QModelIndex getModelIndex (const std::string& id, int column) const; }; } From 00fcb79f082be9ecd7b6041d8ff576c1bc08c556 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 13:43:16 +0200 Subject: [PATCH 67/92] moved record creation to GenericCreator class (now taking ID from user instead of using a procedurally generated one) --- apps/opencs/view/world/creator.cpp | 2 +- apps/opencs/view/world/creator.hpp | 15 ++++-- apps/opencs/view/world/genericcreator.cpp | 64 ++++++++++++++++++++--- apps/opencs/view/world/genericcreator.hpp | 29 +++++++++- apps/opencs/view/world/table.cpp | 16 ------ apps/opencs/view/world/table.hpp | 2 - apps/opencs/view/world/tablebottombox.cpp | 5 +- apps/opencs/view/world/tablebottombox.hpp | 3 +- apps/opencs/view/world/tablesubview.cpp | 2 +- 9 files changed, 103 insertions(+), 35 deletions(-) diff --git a/apps/opencs/view/world/creator.cpp b/apps/opencs/view/world/creator.cpp index c0378151ed..d753a2b476 100644 --- a/apps/opencs/view/world/creator.cpp +++ b/apps/opencs/view/world/creator.cpp @@ -7,7 +7,7 @@ CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {} CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data, - QUndoStack& undoStack) const + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const { return 0; } \ No newline at end of file diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 9f586bcba6..564a5584e0 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -8,6 +8,7 @@ class QUndoStack; namespace CSMWorld { class Data; + class UniversalId; } namespace CSVWorld @@ -35,7 +36,8 @@ namespace CSVWorld virtual ~CreatorFactoryBase(); - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const = 0; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const = 0; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -47,7 +49,8 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function always returns 0. @@ -58,7 +61,8 @@ namespace CSVWorld { public: - virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStac) const; + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; ///< The ownership of the returned Creator is transferred to the caller. /// /// \note The function can return a 0-pointer, which means no UI for creating/deleting @@ -66,9 +70,10 @@ namespace CSVWorld }; template - Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack) const + Creator *CreatorFactory::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const { - return new CreatorT (data, undoStack); + return new CreatorT (data, undoStack, id); } } diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 9a855fc4b9..10cbf9447b 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -4,26 +4,78 @@ #include #include #include +#include -CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack) +#include "../../model/world/commands.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" + +void CSVWorld::GenericCreator::update() +{ + mErrors = getErrors(); + + mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); + + mCreate->setEnabled (mErrors.empty()); +} + +CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: mData (data), mUndoStack (undoStack), mListId (id) { QHBoxLayout *layout = new QHBoxLayout; - QLineEdit *name = new QLineEdit; - layout->addWidget (name, 1); + mId = new QLineEdit; + layout->addWidget (mId, 1); - QPushButton *createButton = new QPushButton ("Create"); - layout->addWidget (createButton); + mCreate = new QPushButton ("Create"); + layout->addWidget (mCreate); QPushButton *cancelButton = new QPushButton ("Cancel"); layout->addWidget (cancelButton); + setLayout (layout); + connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); + connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); - setLayout (layout); + connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); } void CSVWorld::GenericCreator::reset() { + mId->setText (""); + update(); +} + +std::string CSVWorld::GenericCreator::getErrors() const +{ + std::string errors; + + std::string id = mId->text().toUtf8().constData(); + + if (id.empty()) + { + errors = "Missing ID"; + } + else + { + + } + + return errors; +} + +void CSVWorld::GenericCreator::textChanged (const QString& text) +{ + update(); +} + +void CSVWorld::GenericCreator::create() +{ + mUndoStack.push (new CSMWorld::CreateCommand ( + dynamic_cast (*mData.getTableModel (mListId)), + mId->text().toUtf8().constData())); + emit done(); } \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 553315ae34..a89b4c31f2 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -1,19 +1,46 @@ #ifndef CSV_WORLD_GENERICCREATOR_H #define CSV_WORLD_GENERICCREATOR_H +class QPushButton; +class QLineEdit; + #include "creator.hpp" +#include "../../model/world/universalid.hpp" + namespace CSVWorld { class GenericCreator : public Creator { Q_OBJECT + CSMWorld::Data& mData; + QUndoStack& mUndoStack; + CSMWorld::UniversalId mListId; + QPushButton *mCreate; + QLineEdit *mId; + std::string mErrors; + + private: + + void update(); + public: - GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack); + GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + private slots: + + void textChanged (const QString& text); + + void create(); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 441828e69c..c35d7358a0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -187,22 +187,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); } -#include /// \todo remove -void CSVWorld::Table::createRecord() -{ - if (!mEditLock) - { - /// \todo ask the user for an ID instead. - static int index = 0; - - std::ostringstream stream; - stream << "id" << index++; - - mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); - } - -} - void CSVWorld::Table::revertRecord() { if (!mEditLock) diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 913f048894..2a59270b89 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -71,8 +71,6 @@ namespace CSVWorld private slots: - void createRecord(); - void revertRecord(); void deleteRecord(); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index ae2b3d9208..f796d89600 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -40,7 +40,7 @@ void CSVWorld::TableBottomBox::updateStatus() } CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMWorld::Data& data, QUndoStack& undoStack, QWidget *parent) + CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent) : QWidget (parent), mShowStatusBar (false), mCreating (false) { for (int i=0; i<4; ++i) @@ -60,7 +60,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto setLayout (mLayout); - mCreator = creatorFactory.makeCreator (data, undoStack); + mCreator = creatorFactory.makeCreator (data, undoStack, id); mLayout->addWidget (mCreator); @@ -139,6 +139,7 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi void CSVWorld::TableBottomBox::createRequest() { + mCreator->reset(); mLayout->setCurrentWidget (mCreator); setVisible (true); mCreating = true; diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 50741167c1..62c552c93a 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -11,6 +11,7 @@ class QUndoStack; namespace CSMWorld { class Data; + class UniversalId; } namespace CSVWorld @@ -41,7 +42,7 @@ namespace CSVWorld public: TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data, - QUndoStack& undoStack, QWidget *parent = 0); + QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0); virtual ~TableBottomBox(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index a76042842d..8a828c9bd2 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -18,7 +18,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->setContentsMargins (QMargins (0, 0, 0, 0)); layout->addWidget (mBottom = - new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), this), 0); + new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); layout->insertWidget (0, mTable = new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2); From 4f6e99c391b0ef8408f3ef8bc7fc04ac879cdc5b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 14:00:26 +0200 Subject: [PATCH 68/92] more compact layout for the bottom box --- apps/opencs/view/world/genericcreator.cpp | 1 + apps/opencs/view/world/tablebottombox.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 10cbf9447b..f4dd53207d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -24,6 +24,7 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo : mData (data), mUndoStack (undoStack), mListId (id) { QHBoxLayout *layout = new QHBoxLayout; + layout->setContentsMargins (0, 0, 0, 0); mId = new QLineEdit; layout->addWidget (mId, 1); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index f796d89600..b6d0e7807c 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -49,6 +49,7 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto setVisible (false); mLayout = new QStackedLayout; + mLayout->setContentsMargins (0, 0, 0, 0); mStatus = new QLabel; From 32c697abc65986b18f32690f89e9687ac2d53b51 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 14:40:11 +0200 Subject: [PATCH 69/92] validate IDs entered by the user --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/world/genericcreator.cpp | 3 +++ apps/opencs/view/world/idvalidator.cpp | 26 +++++++++++++++++++++++ apps/opencs/view/world/idvalidator.hpp | 23 ++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/view/world/idvalidator.cpp create mode 100644 apps/opencs/view/world/idvalidator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index bca37755c5..233c3d0fe9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -63,7 +63,7 @@ opencs_units (view/world opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate - scripthighlighter + scripthighlighter idvalidator ) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index f4dd53207d..f30ada0923 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -10,6 +10,8 @@ #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" +#include "idvalidator.hpp" + void CSVWorld::GenericCreator::update() { mErrors = getErrors(); @@ -27,6 +29,7 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo layout->setContentsMargins (0, 0, 0, 0); mId = new QLineEdit; + mId->setValidator (new IdValidator (this)); layout->addWidget (mId, 1); mCreate = new QPushButton ("Create"); diff --git a/apps/opencs/view/world/idvalidator.cpp b/apps/opencs/view/world/idvalidator.cpp new file mode 100644 index 0000000000..cf6e5d77ba --- /dev/null +++ b/apps/opencs/view/world/idvalidator.cpp @@ -0,0 +1,26 @@ + +#include "idvalidator.hpp" + +bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const +{ + if (c.isLetter() || c=='_') + return true; + + if (!first && (c.isDigit() || c.isSpace())) + return true; + + return false; +} + +CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {} + +QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const +{ + bool first = true; + + for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false) + if (!isValid (*iter, first)) + return QValidator::Invalid; + + return QValidator::Acceptable; +} \ No newline at end of file diff --git a/apps/opencs/view/world/idvalidator.hpp b/apps/opencs/view/world/idvalidator.hpp new file mode 100644 index 0000000000..db0ecb27a7 --- /dev/null +++ b/apps/opencs/view/world/idvalidator.hpp @@ -0,0 +1,23 @@ +#ifndef CSV_WORLD_IDVALIDATOR_H +#define CSV_WORLD_IDVALIDATOR_H + +#include + +namespace CSVWorld +{ + class IdValidator : public QValidator + { + private: + + bool isValid (const QChar& c, bool first) const; + + public: + + IdValidator (QObject *parent = 0); + + virtual State validate (QString& input, int& pos) const; + + }; +} + +#endif From 124a70906f06849ba42e0e804d41505cab2cda75 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 14:51:47 +0200 Subject: [PATCH 70/92] added creator edit lock --- apps/opencs/view/world/creator.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 21 +++++++++++++++------ apps/opencs/view/world/genericcreator.hpp | 3 +++ apps/opencs/view/world/tablebottombox.cpp | 6 ++++++ apps/opencs/view/world/tablebottombox.hpp | 2 ++ apps/opencs/view/world/tablesubview.cpp | 1 + 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 564a5584e0..a5c1991c39 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -24,6 +24,8 @@ namespace CSVWorld virtual void reset() = 0; + virtual void setEditLock (bool locked) = 0; + signals: void done(); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index f30ada0923..cdfd715160 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -18,12 +18,12 @@ void CSVWorld::GenericCreator::update() mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); - mCreate->setEnabled (mErrors.empty()); + mCreate->setEnabled (mErrors.empty() && !mLocked); } CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) -: mData (data), mUndoStack (undoStack), mListId (id) +: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) { QHBoxLayout *layout = new QHBoxLayout; layout->setContentsMargins (0, 0, 0, 0); @@ -46,6 +46,12 @@ CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undo connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); } +void CSVWorld::GenericCreator::setEditLock (bool locked) +{ + mLocked = locked; + update(); +} + void CSVWorld::GenericCreator::reset() { mId->setText (""); @@ -77,9 +83,12 @@ void CSVWorld::GenericCreator::textChanged (const QString& text) void CSVWorld::GenericCreator::create() { - mUndoStack.push (new CSMWorld::CreateCommand ( - dynamic_cast (*mData.getTableModel (mListId)), - mId->text().toUtf8().constData())); + if (!mLocked) + { + mUndoStack.push (new CSMWorld::CreateCommand ( + dynamic_cast (*mData.getTableModel (mListId)), + mId->text().toUtf8().constData())); - emit done(); + emit done(); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index a89b4c31f2..b9ea58ad5c 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -20,6 +20,7 @@ namespace CSVWorld QPushButton *mCreate; QLineEdit *mId; std::string mErrors; + bool mLocked; private: @@ -30,6 +31,8 @@ namespace CSVWorld GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + virtual void setEditLock (bool locked); + virtual void reset(); virtual std::string getErrors() const; diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index b6d0e7807c..6a5dc83c1e 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -68,6 +68,12 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); } +void CSVWorld::TableBottomBox::setEditLock (bool locked) +{ + if (mCreator) + mCreator->setEditLock (locked); +} + CSVWorld::TableBottomBox::~TableBottomBox() { delete mCreator; diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 62c552c93a..21e68f51a3 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -46,6 +46,8 @@ namespace CSVWorld virtual ~TableBottomBox(); + void setEditLock (bool locked); + void setStatusBar (bool show); bool canCreateAndDelete() const; diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 8a828c9bd2..fb1a255cb6 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -46,6 +46,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D void CSVWorld::TableSubView::setEditLock (bool locked) { mTable->setEditLock (locked); + mBottom->setEditLock (locked); } void CSVWorld::TableSubView::editRequest (int row) From 5ec9d370cb1a68738e098397ba1691e17e539c23 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 15:27:15 +0200 Subject: [PATCH 71/92] check if ID is already in use when creating new IDs --- apps/opencs/model/world/data.cpp | 20 +++++++++++++++++++- apps/opencs/model/world/data.hpp | 2 ++ apps/opencs/view/world/genericcreator.cpp | 4 ++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d54b3ac161..2a95dfafeb 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -394,4 +394,22 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.skipRecord(); } } -} \ No newline at end of file +} + +bool CSMWorld::Data::hasId (const std::string& id) const +{ + return + getGlobals().searchId (id)!=-1 || + getGmsts().searchId (id)!=-1 || + getSkills().searchId (id)!=-1 || + getClasses().searchId (id)!=-1 || + getFactions().searchId (id)!=-1 || + getRaces().searchId (id)!=-1 || + getSounds().searchId (id)!=-1 || + getScripts().searchId (id)!=-1 || + getRegions().searchId (id)!=-1 || + getBirthsigns().searchId (id)!=-1 || + getSpells().searchId (id)!=-1 || + getCells().searchId (id)!=-1 || + getReferenceables().searchId (id)!=-1; +} diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index ca030d9b22..10a1fe50de 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -123,6 +123,8 @@ namespace CSMWorld void loadFile (const boost::filesystem::path& path, bool base); ///< Merging content of a file into base or modified. + + bool hasId (const std::string& id) const; }; } diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index cdfd715160..0ed7fde6d6 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -68,9 +68,9 @@ std::string CSVWorld::GenericCreator::getErrors() const { errors = "Missing ID"; } - else + else if (mData.hasId (id)) { - + errors = "ID is already in use"; } return errors; From 899e18b2cfe2c8333457c46ce092eb7c07c9d949 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 15:28:35 +0200 Subject: [PATCH 72/92] report ID errors also via input field tool tips --- apps/opencs/view/world/genericcreator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 0ed7fde6d6..de9c5941fd 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -17,6 +17,7 @@ void CSVWorld::GenericCreator::update() mErrors = getErrors(); mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str())); + mId->setToolTip (QString::fromUtf8 (mErrors.c_str())); mCreate->setEnabled (mErrors.empty() && !mLocked); } From 54f91d4b3a365c9eb8e9196b2512db6601873c47 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 06:48:25 -0700 Subject: [PATCH 73/92] Implement hand-to-hand attacks --- apps/openmw/mwclass/creature.cpp | 18 ++++++---- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/npc.cpp | 61 ++++++++++++++++++++++++++------ apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 9 ++--- 6 files changed, 71 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 43d6c61590..960f574309 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -154,7 +154,7 @@ namespace MWClass { } - void Creature::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { // NOTE: 'object' and/or 'attacker' may be empty. @@ -178,13 +178,19 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } - if(damage > 0.0f) + if(ishealth) { - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + if(damage > 0.0f) + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage); + getCreatureStats(ptr).setFatigue(fatigue); } - - float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; - setActorHealth(ptr, health, attacker); } void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 452a0054fe..0d8694ff81 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -44,7 +44,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; - virtual void onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index e1e4e78566..c0d42ec3d0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -346,10 +346,11 @@ namespace MWClass if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) { - othercls.onHit(victim, 0.0f, weapon, ptr, false); + othercls.onHit(victim, 0.0f, false, weapon, ptr, false); return; } + bool healthdmg; float damage = 0.0f; if(!weapon.isEmpty()) { @@ -373,15 +374,43 @@ namespace MWClass } damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } + healthdmg = true; + } + else + { + // Note: MCP contains an option to include Strength in hand-to-hand damage + // calculations. Some mods recommend using it, so we may want to include am + // option for it. + float minstrike = gmst.find("fMinHandToHandMult")->getFloat(); + float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); + damage = npcstats.getSkill(weapskill).getModified(); + damage *= minstrike + ((maxstrike-minstrike)*npcstats.getAttackStrength()); + if(!othercls.hasDetected(victim, ptr)) + { + damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); + MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); + MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); + } + + healthdmg = (othercls.getCreatureStats(victim).getFatigue().getCurrent() < 1.0f || + npcstats.isWerewolf()); + if(healthdmg) + { + // Not sure this is right... + damage *= gmst.find("fHandtoHandHealthPer")->getFloat() * 1.5f; + damage /= othercls.getArmorRating(victim); + } } if(ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, weapskill, 0); - othercls.onHit(victim, damage, weapon, ptr, true); + othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } - void Npc::onHit(const MWWorld::Ptr &ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const + void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + // NOTE: 'object' and/or 'attacker' may be empty. if(!successful) @@ -389,7 +418,7 @@ namespace MWClass // TODO: Handle HitAttemptOnMe script function // Missed - MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f); + sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); return; } @@ -429,13 +458,14 @@ namespace MWClass }; int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; - MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { - switch(get(armor).getEquipmentSkill(armor)) + if(object.isEmpty()) + sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); + else switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); @@ -448,12 +478,23 @@ namespace MWClass break; } } - - sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + else if(object.isEmpty()) + sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); } - float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; - setActorHealth(ptr, health, attacker); + if(ishealth) + { + if(damage > 0.0f) + sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); + float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; + setActorHealth(ptr, health, attacker); + } + else + { + MWMechanics::DynamicStat fatigue(getCreatureStats(ptr).getFatigue()); + fatigue.setCurrent(fatigue.getCurrent() - damage); + getCreatureStats(ptr).setFatigue(fatigue); + } } void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index a527740dee..3d429c982d 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -70,7 +70,7 @@ namespace MWClass virtual void hit(const MWWorld::Ptr& ptr, int type) const; - virtual void onHit(const MWWorld::Ptr& ptr, float damage, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 9bcfaa026e..123027c9c7 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -96,7 +96,7 @@ namespace MWWorld throw std::runtime_error("class cannot hit"); } - void Class::onHit(const Ptr& ptr, float damage, const Ptr& object, const Ptr& attacker, bool successful) const + void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const { throw std::runtime_error("class cannot be hit"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 9755686c9c..bfeeaba9f5 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -115,10 +115,11 @@ namespace MWWorld /// enums. ignored for creature attacks. /// (default implementation: throw an exceoption) - virtual void onHit(const Ptr& ptr, float damage, const Ptr &object, const Ptr &attacker, bool successful) const; - ///< Alerts \a ptr that it's being hit for \a damage health by \a object (sword, arrow, - /// etc). \a attacker specifies the actor responsible for the attack, and \a successful - /// specifies if the hit is successful or not. + virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; + ///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is + /// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the + /// actor responsible for the attack, and \a successful specifies if the hit is + /// successful or not. virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const; ///< Sets a new current health value for the actor, optionally specifying the object causing From 82958e65146bf8ac06aefb33efe02098e03376e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 28 Jul 2013 18:08:57 +0200 Subject: [PATCH 74/92] added custom creator for cell records --- apps/opencs/CMakeLists.txt | 1 + apps/opencs/view/world/cellcreator.cpp | 81 +++++++++++++++++++++++ apps/opencs/view/world/cellcreator.hpp | 40 +++++++++++ apps/opencs/view/world/genericcreator.cpp | 32 ++++++--- apps/opencs/view/world/genericcreator.hpp | 12 +++- apps/opencs/view/world/subviews.cpp | 5 +- 6 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 apps/opencs/view/world/cellcreator.cpp create mode 100644 apps/opencs/view/world/cellcreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 233c3d0fe9..8e5d81d764 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -58,6 +58,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator + cellcreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/view/world/cellcreator.cpp b/apps/opencs/view/world/cellcreator.cpp new file mode 100644 index 0000000000..74fb068700 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.cpp @@ -0,0 +1,81 @@ + +#include "cellcreator.hpp" + +#include +#include + +#include +#include +#include + +std::string CSVWorld::CellCreator::getId() const +{ + if (mType->currentIndex()==0) + return GenericCreator::getId(); + + std::ostringstream stream; + + stream << "#" << mX->value() << " " << mY->value(); + + return stream.str(); +} + +CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + mY = new QSpinBox (this); + mY->setVisible (false); + mY->setMinimum (std::numeric_limits::min()); + mY->setMaximum (std::numeric_limits::max()); + connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mY, true); + + mYLabel = new QLabel ("Y", this); + mYLabel->setVisible (false); + insertAtBeginning (mYLabel, false); + + mX = new QSpinBox (this); + mX->setVisible (false); + mX->setMinimum (std::numeric_limits::min()); + mX->setMaximum (std::numeric_limits::max()); + connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int))); + insertAtBeginning (mX, true); + + mXLabel = new QLabel ("X", this); + mXLabel->setVisible (false); + insertAtBeginning (mXLabel, false); + + mType = new QComboBox (this); + + mType->addItem ("Interior Cell"); + mType->addItem ("Exterior Cell"); + + connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int))); + + insertAtBeginning (mType, false); +} + +void CSVWorld::CellCreator::reset() +{ + mX->setValue (0); + mY->setValue (0); + mType->setCurrentIndex (0); + GenericCreator::reset(); +} + +void CSVWorld::CellCreator::setType (int index) +{ + setManualEditing (index==0); + mXLabel->setVisible (index==1); + mX->setVisible (index==1); + mYLabel->setVisible (index==1); + mY->setVisible (index==1); + + update(); +} + +void CSVWorld::CellCreator::valueChanged (int index) +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/cellcreator.hpp b/apps/opencs/view/world/cellcreator.hpp new file mode 100644 index 0000000000..a5473e2c97 --- /dev/null +++ b/apps/opencs/view/world/cellcreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_CELLCREATOR_H +#define CSV_WORLD_CELLCREATOR_H + +class QLabel; +class QSpinBox; +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class CellCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + QLabel *mXLabel; + QSpinBox *mX; + QLabel *mYLabel; + QSpinBox *mY; + + protected: + + virtual std::string getId() const; + + public: + + CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id); + + virtual void reset(); + + private slots: + + void setType (int index); + + void valueChanged (int index); + }; +} + +#endif diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index de9c5941fd..ba965bc94d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -22,24 +22,39 @@ void CSVWorld::GenericCreator::update() mCreate->setEnabled (mErrors.empty() && !mLocked); } +void CSVWorld::GenericCreator::setManualEditing (bool enabled) +{ + mId->setVisible (enabled); +} + +void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (0, widget, stretched ? 1 : 0); +} + +std::string CSVWorld::GenericCreator::getId() const +{ + return mId->text().toUtf8().constData(); +} + CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) { - QHBoxLayout *layout = new QHBoxLayout; - layout->setContentsMargins (0, 0, 0, 0); + mLayout = new QHBoxLayout; + mLayout->setContentsMargins (0, 0, 0, 0); mId = new QLineEdit; mId->setValidator (new IdValidator (this)); - layout->addWidget (mId, 1); + mLayout->addWidget (mId, 1); mCreate = new QPushButton ("Create"); - layout->addWidget (mCreate); + mLayout->addWidget (mCreate); QPushButton *cancelButton = new QPushButton ("Cancel"); - layout->addWidget (cancelButton); + mLayout->addWidget (cancelButton); - setLayout (layout); + setLayout (mLayout); connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done())); connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create())); @@ -63,7 +78,7 @@ std::string CSVWorld::GenericCreator::getErrors() const { std::string errors; - std::string id = mId->text().toUtf8().constData(); + std::string id = getId(); if (id.empty()) { @@ -87,8 +102,7 @@ void CSVWorld::GenericCreator::create() if (!mLocked) { mUndoStack.push (new CSMWorld::CreateCommand ( - dynamic_cast (*mData.getTableModel (mListId)), - mId->text().toUtf8().constData())); + dynamic_cast (*mData.getTableModel (mListId)), getId())); emit done(); } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index b9ea58ad5c..2798d89a14 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -3,6 +3,7 @@ class QPushButton; class QLineEdit; +class QHBoxLayout; #include "creator.hpp" @@ -20,12 +21,20 @@ namespace CSVWorld QPushButton *mCreate; QLineEdit *mId; std::string mErrors; + QHBoxLayout *mLayout; bool mLocked; - private: + protected: void update(); + virtual void setManualEditing (bool enabled); + ///< Enable/disable manual ID editing (enabled by default). + + void insertAtBeginning (QWidget *widget, bool stretched); + + virtual std::string getId() const; + public: GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, @@ -39,6 +48,7 @@ namespace CSVWorld ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. + private slots: void textChanged (const QString& text); diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 3ad4b36c6d..9fbbe4a0da 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -8,6 +8,7 @@ #include "scriptsubview.hpp" #include "regionmapsubview.hpp" #include "genericcreator.hpp" +#include "cellcreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -28,7 +29,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_Cells, CSMWorld::UniversalId::Type_Referenceables, CSMWorld::UniversalId::Type_References, @@ -39,6 +39,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Cells, + new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); From 465f4d2063fcd9e6d4b69a2e534c15aec0be7b41 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 09:56:03 -0700 Subject: [PATCH 75/92] Properly handle starting an animation at the end --- apps/openmw/mwrender/animation.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 8d6a47e5ec..843e5367e7 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -589,7 +589,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo state.mSource = *iter; state.mSpeedMult = speedmult; state.mLoopCount = loops; - state.mPlaying = true; + state.mPlaying = (state.mTime < state.mStopTime); state.mPriority = priority; state.mGroups = groups; state.mAutoDisable = autodisable; @@ -602,6 +602,22 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo textkey++; } + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + if(state.mTime >= state.mLoopStopTime) + break; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey); + textkey++; + } + } + break; } } From 1af48ab6e0d4cdaa2f298b983c30437ea77e1b14 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 15:17:01 -0700 Subject: [PATCH 76/92] Decrease weapon condition on successful hits --- apps/openmw/mwclass/npc.cpp | 89 +++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c0d42ec3d0..7bb28ffe7c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -354,6 +354,7 @@ namespace MWClass float damage = 0.0f; if(!weapon.isEmpty()) { + const bool weaphashealth = get(weapon).hasItemHealth(weapon); const unsigned char *attack = NULL; if(type == MWMechanics::CreatureStats::AT_Chop) attack = weapon.get()->mBase->mData.mChop; @@ -365,13 +366,23 @@ namespace MWClass { damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength()); damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); - //damage *= weapon_current_health / weapon_max_health; + if(weaphashealth) + { + int weapmaxhealth = weapon.get()->mBase->mData.mHealth; + if(weapon.getCellRef().mCharge == -1) + weapon.getCellRef().mCharge = weapmaxhealth; + damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth; + } if(!othercls.hasDetected(victim, ptr)) { damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } + weapon.getCellRef().mCharge -= std::min(std::max(1, + (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), + weapon.getCellRef().mCharge); + damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } healthdmg = true; @@ -396,8 +407,7 @@ namespace MWClass npcstats.isWerewolf()); if(healthdmg) { - // Not sure this is right... - damage *= gmst.find("fHandtoHandHealthPer")->getFloat() * 1.5f; + damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); damage /= othercls.getArmorRating(victim); } } @@ -440,46 +450,47 @@ namespace MWClass MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); - // Hit percentages: - // cuirass = 30% - // shield, helmet, greaves, boots, pauldrons = 10% each - // guantlets = 5% each - static const int hitslots[20] = { - MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, - MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, - MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, - MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft, - MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet, - MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves, - MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots, - MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron, - MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron, - MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet - }; - int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; - - MWWorld::InventoryStore &inv = getInventoryStore(ptr); - MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); - MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); - if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) + if(object.isEmpty()) + sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); + else { - if(object.isEmpty()) - sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); - else switch(get(armor).getEquipmentSkill(armor)) + // Hit percentages: + // cuirass = 30% + // shield, helmet, greaves, boots, pauldrons = 10% each + // guantlets = 5% each + static const int hitslots[20] = { + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass, + MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft, + MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet, + MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves, + MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots, + MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron, + MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron, + MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet + }; + int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; + + MWWorld::InventoryStore &inv = getInventoryStore(ptr); + MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); + MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); + if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { - case ESM::Skill::LightArmor: - sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); - break; - case ESM::Skill::MediumArmor: - sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); - break; - case ESM::Skill::HeavyArmor: - sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); - break; + switch(get(armor).getEquipmentSkill(armor)) + { + case ESM::Skill::LightArmor: + sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::MediumArmor: + sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f); + break; + case ESM::Skill::HeavyArmor: + sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f); + break; + } } } - else if(object.isEmpty()) - sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); } if(ishealth) From 39507e3f0f3314b5a17585945e6c3c209b91480b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 15:51:17 -0700 Subject: [PATCH 77/92] Decrease armor condition on successful hits --- apps/openmw/mwclass/npc.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 7bb28ffe7c..8bb332ae1b 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -382,8 +382,6 @@ namespace MWClass weapon.getCellRef().mCharge -= std::min(std::max(1, (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); - - damage /= std::min(1.0f + othercls.getArmorRating(victim)/std::max(1.0f, damage), 4.0f); } healthdmg = true; } @@ -406,10 +404,7 @@ namespace MWClass healthdmg = (othercls.getCreatureStats(victim).getFatigue().getCurrent() < 1.0f || npcstats.isWerewolf()); if(healthdmg) - { damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); - damage /= othercls.getArmorRating(victim); - } } if(ptr.getRefData().getHandle() == "player") skillUsageSucceeded(ptr, weapskill, 0); @@ -451,8 +446,12 @@ namespace MWClass MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); if(object.isEmpty()) + { + if(ishealth) + damage /= getArmorRating(ptr); sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); - else + } + else if(ishealth) { // Hit percentages: // cuirass = 30% @@ -472,11 +471,20 @@ namespace MWClass }; int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)]; + float damagediff = damage; + damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); + damagediff -= damage; + MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot); MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr()); if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name()) { + ESM::CellRef &armorref = armor.getCellRef(); + if(armorref.mCharge == -1) + armorref.mCharge = armor.get()->mBase->mData.mHealth; + armorref.mCharge -= std::min(std::max(1, (int)damagediff), + armorref.mCharge); switch(get(armor).getEquipmentSkill(armor)) { case ESM::Skill::LightArmor: From 3744850545246b0d79cb5dbd4469b7c68353828b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 28 Jul 2013 16:07:09 -0700 Subject: [PATCH 78/92] Fix hand-to-hand health damage reduction --- apps/openmw/mwclass/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8bb332ae1b..f767ac6704 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -448,7 +448,7 @@ namespace MWClass if(object.isEmpty()) { if(ishealth) - damage /= getArmorRating(ptr); + damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f); sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f); } else if(ishealth) From 6164e5bae632015478ed8120fac599390bf4c9a0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 29 Jul 2013 02:32:08 +0200 Subject: [PATCH 79/92] Restored window resizing --- apps/openmw/mwbase/windowmanager.hpp | 2 + apps/openmw/mwgui/settingswindow.cpp | 8 -- apps/openmw/mwgui/windowmanagerimp.cpp | 52 +++----- apps/openmw/mwgui/windowmanagerimp.hpp | 2 + apps/openmw/mwinput/inputmanagerimp.cpp | 20 ++- apps/openmw/mwinput/inputmanagerimp.hpp | 5 +- apps/openmw/mwrender/renderingmanager.cpp | 52 ++++---- apps/openmw/mwrender/renderingmanager.hpp | 5 +- apps/openmw/mwworld/weather.cpp | 3 +- extern/sdl4ogre/CMakeLists.txt | 1 + extern/sdl4ogre/events.h | 6 +- extern/sdl4ogre/sdlinputwrapper.cpp | 76 ++++++++++-- extern/sdl4ogre/sdlinputwrapper.hpp | 6 +- extern/sdl4ogre/sdlwindowhelper.cpp | 115 ++++++++++++++++++ extern/sdl4ogre/sdlwindowhelper.hpp | 31 +++++ libs/openengine/gui/manager.cpp | 5 + libs/openengine/gui/manager.hpp | 2 + libs/openengine/ogre/renderer.cpp | 141 ++-------------------- libs/openengine/ogre/renderer.hpp | 20 +-- 19 files changed, 307 insertions(+), 245 deletions(-) create mode 100644 extern/sdl4ogre/sdlwindowhelper.cpp create mode 100644 extern/sdl4ogre/sdlwindowhelper.hpp diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 36d4ab8bb5..d85c28fe24 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -237,6 +237,8 @@ namespace MWBase virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; + virtual void windowResized(int x, int y) = 0; + virtual void executeInConsole (const std::string& path) = 0; virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 8443aaf30c..eada151a43 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -272,15 +272,12 @@ namespace MWGui if (index == MyGUI::ITEM_NONE) return; - /* ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); dialog->open("#{sNotifyMessage67}"); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventCancelClicked.clear(); dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); - */ - onResolutionAccept(); } void SettingsWindow::onResolutionAccept() @@ -293,9 +290,6 @@ namespace MWGui Settings::Manager::setInt("resolution y", "Video", resY); apply(); - - MWBase::Environment::get().getWindowManager()-> - messageBox("New resolution will be applied after a restart", std::vector()); } void SettingsWindow::onResolutionCancel() @@ -364,8 +358,6 @@ namespace MWGui { Settings::Manager::setBool("fullscreen", "Video", newState); apply(); - MWBase::Environment::get().getWindowManager()-> - messageBox("Fullscreen will be applied after a restart", std::vector()); } } else if (_sender == mVSyncButton) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 38bc0481d4..6fcc19a477 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -877,49 +877,31 @@ namespace MWGui setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")); - //bool changeRes = false; - bool windowRecreated = false; for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { - /*if (it->first == "Video" && ( - it->second == "resolution x" - || it->second == "resolution y")) - { - changeRes = true; - }*/ - if (it->first == "Video" && it->second == "vsync") - windowRecreated = true; - else if (it->first == "HUD" && it->second == "crosshair") + if (it->first == "HUD" && it->second == "crosshair") mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); else if (it->first == "GUI" && it->second == "subtitles") mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); } + } - /* - if (changeRes) - { - int x = Settings::Manager::getInt("resolution x", "Video"); - int y = Settings::Manager::getInt("resolution y", "Video"); - mHud->onResChange(x, y); - mConsole->onResChange(x, y); - mMenu->onResChange(x, y); - mSettingsWindow->center(); - mAlchemyWindow->center(); - mScrollWindow->center(); - mBookWindow->center(); - mQuickKeysMenu->center(); - mSpellBuyingWindow->center(); - mLoadingScreen->onResChange (x,y); - mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); - mInputBlocker->setSize(MyGUI::IntSize(x,y)); - } - */ - if (windowRecreated) - { - mGuiManager->updateWindow (mRendering->getWindow ()); - mLoadingScreen->updateWindow (mRendering->getWindow ()); - } + void WindowManager::windowResized(int x, int y) + { + mHud->onResChange(x, y); + mConsole->onResChange(x, y); + mMenu->onResChange(x, y); + mSettingsWindow->center(); + mAlchemyWindow->center(); + mScrollWindow->center(); + mBookWindow->center(); + mQuickKeysMenu->center(); + mSpellBuyingWindow->center(); + mLoadingScreen->onResChange (x,y); + mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y)); + mInputBlocker->setSize(MyGUI::IntSize(x,y)); + mGuiManager->windowResized(); } void WindowManager::pushGuiMode(GuiMode mode) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 42f2f4dc27..6395b1bb57 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -225,6 +225,8 @@ namespace MWGui virtual void processChangedSettings(const Settings::CategorySettingVector& changed); + virtual void windowResized(int x, int y); + virtual void executeInConsole (const std::string& path); virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 0addc7daf3..eae5d7d193 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -385,7 +385,7 @@ namespace MWInput if (mControlSwitch["playerviewswitch"]) { // work around preview mode toggle when pressing Alt+Tab - if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(KMOD_ALT)) { + if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) { if (mPreviewPOVDelay <= 0.5 && (mPreviewPOVDelay += dt) > 0.5) { @@ -434,13 +434,9 @@ namespace MWInput void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) { - bool changeRes = false; for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it) { - if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y")) - changeRes = true; - if (it->first == "Input" && it->second == "invert y axis") mInvertY = Settings::Manager::getBool("invert y axis", "Input"); @@ -451,9 +447,6 @@ namespace MWInput mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); } - - if (changeRes) - adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video")); } bool InputManager::getControlSwitch (const std::string& sw) @@ -617,15 +610,18 @@ namespace MWInput return true; } - bool InputManager::windowFocusChange(bool have_focus) + void InputManager::windowFocusChange(bool have_focus) { - return true; } - bool InputManager::windowVisibilityChange(bool visible) + void InputManager::windowVisibilityChange(bool visible) { //TODO: Pause game? - return true; + } + + void InputManager::windowResized(int x, int y) + { + mOgre.windowResized(x,y); } void InputManager::toggleMainMenu() diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 0c7940fd65..ef7ef75a89 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -94,8 +94,9 @@ namespace MWInput virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); - virtual bool windowVisibilityChange( bool visible ); - virtual bool windowFocusChange( bool have_focus ); + virtual void windowVisibilityChange( bool visible ); + virtual void windowFocusChange( bool have_focus ); + virtual void windowResized (int x, int y); virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index de4eafd6d9..4a9de259f7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include #include @@ -77,9 +79,9 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b } mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5); - mRendering.setWindowEventListener(this); mRendering.getWindow()->addListener(this); + mRendering.setWindowListener(this); mCompositors = new Compositors(mRendering.getViewport()); @@ -186,7 +188,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b RenderingManager::~RenderingManager () { mRendering.getWindow()->removeListener(this); - mRendering.removeWindowEventListener(this); delete mPlayerAnimation; delete mCamera; @@ -718,7 +719,7 @@ Compositors* RenderingManager::getCompositors() void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings) { - //bool changeRes = false; + bool changeRes = false; bool rebuild = false; // rebuild static geometry (necessary after any material changes) for (Settings::CategorySettingVector::const_iterator it=settings.begin(); it != settings.end(); ++it) @@ -732,11 +733,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior()) configureFog(*MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()); } - /*else if (it->first == "Video" && ( + else if (it->first == "Video" && ( it->second == "resolution x" || it->second == "resolution y" || it->second == "fullscreen")) - changeRes = true;*/ + changeRes = true; else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); else if ((it->second == "texture filtering" && it->first == "General") @@ -790,24 +791,31 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec } } - /* if (changeRes) { unsigned int x = Settings::Manager::getInt("resolution x", "Video"); unsigned int y = Settings::Manager::getInt("resolution y", "Video"); + bool fullscreen = Settings::Manager::getBool("fullscreen", "Video"); + + SDL_Window* window = mRendering.getSDLWindow(); + + SDL_SetWindowFullscreen(window, 0); - SDL_SetWindowFullscreen(mRendering.getSDLWindow(), 0); + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) + SDL_RestoreWindow(window); - if (x != mRendering.getWindow()->getWidth() || y != mRendering.getWindow()->getHeight()) + if (fullscreen) { - SDL_SetWindowSize(mRendering.getSDLWindow(), x, y); - mRendering.getWindow()->resize(x, y); + SDL_DisplayMode mode; + SDL_GetWindowDisplayMode(window, &mode); + mode.w = x; + mode.h = y; + SDL_SetWindowDisplayMode(window, &mode); + SDL_SetWindowFullscreen(window, fullscreen); } - - SDL_SetWindowFullscreen(mRendering.getSDLWindow(), Settings::Manager::getBool("fullscreen", "Video") ? SDL_WINDOW_FULLSCREEN : 0); - //mRendering.getWindow()->setFullscreen(Settings::Manager::getBool("fullscreen", "Video"), x, y); + else + SDL_SetWindowSize(window, x, y); } - */ mWater->processChangedSettings(settings); @@ -825,24 +833,14 @@ void RenderingManager::setMenuTransparency(float val) tex->getBuffer()->unlock(); } -void RenderingManager::windowResized(Ogre::RenderWindow* rw) +void RenderingManager::windowResized(int x, int y) { - Settings::Manager::setInt("resolution x", "Video", rw->getWidth()); - Settings::Manager::setInt("resolution y", "Video", rw->getHeight()); - mRendering.adjustViewport(); mCompositors->recreate(); - mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); - - const Settings::CategorySettingVector& changed = Settings::Manager::apply(); - MWBase::Environment::get().getInputManager()->processChangedSettings(changed); - MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); -} + mVideoPlayer->setResolution (x, y); -void RenderingManager::windowClosed(Ogre::RenderWindow* rw) -{ - Ogre::Root::getSingleton ().queueEndRendering (); + MWBase::Environment::get().getWindowManager()->windowResized(x,y); } void RenderingManager::applyCompositors() diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index cdcde8246e..8b55d90bfc 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -50,7 +50,7 @@ namespace MWRender class VideoPlayer; class Animation; -class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener +class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener { private: virtual MWRender::Objects& getObjects(); @@ -204,8 +204,7 @@ public: void frameStarted(float dt); protected: - virtual void windowResized(Ogre::RenderWindow* rw); - virtual void windowClosed(Ogre::RenderWindow* rw); + virtual void windowResized(int x, int y); private: sh::Factory* mFactory; diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index c8283fe0db..587b7847bc 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -551,7 +551,6 @@ void WeatherManager::stopSounds(bool stopAll) Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const { - const MWBase::World *world = MWBase::Environment::get().getWorld(); std::vector probability; RegionModMap::const_iterator iter = mRegionMods.find(Misc::StringUtils::lowerCase(region->mId)); @@ -581,7 +580,7 @@ Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const int chance = (rand() % 100) + 1; // 1..100 int sum = 0; - int i = 0; + unsigned int i = 0; for (; i < probability.size(); ++i) { sum += probability[i]; diff --git a/extern/sdl4ogre/CMakeLists.txt b/extern/sdl4ogre/CMakeLists.txt index 493d1ce476..5b31974e5f 100644 --- a/extern/sdl4ogre/CMakeLists.txt +++ b/extern/sdl4ogre/CMakeLists.txt @@ -5,6 +5,7 @@ set(SDL4OGRE_LIBRARY "sdl4ogre") set(SDL4OGRE_SOURCE_FILES sdlinputwrapper.cpp sdlcursormanager.cpp + sdlwindowhelper.cpp ) set(SDL4OGRE_HEADER_FILES diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index 13f8b31011..e6e8434cb9 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -65,10 +65,12 @@ public: virtual ~WindowListener() {} /** @remarks The window's visibility changed */ - virtual bool windowVisibilityChange( bool visible ) = 0; + virtual void windowVisibilityChange( bool visible ) {}; /** @remarks The window got / lost input focus */ - virtual bool windowFocusChange( bool have_focus ) = 0; + virtual void windowFocusChange( bool have_focus ) {} + + virtual void windowResized (int x, int y) {} }; } diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index 89dc650650..a30f10d6a3 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -4,13 +4,6 @@ #include #include -/* -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX -# include -# include -# include -#endif -*/ namespace SFO { @@ -19,7 +12,6 @@ namespace SFO InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) : mSDLWindow(window), mOgreWindow(ogreWindow), - mOwnWindow(false), mWarpCompensate(false), mMouseRelative(false), mGrabPointer(false), @@ -27,14 +19,18 @@ namespace SFO mMouseZ(0), mMouseY(0), mMouseX(0), - mMouseInWindow(true) + mMouseInWindow(true), + mJoyListener(NULL), + mKeyboardListener(NULL), + mMouseListener(NULL), + mWindowListener(NULL) { _setupOISKeys(); } InputWrapper::~InputWrapper() { - if(mSDLWindow != NULL && mOwnWindow) + if(mSDLWindow != NULL) SDL_DestroyWindow(mSDLWindow); mSDLWindow = NULL; } @@ -76,6 +72,25 @@ namespace SFO case SDL_TEXTINPUT: mKeyboardListener->textInput(evt.text); break; + case SDL_JOYAXISMOTION: + if (mJoyListener) + mJoyListener->axisMoved(evt.jaxis, evt.jaxis.axis); + break; + case SDL_JOYBUTTONDOWN: + if (mJoyListener) + mJoyListener->buttonPressed(evt.jbutton, evt.jbutton.button); + break; + case SDL_JOYBUTTONUP: + if (mJoyListener) + mJoyListener->buttonReleased(evt.jbutton, evt.jbutton.button); + break; + case SDL_JOYDEVICEADDED: + //SDL_JoystickOpen(evt.jdevice.which); + //std::cout << "Detected a new joystick: " << SDL_JoystickNameForIndex(evt.jdevice.which) << std::endl; + break; + case SDL_JOYDEVICEREMOVED: + //std::cout << "A joystick has been removed" << std::endl; + break; case SDL_WINDOWEVENT: handleWindowEvent(evt); break; @@ -100,25 +115,62 @@ namespace SFO SDL_SetWindowGrab(mSDLWindow, SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE); break; - case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + int w,h; + SDL_GetWindowSize(mSDLWindow, &w, &h); + // TODO: Fix Ogre to handle this more consistently +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + mOgreWindow->windowMovedOrResized(); +#else + mOgreWindow->resize(w, h); +#endif + if (mWindowListener) + mWindowListener->windowResized(evt.window.data1, evt.window.data2); + + case SDL_WINDOWEVENT_RESIZED: + // TODO: Fix Ogre to handle this more consistently + #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + mOgreWindow->windowMovedOrResized(); + #else + mOgreWindow->resize(evt.window.data1, evt.window.data2); + #endif + if (mWindowListener) + mWindowListener->windowResized(evt.window.data1, evt.window.data2); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + if (mWindowListener) + mWindowListener->windowFocusChange(true); + break; case SDL_WINDOWEVENT_FOCUS_LOST: + if (mWindowListener) + mWindowListener->windowFocusChange(false); + break; case SDL_WINDOWEVENT_CLOSE: break; case SDL_WINDOWEVENT_SHOWN: mOgreWindow->setVisible(true); + if (mWindowListener) + mWindowListener->windowVisibilityChange(true); break; case SDL_WINDOWEVENT_HIDDEN: mOgreWindow->setVisible(false); + if (mWindowListener) + mWindowListener->windowVisibilityChange(false); break; } } - bool InputWrapper::isModifierHeld(int mod) + bool InputWrapper::isModifierHeld(SDL_Keymod mod) { return SDL_GetModState() & mod; } + bool InputWrapper::isKeyDown(SDL_Scancode key) + { + return SDL_GetKeyboardState(NULL)[key]; + } + /// \brief Moves the mouse to the specified point within the viewport void InputWrapper::warpMouse(int x, int y) { diff --git a/extern/sdl4ogre/sdlinputwrapper.hpp b/extern/sdl4ogre/sdlinputwrapper.hpp index 08b329925f..66bf60c7c3 100644 --- a/extern/sdl4ogre/sdlinputwrapper.hpp +++ b/extern/sdl4ogre/sdlinputwrapper.hpp @@ -22,9 +22,11 @@ namespace SFO void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; } void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; } void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; } + void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; } void capture(); - bool isModifierHeld(int mod); + bool isModifierHeld(SDL_Keymod mod); + bool isKeyDown(SDL_Scancode key); void setMouseRelative(bool relative); bool getMouseRelative() { return mMouseRelative; } @@ -47,6 +49,7 @@ namespace SFO SFO::MouseListener* mMouseListener; SFO::KeyListener* mKeyboardListener; SFO::WindowListener* mWindowListener; + SFO::JoyListener* mJoyListener; typedef boost::unordered_map KeyMap; KeyMap mKeyMap; @@ -66,7 +69,6 @@ namespace SFO SDL_Window* mSDLWindow; Ogre::RenderWindow* mOgreWindow; - bool mOwnWindow; }; } diff --git a/extern/sdl4ogre/sdlwindowhelper.cpp b/extern/sdl4ogre/sdlwindowhelper.cpp new file mode 100644 index 0000000000..14371c947d --- /dev/null +++ b/extern/sdl4ogre/sdlwindowhelper.cpp @@ -0,0 +1,115 @@ +#include "sdlwindowhelper.hpp" + +#include +#include + +#include +#include + +namespace SFO +{ + +SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h, + const std::string& title, bool fullscreen, Ogre::NameValuePairList params) + : mSDLWindow(window) +{ + //get the native whnd + struct SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + + if (SDL_GetWindowWMInfo(mSDLWindow, &wmInfo) == -1) + throw std::runtime_error("Couldn't get WM Info!"); + + Ogre::String winHandle; + + switch (wmInfo.subsystem) + { +#ifdef WIN32 + case SDL_SYSWM_WINDOWS: + // Windows code + winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window); + break; +#elif __MACOSX__ + case SDL_SYSWM_COCOA: + //required to make OGRE play nice with our window + params.insert(std::make_pair("macAPI", "cocoa")); + params.insert(std::make_pair("macAPICocoaUseNSView", "true")); + + winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo)); + break; +#else + case SDL_SYSWM_X11: + winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window); + break; +#endif + default: + throw std::runtime_error("Unexpected WM!"); + break; + } + + /// \todo externalWindowHandle is deprecated according to the source code. Figure out a way to get parentWindowHandle + /// to work properly. On Linux/X11 it causes an occasional GLXBadDrawable error. + params.insert(std::make_pair("externalWindowHandle", winHandle)); + + mWindow = Ogre::Root::getSingleton().createRenderWindow(title, w, h, fullscreen, ¶ms); +} + +void SDLWindowHelper::setWindowIcon(const std::string &name) +{ + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); + if (texture.isNull()) + { + std::stringstream error; + error << "Window icon not found: " << name; + throw std::runtime_error(error.str()); + } + Ogre::Image image; + texture->convertToImage(image); + + SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF); + + //copy the Ogre texture to an SDL surface + for(size_t x = 0; x < texture->getWidth(); ++x) + { + for(size_t y = 0; y < texture->getHeight(); ++y) + { + Ogre::ColourValue clr = image.getColourAt(x, y, 0); + + //set the pixel on the SDL surface to the same value as the Ogre texture's + int bpp = surface->format->BytesPerPixel; + /* Here p is the address to the pixel we want to set */ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; + Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255); + switch(bpp) { + case 1: + *p = pixel; + break; + + case 2: + *(Uint16 *)p = pixel; + break; + + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (pixel >> 16) & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = pixel & 0xff; + } else { + p[0] = pixel & 0xff; + p[1] = (pixel >> 8) & 0xff; + p[2] = (pixel >> 16) & 0xff; + } + break; + + case 4: + *(Uint32 *)p = pixel; + break; + } + } + } + + SDL_SetWindowIcon(mSDLWindow, surface); + SDL_FreeSurface(surface); +} + +} diff --git a/extern/sdl4ogre/sdlwindowhelper.hpp b/extern/sdl4ogre/sdlwindowhelper.hpp new file mode 100644 index 0000000000..834716b223 --- /dev/null +++ b/extern/sdl4ogre/sdlwindowhelper.hpp @@ -0,0 +1,31 @@ +#ifndef SDL4OGRE_SDLWINDOWHELPER_H +#define SDL4OGRE_SDLWINDOWHELPER_H + +#include + +namespace Ogre +{ + class RenderWindow; +} +struct SDL_Window; + +namespace SFO +{ + + /// @brief Creates an Ogre window from an SDL window and allows setting an Ogre texture as window icon + class SDLWindowHelper + { + public: + SDLWindowHelper (SDL_Window* window, int w, int h, const std::string& title, bool fullscreen, Ogre::NameValuePairList params); + void setWindowIcon(const std::string& name); + Ogre::RenderWindow* getWindow() { return mWindow; } + + private: + Ogre::RenderWindow* mWindow; + SDL_Window* mSDLWindow; + }; + +} + + +#endif diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index a0a4ab0aea..9fd57e2c15 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -612,6 +612,11 @@ void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd) } } +void MyGUIManager::windowResized() +{ + mRenderManager->setActiveViewport(0); +} + void MyGUIManager::shutdown() { mGui->shutdown (); diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 9535f2a24e..cca70dfcfe 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -43,6 +43,8 @@ namespace GUI void updateWindow (Ogre::RenderWindow* wnd); + void windowResized(); + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); void shutdown(); diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index de090a0f1d..f0d6bdb0fa 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -3,7 +3,6 @@ #include "particles.hpp" #include -#include #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -19,6 +18,8 @@ #include +#include + #include #include #include @@ -54,9 +55,6 @@ void OgreRenderer::cleanup() delete mRoot; mRoot = NULL; - if (mWindowIconSurface) - SDL_FreeSurface(mWindowIconSurface); - // If we don't do this, the desktop resolution is not restored on exit SDL_SetWindowFullscreen(mSDLWindow, 0); @@ -237,25 +235,6 @@ void OgreRenderer::configure(const std::string &logPath, rs->setConfigOption ("RTT Preferred Mode", rttMode); } -void OgreRenderer::recreateWindow(const std::string &title, const WindowSettings &settings) -{ - Ogre::ColourValue viewportBG = mView->getBackgroundColour(); - - mRoot->destroyRenderTarget(mWindow); - NameValuePairList params; - params.insert(std::make_pair("title", title)); - params.insert(std::make_pair("FSAA", settings.fsaa)); - params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false")); - - mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); - - // Create one viewport, entire window - mView = mWindow->addViewport(mCamera); - mView->setBackgroundColour(viewportBG); - - adjustViewport(); -} - void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings) { assert(mRoot); @@ -286,55 +265,14 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& settings.window_x, // width, in pixels settings.window_y, // height, in pixels SDL_WINDOW_SHOWN - | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) + | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) | SDL_WINDOW_RESIZABLE ); - //get the native whnd - struct SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - - if(SDL_FALSE == SDL_GetWindowWMInfo(mSDLWindow, &wmInfo)) - throw std::runtime_error("Couldn't get WM Info!"); - - Ogre::String winHandle; - - switch(wmInfo.subsystem) - { -#ifdef WIN32 - case SDL_SYSWM_WINDOWS: - // Windows code - winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window); - break; -#elif __MACOSX__ - case SDL_SYSWM_COCOA: - //required to make OGRE play nice with our window - params.insert(std::make_pair("macAPI", "cocoa")); - params.insert(std::make_pair("macAPICocoaUseNSView", "true")); - - winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo)); - break; -#else - case SDL_SYSWM_X11: - winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window); - break; -#endif - default: - throw std::runtime_error("Unexpected WM!"); - break; - } - - /// \todo externalWindowHandle is deprecated according to the source code. Figure out a way to get parentWindowHandle - /// to work properly. On Linux/X11 it causes an occasional GLXBadDrawable error. - params.insert(std::make_pair("externalWindowHandle", winHandle)); - - mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); - - // Set the window icon + SFO::SDLWindowHelper helper(mSDLWindow, settings.window_x, settings.window_y, title, settings.fullscreen, params); if (settings.icon != "") - { - mWindowIconSurface = ogreTextureToSDLSurface(settings.icon); - SDL_SetWindowIcon(mSDLWindow, mWindowIconSurface); - } + helper.setWindowIcon(settings.icon); + mWindow = helper.getWindow(); + // create the semi-transparent black background texture used by the GUI. // has to be created in code with TU_DYNAMIC_WRITE_ONLY param @@ -380,73 +318,12 @@ void OgreRenderer::adjustViewport() } } -void OgreRenderer::setWindowEventListener(Ogre::WindowEventListener* listener) -{ - Ogre::WindowEventUtilities::addWindowEventListener(mWindow, listener); -} - -void OgreRenderer::removeWindowEventListener(Ogre::WindowEventListener* listener) -{ - Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, listener); -} - void OgreRenderer::setFov(float fov) { mCamera->setFOVy(Degree(fov)); } -SDL_Surface* OgreRenderer::ogreTextureToSDLSurface(const std::string& name) +void OgreRenderer::windowResized(int x, int y) { - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME); - if (texture.isNull()) - { - std::stringstream error; - error << "Window icon not found: " << name; - throw std::runtime_error(error.str()); - } - Ogre::Image image; - texture->convertToImage(image); - - SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF); - - //copy the Ogre texture to an SDL surface - for(size_t x = 0; x < texture->getWidth(); ++x) - { - for(size_t y = 0; y < texture->getHeight(); ++y) - { - Ogre::ColourValue clr = image.getColourAt(x, y, 0); - - //set the pixel on the SDL surface to the same value as the Ogre texture's - int bpp = surface->format->BytesPerPixel; - /* Here p is the address to the pixel we want to set */ - Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; - Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255); - switch(bpp) { - case 1: - *p = pixel; - break; - - case 2: - *(Uint16 *)p = pixel; - break; - - case 3: - if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { - p[0] = (pixel >> 16) & 0xff; - p[1] = (pixel >> 8) & 0xff; - p[2] = pixel & 0xff; - } else { - p[0] = pixel & 0xff; - p[1] = (pixel >> 8) & 0xff; - p[2] = (pixel >> 16) & 0xff; - } - break; - - case 4: - *(Uint32 *)p = pixel; - break; - } - } - } - return surface; + mWindowListener->windowResized(x,y); } diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index f4b38c52d8..9e83bf4f67 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -74,6 +74,12 @@ namespace OEngine class Fader; + class WindowSizeListener + { + public: + virtual void windowResized (int x, int y) = 0; + }; + class OgreRenderer { #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE @@ -83,7 +89,6 @@ namespace OEngine #endif Ogre::RenderWindow *mWindow; SDL_Window *mSDLWindow; - SDL_Surface *mWindowIconSurface; Ogre::SceneManager *mScene; Ogre::Camera *mCamera; Ogre::Viewport *mView; @@ -107,7 +112,7 @@ namespace OEngine std::vector mAffectorFactories; bool logging; - SDL_Surface* ogreTextureToSDLSurface(const std::string& name); + WindowSizeListener* mWindowListener; public: OgreRenderer() @@ -117,7 +122,7 @@ namespace OEngine , mScene(NULL) , mCamera(NULL) , mView(NULL) - , mWindowIconSurface(NULL) + , mWindowListener(NULL) #ifdef ENABLE_PLUGIN_CgProgramManager , mCgPlugin(NULL) #endif @@ -140,9 +145,6 @@ namespace OEngine ~OgreRenderer() { cleanup(); } - void setWindowEventListener(Ogre::WindowEventListener* listener); - void removeWindowEventListener(Ogre::WindowEventListener* listener); - /** Configure the renderer. This will load configuration files and set up the Root and logging classes. */ void configure( @@ -154,8 +156,6 @@ namespace OEngine /// Create a window with the given title void createWindow(const std::string &title, const WindowSettings& settings); - void recreateWindow (const std::string &title, const WindowSettings& settings); - /// Set up the scene manager, camera and viewport void createScene(const std::string& camName="Camera",// Camera name float fov=55, // Field of view angle @@ -181,6 +181,8 @@ namespace OEngine float getFPS(); + void windowResized(int x, int y); + /// Get the Root Ogre::Root *getRoot() { return mRoot; } @@ -202,6 +204,8 @@ namespace OEngine /// Viewport Ogre::Viewport *getViewport() { return mView; } + void setWindowListener(WindowSizeListener* listener) { mWindowListener = listener; } + void adjustViewport(); }; } From 0d2b357bdfe86384bcecfa9e697905e889b81878 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 12:38:57 +0200 Subject: [PATCH 80/92] use SDL_WINDOWPOS_CENTERED_DISPLAY and SDL_WINDOWPOS_UNDEFINED_DISPLAY instead of coordinates --- libs/openengine/ogre/renderer.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index de090a0f1d..eceab43da8 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -266,25 +266,23 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& params.insert(std::make_pair("FSAA", settings.fsaa)); params.insert(std::make_pair("vsync", settings.vsync ? "true" : "false")); - int pos_x = SDL_WINDOWPOS_UNDEFINED, - pos_y = SDL_WINDOWPOS_UNDEFINED; + int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen), + pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(settings.screen); if(settings.fullscreen) { - SDL_Rect display_bounds; - if(SDL_GetDisplayBounds(settings.screen, &display_bounds) != 0) - throw std::runtime_error("Couldn't get display bounds!"); - pos_x = display_bounds.x; - pos_y = display_bounds.y; + pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen); + pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(settings.screen); } + // Create an application window with the following settings: mSDLWindow = SDL_CreateWindow( - "OpenMW", // window title - pos_x, // initial x position - pos_y, // initial y position - settings.window_x, // width, in pixels - settings.window_y, // height, in pixels + "OpenMW", // window title + pos_x, // initial x position + pos_y, // initial y position + settings.window_x, // width, in pixels + settings.window_y, // height, in pixels SDL_WINDOW_SHOWN | (settings.fullscreen ? SDL_WINDOW_FULLSCREEN : 0) ); From 1b310c5c5b935e2bd143e3f055f19ce19a6cb728 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 13:34:56 +0200 Subject: [PATCH 81/92] use SDL resolutions in the ingame settings --- apps/openmw/mwgui/settingswindow.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index eada151a43..4ce6ac0bfe 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" @@ -172,16 +174,14 @@ namespace MWGui mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); // fill resolution list - Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); - Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; + int screen = Settings::Manager::getInt("screen", "Video"); + int numDisplayModes = SDL_GetNumDisplayModes(screen); std::vector < std::pair > resolutions; - for (Ogre::StringVector::const_iterator it=videoModes.begin(); - it!=videoModes.end(); ++it) + for (int i = 0; i < numDisplayModes; i++) { - - int resX, resY; - parseResolution (resX, resY, *it); - resolutions.push_back(std::make_pair(resX, resY)); + SDL_DisplayMode mode; + SDL_GetDisplayMode(screen, i, &mode); + resolutions.push_back(std::make_pair(mode.w, mode.h)); } std::sort(resolutions.begin(), resolutions.end(), sortResolutions); for (std::vector < std::pair >::const_iterator it=resolutions.begin(); From 661b290c4924ca1526c30797b94716f0f8ddd269 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 29 Jul 2013 15:00:41 +0200 Subject: [PATCH 82/92] automatically scroll table to the position of a newly created record --- apps/opencs/view/world/creator.hpp | 4 ++++ apps/opencs/view/world/genericcreator.cpp | 5 ++++- apps/opencs/view/world/table.cpp | 8 ++++++++ apps/opencs/view/world/table.hpp | 2 ++ apps/opencs/view/world/tablebottombox.cpp | 3 +++ apps/opencs/view/world/tablebottombox.hpp | 6 ++++++ apps/opencs/view/world/tablesubview.cpp | 3 +++ 7 files changed, 30 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index a5c1991c39..df9b116eed 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -29,6 +29,10 @@ namespace CSVWorld signals: void done(); + + void requestFocus (const std::string& id); + ///< Request owner of this creator to focus the just created \a id. The owner may + /// ignore this request. }; /// \brief Base class for Creator factory diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index ba965bc94d..41ae5c90a7 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -101,9 +101,12 @@ void CSVWorld::GenericCreator::create() { if (!mLocked) { + std::string id = getId(); + mUndoStack.push (new CSMWorld::CreateCommand ( - dynamic_cast (*mData.getTableModel (mListId)), getId())); + dynamic_cast (*mData.getTableModel (mListId)), id)); emit done(); + emit requestFocus (id); } } \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index c35d7358a0..fdbf67e427 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -282,4 +282,12 @@ void CSVWorld::Table::tableSizeUpdate() void CSVWorld::Table::selectionSizeUpdate() { selectionSizeChanged (selectionModel()->selectedRows().size()); +} + +void CSVWorld::Table::requestFocus (const std::string& id) +{ + QModelIndex index = mProxyModel->getModelIndex (id, 0); + + if (index.isValid()) + scrollTo (index, QAbstractItemView::PositionAtTop); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 2a59270b89..0c24e7b54c 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -83,6 +83,8 @@ namespace CSVWorld void selectionSizeUpdate(); + void requestFocus (const std::string& id); + }; } diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 6a5dc83c1e..6cf21a1322 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -66,6 +66,9 @@ CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFacto mLayout->addWidget (mCreator); connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone())); + + connect (mCreator, SIGNAL (requestFocus (const std::string&)), + this, SIGNAL (requestFocus (const std::string&))); } void CSVWorld::TableBottomBox::setEditLock (bool locked) diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 21e68f51a3..a5ae5e0bd9 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -55,6 +55,12 @@ namespace CSVWorld /// /// \note The BotomBox does not partake in the deletion of records. + signals: + + void requestFocus (const std::string& id); + ///< Request owner of this box to focus the just created \a id. The owner may + /// ignore this request. + private slots: void createRequestDone(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index fb1a255cb6..af3d186e85 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -41,6 +41,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D if (mBottom->canCreateAndDelete()) connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); + + connect (mBottom, SIGNAL (requestFocus (const std::string&)), + mTable, SLOT (requestFocus (const std::string&))); } void CSVWorld::TableSubView::setEditLock (bool locked) From 34c825ce5287ef5894a74c08f0511c857fe372b5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 29 Jul 2013 16:21:42 +0200 Subject: [PATCH 83/92] added icons and type listing to UniversalId class --- apps/opencs/model/world/universalid.cpp | 139 +++++++++++-------- apps/opencs/model/world/universalid.hpp | 21 ++- apps/opencs/view/world/refidtypedelegate.cpp | 29 ++-- 3 files changed, 104 insertions(+), 85 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c66101950b..5c85ff6208 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -12,81 +12,82 @@ namespace CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Type mType; const char *mName; + const char *mIcon; }; static const TypeData sNoArg[] = { - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Referenceables" }, + "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "References" }, + "References", 0 }, { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, - "Region Map" }, + "Region Map", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIdArg[] = { - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList, - "Creature Levelled List" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous, - "Miscellaneous" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Reference, "Reference" }, - - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, + "Creature Levelled List", ":./creature.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, + "Item Levelled List", ":./item.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, + "Miscellaneous", ":./misc.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, + { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, + + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData); @@ -268,6 +269,28 @@ std::string CSMWorld::UniversalId::toString() const return stream.str(); } +std::string CSMWorld::UniversalId::getIcon() const +{ + const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg : + (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg); + + for (int i=0; typeData[i].mName; ++i) + if (typeData[i].mType==mType) + return typeData[i].mIcon ? typeData[i].mIcon : ""; + + throw std::logic_error ("failed to retrieve UniversalId type icon"); +} + +std::vector CSMWorld::UniversalId::listReferenceableTypes() +{ + std::vector list; + + for (int i=0; sIdArg[i].mName; ++i) + if (sIdArg[i].mClass==Class_RefRecord) + list.push_back (sIdArg[i].mType); + + return list; +} std::pair CSMWorld::UniversalId::getIdArgPair (unsigned int index) { diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 453da3ef32..7dc4c851aa 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -14,13 +15,14 @@ namespace CSMWorld enum Class { - Class_None = 0, - Class_Record, - Class_SubRecord, - Class_RecordList, - Class_Collection, // multiple types of records combined - Class_Transient, // not part of the world data or the project data - Class_NonRecord // record like data that is not part of the world + Class_None = 0, + Class_Record, + Class_RefRecord, // referenceable record + Class_SubRecord, + Class_RecordList, + Class_Collection, // multiple types of records combined + Class_Transient, // not part of the world data or the project data + Class_NonRecord // record like data that is not part of the world }; enum ArgumentType @@ -126,6 +128,11 @@ namespace CSMWorld std::string toString() const; + std::string getIcon() const; + ///< Will return an empty string, if no icon is available. + + static std::vector listReferenceableTypes(); + static std::pair getIdArgPair (unsigned int index); static unsigned int getIdArgSize (); }; diff --git a/apps/opencs/view/world/refidtypedelegate.cpp b/apps/opencs/view/world/refidtypedelegate.cpp index 40bf88584d..bf3acbb206 100755 --- a/apps/opencs/view/world/refidtypedelegate.cpp +++ b/apps/opencs/view/world/refidtypedelegate.cpp @@ -27,26 +27,15 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto { UidTypeList list; - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Container, ":./container.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Creature, ":./creature.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Door, ":./door.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Ingredient, ":./ingredient.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_CreatureLevelledList, ":./creature.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_ItemLevelledList, ":./item.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Light, ":./light.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Lockpick, ":./lockpick.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Miscellaneous, ":./misc.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Npc, ":./npc.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Probe, ":./probe.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Repair, ":./repair.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Static, ":./static.png")); - list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Weapon, ":./weapon.png")); + std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); + + for (std::vector::const_iterator iter (types.begin()); + iter!=types.end(); ++iter) + { + CSMWorld::UniversalId id (*iter, ""); + + list.push_back (std::make_pair (id.getType(), id.getIcon().c_str())); + } return list; } From d2b7cb5bb2f168f1a189b6cb4b43e3bdf1e5c914 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 16:43:16 +0200 Subject: [PATCH 84/92] First/Third person transitions with mouse wheel --- apps/openmw/mwrender/camera.cpp | 29 ++++++++++++++++++----- apps/openmw/mwrender/camera.hpp | 8 +++++++ apps/openmw/mwrender/renderingmanager.cpp | 10 +++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 59b320b65e..7dd1c84d86 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -25,7 +25,9 @@ namespace MWRender mHeight(128.f), mCameraDistance(300.f), mDistanceAdjusted(false), - mAnimation(NULL) + mAnimation(NULL), + mNearest(30.f), + mFurthest(800.f) { mVanity.enabled = false; mVanity.allowed = true; @@ -232,16 +234,21 @@ namespace MWRender if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) return; + mIsFurthest = false; + mIsNearest = false; + Ogre::Vector3 v(0.f, 0.f, dist); if (adjust) { v += mCamera->getPosition(); } - if (v.z > 800.f) { - v.z = 800.f; - } else if (v.z < 10.f) { + if (v.z >= mFurthest) { + v.z = mFurthest; + mIsFurthest = true; + } else if (!override && v.z < 10.f) { v.z = 10.f; - } else if (override && v.z < 50.f) { - v.z = 50.f; + } else if (override && v.z <= mNearest) { + v.z = mNearest; + mIsNearest = true; } mCamera->setPosition(v); @@ -320,4 +327,14 @@ namespace MWRender { return mPreviewMode || mVanity.enabled; } + + bool Camera::isNearest() + { + return mIsNearest; + } + + bool Camera::isFurthest() + { + return mIsFurthest; + } } diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index 2993bad052..cbfbd09197 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -33,6 +33,10 @@ namespace MWRender bool mFirstPersonView; bool mPreviewMode; bool mFreeLook; + float mNearest; + float mFurthest; + bool mIsNearest; + bool mIsFurthest; struct { bool enabled, allowed; @@ -99,6 +103,10 @@ namespace MWRender void togglePlayerLooking(bool enable); bool isVanityOrPreviewModeEnabled(); + + bool isNearest(); + + bool isFurthest(); }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4a9de259f7..2f48a0ce91 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -903,7 +903,15 @@ void RenderingManager::setCameraDistance(float dist, bool adjust, bool override) { if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson()) { - mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override); + if(mCamera->isNearest() && dist > 0.f) + mCamera->toggleViewMode(); + else + mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override); + } + else if(mCamera->isFirstPerson() && dist < 0.f) + { + mCamera->toggleViewMode(); + mCamera->setCameraDistance(0.f, false, override); } } From 7c24c0a64efc6de3f91dee20221cadc8ddb4ba7b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 16:45:35 +0200 Subject: [PATCH 85/92] fix uninitialized variables --- apps/openmw/mwrender/camera.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 7dd1c84d86..941b8fde7e 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -27,7 +27,9 @@ namespace MWRender mDistanceAdjusted(false), mAnimation(NULL), mNearest(30.f), - mFurthest(800.f) + mFurthest(800.f), + mIsNearest(false), + mIsFurthest(false) { mVanity.enabled = false; mVanity.allowed = true; From 7b40e57ba1aab624da88b3be2cd20f5aaac4efbd Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 18:27:00 +0200 Subject: [PATCH 86/92] Allow the 'Activate' key to close the countdialog window; cleanups --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/countdialog.cpp | 7 +++++- apps/openmw/mwgui/countdialog.hpp | 3 ++- apps/openmw/mwgui/messagebox.cpp | 6 ++--- apps/openmw/mwgui/messagebox.hpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.cpp | 8 ++++++- apps/openmw/mwgui/windowmanagerimp.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 31 ++++++++++--------------- 8 files changed, 34 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d85c28fe24..e4a0c209e6 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -215,6 +215,7 @@ namespace MWBase virtual void removeStaticMessageBox() = 0; virtual void enterPressed () = 0; + virtual void activateKeyPressed () = 0; virtual int readPressedButton() = 0; ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index c429b0ccf5..02ccbbc053 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -49,11 +49,16 @@ namespace MWGui mItemEdit->setCaption(boost::lexical_cast(maxCount)); } - void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) + void CountDialog::cancel() { setVisible(false); } + void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) + { + cancel(); + } + void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); diff --git a/apps/openmw/mwgui/countdialog.hpp b/apps/openmw/mwgui/countdialog.hpp index 82a833bb7d..06de3eb887 100644 --- a/apps/openmw/mwgui/countdialog.hpp +++ b/apps/openmw/mwgui/countdialog.hpp @@ -10,6 +10,7 @@ namespace MWGui public: CountDialog(); void open(const std::string& item, const std::string& message, const int maxCount); + void cancel(); typedef MyGUI::delegates::CMultiDelegate2 EventHandle_WidgetInt; @@ -25,7 +26,7 @@ namespace MWGui MyGUI::TextBox* mLabelText; MyGUI::Button* mOkButton; MyGUI::Button* mCancelButton; - + void onCancelButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender); void onEditTextChange(MyGUI::EditBox* _sender); diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 2fc50f257c..3a969c7ca3 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -146,10 +146,10 @@ namespace MWGui mMessageBoxSpeed = speed; } - void MessageBoxManager::enterPressed () + void MessageBoxManager::okayPressed () { if(mInterMessageBoxe != NULL) - mInterMessageBoxe->enterPressed(); + mInterMessageBoxe->okayPressed(); } int MessageBoxManager::readPressedButton () @@ -379,7 +379,7 @@ namespace MWGui } } - void InteractiveMessageBox::enterPressed() + void InteractiveMessageBox::okayPressed() { std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 0e47b0323b..4ef645f5e6 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -40,7 +40,7 @@ namespace MWGui bool removeMessageBox (MessageBox *msgbox); void setMessageBoxSpeed (int speed); - void enterPressed(); + void okayPressed(); int readPressedButton (); typedef MyGUI::delegates::CMultiDelegate1 EventHandle_Int; @@ -82,7 +82,7 @@ namespace MWGui { public: InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector& buttons); - void enterPressed (); + void okayPressed (); void mousePressed (MyGUI::Widget* _widget); int readPressedButton (); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6fcc19a477..f5f16f85c3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -663,7 +663,13 @@ namespace MWGui void WindowManager::enterPressed () { - mMessageBoxManager->enterPressed(); + mMessageBoxManager->okayPressed(); + } + + void WindowManager::activateKeyPressed () + { + mMessageBoxManager->okayPressed(); + mCountDialog->setVisible(false); } int WindowManager::readPressedButton () diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 6395b1bb57..82efd006ab 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -204,6 +204,7 @@ namespace MWGui virtual void staticMessageBox(const std::string& message); virtual void removeStaticMessageBox(); virtual void enterPressed (); + virtual void activateKeyPressed (); virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) virtual void onFrame (float frameDuration); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index eae5d7d193..973089d752 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -204,13 +204,15 @@ namespace MWInput case A_Activate: resetIdleTime(); - if (mWindows.getMode() == MWGui::GM_Container) { - toggleContainer (); - } else if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { - MWBase::Environment::get().getWindowManager()->enterPressed(); - } else { - activate(); + if (mWindows.isGuiMode()) + { + if (mWindows.getMode() == MWGui::GM_Container) + toggleContainer (); + else + MWBase::Environment::get().getWindowManager()->activateKeyPressed(); } + else + activate(); break; case A_Journal: toggleJournal (); @@ -681,10 +683,8 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().isModalAny()) return; - bool gameMode = !mWindows.isGuiMode(); - // Toggle between game mode and inventory mode - if(gameMode) + if(!mWindows.isGuiMode()) mWindows.pushGuiMode(MWGui::GM_Inventory); else { @@ -701,9 +701,7 @@ namespace MWInput if (MyGUI::InputManager::getInstance ().isModalAny()) return; - bool gameMode = !mWindows.isGuiMode(); - - if(!gameMode) + if(mWindows.isGuiMode()) { if (mWindows.getMode() == MWGui::GM_Container) mWindows.popGuiMode(); @@ -713,17 +711,14 @@ namespace MWInput } - void InputManager::toggleConsole() { if (MyGUI::InputManager::getInstance ().isModalAny()) return; - bool gameMode = !mWindows.isGuiMode(); - // Switch to console mode no matter what mode we are currently // in, except of course if we are already in console mode - if (!gameMode) + if (mWindows.isGuiMode()) { if (mWindows.getMode() == MWGui::GM_Console) mWindows.popGuiMode(); @@ -740,9 +735,7 @@ namespace MWInput return; // Toggle between game mode and journal mode - bool gameMode = !mWindows.isGuiMode(); - - if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) + if(!mWindows.isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) { MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); mWindows.pushGuiMode(MWGui::GM_Journal); From 40d3efc671cd972b4b147d482923bc04c28b580d Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Mon, 29 Jul 2013 18:35:23 +0200 Subject: [PATCH 87/92] use cancel methid --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index f5f16f85c3..03fd7c7962 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -669,7 +669,7 @@ namespace MWGui void WindowManager::activateKeyPressed () { mMessageBoxManager->okayPressed(); - mCountDialog->setVisible(false); + mCountDialog->cancel(); } int WindowManager::readPressedButton () From 3961c276b85d993b00f146f4ec486ddfdf200f5d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 30 Jul 2013 06:00:20 +0200 Subject: [PATCH 88/92] Added enemy health meter --- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 3 +++ apps/openmw/mwgui/hud.cpp | 30 ++++++++++++++++++++++ apps/openmw/mwgui/hud.hpp | 9 +++++-- apps/openmw/mwgui/windowmanagerimp.cpp | 5 ++++ apps/openmw/mwgui/windowmanagerimp.hpp | 2 ++ files/mygui/openmw_hud.layout | 5 ++++ files/mygui/openmw_hud_energybar.skin.xml | 13 ++++++++++ files/mygui/smallbars.png | Bin 3205 -> 2985 bytes 9 files changed, 67 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index d85c28fe24..ab4c943bf0 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -265,6 +265,8 @@ namespace MWBase virtual void changePointer (const std::string& name) = 0; + virtual void setEnemy (const MWWorld::Ptr& enemy) = 0; + virtual const Translation::Storage& getTranslationDataStorage() const = 0; virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f767ac6704..30ae1880ce 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -418,6 +418,9 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. + if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->setEnemy(ptr); + if(!successful) { // TODO: Handle HitAttemptOnMe script function diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 469c45936f..4eea0d0d0d 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -9,6 +9,8 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "inventorywindow.hpp" #include "console.hpp" #include "spellicons.hpp" @@ -47,6 +49,7 @@ namespace MWGui , mWeaponVisible(true) , mSpellVisible(true) , mWorldMouseOver(false) + , mEnemyHealthTimer(0) { setCoord(0,0, width, height); @@ -55,6 +58,7 @@ namespace MWGui getWidget(mHealth, "Health"); getWidget(mMagicka, "Magicka"); getWidget(mStamina, "Stamina"); + getWidget(mEnemyHealth, "EnemyHealth"); mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; @@ -320,6 +324,13 @@ namespace MWGui mCellNameBox->setVisible(false); if (mWeaponSpellTimer < 0) mWeaponSpellBox->setVisible(false); + + mEnemyHealthTimer -= dt; + if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0) + { + mEnemyHealth->setVisible(false); + mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); + } } void HUD::onResChange(int width, int height) @@ -535,6 +546,25 @@ namespace MWGui void HUD::update() { mSpellIcons->updateWidgets(mEffectBox, true); + + if (!mEnemy.isEmpty() && mEnemyHealth->getVisible()) + { + MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); + mEnemyHealth->setProgressRange(100); + mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); + } + } + + void HUD::setEnemy(const MWWorld::Ptr &enemy) + { + mEnemy = enemy; + mEnemyHealthTimer = 5; + if (!mEnemyHealth->getVisible()) + mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20)); + mEnemyHealth->setVisible(true); + MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy); + mEnemyHealth->setProgressRange(100); + mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); } } diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index 1dd53683b8..76d8782696 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -46,12 +46,14 @@ namespace MWGui void update(); + void setEnemy(const MWWorld::Ptr& enemy); + private: - MyGUI::ProgressPtr mHealth, mMagicka, mStamina; + MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; - MyGUI::ProgressPtr mWeapStatus, mSpellStatus; + MyGUI::ProgressBar *mWeapStatus, *mSpellStatus; MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Button* mMinimapButton; MyGUI::ScrollView* mMinimap; @@ -89,6 +91,9 @@ namespace MWGui SpellIcons* mSpellIcons; + MWWorld::Ptr mEnemy; + float mEnemyHealthTimer; + void onWorldClicked(MyGUI::Widget* _sender); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6fcc19a477..9327ea7b9f 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1300,4 +1300,9 @@ namespace MWGui SDL_StopTextInput(); } + void WindowManager::setEnemy(const MWWorld::Ptr &enemy) + { + mHud->setEnemy(enemy); + } + } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 6395b1bb57..cbfab2fa29 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -256,6 +256,8 @@ namespace MWGui virtual void changePointer (const std::string& name); + virtual void setEnemy (const MWWorld::Ptr& enemy); + virtual const Translation::Storage& getTranslationDataStorage() const; void onSoulgemDialogButtonPressed (int button); diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 67d0632f84..c69323fe56 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -3,6 +3,11 @@ + + + + + diff --git a/files/mygui/openmw_hud_energybar.skin.xml b/files/mygui/openmw_hud_energybar.skin.xml index 03d2835d6e..9ec065e360 100644 --- a/files/mygui/openmw_hud_energybar.skin.xml +++ b/files/mygui/openmw_hud_energybar.skin.xml @@ -42,6 +42,11 @@ + + + + + @@ -69,4 +74,12 @@ + + + + + + + + diff --git a/files/mygui/smallbars.png b/files/mygui/smallbars.png index f938412c259c540d8e085a339fa0d94c4294a361..3c007a55cea32c99bbb4acb41e27419cc0290d49 100644 GIT binary patch delta 303 zcmV+~0nq-18L1bL7YZl{1^@s6*`Hf8u{K@`4+dgOM??Vs0RI60puMM)ks%j<00(qQ zO+^RY2Oa|$Dm!$xI{*Lx+et)0R4C6~(mhTCF%X8~cLqx-K%%6er{p$VgA1jn<21Cn zKtxu_CQE|6YY)ZlO89d#(&HKbjBVT3r{Ld6ZZsn$H)?r$FgMJch>Drx08!?Db)po~l!vxt%)P*`LO!*yfBTkmxb%aKK>-}7;N+!) zV=NihNlGqR;4})HC1cx3gVuK`w!x^RkC#sX39%OKGAA^)QjI38R01Nus0yA2(q6aWAK07*p#PDHLkV1m~s Bgf0L8 delta 526 zcmYk3Jxc>Y5Qg752aG|mJjEs;YA4oqN)%I=B4`&#z}_?pVsoNc2_o7EX{-bbgK6#a z3;Y$)1oEZP``C3hxp+Ipd-u%jymLGE>|QP0y6H^H0q|DK&)nan-TG27?PU&k3wyxX zGH`wg{AzhNfs=LMqYP|30B+^3)Yt*&HJ9DoKP5a;Nl|sp6|ZryLz7p4g_MS*jE2fo zqLFfcOrx{h4kEyd`?&23O`v7N`{L2000x;GjDyJ`B1dZ_YSKC~9- Date: Tue, 30 Jul 2013 06:17:21 +0200 Subject: [PATCH 89/92] Fix enemy health meter for creatures --- apps/openmw/mwclass/npc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 30ae1880ce..e91055f1d6 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -329,6 +329,9 @@ namespace MWClass return; } + if(ptr.getRefData().getHandle() == "player") + MWBase::Environment::get().getWindowManager()->setEnemy(ptr); + int weapskill = ESM::Skill::HandToHand; if(!weapon.isEmpty()) weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon); @@ -418,9 +421,6 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. - if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") - MWBase::Environment::get().getWindowManager()->setEnemy(ptr); - if(!successful) { // TODO: Handle HitAttemptOnMe script function From 57be764cce50d6d922dfad8156d8cd0ced4305e1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 30 Jul 2013 10:27:17 +0200 Subject: [PATCH 90/92] added specialised Creator for referenceable records --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/commands.cpp | 9 +++- apps/opencs/model/world/commands.hpp | 5 +++ apps/opencs/model/world/idtable.cpp | 4 +- apps/opencs/model/world/idtable.hpp | 5 ++- apps/opencs/view/world/genericcreator.cpp | 15 ++++++- apps/opencs/view/world/genericcreator.hpp | 9 ++++ .../view/world/referenceablecreator.cpp | 43 +++++++++++++++++++ .../view/world/referenceablecreator.hpp | 30 +++++++++++++ apps/opencs/view/world/subviews.cpp | 5 ++- 10 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 apps/opencs/view/world/referenceablecreator.cpp create mode 100644 apps/opencs/view/world/referenceablecreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 8e5d81d764..75258a175a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -58,7 +58,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator - cellcreator + cellcreator referenceablecreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 3bcdf8b050..9281e6e330 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -26,14 +26,19 @@ void CSMWorld::ModifyCommand::undo() } CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) -: QUndoCommand (parent), mModel (model), mId (id) +: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) { setText (("Create record " + id).c_str()); } +void CSMWorld::CreateCommand::setType (UniversalId::Type type) +{ + mType = type; +} + void CSMWorld::CreateCommand::redo() { - mModel.addRecord (mId); + mModel.addRecord (mId, mType); } void CSMWorld::CreateCommand::undo() diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 1fa124277f..6272c3fb7b 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -9,6 +9,8 @@ #include #include +#include "universalid.hpp" + class QModelIndex; class QAbstractItemModel; @@ -39,11 +41,14 @@ namespace CSMWorld { IdTable& mModel; std::string mId; + UniversalId::Type mType; public: CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + void setType (UniversalId::Type type); + virtual void redo(); virtual void undo(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 25258398bd..3d36b55038 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -116,13 +116,13 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const return QModelIndex(); } -void CSMWorld::IdTable::addRecord (const std::string& id) +void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type) { int index = mIdCollection->getAppendIndex(); beginInsertRows (QModelIndex(), index, index); - mIdCollection->appendBlankRecord (id); + mIdCollection->appendBlankRecord (id, type); endInsertRows(); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 98bf6ad6d1..556d7f0bad 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -3,6 +3,8 @@ #include +#include "universalid.hpp" + namespace CSMWorld { class CollectionBase; @@ -44,7 +46,8 @@ namespace CSMWorld virtual QModelIndex parent (const QModelIndex& index) const; - void addRecord (const std::string& id); + void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); + ///< \param type Will be ignored, unless the collection supports multiple record types QModelIndex getModelIndex (const std::string& id, int column) const; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 41ae5c90a7..6c402818ce 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -1,6 +1,8 @@ #include "genericcreator.hpp" +#include + #include #include #include @@ -32,11 +34,18 @@ void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretche mLayout->insertWidget (0, widget, stretched ? 1 : 0); } +void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched) +{ + mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0); +} + std::string CSVWorld::GenericCreator::getId() const { return mId->text().toUtf8().constData(); } +void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {} + CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) @@ -103,9 +112,13 @@ void CSVWorld::GenericCreator::create() { std::string id = getId(); - mUndoStack.push (new CSMWorld::CreateCommand ( + std::auto_ptr command (new CSMWorld::CreateCommand ( dynamic_cast (*mData.getTableModel (mListId)), id)); + configureCreateCommand (*command); + + mUndoStack.push (command.release()); + emit done(); emit requestFocus (id); } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 2798d89a14..9022b7e390 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -9,6 +9,11 @@ class QHBoxLayout; #include "../../model/world/universalid.hpp" +namespace CSMWorld +{ + class CreateCommand; +} + namespace CSVWorld { class GenericCreator : public Creator @@ -33,8 +38,12 @@ namespace CSVWorld void insertAtBeginning (QWidget *widget, bool stretched); + void insertBeforeButtons (QWidget *widget, bool stretched); + virtual std::string getId() const; + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + public: GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp new file mode 100644 index 0000000000..718fe9ca7c --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -0,0 +1,43 @@ + +#include "referenceablecreator.hpp" + +#include +#include + +#include "../../model/world/universalid.hpp" +#include "../../model/world/commands.hpp" + +void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + command.setType ( + static_cast (mType->itemData (mType->currentIndex()).toInt())); +} + +CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Type", this); + insertBeforeButtons (label, false); + + std::vector types = CSMWorld::UniversalId::listReferenceableTypes(); + + mType = new QComboBox (this); + + for (std::vector::const_iterator iter (types.begin()); + iter!=types.end(); ++iter) + { + CSMWorld::UniversalId id (*iter, ""); + + mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), + static_cast (id.getType())); + } + + insertBeforeButtons (mType, false); +} + +void CSVWorld::ReferenceableCreator::reset() +{ + mType->setCurrentIndex (0); + GenericCreator::reset(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referenceablecreator.hpp b/apps/opencs/view/world/referenceablecreator.hpp new file mode 100644 index 0000000000..06e0e582be --- /dev/null +++ b/apps/opencs/view/world/referenceablecreator.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_WORLD_REFERENCEABLECREATOR_H +#define CSV_WORLD_REFERENCEABLECREATOR_H + +class QComboBox; + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class ReferenceableCreator : public GenericCreator + { + Q_OBJECT + + QComboBox *mType; + + private: + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 9fbbe4a0da..22eb0ea4cf 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -9,6 +9,7 @@ #include "regionmapsubview.hpp" #include "genericcreator.hpp" #include "cellcreator.hpp" +#include "referenceablecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -29,7 +30,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_Referenceables, CSMWorld::UniversalId::Type_References, CSMWorld::UniversalId::Type_None // end marker @@ -42,6 +42,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Cells, new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Referenceables, + new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); From 102700a49879388b6850cdcadabfc51bb1caa177 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 30 Jul 2013 12:53:03 +0200 Subject: [PATCH 91/92] added specialised Creator for references --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/commands.cpp | 8 +++ apps/opencs/model/world/commands.hpp | 4 ++ apps/opencs/model/world/data.cpp | 10 +++ apps/opencs/model/world/data.hpp | 4 ++ apps/opencs/model/world/refcollection.cpp | 12 ++-- apps/opencs/model/world/refcollection.hpp | 2 + apps/opencs/view/world/genericcreator.cpp | 10 +++ apps/opencs/view/world/genericcreator.hpp | 4 ++ apps/opencs/view/world/referencecreator.cpp | 70 +++++++++++++++++++++ apps/opencs/view/world/referencecreator.hpp | 40 ++++++++++++ apps/opencs/view/world/subviews.cpp | 9 ++- 12 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 apps/opencs/view/world/referencecreator.cpp create mode 100644 apps/opencs/view/world/referencecreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 75258a175a..6da863b974 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -58,7 +58,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator - cellcreator referenceablecreator + cellcreator referenceablecreator referencecreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 9281e6e330..43ecaca63c 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -31,6 +31,11 @@ CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, Q setText (("Create record " + id).c_str()); } +void CSMWorld::CreateCommand::addValue (int column, const QVariant& value) +{ + mValues[column] = value; +} + void CSMWorld::CreateCommand::setType (UniversalId::Type type) { mType = type; @@ -39,6 +44,9 @@ void CSMWorld::CreateCommand::setType (UniversalId::Type type) void CSMWorld::CreateCommand::redo() { mModel.addRecord (mId, mType); + + for (std::map::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter) + mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second); } void CSMWorld::CreateCommand::undo() diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 6272c3fb7b..3fc2522ea5 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -4,6 +4,7 @@ #include "record.hpp" #include +#include #include #include @@ -42,6 +43,7 @@ namespace CSMWorld IdTable& mModel; std::string mId; UniversalId::Type mType; + std::map mValues; public: @@ -49,6 +51,8 @@ namespace CSMWorld void setType (UniversalId::Type type); + void addValue (int column, const QVariant& value); + virtual void redo(); virtual void undo(); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 2a95dfafeb..594bc19c2e 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -300,6 +300,16 @@ CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables() return mReferenceables; } +const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const +{ + return mRefs; +} + +CSMWorld::RefCollection& CSMWorld::Data::getReferences() +{ + return mRefs; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 10a1fe50de..8b2403db39 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -112,6 +112,10 @@ namespace CSMWorld RefIdCollection& getReferenceables(); + const RefCollection& getReferences() const; + + RefCollection& getReferences(); + QAbstractItemModel *getTableModel (const UniversalId& id); ///< If no table model is available for \a id, an exception is thrown. /// diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 6a1e8045b3..085817753e 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -21,10 +21,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool while (cell2.getNextRef (reader, ref)) { /// \todo handle deleted and moved references - std::ostringstream stream; - stream << "ref#" << mNextId++; - - ref.load (reader, cell2, stream.str()); + ref.load (reader, cell2, getNewId()); Record record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; @@ -34,4 +31,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool } mCells.setRecord (cellIndex, cell); +} + +std::string CSMWorld::RefCollection::getNewId() +{ + std::ostringstream stream; + stream << "ref#" << mNextId++; + return stream.str(); } \ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index a2063590b9..895315a179 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -21,6 +21,8 @@ namespace CSMWorld void load (ESM::ESMReader& reader, int cellIndex, bool base); ///< Load a sequence of references. + + std::string getNewId(); }; } diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index 6c402818ce..7502b65551 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -46,6 +46,16 @@ std::string CSVWorld::GenericCreator::getId() const void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {} +const CSMWorld::Data& CSVWorld::GenericCreator::getData() const +{ + return mData; +} + +CSMWorld::Data& CSVWorld::GenericCreator::getData() +{ + return mData; +} + CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : mData (data), mUndoStack (undoStack), mListId (id), mLocked (false) diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 9022b7e390..5409f08399 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -44,6 +44,10 @@ namespace CSVWorld virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + const CSMWorld::Data& getData() const; + + CSMWorld::Data& getData(); + public: GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, diff --git a/apps/opencs/view/world/referencecreator.cpp b/apps/opencs/view/world/referencecreator.cpp new file mode 100644 index 0000000000..48a168a354 --- /dev/null +++ b/apps/opencs/view/world/referencecreator.cpp @@ -0,0 +1,70 @@ + +#include "referencecreator.hpp" + +#include +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" + +std::string CSVWorld::ReferenceCreator::getId() const +{ + return mId; +} + +void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + /// \todo avoid using hard-coded column numbers + command.addValue (2, mCell->text()); +} + +CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) +: GenericCreator (data, undoStack, id) +{ + QLabel *label = new QLabel ("Cell", this); + insertBeforeButtons (label, false); + + mCell = new QLineEdit (this); + insertBeforeButtons (mCell, true); + + setManualEditing (false); + + connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged())); +} + +void CSVWorld::ReferenceCreator::reset() +{ + mCell->setText (""); + mId = getData().getReferences().getNewId(); + GenericCreator::reset(); +} + +std::string CSVWorld::ReferenceCreator::getErrors() const +{ + std::string errors = GenericCreator::getErrors(); + + std::string cell = mCell->text().toUtf8().constData(); + + if (cell.empty()) + { + if (!errors.empty()) + errors += "
"; + + errors += "Missing Cell ID"; + } + else if (getData().getCells().searchId (cell)==-1) + { + if (!errors.empty()) + errors += "
"; + + errors += "Invalid Cell ID"; + } + + return errors; +} + +void CSVWorld::ReferenceCreator::cellChanged() +{ + update(); +} \ No newline at end of file diff --git a/apps/opencs/view/world/referencecreator.hpp b/apps/opencs/view/world/referencecreator.hpp new file mode 100644 index 0000000000..27f81564fd --- /dev/null +++ b/apps/opencs/view/world/referencecreator.hpp @@ -0,0 +1,40 @@ +#ifndef CSV_WORLD_REFERENCECREATOR_H +#define CSV_WORLD_REFERENCECREATOR_H + +#include "genericcreator.hpp" + +class QLineEdit; + +namespace CSVWorld +{ + class ReferenceCreator : public GenericCreator + { + Q_OBJECT + + QLineEdit *mCell; + std::string mId; + + private: + + virtual std::string getId() const; + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id); + + virtual void reset(); + + virtual std::string getErrors() const; + ///< Return formatted error descriptions for the current state of the creator. if an empty + /// string is returned, there is no error. + + private slots: + + void cellChanged(); + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 22eb0ea4cf..8990e2c724 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -10,9 +10,12 @@ #include "genericcreator.hpp" #include "cellcreator.hpp" #include "referenceablecreator.hpp" +#include "referencecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { + // Regular record tables (including references which are actually sub-records, but are promoted + // to top-level records within the editor) manager.add (CSMWorld::UniversalId::Type_Gmsts, new CSVDoc::SubViewFactoryWithCreator); @@ -30,7 +33,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_References, CSMWorld::UniversalId::Type_None // end marker }; @@ -45,7 +47,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Referenceables, new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_References, + new CSVDoc::SubViewFactoryWithCreator >); + + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + // Other stuff (combined record tables) manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); } \ No newline at end of file From 434f97c95c7593028fd4f250f11709e6c5f3b925 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 30 Jul 2013 12:55:02 +0200 Subject: [PATCH 92/92] fixed two missing icons --- apps/opencs/model/world/universalid.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 5c85ff6208..4674f23f16 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -68,11 +68,11 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, "Creature Levelled List", ":./creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List", ":./item.png" }, + "Item Levelled List", ":./leveled-item.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, - "Miscellaneous", ":./misc.png" }, + "Miscellaneous", ":./miscellaneous.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },