diff --git a/apps/openmw-mp/processors/world/ProcessorContainer.hpp b/apps/openmw-mp/processors/world/ProcessorContainer.hpp index 6a4939da7..f5398eae0 100644 --- a/apps/openmw-mp/processors/world/ProcessorContainer.hpp +++ b/apps/openmw-mp/processors/world/ProcessorContainer.hpp @@ -20,18 +20,7 @@ namespace mwmp LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received %s from %s", strPacketID.c_str(), player->npc.mName.c_str()); LOG_APPEND(Log::LOG_INFO, "- action: %i", (int) event.action); - // Until we have a timestamp-based system, send packets pertaining to more - // than one container (i.e. replies to server requests for container contents) - // only to players who have the container's cell loaded - if (event.action == BaseEvent::Action::Set && event.worldObjects.size() > 1) - { - Cell *serverCell = CellController::get().getCell(event.cell); - - if (serverCell != nullptr) - serverCell->sendToLoaded(&packet, &event); - } - - // Otherwise, don't have any hardcoded sync and expect Lua scripts to forward + // Don't have any hardcoded sync, and instead expect Lua scripts to forward // container packets to ensure their integrity based on what exists in the // server data @@ -40,8 +29,6 @@ namespace mwmp Networking::get().getState().getEventCtrl().Call(player, containers); - Networking::get().getState().getObjectCtrl().sendContainers(player, containers, event.cell); - LOG_APPEND(Log::LOG_INFO, "- Finished processing ID_CONTAINER"); } }; diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 213bebe8b..0636468c5 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -39,6 +39,7 @@ namespace MWBase class ResponseCallback { public: + virtual ~ResponseCallback() = default; virtual void addResponse(const std::string& title, const std::string& text) = 0; }; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4b6824cbb..7415328f8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -536,30 +536,29 @@ namespace MWInput isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; if(triedToMove) resetIdleTime(); - if (actionIsActive(A_MoveLeft)) + if (actionIsActive(A_MoveLeft) && !actionIsActive(A_MoveRight)) { triedToMove = true; mPlayer->setLeftRight (-1); } - else if (actionIsActive(A_MoveRight)) + else if (actionIsActive(A_MoveRight) && !actionIsActive(A_MoveLeft)) { triedToMove = true; mPlayer->setLeftRight (1); } - if (actionIsActive(A_MoveForward)) + if (actionIsActive(A_MoveForward) && !actionIsActive(A_MoveBackward)) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (1); } - else if (actionIsActive(A_MoveBackward)) + else if (actionIsActive(A_MoveBackward) && !actionIsActive(A_MoveForward)) { triedToMove = true; mPlayer->setAutoMove (false); mPlayer->setForwardBackward (-1); } - else if(mPlayer->getAutoMove()) { triedToMove = true; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index c97c0a202..eaebb03b1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -428,7 +428,7 @@ namespace MWMechanics { // Player followers and escorters with high fight should not initiate combat with the player or with // other player followers or escorters - if (std::find(playerAllies.begin(), playerAllies.end(), actor1) == playerAllies.end()) + if (!isPlayerFollowerOrEscorter) aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2); } /* diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 071f61ab0..91753521a 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -1,6 +1,7 @@ #include "combat.hpp" #include +#include #include @@ -194,7 +195,11 @@ namespace MWMechanics if (!(weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver || weapon.get()->mBase->mData.mFlags & ESM::Weapon::Magical)) - damage *= multiplier; + { + if (weapon.getClass().getEnchantment(weapon).empty() + || !Settings::Manager::getBool("enchanted weapons are magical", "Game")) + damage *= multiplier; + } if ((weapon.get()->mBase->mData.mFlags & ESM::Weapon::Silver) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fa1e5e5ee..0fb247469 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1779,11 +1779,11 @@ namespace MWMechanics { if (werewolf) { - player->saveSkillsAttributes(); - player->setWerewolfSkillsAttributes(); + player->saveStats(); + player->setWerewolfStats(); } else - player->restoreSkillsAttributes(); + player->restoreStats(); } // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one. diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index fde097a54..0638a5c24 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -7,6 +7,7 @@ #include #include +#include /* Start of tes3mp addition @@ -504,6 +505,9 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); } + if (target == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState()) + magnitudeMult = 0; + // If player is attempting to cast a harmful spell, show the target's HP bar if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); @@ -582,9 +586,12 @@ namespace MWMechanics ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; absorbEffects.push_back(effect_); - // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); + if (reflected && Settings::Manager::getBool("classic reflected absorb attribute behavior", "Game")) + target.getClass().getCreatureStats(target).getActiveSpells().addSpell("", true, + absorbEffects, mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); + else + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); } } } @@ -928,7 +935,8 @@ namespace MWMechanics const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster); float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); - fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue); + fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); + stats.setFatigue(fatigue); bool fail = false; @@ -951,9 +959,7 @@ namespace MWMechanics (dedicatedAttack && dedicatedAttack->success == false)) { if (mCaster == getPlayer()) - { MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); - } fail = true; } /* @@ -1178,8 +1184,6 @@ namespace MWMechanics bool receivedMagicDamage = false; - bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); - switch (effectKey.mId) { case ESM::MagicEffect::DamageAttribute: @@ -1202,40 +1206,25 @@ namespace MWMechanics adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude); break; case ESM::MagicEffect::DamageHealth: - if (!godmode) - { - receivedMagicDamage = true; - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); - } - + receivedMagicDamage = true; + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); break; case ESM::MagicEffect::DamageMagicka: case ESM::MagicEffect::DamageFatigue: - if (!godmode) - { - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); - } - + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); break; case ESM::MagicEffect::AbsorbHealth: - if (!godmode) - { - if (magnitude > 0.f) - receivedMagicDamage = true; - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); - } + if (magnitude > 0.f) + receivedMagicDamage = true; + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); break; case ESM::MagicEffect::AbsorbMagicka: case ESM::MagicEffect::AbsorbFatigue: - if (!godmode) - { - adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); - } - + adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); break; case ESM::MagicEffect::DisintegrateArmor: @@ -1258,6 +1247,7 @@ namespace MWMechanics if (disintegrateSlot(actor, priorities[i], magnitude)) break; } + break; } case ESM::MagicEffect::DisintegrateWeapon: @@ -1280,12 +1270,9 @@ namespace MWMechanics if (weather > 1) damageScale *= fMagicSunBlockedMult; - if (!godmode) - { - adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); - if (magnitude * damageScale > 0.f) - receivedMagicDamage = true; - } + adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); + if (magnitude * damageScale > 0.f) + receivedMagicDamage = true; break; } @@ -1295,12 +1282,8 @@ namespace MWMechanics case ESM::MagicEffect::FrostDamage: case ESM::MagicEffect::Poison: { - if (!godmode) - { - adjustDynamicStat(creatureStats, 0, -magnitude); - receivedMagicDamage = true; - } - + adjustDynamicStat(creatureStats, 0, -magnitude); + receivedMagicDamage = true; break; } diff --git a/apps/openmw/mwmp/WorldEvent.cpp b/apps/openmw/mwmp/WorldEvent.cpp index c89114940..366555f3c 100644 --- a/apps/openmw/mwmp/WorldEvent.cpp +++ b/apps/openmw/mwmp/WorldEvent.cpp @@ -708,23 +708,22 @@ void WorldEvent::playVideo() void WorldEvent::addAllContainers(MWWorld::CellStore* cellStore) { - MWWorld::CellRefList *containerList = cellStore->getContainers(); - - for (auto &container : containerList->mList) + for (auto &ref : cellStore->getContainers()->mList) { - mwmp::WorldObject worldObject; - worldObject.refId = container.mRef.getRefId(); - worldObject.refNumIndex = container.mRef.getRefNum().mIndex; - worldObject.mpNum = container.mRef.getMpNum(); - - MWWorld::ContainerStore& containerStore = container.mClass->getContainerStore(MWWorld::Ptr(&container, 0)); + MWWorld::Ptr ptr(&ref, 0); + addEntireContainer(ptr); + } - for (const auto itemPtr : containerStore) - { - addContainerItem(worldObject, itemPtr, 0); - } + for (auto &ref : cellStore->getNpcs()->mList) + { + MWWorld::Ptr ptr(&ref, 0); + addEntireContainer(ptr); + } - worldObjects.push_back(move(worldObject)); + for (auto &ref : cellStore->getCreatures()->mList) + { + MWWorld::Ptr ptr(&ref, 0); + addEntireContainer(ptr); } } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 981f63e34..dd5d7a853 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -256,11 +256,7 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); - // a bit pointless to set owner for the player - if (actorPtr != MWMechanics::getPlayer()) - return add(ref.getPtr(), count, actorPtr, true); - else - return add(ref.getPtr(), count, actorPtr, false); + return add(ref.getPtr(), count, actorPtr, true); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 19bf7f55e..34c5f713d 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -47,37 +47,45 @@ namespace MWWorld mPlayer.mData.setPosition(playerPos); } - void Player::saveSkillsAttributes() + void Player::saveStats() { MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); + for (int i=0; i& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer()); + MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer()); + MWMechanics::DynamicStat health = creatureStats.getDynamic(0); + creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->getFloat())); for (int i=0; i& gmst = MWBase::Environment::get().getWorld()->getStore().get(); - MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); + MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer()); + MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer()); + MWMechanics::DynamicStat health = creatureStats.getDynamic(0); + creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->getFloat())); for(size_t i = 0;i < ESM::Attribute::Length;++i) { // Oh, Bethesda. It's "Intelligence". std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : ESM::Attribute::sAttributeNames[i]); - MWMechanics::AttributeValue value = stats.getAttribute(i); + MWMechanics::AttributeValue value = npcStats.getAttribute(i); value.setBase(int(gmst.find(name)->getFloat())); - stats.setAttribute(i, value); + npcStats.setAttribute(i, value); } for(size_t i = 0;i < ESM::Skill::Length;i++) @@ -90,9 +98,9 @@ namespace MWWorld std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : ESM::Skill::sSkillNames[i]); - MWMechanics::SkillValue value = stats.getSkill(i); + MWMechanics::SkillValue value = npcStats.getSkill(i); value.setBase(int(gmst.find(name)->getFloat())); - stats.setSkill(i, value); + npcStats.setSkill(i, value); } } @@ -366,8 +374,8 @@ namespace MWWorld if (player.mObject.mNpcStats.mWerewolfDeprecatedData && player.mObject.mNpcStats.mIsWerewolf) { - saveSkillsAttributes(); - setWerewolfSkillsAttributes(); + saveStats(); + setWerewolfStats(); } getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear(); diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 4ba66e37e..aabbe7015 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -46,7 +46,7 @@ namespace MWWorld int mCurrentCrimeId; // the id assigned witnesses int mPaidCrimeId; // the last id paid off (0 bounty) - // Saved skills and attributes prior to becoming a werewolf + // Saved stats prior to becoming a werewolf MWMechanics::SkillValue mSaveSkills[ESM::Skill::Length]; MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; @@ -56,9 +56,9 @@ namespace MWWorld Player(const ESM::NPC *player); - void saveSkillsAttributes(); - void restoreSkillsAttributes(); - void setWerewolfSkillsAttributes(); + void saveStats(); + void restoreStats(); + void setWerewolfStats(); // For mark/recall magic effects void markPosition (CellStore* markedCell, const ESM::Position& markedPosition); diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index b99f151aa..0f8fb0c49 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -62,10 +62,11 @@ Wizard::MainWizard::MainWizard(QWidget *parent) : setupInstallations(); setupPages(); - const boost::filesystem::path& installedPath = mCfgMgr.getInstallPath(); - if (!installedPath.empty()) + const boost::filesystem::path& installationPath = mCfgMgr.getInstallPath(); + if (!installationPath.empty()) { - addInstallation(toQString(installedPath)); + const boost::filesystem::path& dataPath = installationPath / "Data Files"; + addInstallation(toQString(dataPath)); } } diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index b35612ee4..29dbe0391 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -493,8 +493,10 @@ bool Config::GameSettings::hasMaster() { bool result = false; QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey)); - for (int i = 0; i < content.count(); ++i) { - if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { + for (int i = 0; i < content.count(); ++i) + { + if (content.at(i).endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive) || content.at(i).endsWith(QLatin1String(".esm"), Qt::CaseInsensitive)) + { result = true; break; } diff --git a/components/sceneutil/controller.hpp b/components/sceneutil/controller.hpp index 775cb23b0..d02b65cf1 100644 --- a/components/sceneutil/controller.hpp +++ b/components/sceneutil/controller.hpp @@ -25,6 +25,8 @@ namespace SceneUtil class ControllerFunction { public: + virtual ~ControllerFunction() = default; + virtual float calculate(float input) const = 0; /// Get the "stop time" of the controller function, typically the maximum of the calculate() function. diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 45ac81d64..7a9b89295 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -90,12 +90,26 @@ difficulty This setting adjusts the difficulty of the game and is intended to be in the range -100 to 100 inclusive. Given the default game setting for fDifficultyMult of 5.0, a value of -100 results in the player taking 80% of the usual damage, doing 6 times the normal damage. -A value of 100 results in the player taking 6 times as much damage, but inflicting only 80% of the usual damage. -Values less than -500 will result in the player receiving no damage, -and values greater than 500 will result in the player inflicting no damage. +A value of 100 results in the player taking 6 times as much damage, while inflicting only 80% of the usual damage. +Values below -500 will result in the player receiving no damage, +and values above 500 will result in the player inflicting no damage. This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu. +classic reflect absorb attribute behavior +----------------------------------------- + +:Type: boolean +:Range: True/False +:Default: True + +If this setting is true, "Absorb Attribute" spells which were reflected by the target are not "mirrored", +and the caster will absorb their own attribute resulting in no effect on both the caster and the target. +This makes the gameplay as a mage easier, but these spells become imbalanced. +This is how the original Morrowind behaves. + +This setting can only be configured by editing the settings configuration file. + show effect duration -------------------- @@ -108,6 +122,18 @@ The remaining duration is displayed in the tooltip by hovering over the magical This setting can only be configured by editing the settings configuration file. +enchanted weapons are magical +----------------------------- + +:Type: boolean +:Range: True/False +:Default: True + +Makes enchanted weapons without Magical flag bypass normal weapons resistance (and weakness) certain creatures have. +This is how original Morrowind behaves. + +This setting can only be configured by editing the settings configuration file. + prevent merchant equipping -------------------------- diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9a62d986d..85d603d84 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -176,12 +176,18 @@ best attack = false # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). difficulty = 0 +# Replicate how reflected "absorb attribute" spells do not have any effect in Morrowind engine. The caster absorbs the attribute from themselves. +classic reflect absorb attribute behavior = true + # Show duration of magic effect and lights in the spells window. show effect duration = false # Prevents merchants from equipping items that are sold to them. prevent merchant equipping = false +# Make enchanted weaponry without Magical flag bypass normal weapons resistance +enchanted weapons are magical = true + # Makes player followers and escorters start combat with enemies who have started combat with them # or the player. Otherwise they wait for the enemies or the player to do an attack first. followers attack on sight = false