Merge branch 'master' into pineapple/fix-video-incomplete-type-2

Resolve merge issues related to CHANGELOG.md

* master:
  Optimize skinning (task #4605)
  Update changelog
  Update some comments
  Set the OpenAL source offset after setting the buffer
  Make Move and MoveWorld console commands move actors standing on moving object (bug #2274)
  Adding Changelog entry
  Allow messageboxes arguments to have newline characters (bug #3836)
  Check for impact immediately when launch a projectile (bug #3059)
  Fix gold count calculation in pickupObject (bug #4604)
  Correct special case soundgen comparisons
  Move "land" check earlier
  Fixes #3681
  Play landing sound manually and ignore land soundgen textkeys (bug #2256)
  Make some more optimizations to actor processing loops
  Fix freeze in getActorsSidingWith
  Addiong missing "to" word
  Adding common problems that were previous on the site FAQ
  Treat <> and << operators as < and  >< and >> as > in scripts
  stage1: priorities for event music and other minor improvements to the music system

# Conflicts:
#	CHANGELOG.md
pull/541/head
Sophie Kirschner 6 years ago
commit a1e076a37e

@ -4,6 +4,8 @@
Bug #1990: Sunrise/sunset not set correct
Bug #2131: Lustidrike's spell misses the player every time
Bug #2222: Fatigue's effect on selling price is backwards
Bug #2256: Landing sound not playing when jumping immediately after landing
Bug #2274: Thin platform clips through player character instead of lifting
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
@ -15,6 +17,7 @@
Bug #2872: Tab completion in console doesn't work with explicit reference
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3049: Drain and Fortify effects are not properly applied on health, magicka and fatigue
Bug #3059: Unable to hit with marksman weapons when too close to an enemy
Bug #3072: Fatal error on AddItem <item> that has a script containing Equip <item>
Bug #3249: Fixed revert function not updating views properly
Bug #3374: Touch spells not hitting kwama foragers
@ -22,7 +25,9 @@
Bug #3533: GetSpellEffects should detect effects with zero duration
Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning
Bug #3681: OpenMW-CS: Clicking Scripts in Preferences spawns many color pickers
Bug #3788: GetPCInJail and GetPCTraveling do not work as in vanilla
Bug #3836: Script fails to compile when command argument contains "\n"
Bug #3876: Landscape texture painting is misaligned
Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
@ -98,6 +103,8 @@
Bug #4575: Weird result of attack animation blending with movement animations
Bug #4576: Reset of idle animations when attack can not be started
Bug #4591: Attack strength should be 0 if player did not hold the attack button
Bug #4597: <> operator causes a compile error
Bug #4604: Picking up gold from the ground only makes 1 grabbed
Feature #1645: Casting effects from objects
Feature #2606: Editor: Implemented (optional) case sensitive global search
Feature #3083: Play animation when NPC is casting spell via script
@ -123,6 +130,8 @@
Feature #4581: Use proper logging system
Task #2490: Don't open command prompt window on Release-mode builds automatically
Task #4545: Enable is_pod string test
Task #4605: Optimize skinning
Task #4606: Support Rapture3D's OpenAL driver
Task #4613: Incomplete type errors when compiling with g++ on OSX 10.9
0.44.0

@ -1,11 +1,9 @@
#include "coloreditor.hpp"
#include <QApplication>
#include <QColor>
#include <QColorDialog>
#include <QDesktopWidget>
#include <QPainter>
#include <QRect>
#include <QShowEvent>
#include "colorpickerpopup.hpp"
@ -27,9 +25,7 @@ CSVWidget::ColorEditor::ColorEditor(QWidget *parent, const bool popupOnStart)
mColorPicker(new ColorPickerPopup(this)),
mPopupOnStart(popupOnStart)
{
setCheckable(true);
connect(this, SIGNAL(clicked()), this, SLOT(showPicker()));
connect(mColorPicker, SIGNAL(hid()), this, SLOT(pickerHid()));
connect(mColorPicker, SIGNAL(colorChanged(const QColor &)), this, SLOT(pickerColorChanged(const QColor &)));
}
@ -85,20 +81,7 @@ void CSVWidget::ColorEditor::setColor(const int colorInt)
void CSVWidget::ColorEditor::showPicker()
{
if (isChecked())
{
mColorPicker->showPicker(calculatePopupPosition(), mColor);
}
else
{
mColorPicker->hide();
}
}
void CSVWidget::ColorEditor::pickerHid()
{
setChecked(false);
emit pickingFinished();
mColorPicker->showPicker(calculatePopupPosition(), mColor);
}
void CSVWidget::ColorEditor::pickerColorChanged(const QColor &color)

@ -45,7 +45,6 @@ namespace CSVWidget
private slots:
void showPicker();
void pickerHid();
void pickerColorChanged(const QColor &color);
signals:

@ -4,7 +4,6 @@
#include <QPushButton>
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QLayout>
#include <QStyleOption>
@ -19,7 +18,6 @@ CSVWidget::ColorPickerPopup::ColorPickerPopup(QWidget *parent)
mColorPicker->setWindowFlags(Qt::Widget);
mColorPicker->setOptions(QColorDialog::NoButtons | QColorDialog::DontUseNativeDialog);
mColorPicker->installEventFilter(this);
mColorPicker->open();
connect(mColorPicker,
SIGNAL(currentColorChanged(const QColor &)),
this,
@ -39,8 +37,9 @@ void CSVWidget::ColorPickerPopup::showPicker(const QPoint &position, const QColo
geometry.moveTo(position);
setGeometry(geometry);
mColorPicker->setCurrentColor(initialColor);
show();
// Calling getColor() creates a blocking dialog that will continue execution once the user chooses OK or Cancel
QColor color = mColorPicker->getColor(initialColor);
mColorPicker->setCurrentColor(color);
}
void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
@ -63,12 +62,6 @@ void CSVWidget::ColorPickerPopup::mousePressEvent(QMouseEvent *event)
QFrame::mousePressEvent(event);
}
void CSVWidget::ColorPickerPopup::hideEvent(QHideEvent *event)
{
QFrame::hideEvent(event);
emit hid();
}
bool CSVWidget::ColorPickerPopup::eventFilter(QObject *object, QEvent *event)
{
if (object == mColorPicker && event->type() == QEvent::KeyPress)

@ -20,11 +20,9 @@ namespace CSVWidget
protected:
virtual void mousePressEvent(QMouseEvent *event);
virtual void hideEvent(QHideEvent *event);
virtual bool eventFilter(QObject *object, QEvent *event);
signals:
void hid();
void colorChanged(const QColor &color);
};
}

@ -412,6 +412,7 @@ namespace MWBase
/// @note throws an exception when invoked on a teleport door
virtual void activateDoor(const MWWorld::Ptr& door, int state) = 0;
virtual void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors) = 0; ///< get a list of actors standing on \a object
virtual bool getPlayerStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::ConstPtr& object) = 0; ///< @return true if any actor is standing on \a object
virtual bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) = 0; ///< @return true if the player is colliding with \a object
@ -490,8 +491,8 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) = 0;
virtual void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) = 0;
virtual void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) = 0;
virtual void applyLoopingParticles(const MWWorld::Ptr& ptr) = 0;

@ -1241,11 +1241,10 @@ namespace MWClass
{
MWBase::World *world = MWBase::Environment::get().getWorld();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return "DefaultLandWater";
if(world->isOnGround(ptr))
return "DefaultLand";
return "";
return "DefaultLand";
}
if(name == "swimleft")
return "Swim Left";

@ -681,6 +681,8 @@ namespace MWGui
return;
int count = object.getRefData().getCount();
if (object.getClass().isGold(object))
count *= object.getClass().getValue(object);
MWWorld::Ptr player = MWMechanics::getPlayer();
MWBase::Environment::get().getWorld()->breakInvisibility(player);

@ -1782,6 +1782,8 @@ namespace MWMechanics
if (iteratedActor == getPlayer())
continue;
const bool sameActor = (iteratedActor == actor);
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
if (stats.isDead())
continue;
@ -1790,14 +1792,13 @@ namespace MWMechanics
// Actors that are targeted by this actor's Follow or Escort packages also side with them
for (auto package = stats.getAiSequence().begin(); package != stats.getAiSequence().end(); ++package)
{
const MWWorld::Ptr &target = (*package)->getTarget();
if ((*package)->sideWithTarget() && !target.isEmpty())
if ((*package)->sideWithTarget() && !(*package)->getTarget().isEmpty())
{
if (iteratedActor == actor)
if (sameActor)
{
list.push_back(target);
list.push_back((*package)->getTarget());
}
else if (target == actor)
else if ((*package)->getTarget() == actor)
{
list.push_back(iteratedActor);
}
@ -1816,7 +1817,7 @@ namespace MWMechanics
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
const MWWorld::Ptr &iteratedActor = iter->first;
if (iteratedActor == getPlayer())
if (iteratedActor == getPlayer() || iteratedActor == actor)
continue;
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
@ -1879,7 +1880,7 @@ namespace MWMechanics
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
const MWWorld::Ptr &iteratedActor = iter->first;
if (iteratedActor == getPlayer())
if (iteratedActor == getPlayer() || iteratedActor == actor)
continue;
const CreatureStats &stats = iteratedActor.getClass().getCreatureStats(iteratedActor);
@ -1909,8 +1910,11 @@ namespace MWMechanics
getObjectsInRange(position, aiProcessingDistance, neighbors);
for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)
{
if (*neighbor == actor)
continue;
const CreatureStats &stats = neighbor->getClass().getCreatureStats(*neighbor);
if (stats.isDead() || *neighbor == actor)
if (stats.isDead())
continue;
if (stats.getAiSequence().isInCombat(actor))

@ -938,11 +938,14 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
}
}
if (soundgen == "land") // Morrowind ignores land soundgen for some reason
return;
std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen);
if(!sound.empty())
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0)
if(soundgen == "left" || soundgen == "right")
{
// Don't make foot sounds local for the player, it makes sense to keep them
// positioned on the ground.
@ -2027,6 +2030,12 @@ void CharacterController::update(float duration)
cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1);
}
}
// Play landing sound
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
std::string sound = cls.getSoundIdFromSndGen(mPtr, "land");
if (!sound.empty())
sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal);
}
else
{

@ -123,7 +123,8 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) * attackStrength;
MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed, attackStrength);
MWWorld::Ptr weaponPtr = *weapon;
MWBase::Environment::get().getWorld()->launchProjectile(actor, weaponPtr, launchPos, orient, weaponPtr, speed, attackStrength);
showWeapon(false);
@ -149,9 +150,11 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * attackStrength;
MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed, attackStrength);
MWWorld::Ptr weaponPtr = *weapon;
MWWorld::Ptr ammoPtr = *ammo;
MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);
inv.remove(*ammo, 1, actor);
inv.remove(ammoPtr, 1, actor);
mAmmunition.reset();
}
}

@ -29,6 +29,18 @@ namespace MWScript
{
namespace Transformation
{
void moveStandingActors(const MWWorld::Ptr &ptr, const osg::Vec3f& diff)
{
std::vector<MWWorld::Ptr> actors;
MWBase::Environment::get().getWorld()->getActorsStandingOn (ptr, actors);
for (auto& actor : actors)
{
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
actorPos += diff;
MWBase::Environment::get().getWorld()->moveObject(actor, actorPos.x(), actorPos.y(), actorPos.z());
}
}
template<class R>
class OpSetScale : public Interpreter::Opcode0
{
@ -666,6 +678,10 @@ namespace MWScript
osg::Vec3f diff = ptr.getRefData().getBaseNode()->getAttitude() * posChange;
osg::Vec3f worldPos(ptr.getRefData().getPosition().asVec3());
worldPos += diff;
// We should move actors, standing on moving object, too.
// This approach can be used to create elevators.
moveStandingActors(ptr, diff);
MWBase::Environment::get().getWorld()->moveObject(ptr, worldPos.x(), worldPos.y(), worldPos.z());
}
};
@ -688,22 +704,21 @@ namespace MWScript
runtime.pop();
const float *objPos = ptr.getRefData().getPosition().pos;
osg::Vec3f diff;
MWWorld::Ptr updated;
if (axis == "x")
{
updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+movement, objPos[1], objPos[2]);
}
diff.x() += movement;
else if (axis == "y")
{
updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1]+movement, objPos[2]);
}
diff.y() += movement;
else if (axis == "z")
{
updated = MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0], objPos[1], objPos[2]+movement);
}
diff.z() += movement;
else
throw std::runtime_error ("invalid movement axis: " + axis);
// We should move actors, standing on moving object, too.
// This approach can be used to create elevators.
moveStandingActors(ptr, diff);
MWBase::Environment::get().getWorld()->moveObject(ptr, objPos[0]+diff.x(), objPos[1]+diff.y(), objPos[2]+diff.z());
}
};

@ -1145,14 +1145,24 @@ bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset)
initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),
sound->getIsLooping(), sound->getUseEnv());
alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcef(source, AL_SEC_OFFSET, offset);
if(getALError() != AL_NO_ERROR)
{
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
alGetError();
return false;
}
alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcePlay(source);
if(getALError() != AL_NO_ERROR)
{
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
alGetError();
return false;
}
mFreeSources.pop_front();
sound->mHandle = MAKE_PTRID(source);
@ -1175,14 +1185,24 @@ bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset)
initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(),
sound->getUseEnv());
alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcef(source, AL_SEC_OFFSET, offset);
if(getALError() != AL_NO_ERROR)
{
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
alGetError();
return false;
}
alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcePlay(source);
if(getALError() != AL_NO_ERROR)
{
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
alGetError();
return false;
}
mFreeSources.pop_front();
sound->mHandle = MAKE_PTRID(source);
@ -1197,9 +1217,8 @@ void OpenAL_Output::finishSound(Sound *sound)
ALuint source = GET_PTRID(sound->mHandle);
sound->mHandle = 0;
// Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state,
// which works around a bug in the MacOS OpenAL implementation which would otherwise think
// the initial queue already played when it hasn't.
// Rewind the stream to put the source back into an AL_INITIAL state, for
// the next time it's used.
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
getALError();
@ -1302,9 +1321,8 @@ void OpenAL_Output::finishStream(Stream *sound)
sound->mHandle = 0;
mStreamThread->remove(stream);
// Rewind the stream instead of stopping it, this puts the source into an AL_INITIAL state,
// which works around a bug in the MacOS OpenAL implementation which would otherwise think
// the initial queue already played when it hasn't.
// Rewind the stream to put the source back into an AL_INITIAL state, for
// the next time it's used.
alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0);
getALError();

@ -2377,6 +2377,11 @@ namespace MWWorld
return !actors.empty();
}
void World::getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors)
{
mPhysics->getActorsStandingOn(object, actors);
}
bool World::getPlayerCollidingWith (const MWWorld::ConstPtr& object)
{
MWWorld::Ptr player = getPlayerPtr();
@ -2892,10 +2897,34 @@ namespace MWWorld
}
}
void World::launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength)
void World::launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength)
{
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);
// An initial position of projectile can be outside shooter's collision box, so any object between shooter and launch position will be ignored.
// To avoid this issue, we should check for impact immediately before launch the projectile.
// So we cast a 1-yard-length ray from shooter to launch position and check if there are collisions in this area.
// TODO: as a better solutuon we should handle projectiles during physics update, not during world update.
const osg::Vec3f sourcePos = worldPos + orient * osg::Vec3f(0,-1,0) * 64.f;
// Early out if the launch position is underwater
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
if (underwater)
{
mRendering->emitWaterRipple(worldPos);
return;
}
// For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result.
std::vector<MWWorld::Ptr> targetActors;
if (!actor.isEmpty() && actor.getClass().isActor() && actor != MWMechanics::getPlayer())
actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors);
// Check for impact, if yes, handle hit, if not, launch projectile
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(sourcePos, worldPos, actor, targetActors, 0xff, MWPhysics::CollisionType_Projectile);
if (result.mHit)
MWMechanics::projectileHit(actor, result.mHitObject, bow, projectile, result.mHitPos, attackStrength);
else
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed, attackStrength);
}
void World::launchMagicBolt (const std::string &spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection)

@ -525,6 +525,7 @@ namespace MWWorld
/// @note throws an exception when invoked on a teleport door
void activateDoor(const MWWorld::Ptr& door, int state) override;
void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector<MWWorld::Ptr> &actors); ///< get a list of actors standing on \a object
bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object
bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object
bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object
@ -607,8 +608,8 @@ namespace MWWorld
void castSpell (const MWWorld::Ptr& actor, bool manualSpell=false) override;
void launchMagicBolt (const std::string& spellId, const MWWorld::Ptr& caster, const osg::Vec3f& fallbackDirection) override;
void launchProjectile (MWWorld::Ptr actor, MWWorld::ConstPtr projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr bow, float speed, float attackStrength) override;
void launchProjectile (MWWorld::Ptr& actor, MWWorld::Ptr& projectile,
const osg::Vec3f& worldPos, const osg::Quat& orient, MWWorld::Ptr& bow, float speed, float attackStrength) override;
void applyLoopingParticles(const MWWorld::Ptr& ptr) override;

@ -306,10 +306,22 @@ namespace Compiler
int i = 0;
std::string lowerCase = Misc::StringUtils::lowerCase(name);
bool isKeyword = false;
for (; sKeywords[i]; ++i)
if (lowerCase==sKeywords[i])
{
isKeyword = true;
break;
}
// Russian localization and some mods use a quirk - add newline character directly
// to compiled bytecode via HEX-editor to implement multiline messageboxes.
// Of course, original editor will not compile such script.
// Allow messageboxes to bybass the "incomplete string or name" error.
if (lowerCase == "messagebox")
enableIgnoreNewlines();
else if (isKeyword)
mIgnoreNewline = false;
if (sKeywords[i])
{
@ -357,9 +369,14 @@ namespace Compiler
// }
else if (c=='\n')
{
error = true;
mErrorHandler.error ("incomplete string or name", mLoc);
break;
if (mIgnoreNewline)
mErrorHandler.warning ("string contains newline character, make sure that it is intended", mLoc);
else
{
error = true;
mErrorHandler.error ("incomplete string or name", mLoc);
break;
}
}
}
else if (!(c=='"' && name.empty()))
@ -502,6 +519,11 @@ namespace Compiler
if (get (c) && c!='=') // <== is a allowed as an alternative to <= :(
putback (c);
}
else if (c == '<' || c == '>') // Treat <> and << as <
{
special = S_cmpLT;
mErrorHandler.warning (std::string("invalid operator <") + c + ", treating it as <", mLoc);
}
else
{
putback (c);
@ -525,6 +547,11 @@ namespace Compiler
if (get (c) && c!='=') // >== is a allowed as an alternative to >= :(
putback (c);
}
else if (c == '<' || c == '>') // Treat >< and >> as >
{
special = S_cmpGT;
mErrorHandler.warning (std::string("invalid operator >") + c + ", treating it as >", mLoc);
}
else
{
putback (c);
@ -578,7 +605,7 @@ namespace Compiler
const Extensions *extensions)
: mErrorHandler (errorHandler), mStream (inputStream), mExtensions (extensions),
mPutback (Putback_None), mPutbackCode(0), mPutbackInteger(0), mPutbackFloat(0),
mStrictKeywords (false), mTolerantNames (false)
mStrictKeywords (false), mTolerantNames (false), mIgnoreNewline(false)
{
}
@ -631,6 +658,11 @@ namespace Compiler
mExtensions->listKeywords (keywords);
}
void Scanner::enableIgnoreNewlines()
{
mIgnoreNewline = true;
}
void Scanner::enableStrictKeywords()
{
mStrictKeywords = true;

@ -39,6 +39,7 @@ namespace Compiler
TokenLoc mPutbackLoc;
bool mStrictKeywords;
bool mTolerantNames;
bool mIgnoreNewline;
public:
@ -126,6 +127,11 @@ namespace Compiler
void listKeywords (std::vector<std::string>& keywords);
///< Append all known keywords to \a keywords.
/// Treat newline character as a part of script command.
///
/// \attention This mode lasts only until the next keyword is reached.
void enableIgnoreNewlines();
/// Do not accept keywords in quotation marks anymore.
///
/// \attention This mode lasts only until the next newline is reached.

@ -8,6 +8,31 @@
#include "skeleton.hpp"
#include "util.hpp"
namespace
{
inline void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result)
{
osg::Matrixf m = invBindMatrix * matrix;
float* ptr = m.ptr();
float* ptrresult = result.ptr();
ptrresult[0] += ptr[0] * weight;
ptrresult[1] += ptr[1] * weight;
ptrresult[2] += ptr[2] * weight;
ptrresult[4] += ptr[4] * weight;
ptrresult[5] += ptr[5] * weight;
ptrresult[6] += ptr[6] * weight;
ptrresult[8] += ptr[8] * weight;
ptrresult[9] += ptr[9] * weight;
ptrresult[10] += ptr[10] * weight;
ptrresult[12] += ptr[12] * weight;
ptrresult[13] += ptr[13] * weight;
ptrresult[14] += ptr[14] * weight;
}
}
namespace SceneUtil
{
@ -141,28 +166,6 @@ bool RigGeometry::initFromParentSkeleton(osg::NodeVisitor* nv)
return true;
}
void accumulateMatrix(const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, float weight, osg::Matrixf& result)
{
osg::Matrixf m = invBindMatrix * matrix;
float* ptr = m.ptr();
float* ptrresult = result.ptr();
ptrresult[0] += ptr[0] * weight;
ptrresult[1] += ptr[1] * weight;
ptrresult[2] += ptr[2] * weight;
ptrresult[4] += ptr[4] * weight;
ptrresult[5] += ptr[5] * weight;
ptrresult[6] += ptr[6] * weight;
ptrresult[8] += ptr[8] * weight;
ptrresult[9] += ptr[9] * weight;
ptrresult[10] += ptr[10] * weight;
ptrresult[12] += ptr[12] * weight;
ptrresult[13] += ptr[13] * weight;
ptrresult[14] += ptr[14] * weight;
}
void RigGeometry::cull(osg::NodeVisitor* nv)
{
if (!mSkeleton)
@ -173,7 +176,8 @@ void RigGeometry::cull(osg::NodeVisitor* nv)
return;
}
if ((!mSkeleton->getActive() && mLastFrameNumber != 0) || mLastFrameNumber == nv->getTraversalNumber())
unsigned int traversalNumber = nv->getTraversalNumber();
if (mLastFrameNumber == traversalNumber || (mLastFrameNumber != 0 && !mSkeleton->getActive()))
{
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
nv->pushOntoNodePath(&geom);
@ -181,10 +185,10 @@ void RigGeometry::cull(osg::NodeVisitor* nv)
nv->popFromNodePath();
return;
}
mLastFrameNumber = nv->getTraversalNumber();
mLastFrameNumber = traversalNumber;
osg::Geometry& geom = *getGeometry(mLastFrameNumber);
mSkeleton->updateBoneMatrices(nv->getTraversalNumber());
mSkeleton->updateBoneMatrices(traversalNumber);
// skinning
const osg::Vec3Array* positionSrc = static_cast<osg::Vec3Array*>(mSourceGeometry->getVertexArray());
@ -195,34 +199,31 @@ void RigGeometry::cull(osg::NodeVisitor* nv)
osg::Vec3Array* normalDst = static_cast<osg::Vec3Array*>(geom.getNormalArray());
osg::Vec4Array* tangentDst = static_cast<osg::Vec4Array*>(geom.getTexCoordArray(7));
for (Bone2VertexMap::const_iterator it = mBone2VertexMap.begin(); it != mBone2VertexMap.end(); ++it)
for (auto &pair : mBone2VertexMap)
{
osg::Matrixf resultMat (0, 0, 0, 0,
osg::Matrixf resultMat (0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 1);
for (std::vector<BoneWeight>::const_iterator weightIt = it->first.begin(); weightIt != it->first.end(); ++weightIt)
for (auto &weight : pair.first)
{
Bone* bone = weightIt->first.first;
const osg::Matrix& invBindMatrix = weightIt->first.second;
float weight = weightIt->second;
const osg::Matrixf& boneMatrix = bone->mMatrixInSkeletonSpace;
accumulateMatrix(invBindMatrix, boneMatrix, weight, resultMat);
accumulateMatrix(weight.first.second, weight.first.first->mMatrixInSkeletonSpace, weight.second, resultMat);
}
if (mGeomToSkelMatrix)
resultMat *= (*mGeomToSkelMatrix);
for (std::vector<unsigned short>::const_iterator vertexIt = it->second.begin(); vertexIt != it->second.end(); ++vertexIt)
for (auto &vertex : pair.second)
{
unsigned short vertex = *vertexIt;
(*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]);
if (normalDst)
(*normalDst)[vertex] = osg::Matrix::transform3x3((*normalSrc)[vertex], resultMat);
(*normalDst)[vertex] = osg::Matrixf::transform3x3((*normalSrc)[vertex], resultMat);
if (tangentDst)
{
osg::Vec4f srcTangent = (*tangentSrc)[vertex];
osg::Vec3f transformedTangent = osg::Matrix::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat);
const osg::Vec4f& srcTangent = (*tangentSrc)[vertex];
osg::Vec3f transformedTangent = osg::Matrixf::transform3x3(osg::Vec3f(srcTangent.x(), srcTangent.y(), srcTangent.z()), resultMat);
(*tangentDst)[vertex] = osg::Vec4f(transformedTangent, srcTangent.w());
}
}

@ -1383,9 +1383,9 @@ We add several new script instructions:
* PlayBackgroundMusic (t)
* PlaylistBackgroundMusic (pl)
* StopBackgroundMusic
* PlayEventMusic (t, loop=0, force=0)
* PlaylistEventMusic (pl, loop=0, force=0)
* StopEventMusic
* PlayEventMusic (t, priority, loop=0, force=0)
* PlaylistEventMusic (pl, priority, loop=0, force=0)
* StopEventMusic (priority)
Arguments:
@ -1393,8 +1393,14 @@ Arguments:
* pl (string): a playlist (specified by its ID)
* loop (integer): looping or not
* force (integer): Terminate any other running event music first. If this flag is 0 and there is already event music playing the new event music is ignored. Event music provided by content developers will usually not set this flag.
* priority (integer): Priority for event music. If there are multiple instances of active event music only the one with the highest priority will play. There can only ever be one instance of event music for any priority level. If event music of higher priority stops and there is event music of lower priority the event music of lower priority starts to play.
Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart.
Note: An attempt to play a track or playlist that is currently playing is ignored. In this case the track or playlist does not restart. Changes in looping behaviour are considered though.
New games starting with a new format omwgame file can use priorities as the developer sees fit. For old games where automatic injection of scripts and music records take place we define the following priorities:
* 10: Combat music
* 0: Basic event music
### Transition of Existing System

@ -47,4 +47,33 @@ Installing game files via Steam on macOS: DISK WRITE ERROR
[other entries]
}
Restart the Steam client. The download should now proceed.
Restart the Steam client. The download should now proceed.
In-game textures show up as pink
################################
:Symptoms:
Some textures don't show up and are replaced by pink "filler" textures.
:Cause:
Textures shipped with Morrowind are compressed with S3TC, a texture compression algorithm that was patented in
the United States until October 2017. Software drivers and operating system distributions released before that date
may not include support for this algorithm.
:Fix:
Upgrade your graphics drivers and/or operating system to the latest version.
Music plays, but only a black screen is shown
#############################################
:Symptoms:
When launching OpenMW, audio is heard but there is only a black screen.
:Cause:
This can occur when you did not import the Morrowind.ini file when the launcher asked for it.
:Fix:
To fix it, you need to delete the `launcher.cfg` file from your configuration path
([Path description on Wiki](https://wiki.openmw.org/index.php?title=Paths)), then start the launcher, select your
Morrowind installation, and when the launcher asks whether the `Morrowind.ini` file should be imported, make sure
to select "Yes".
Loading…
Cancel
Save