diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index ec19c7321..1ca0fc611 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -15,7 +15,7 @@ ccache --version cmake --version qmake --version -curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-8f5ef6e.zip -o ~/openmw-deps.zip +curl -fSL -R -J https://gitlab.com/OpenMW/openmw-deps/-/raw/main/macos/openmw-deps-20210617.zip -o ~/openmw-deps.zip unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null # additional libraries diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d66eea7b..085451e40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,11 @@ if(NOT HAVE_STDINT_H) endif() if(OPENMW_USE_SYSTEM_OSG) - find_package(OpenSceneGraph 3.3.4 REQUIRED ${USED_OSG_COMPONENTS}) + find_package(OpenSceneGraph 3.4.0 REQUIRED ${USED_OSG_COMPONENTS}) + if (${OPENSCENEGRAPH_VERSION} VERSION_GREATER 3.6.2 AND ${OPENSCENEGRAPH_VERSION} VERSION_LESS 3.6.5) + message(FATAL_ERROR "OpenSceneGraph version ${OPENSCENEGRAPH_VERSION} has critical regressions which cause crashes. Please upgrade to 3.6.5 or later. We strongly recommend using the tip of the official 'OpenSceneGraph-3.6' branch or the tip of '3.6' OpenMW/osg (OSGoS).") + endif() + if(OSG_STATIC) find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS}) endif() diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b2a08030b..af0742d23 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -27,7 +27,9 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwmechanics/aipackage.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/summoning.hpp" #include "../mwscript/interpretercontext.hpp" @@ -417,6 +419,23 @@ namespace MWGui for (const auto& creature : creatureMap) MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(ptr, creature.second); creatureMap.clear(); + + // Check if we are a summon and inform our master we've bit the dust + for(const auto& package : creatureStats.getAiSequence()) + { + if(package->followTargetThroughDoors() && !package->getTarget().isEmpty()) + { + const auto& summoner = package->getTarget(); + auto& summons = summoner.getClass().getCreatureStats(summoner).getSummonedCreatureMap(); + auto it = std::find_if(summons.begin(), summons.end(), [&] (const auto& entry) { return entry.second == creatureStats.getActorId(); }); + if(it != summons.end()) + { + MWMechanics::purgeSummonEffect(summoner, *it); + summons.erase(it); + break; + } + } + } } MWBase::Environment::get().getWorld()->deleteObject(ptr); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a981f50fa..05047336e 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1303,7 +1303,7 @@ namespace MWMechanics // Update bound effects // Note: in vanilla MW multiple bound items of the same type can be created by different spells. // As these extra copies are kinda useless this may or may not be important. - static std::map boundItemsMap; + static std::map boundItemsMap; if (boundItemsMap.empty()) { boundItemsMap[ESM::MagicEffect::BoundBattleAxe] = "sMagicBoundBattleAxeID"; @@ -1319,28 +1319,30 @@ namespace MWMechanics boundItemsMap[ESM::MagicEffect::BoundSpear] = "sMagicBoundSpearID"; } - for (std::map::iterator it = boundItemsMap.begin(); it != boundItemsMap.end(); ++it) + if(ptr.getClass().hasInventoryStore(ptr)) { - bool found = creatureStats.mBoundItems.find(it->first) != creatureStats.mBoundItems.end(); - float magnitude = effects.get(it->first).getMagnitude(); - if (found != (magnitude > 0)) + for (const auto& [effect, itemGmst] : boundItemsMap) { - if (magnitude > 0) - creatureStats.mBoundItems.insert(it->first); - else - creatureStats.mBoundItems.erase(it->first); - - std::string itemGmst = it->second; - std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( - itemGmst)->mValue.getString(); - - magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); - - if (it->first == ESM::MagicEffect::BoundGloves) + bool found = creatureStats.mBoundItems.find(effect) != creatureStats.mBoundItems.end(); + float magnitude = effects.get(effect).getMagnitude(); + if (found != (magnitude > 0)) { - item = MWBase::Environment::get().getWorld()->getStore().get().find( - "sMagicBoundRightGauntletID")->mValue.getString(); + if (magnitude > 0) + creatureStats.mBoundItems.insert(effect); + else + creatureStats.mBoundItems.erase(effect); + + std::string item = MWBase::Environment::get().getWorld()->getStore().get().find( + itemGmst)->mValue.getString(); + magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); + + if (effect == ESM::MagicEffect::BoundGloves) + { + item = MWBase::Environment::get().getWorld()->getStore().get().find( + "sMagicBoundRightGauntletID")->mValue.getString(); + magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr); + } } } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 949779099..84e3510ce 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -625,6 +625,28 @@ namespace MWMechanics mAiSequence.readState(state.mAiSequence); mMagicEffects.readState(state.mMagicEffects); + // Rebuild the bound item cache + for(int effectId = ESM::MagicEffect::BoundDagger; effectId <= ESM::MagicEffect::BoundGloves; effectId++) + { + if(mMagicEffects.get(effectId).getMagnitude() > 0) + mBoundItems.insert(effectId); + else + { + // Check active spell effects + // We can't use mActiveSpells::getMagicEffects here because it doesn't include expired effects + auto spell = std::find_if(mActiveSpells.begin(), mActiveSpells.end(), [&] (const auto& spell) + { + const auto& effects = spell.second.mEffects; + return std::find_if(effects.begin(), effects.end(), [&] (const auto& effect) + { + return effect.mEffectId == effectId; + }) != effects.end(); + }); + if(spell != mActiveSpells.end()) + mBoundItems.insert(effectId); + } + } + mSummonedCreatures = state.mSummonedCreatureMap; mSummonGraveyard = state.mSummonGraveyard; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 53d4713a8..313b9441e 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -77,10 +77,8 @@ namespace MWMechanics void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { - if (target.isEmpty()) - return; - - if (target.getClass().isActor()) + const bool targetIsActor = !target.isEmpty() && target.getClass().isActor(); + if (targetIsActor) { // Early-out for characters that have departed. const auto& stats = target.getClass().getCreatureStats(target); @@ -102,7 +100,7 @@ namespace MWMechanics return; const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search (mId); - if (spell && target.getClass().isActor() && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight)) + if (spell && targetIsActor && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight)) { int requiredResistance = (spell->mData.mType == ESM::Spell::ST_Disease) ? ESM::MagicEffect::ResistCommonDisease @@ -125,13 +123,13 @@ namespace MWMechanics // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell. // Otherwise, they'd only apply after the whole spell was added. MagicEffects targetEffects; - if (target.getClass().isActor()) + if (targetIsActor) targetEffects += target.getClass().getCreatureStats(target).getMagicEffects(); bool castByPlayer = (!caster.isEmpty() && caster == getPlayer()); ActiveSpells targetSpells; - if (target.getClass().isActor()) + if (targetIsActor) targetSpells = target.getClass().getCreatureStats(target).getActiveSpells(); bool canCastAnEffect = false; // For bound equipment.If this remains false @@ -143,7 +141,7 @@ namespace MWMechanics int currentEffectIndex = 0; for (std::vector::const_iterator effectIt (effects.mList.begin()); - effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex) + !target.isEmpty() && effectIt != effects.mList.end(); ++effectIt, ++currentEffectIndex) { if (effectIt->mRange != range) continue; @@ -329,7 +327,7 @@ namespace MWMechanics } // Re-casting a summon effect will remove the creature from previous castings of that effect. - if (isSummoningEffect(effectIt->mEffectID) && target.getClass().isActor()) + if (isSummoningEffect(effectIt->mEffectID) && targetIsActor) { CreatureStats& targetStats = target.getClass().getCreatureStats(target); ESM::SummonKey key(effectIt->mEffectID, mId, currentEffectIndex); @@ -385,16 +383,19 @@ namespace MWMechanics if (!exploded) MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, target, range, mId, mSourceName, mFromProjectile); - if (!reflectedEffects.mList.empty()) - inflict(caster, target, reflectedEffects, range, true, exploded); - - if (!appliedLastingEffects.empty()) + if (!target.isEmpty()) { - int casterActorId = -1; - if (!caster.isEmpty() && caster.getClass().isActor()) - casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); - target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, - mSourceName, casterActorId); + if (!reflectedEffects.mList.empty()) + inflict(caster, target, reflectedEffects, range, true, exploded); + + if (!appliedLastingEffects.empty()) + { + int casterActorId = -1; + if (!caster.isEmpty() && caster.getClass().isActor()) + casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); + target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, + mSourceName, casterActorId); + } } } diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index 430834b49..6beab007c 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -201,16 +201,10 @@ namespace MWMechanics continue; } MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second); - if (ptr.isEmpty() || (ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished())) + if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead() && ptr.getClass().getCreatureStats(ptr).isDeathAnimationFinished()) { // Purge the magic effect so a new creature can be summoned if desired - const ESM::SummonKey& key = it->first; - creatureStats.getActiveSpells().purgeEffect(key.mEffectId, key.mSourceId, key.mEffectIndex); - creatureStats.getSpells().purgeEffect(key.mEffectId, key.mSourceId); - if (mActor.getClass().hasInventoryStore(mActor)) - mActor.getClass().getInventoryStore(mActor).purgeEffect(key.mEffectId, key.mSourceId, false, key.mEffectIndex); - - MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(mActor, it->second); + purgeSummonEffect(mActor, *it); creatureMap.erase(it++); } else @@ -218,4 +212,14 @@ namespace MWMechanics } } + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon) + { + auto& creatureStats = summoner.getClass().getCreatureStats(summoner); + creatureStats.getActiveSpells().purgeEffect(summon.first.mEffectId, summon.first.mSourceId, summon.first.mEffectIndex); + creatureStats.getSpells().purgeEffect(summon.first.mEffectId, summon.first.mSourceId); + if (summoner.getClass().hasInventoryStore(summoner)) + summoner.getClass().getInventoryStore(summoner).purgeEffect(summon.first.mEffectId, summon.first.mSourceId, false, summon.first.mEffectIndex); + + MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(summoner, summon.second); + } } diff --git a/apps/openmw/mwmechanics/summoning.hpp b/apps/openmw/mwmechanics/summoning.hpp index 7e787499e..3c3e18a96 100644 --- a/apps/openmw/mwmechanics/summoning.hpp +++ b/apps/openmw/mwmechanics/summoning.hpp @@ -17,6 +17,8 @@ namespace MWMechanics std::string getSummonedCreature(int effectId); + void purgeSummonEffect(const MWWorld::Ptr& summoner, const std::pair& summon); + struct UpdateSummonedCreatures : public EffectSourceVisitor { UpdateSummonedCreatures(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index b6bae8cbb..8e29f0af4 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -477,6 +477,8 @@ namespace MWRender constexpr auto copyMask = ~Mask_UpdateVisitor; AnalyzeVisitor analyzeVisitor(copyMask); + osg::Vec3f center3 = { center.x(), center.y(), 0.f }; + analyzeVisitor.mCurrentDistance = (viewPoint - center3).length2(); float minSize = mMinSize; if (mMinSizeMergeFactor) minSize *= mMinSizeMergeFactor; @@ -546,7 +548,6 @@ namespace MWRender continue; } - analyzeVisitor.mCurrentDistance = dSqr; auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index c4233059c..2c83c43fe 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -158,7 +158,7 @@ namespace SceneUtil static constexpr int queryBlockSize(int sz) { - return 3 * osg::Vec4::num_components * sizeof(GL_FLOAT) * sz; + return 3 * osg::Vec4::num_components * sizeof(GLfloat) * sz; } void setCachedSunPos(const osg::Vec4& pos) @@ -215,9 +215,9 @@ namespace SceneUtil } Offsets(int offsetColors, int offsetPosition, int offsetAttenuationRadius, int stride) - : mStride((offsetAttenuationRadius + sizeof(GL_FLOAT) * osg::Vec4::num_components + stride) / 4) + : mStride((offsetAttenuationRadius + sizeof(GLfloat) * osg::Vec4::num_components + stride) / 4) { - constexpr auto sizeofFloat = sizeof(GL_FLOAT); + constexpr auto sizeofFloat = sizeof(GLfloat); const auto diffuseOffset = offsetColors / sizeofFloat; mValues[Diffuse] = diffuseOffset; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index e9eb9b2b2..88d44d2f6 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -566,8 +566,9 @@ MWShadowTechnique::ShadowData::ShadowData(MWShadowTechnique::ViewDependentData* _camera = new osg::Camera; _camera->setName("ShadowCamera"); _camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); +#ifndef __APPLE__ // workaround shadow issue on macOS, https://gitlab.com/OpenMW/openmw/-/issues/6057 _camera->setImplicitBufferAttachmentMask(0, 0); - +#endif //_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); _camera->setClearColor(osg::Vec4(0.0f,0.0f,0.0f,0.0f));