Resolve conflicts in pull request #43

# Conflicts:
#	README.md
This commit is contained in:
David Cernat 2016-08-24 22:03:35 +03:00
commit 4d2ca20445
14 changed files with 176 additions and 84 deletions

View file

@ -101,6 +101,7 @@ Programmers
Nikolay Kasyanov (corristo) Nikolay Kasyanov (corristo)
nobrakal nobrakal
Nolan Poe (nopoe) Nolan Poe (nopoe)
Oleg Chkan (mrcheko)
Paul Cercueil (pcercuei) Paul Cercueil (pcercuei)
Paul McElroy (Greendogo) Paul McElroy (Greendogo)
Pi03k Pi03k

View file

@ -1,3 +1,68 @@
0.40.0
------
Bug #1320: AiWander - Creatures in cells without pathgrids do not wander
Bug #1873: Death events are triggered at the beginning of the death animation
Bug #1996: Resting interrupts magic effects
Bug #2399: Vampires can rest in broad daylight and survive the experience
Bug #2604: Incorrect magicka recalculation
Bug #2721: Telekinesis extends interaction range where it shouldn't
Bug #2981: When waiting, NPCs can go where they wouldn't go normally.
Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup
Bug #3071: Slowfall does not stop momentum when jumping
Bug #3085: Plugins can not replace parent cell references with a cell reference of different type
Bug #3145: Bug with AI Cliff Racer. He will not attack you, unless you put in front of him.
Bug #3149: Editor: Weather tables were missing from regions
Bug #3201: Netch shoots over your head
Bug #3269: If you deselect a mod and try to load a save made inside a cell added by it, you end bellow the terrain in the grid 0/0
Bug #3286: Editor: Script editor tab width
Bug #3329: Teleportation spells cause crash to desktop after build update from 0.37 to 0.38.0
Bug #3331: Editor: Start Scripts table: Adding a script doesn't refresh the list of Start Scripts and allows to add a single script multiple times
Bug #3332: Editor: Scene view: Tool tips only occur when holding the left mouse button
Bug #3340: ESS-Importer does not separate item stacks
Bug #3342: Editor: Creation of pathgrids did not check if the pathgrid already existed
Bug #3346: "Talked to PC" is always 0 for "Hello" dialogue
Bug #3349: AITravel doesn't repeat
Bug #3370: NPCs wandering to invalid locations after training
Bug #3378: "StopCombat" command does not function in vanilla quest
Bug #3384: Battle at Nchurdamz - Larienna Macrina does not stop combat after killing Hrelvesuu
Bug #3388: Monster Respawn tied to Quicksave
Bug #3390: Strange visual effect in Dagoth Ur's chamber
Bug #3391: Inappropriate Blight weather behavior at end of main quest
Bug #3394: Replaced dialogue inherits some of its old data
Bug #3397: Actors that start the game dead always have the same death pose
Bug #3401: Sirollus Saccus sells not glass arrows
Bug #3402: Editor: Weapon data not being properly set
Bug #3405: Mulvisic Othril will not use her chitin throwing stars
Bug #3407: Tanisie Verethi will immediately detect the player
Bug #3408: Improper behavior of ashmire particles
Bug #3412: Ai Wander start time resets when saving/loading the game
Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly
Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck
Bug #3423: Sleep interruption inside dungeons too agressive
Bug #3424: Pickpocketing sometimes won't work
Bug #3432: AiFollow / AiEscort durations handled incorrectly
Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases
Bug #3437: Weather-conditioned dialogue should not play in interiors
Bug #3439: Effects cast by summon stick around after their death
Bug #3440: Parallax maps looks weird
Bug #3443: Class graphic for custom class should be Acrobat
Bug #3446: OpenMW segfaults when using Atrayonis's "Anthology Solstheim: Tomb of the Snow Prince" mod
Bug #3448: After dispelled, invisibility icon is still displayed
Bug #3453: First couple of seconds of NPC speech is muted
Bug #3455: Portable house mods lock player and npc movement up exiting house.
Bug #3456: Equipping an item will undo dispel of constant effect invisibility
Bug #3458: Constant effect restore health doesn't work during Wait
Bug #3466: It is possible to stack multiple scroll effects of the same type
Bug #3471: When two mods delete the same references, many references are not disabled by the engine.
Bug #3473: 3rd person camera can be glitched
Feature #1424: NPC "Face" function
Feature #2974: Editor: Multiple Deletion of Subrecords
Feature #3044: Editor: Render path grid v2
Feature #3362: Editor: Configurable key bindings
Feature #3375: Make sun / moon reflections weather dependent
Feature #3386: Editor: Edit pathgrid
0.39.0 0.39.0
------ ------

View file

@ -25,7 +25,7 @@ endif()
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 39) set(OPENMW_VERSION_MINOR 40)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")

View file

@ -15,6 +15,7 @@ namespace CSMPrefs
ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key, ModifierSetting::ModifierSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label) const std::string& label)
: Setting(parent, values, mutex, key, label) : Setting(parent, values, mutex, key, label)
, mButton(0)
, mEditorActive(false) , mEditorActive(false)
{ {
} }

View file

@ -17,6 +17,7 @@ namespace CSMPrefs
ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key, ShortcutSetting::ShortcutSetting(Category* parent, Settings::Manager* values, QMutex* mutex, const std::string& key,
const std::string& label) const std::string& label)
: Setting(parent, values, mutex, key, label) : Setting(parent, values, mutex, key, label)
, mButton(0)
, mEditorActive(false) , mEditorActive(false)
, mEditorPos(0) , mEditorPos(0)
{ {

View file

@ -74,7 +74,9 @@ osg::Vec3f CSVRender::InstanceMode::getSelectionCenter(const std::vector<osg::re
++objectCount; ++objectCount;
} }
} }
center /= objectCount;
if (objectCount > 0)
center /= objectCount;
return center; return center;
} }
@ -92,7 +94,7 @@ osg::Vec3f CSVRender::InstanceMode::getScreenCoords(const osg::Vec3f& pos)
CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent) CSVRender::InstanceMode::InstanceMode (WorldspaceWidget *worldspaceWidget, QWidget *parent)
: EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing", : EditMode (worldspaceWidget, QIcon (":placeholder"), Mask_Reference, "Instance editing",
parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None), parent), mSubMode (0), mSubModeId ("move"), mSelectionMode (0), mDragMode (DragMode_None),
mDragAxis (-1), mLocked (false) mDragAxis (-1), mLocked (false), mUnitScaleDist(1)
{ {
} }

View file

@ -124,7 +124,7 @@ namespace MWClass
int index = ESM::MagicEffect::effectStringToId("sEffectTelekinesis"); int index = ESM::MagicEffect::effectStringToId("sEffectTelekinesis");
const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(index); const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(index);
animation->addSpellCastGlow(effect); // TODO: Telekinesis glow should only be as long as the door animation animation->addSpellCastGlow(effect, 1); // 1 second glow to match the time taken for a door opening or closing
} }
// make key id lowercase // make key id lowercase

View file

@ -1524,6 +1524,26 @@ bool CharacterController::updateWeaponState()
return forcestateupdate; return forcestateupdate;
} }
void CharacterController::updateAnimQueue()
{
if(mAnimQueue.size() > 1)
{
if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
{
mAnimation->disable(mAnimQueue.front().mGroup);
mAnimQueue.pop_front();
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,
MWRender::Animation::BlendMask_All, false,
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback);
}
}
if(!mAnimQueue.empty())
mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1);
}
void CharacterController::update(float duration) void CharacterController::update(float duration)
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
@ -1534,21 +1554,7 @@ void CharacterController::update(float duration)
updateMagicEffects(); updateMagicEffects();
if(!cls.isActor()) if(!cls.isActor())
{ updateAnimQueue();
if(mAnimQueue.size() > 1)
{
if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
{
mAnimation->disable(mAnimQueue.front().mGroup);
mAnimQueue.pop_front();
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,
MWRender::Animation::BlendMask_All, false,
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback);
}
}
}
else if(!cls.getCreatureStats(mPtr).isDead()) else if(!cls.getCreatureStats(mPtr).isDead())
{ {
bool onground = world->isOnGround(mPtr); bool onground = world->isOnGround(mPtr);
@ -1816,19 +1822,8 @@ void CharacterController::update(float duration)
{ {
idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle)); idlestate = (inwater ? CharState_IdleSwim : (sneak && !inJump ? CharState_IdleSneak : CharState_Idle));
} }
else if(mAnimQueue.size() > 1) else
{ updateAnimQueue();
if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
{
mAnimation->disable(mAnimQueue.front().mGroup);
mAnimQueue.pop_front();
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,
MWRender::Animation::BlendMask_All, false,
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount, loopfallback);
}
}
if (!mSkipAnim) if (!mSkipAnim)
{ {
@ -1994,9 +1989,10 @@ void CharacterController::unpersistAnimationState()
mCurrentIdle.clear(); mCurrentIdle.clear();
mIdleState = CharState_SpecialIdle; mIdleState = CharState_SpecialIdle;
bool loopfallback = (mAnimQueue.front().mGroup.compare(0,4,"idle") == 0);
mAnimation->play(anim.mGroup, mAnimation->play(anim.mGroup,
Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f,
"start", "stop", complete, anim.mLoopCount); "start", "stop", complete, anim.mLoopCount, loopfallback);
} }
} }
@ -2009,6 +2005,27 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
} }
else else
{ {
// If the given animation is a looped animation, is already playing
// and has not yet reached its Loop Stop key, make it the only animation
// in the queue, and retain the loop count from the animation that was
// already playing. This emulates observed behavior from the original
// engine and allows banners to animate correctly.
if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname &&
mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop start") >= 0)
{
float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop");
if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key
endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": stop");
if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop))
{
mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, true);
mAnimQueue.resize(1);
return true;
}
}
count = std::max(count, 1); count = std::max(count, 1);
AnimationQueueEntry entry; AnimationQueueEntry entry;
@ -2032,8 +2049,6 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
} }
else if(mode == 0) else if(mode == 0)
{ {
if (!mAnimQueue.empty())
mAnimation->stopLooping(mAnimQueue.front().mGroup);
mAnimQueue.resize(1); mAnimQueue.resize(1);
mAnimQueue.push_back(entry); mAnimQueue.push_back(entry);
} }

View file

@ -212,6 +212,8 @@ class CharacterController : public MWRender::Animation::TextKeyListener
bool updateCreatureState(); bool updateCreatureState();
void updateIdleStormState(bool inwater); void updateIdleStormState(bool inwater);
void updateAnimQueue();
void updateHeadTracking(float duration); void updateHeadTracking(float duration);
void updateMagicEffects(); void updateMagicEffects();

View file

@ -873,16 +873,6 @@ namespace MWRender
addControllers(); addControllers();
} }
void Animation::stopLooping(const std::string& groupname)
{
AnimStateMap::iterator stateiter = mStates.find(groupname);
if(stateiter != mStates.end())
{
stateiter->second.mLoopCount = 0;
return;
}
}
void Animation::adjustSpeedMult(const std::string &groupname, float speedmult) void Animation::adjustSpeedMult(const std::string &groupname, float speedmult)
{ {
AnimStateMap::iterator state(mStates.find(groupname)); AnimStateMap::iterator state(mStates.find(groupname));
@ -1023,35 +1013,33 @@ namespace MWRender
{ {
float targetTime; float targetTime;
if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) if (!state.shouldLoop())
goto handle_loop;
targetTime = state.getTime() + timepassed;
if(textkey == textkeys.end() || textkey->first > targetTime)
{ {
if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) targetTime = state.getTime() + timepassed;
updatePosition(state.getTime(), targetTime, movement); if(textkey == textkeys.end() || textkey->first > targetTime)
state.setTime(std::min(targetTime, state.mStopTime)); {
if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr())
updatePosition(state.getTime(), targetTime, movement);
state.setTime(std::min(targetTime, state.mStopTime));
}
else
{
if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr())
updatePosition(state.getTime(), textkey->first, movement);
state.setTime(textkey->first);
}
state.mPlaying = (state.getTime() < state.mStopTime);
timepassed = targetTime - state.getTime();
while(textkey != textkeys.end() && textkey->first <= state.getTime())
{
handleTextKey(state, stateiter->first, textkey, textkeys);
++textkey;
}
} }
else if(state.shouldLoop())
{ {
if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr())
updatePosition(state.getTime(), textkey->first, movement);
state.setTime(textkey->first);
}
state.mPlaying = (state.getTime() < state.mStopTime);
timepassed = targetTime - state.getTime();
while(textkey != textkeys.end() && textkey->first <= state.getTime())
{
handleTextKey(state, stateiter->first, textkey, textkeys);
++textkey;
}
if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0)
{
handle_loop:
state.mLoopCount--; state.mLoopCount--;
state.setTime(state.mLoopStartTime); state.setTime(state.mLoopStartTime);
state.mPlaying = true; state.mPlaying = true;
@ -1065,7 +1053,7 @@ namespace MWRender
if(state.getTime() >= state.mLoopStopTime) if(state.getTime() >= state.mLoopStopTime)
break; break;
} }
if(timepassed <= 0.0f) if(timepassed <= 0.0f)
break; break;
@ -1095,6 +1083,13 @@ namespace MWRender
return movement; return movement;
} }
void Animation::setLoopingEnabled(const std::string &groupname, bool enabled)
{
AnimStateMap::iterator state(mStates.find(groupname));
if(state != mStates.end())
state->second.mLoopingEnabled = enabled;
}
void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature)
{ {
osg::ref_ptr<osg::StateSet> previousStateset; osg::ref_ptr<osg::StateSet> previousStateset;
@ -1192,7 +1187,7 @@ namespace MWRender
int mLowestUnusedTexUnit; int mLowestUnusedTexUnit;
}; };
void Animation::addSpellCastGlow(const ESM::MagicEffect *effect) void Animation::addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration)
{ {
osg::Vec4f glowColor(1,1,1,1); osg::Vec4f glowColor(1,1,1,1);
glowColor.x() = effect->mData.mRed / 255.f; glowColor.x() = effect->mData.mRed / 255.f;
@ -1207,10 +1202,10 @@ namespace MWRender
if (mGlowUpdater && mGlowUpdater->isPermanentGlowUpdater()) if (mGlowUpdater && mGlowUpdater->isPermanentGlowUpdater())
{ {
mGlowUpdater->setColor(glowColor); mGlowUpdater->setColor(glowColor);
mGlowUpdater->setDuration(1.5); // Glow length measured from original engine as about 1.5 seconds mGlowUpdater->setDuration(glowDuration);
} }
else else
addGlow(mObjectRoot, glowColor, 1.5); addGlow(mObjectRoot, glowColor, glowDuration);
} }
} }

View file

@ -183,6 +183,7 @@ protected:
float mSpeedMult; float mSpeedMult;
bool mPlaying; bool mPlaying;
bool mLoopingEnabled;
size_t mLoopCount; size_t mLoopCount;
AnimPriority mPriority; AnimPriority mPriority;
@ -190,8 +191,8 @@ protected:
bool mAutoDisable; bool mAutoDisable;
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopingEnabled(true),
mPriority(0), mBlendMask(0), mAutoDisable(true) mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true)
{ {
} }
~AnimState(); ~AnimState();
@ -204,6 +205,11 @@ protected:
{ {
*mTime = time; *mTime = time;
} }
bool shouldLoop() const
{
return getTime() >= mLoopStopTime && mLoopingEnabled && mLoopCount > 0;
}
}; };
typedef std::map<std::string,AnimState> AnimStateMap; typedef std::map<std::string,AnimState> AnimStateMap;
AnimStateMap mStates; AnimStateMap mStates;
@ -354,7 +360,9 @@ public:
void removeEffect (int effectId); void removeEffect (int effectId);
void getLoopingEffects (std::vector<int>& out) const; void getLoopingEffects (std::vector<int>& out) const;
void addSpellCastGlow(const ESM::MagicEffect *effect); // Add a spell casting glow to an object. From measuring video taken from the original engine,
// the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second.
void addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration = 1.5);
virtual void updatePtr(const MWWorld::Ptr &ptr); virtual void updatePtr(const MWWorld::Ptr &ptr);
@ -389,10 +397,6 @@ public:
float speedmult, const std::string &start, const std::string &stop, float speedmult, const std::string &start, const std::string &stop,
float startpoint, size_t loops, bool loopfallback=false); float startpoint, size_t loops, bool loopfallback=false);
/** If the given animation group is currently playing, set its remaining loop count to '0'.
*/
void stopLooping(const std::string& groupName);
/** Adjust the speed multiplier of an already playing animation. /** Adjust the speed multiplier of an already playing animation.
*/ */
void adjustSpeedMult (const std::string& groupname, float speedmult); void adjustSpeedMult (const std::string& groupname, float speedmult);
@ -432,6 +436,8 @@ public:
virtual osg::Vec3f runAnimation(float duration); virtual osg::Vec3f runAnimation(float duration);
void setLoopingEnabled(const std::string &groupname, bool enabled);
/// This is typically called as part of runAnimation, but may be called manually if needed. /// This is typically called as part of runAnimation, but may be called manually if needed.
void updateEffects(float duration); void updateEffects(float duration);

View file

@ -9,7 +9,7 @@ namespace Files
const int escape_hash_filter::sEscapeIdentifier = 'a'; const int escape_hash_filter::sEscapeIdentifier = 'a';
const int escape_hash_filter::sHashIdentifier = 'h'; const int escape_hash_filter::sHashIdentifier = 'h';
escape_hash_filter::escape_hash_filter() : mNext(), mSeenNonWhitespace(false), mFinishLine(false) escape_hash_filter::escape_hash_filter() : mNext(), mPrevious(), mSeenNonWhitespace(false), mFinishLine(false)
{ {
} }
@ -137,4 +137,4 @@ namespace Files
return istream; return istream;
} }
} }

View file

@ -2,6 +2,8 @@
namespace Misc namespace Misc
{ {
MessageFormatParser::~MessageFormatParser() {}
void MessageFormatParser::process(const std::string& m) void MessageFormatParser::process(const std::string& m)
{ {
for (unsigned int i = 0; i < m.size(); ++i) for (unsigned int i = 0; i < m.size(); ++i)

View file

@ -19,6 +19,8 @@ namespace Misc
virtual void visitedCharacter(char c) = 0; virtual void visitedCharacter(char c) = 0;
public: public:
virtual ~MessageFormatParser();
virtual void process(const std::string& message); virtual void process(const std::string& message);
}; };
} }