From 2eaceb71bab24c55fa171b97f678887ee2c50919 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Sep 2014 12:39:10 +0200 Subject: [PATCH 01/10] Reject AiTravel destinations further than 7168 units away, as in vanilla MW (Fixes #1911) --- apps/openmw/mwmechanics/aitravel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index 3fd4704d9..7278e74f2 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -1,5 +1,7 @@ #include "aitravel.hpp" +#include + #include #include "../mwbase/world.hpp" @@ -70,6 +72,12 @@ namespace MWMechanics } } + // Maximum travel distance for vanilla compatibility. + // Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well. + // We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways. + if (Ogre::Vector3(mX, mY, mZ).squaredDistance(Ogre::Vector3(pos.pos)) > 7168*7168) + return false; + bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY; if(!mPathFinder.isPathConstructed() || cellChange) { From 09042ba6d9ed038a11b1e1d7f76e92be17ddd30b Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Sep 2014 13:02:29 +0200 Subject: [PATCH 02/10] Use object name instead of ID when available for tooltips --- apps/openmw/mwgui/tooltips.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 5d481fa37..49953b8c6 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -99,7 +99,9 @@ namespace MWGui setCoord(0, 0, 300, 300); mDynamicToolTipBox->setVisible(true); ToolTipInfo info; - info.caption=mFocusObject.getCellRef().getRefId(); + info.caption = mFocusObject.getClass().getName(mFocusObject); + if (info.caption.empty()) + info.caption=mFocusObject.getCellRef().getRefId(); info.icon=""; tooltipSize = createToolTip(info); } From 07cd647e75e011e014f25cfa9d55ae5ca08d4815 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 03:19:13 +0200 Subject: [PATCH 03/10] Fix broken AI for creatures with OnTarget spells (Bug #1927) --- apps/openmw/mwmechanics/aicombat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index c55cea587..0bea76c58 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -320,7 +320,7 @@ namespace MWMechanics } else //is creature { - weaptype = WeapType_HandToHand; //doesn't matter, should only reflect if it is melee or distant weapon + weaptype = actorClass.getCreatureStats(actor).getDrawState() == DrawState_Spell ? WeapType_Spell : WeapType_HandToHand; weapRange = 150.0f; //TODO: use true attack range (the same problem in Creature::hit) } From 447e93bdb4a8bbceb9dca3da8aea714959073e67 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 03:24:47 +0200 Subject: [PATCH 04/10] Use SpellCast animation for creatures that have it (flame atronach) --- apps/openmw/mwmechanics/character.cpp | 58 +++++++++++++++++++-------- apps/openmw/mwrender/animation.cpp | 20 ++++++++- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fe316b768..0e7548379 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -745,29 +745,55 @@ bool CharacterController::updateCreatureState() { MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); - // These are unique animations and not linked to movement type. Just pick one randomly. - int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] - if (roll == 0) - mCurrentWeapon = "attack1"; - else if (roll == 1) - mCurrentWeapon = "attack2"; - else - mCurrentWeapon = "attack3"; - - mAnimation->play(mCurrentWeapon, Priority_Weapon, - MWRender::Animation::Group_All, true, - 1, "start", "stop", - 0.0f, 0); - mUpperBodyState = UpperCharState_StartToMinAttack; - + std::string startKey = "start"; + std::string stopKey = "stop"; if (weapType == WeapType_Spell) { const std::string spellid = stats.getSpells().getSelectedSpell(); if (!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) { castSpell(spellid); - MWBase::Environment::get().getWorld()->castSpell(mPtr); + + if (!mAnimation->hasAnimation("spellcast")) + MWBase::Environment::get().getWorld()->castSpell(mPtr); // No "release" text key to use, so cast immediately + else + { + const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellid); + const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); + + switch(effectentry.mRange) + { + case 0: mAttackType = "self"; break; + case 1: mAttackType = "touch"; break; + case 2: mAttackType = "target"; break; + } + + startKey = mAttackType + " " + startKey; + stopKey = mAttackType + " " + stopKey; + mCurrentWeapon = "spellcast"; + } } + else + mCurrentWeapon = ""; + } + if (weapType != WeapType_Spell || !mAnimation->hasAnimation("spellcast")) // Not all creatures have a dedicated spellcast animation + { + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] + if (roll == 0) + mCurrentWeapon = "attack1"; + else if (roll == 1) + mCurrentWeapon = "attack2"; + else + mCurrentWeapon = "attack3"; + } + + if (!mCurrentWeapon.empty()) + { + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_All, true, + 1, startKey, stopKey, + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e2f3ce62f..e4ee1cbe6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -805,7 +805,25 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co attachArrow(); else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") - MWBase::Environment::get().getWorld()->castSpell(mPtr); + { + // Make sure this key is actually for the RangeType we are casting. The flame atronach has + // the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type. + // FIXME: This logic should really be in the CharacterController + const std::string& spellid = mPtr.getClass().getCreatureStats(mPtr).getSpells().getSelectedSpell(); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellid); + const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); + int range = 0; + if (evt.compare(off, len, "self release") == 0) + range = 0; + else if (evt.compare(off, len, "touch release") == 0) + range = 1; + else if (evt.compare(off, len, "target release") == 0) + range = 2; + if (effectentry.mRange == range) + { + MWBase::Environment::get().getWorld()->castSpell(mPtr); + } + } else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) mPtr.getClass().block(mPtr); From be7839873a9aedee020a9135a015f0d03657af68 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 03:47:45 +0200 Subject: [PATCH 05/10] Don't trigger dynamic stats recalculation when setting up actor initial stats (Fixes #1927) This caused the magicka value set in the CS to be ignored completely, so flame atronachs were spawning with 50 magicka instead of 105. --- apps/openmw/mwclass/creature.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/creaturestats.cpp | 5 +++++ apps/openmw/mwmechanics/creaturestats.hpp | 1 + 4 files changed, 10 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 8d8a2f823..f0c95e123 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -129,6 +129,8 @@ namespace MWClass data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold); + data->mCreatureStats.setNeedRecalcDynamicStats(false); + // store ptr.getRefData().setCustomData(data.release()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b6d418108..6a7630d39 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -388,6 +388,8 @@ namespace MWClass data->mNpcStats.setGoldPool(gold); + data->mNpcStats.setNeedRecalcDynamicStats(false); + // store ptr.getRefData().setCustomData (data.release()); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 2c3f867f9..8020db85b 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -378,6 +378,11 @@ namespace MWMechanics return false; } + void CreatureStats::setNeedRecalcDynamicStats(bool val) + { + mRecalcDynamicStats = val; + } + void CreatureStats::setKnockedDown(bool value) { mKnockdown = value; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 827408e11..941668fb2 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -92,6 +92,7 @@ namespace MWMechanics void setAttackStrength(float value); bool needToRecalcDynamicStats(); + void setNeedRecalcDynamicStats(bool val); void addToFallHeight(float height); From ac97a0f99d769d8692f55b7df486d78a4f5a4bb8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 04:10:17 +0200 Subject: [PATCH 06/10] Fix dialogue choices being displayed in incorrect order --- apps/openmw/mwgui/dialogue.cpp | 4 ++-- apps/openmw/mwgui/dialogue.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 33b768dfc..5a5da8ea1 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -509,7 +509,7 @@ namespace MWGui const MyGUI::Colour linkHot (223/255.f, 201/255.f, 159/255.f); const MyGUI::Colour linkNormal (150/255.f, 50/255.f, 30/255.f); const MyGUI::Colour linkActive (243/255.f, 237/255.f, 221/255.f); - for (std::map::reverse_iterator it = mChoices.rbegin(); it != mChoices.rend(); ++it) + for (std::vector >::iterator it = mChoices.begin(); it != mChoices.end(); ++it) { Choice* link = new Choice(it->second); mLinks.push_back(link); @@ -600,7 +600,7 @@ namespace MWGui void DialogueWindow::addChoice(const std::string& choice, int id) { - mChoices[choice] = id; + mChoices.push_back(std::make_pair(choice, id)); updateHistory(); } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 71935dfaf..6f16fcb03 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -161,7 +161,7 @@ namespace MWGui bool mGoodbye; std::vector mHistoryContents; - std::map mChoices; + std::vector > mChoices; std::vector mLinks; std::map mTopicLinks; From 08499ea51f6daad9c99d122782ad9c3607752fb3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 04:26:12 +0200 Subject: [PATCH 07/10] Fix Calvus Horatius AiFollow package not being removed after he quits as result of negative profit --- apps/openmw/mwgui/companionwindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/companionwindow.cpp b/apps/openmw/mwgui/companionwindow.cpp index a5cc2976b..2dd130b0d 100644 --- a/apps/openmw/mwgui/companionwindow.cpp +++ b/apps/openmw/mwgui/companionwindow.cpp @@ -151,7 +151,8 @@ void CompanionWindow::onMessageBoxButtonClicked(int button) "minimumprofit", mPtr.getClass().getNpcStats(mPtr).getProfit()); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); - MWBase::Environment::get().getDialogueManager()->startDialogue (mPtr); + // Important for Calvus' contract script to work properly + MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); } } From 54dac1460c4d5f68a2b2e0cd78f39fd594819377 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 04:33:49 +0200 Subject: [PATCH 08/10] Run global scripts after running local scripts Turns out to be a compatibility problem with Calvus Horatius contract scripts. The local script needs to be run first, otherwise the cleanup when he quits as result of negative profit does not work properly. --- apps/openmw/engine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 6f4e40c65..bce7c5d7d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -109,12 +109,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) { if (!paused) { - // global scripts - MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); - // local scripts executeLocalScripts(); + // global scripts + MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); + MWBase::Environment::get().getWorld()->markCellAsUnchanged(); } From c19f89976e4b45da7067f7f1cf86b1cba8fabf58 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 05:13:17 +0200 Subject: [PATCH 09/10] Add missing Hit voice dialogue for friendly hits --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index e4dda2db2..0dc5be7b7 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1120,7 +1120,10 @@ namespace MWMechanics ptr.getClass().getCreatureStats(ptr).friendlyHit(); if (ptr.getClass().getCreatureStats(ptr).getFriendlyHits() < 4) + { + MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); return; + } } // Attacking peaceful NPCs is a crime From c05874e1ff42583d872930d69cf359e57e0416de Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Sep 2014 06:06:20 +0200 Subject: [PATCH 10/10] Reset CreatureLevList spawning flag if levelled list returned no creature --- apps/openmw/mwclass/creaturelevlist.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 754780b1d..b1e27c345 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -83,6 +83,8 @@ namespace MWClass customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawn = false; } + else + customData.mSpawn = false; } void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const