Merge pull request #401 from TES3MP/0.6.3 while resolving conflicts

# Conflicts:
#	apps/openmw-mp/processors/world/ProcessorContainer.hpp
#	apps/openmw/mwmp/WorldEvent.cpp
sol2-server-rewrite
David Cernat 7 years ago
commit f50637bdd4

@ -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_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); LOG_APPEND(Log::LOG_INFO, "- action: %i", (int) event.action);
// Until we have a timestamp-based system, send packets pertaining to more // Don't have any hardcoded sync, and instead expect Lua scripts to forward
// 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
// container packets to ensure their integrity based on what exists in the // container packets to ensure their integrity based on what exists in the
// server data // server data
@ -40,8 +29,6 @@ namespace mwmp
Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CONTAINER>(player, containers); Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CONTAINER>(player, containers);
Networking::get().getState().getObjectCtrl().sendContainers(player, containers, event.cell);
LOG_APPEND(Log::LOG_INFO, "- Finished processing ID_CONTAINER"); LOG_APPEND(Log::LOG_INFO, "- Finished processing ID_CONTAINER");
} }
}; };

@ -39,6 +39,7 @@ namespace MWBase
class ResponseCallback class ResponseCallback
{ {
public: public:
virtual ~ResponseCallback() = default;
virtual void addResponse(const std::string& title, const std::string& text) = 0; virtual void addResponse(const std::string& title, const std::string& text) = 0;
}; };

@ -536,30 +536,29 @@ namespace MWInput
isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25; isRunning = xAxis > .75 || xAxis < .25 || yAxis > .75 || yAxis < .25;
if(triedToMove) resetIdleTime(); if(triedToMove) resetIdleTime();
if (actionIsActive(A_MoveLeft)) if (actionIsActive(A_MoveLeft) && !actionIsActive(A_MoveRight))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setLeftRight (-1); mPlayer->setLeftRight (-1);
} }
else if (actionIsActive(A_MoveRight)) else if (actionIsActive(A_MoveRight) && !actionIsActive(A_MoveLeft))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setLeftRight (1); mPlayer->setLeftRight (1);
} }
if (actionIsActive(A_MoveForward)) if (actionIsActive(A_MoveForward) && !actionIsActive(A_MoveBackward))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setAutoMove (false); mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (1); mPlayer->setForwardBackward (1);
} }
else if (actionIsActive(A_MoveBackward)) else if (actionIsActive(A_MoveBackward) && !actionIsActive(A_MoveForward))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setAutoMove (false); mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (-1); mPlayer->setForwardBackward (-1);
} }
else if(mPlayer->getAutoMove()) else if(mPlayer->getAutoMove())
{ {
triedToMove = true; triedToMove = true;

@ -428,7 +428,7 @@ namespace MWMechanics
{ {
// Player followers and escorters with high fight should not initiate combat with the player or with // Player followers and escorters with high fight should not initiate combat with the player or with
// other player followers or escorters // 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); aggressive = MWBase::Environment::get().getMechanicsManager()->isAggressive(actor1, actor2);
} }
/* /*

@ -1,6 +1,7 @@
#include "combat.hpp" #include "combat.hpp"
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
@ -194,7 +195,11 @@ namespace MWMechanics
if (!(weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver if (!(weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver
|| weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Magical)) || weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Magical))
{
if (weapon.getClass().getEnchantment(weapon).empty()
|| !Settings::Manager::getBool("enchanted weapons are magical", "Game"))
damage *= multiplier; damage *= multiplier;
}
if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver) if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver)
&& actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf()) && actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())

@ -1779,11 +1779,11 @@ namespace MWMechanics
{ {
if (werewolf) if (werewolf)
{ {
player->saveSkillsAttributes(); player->saveStats();
player->setWerewolfSkillsAttributes(); player->setWerewolfStats();
} }
else else
player->restoreSkillsAttributes(); player->restoreStats();
} }
// Werewolfs can not cast spells, so we need to unset the prepared spell if there is one. // Werewolfs can not cast spells, so we need to unset the prepared spell if there is one.

@ -7,6 +7,7 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -504,6 +505,9 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}"); 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 player is attempting to cast a harmful spell, show the target's HP bar
if (castByPlayer && target != caster) if (castByPlayer && target != caster)
MWBase::Environment::get().getWindowManager()->setEnemy(target); MWBase::Environment::get().getWindowManager()->setEnemy(target);
@ -582,7 +586,10 @@ namespace MWMechanics
ActiveSpells::ActiveEffect effect_ = effect; ActiveSpells::ActiveEffect effect_ = effect;
effect_.mMagnitude *= -1; effect_.mMagnitude *= -1;
absorbEffects.push_back(effect_); absorbEffects.push_back(effect_);
// Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies 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, caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); absorbEffects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
} }
@ -928,7 +935,8 @@ namespace MWMechanics
const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster); const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult); 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; bool fail = false;
@ -951,9 +959,7 @@ namespace MWMechanics
(dedicatedAttack && dedicatedAttack->success == false)) (dedicatedAttack && dedicatedAttack->success == false))
{ {
if (mCaster == getPlayer()) if (mCaster == getPlayer())
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
}
fail = true; fail = true;
} }
/* /*
@ -1178,8 +1184,6 @@ namespace MWMechanics
bool receivedMagicDamage = false; bool receivedMagicDamage = false;
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
switch (effectKey.mId) switch (effectKey.mId)
{ {
case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::DamageAttribute:
@ -1202,40 +1206,25 @@ namespace MWMechanics
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude); adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::RestoreHealth, magnitude);
break; break;
case ESM::MagicEffect::DamageHealth: case ESM::MagicEffect::DamageHealth:
if (!godmode)
{
receivedMagicDamage = true; receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
}
break; break;
case ESM::MagicEffect::DamageMagicka: case ESM::MagicEffect::DamageMagicka:
case ESM::MagicEffect::DamageFatigue: case ESM::MagicEffect::DamageFatigue:
if (!godmode)
{
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude); adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
}
break; break;
case ESM::MagicEffect::AbsorbHealth: case ESM::MagicEffect::AbsorbHealth:
if (!godmode)
{
if (magnitude > 0.f) if (magnitude > 0.f)
receivedMagicDamage = true; receivedMagicDamage = true;
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
}
break; break;
case ESM::MagicEffect::AbsorbMagicka: case ESM::MagicEffect::AbsorbMagicka:
case ESM::MagicEffect::AbsorbFatigue: case ESM::MagicEffect::AbsorbFatigue:
if (!godmode)
{
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude); adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
}
break; break;
case ESM::MagicEffect::DisintegrateArmor: case ESM::MagicEffect::DisintegrateArmor:
@ -1258,6 +1247,7 @@ namespace MWMechanics
if (disintegrateSlot(actor, priorities[i], magnitude)) if (disintegrateSlot(actor, priorities[i], magnitude))
break; break;
} }
break; break;
} }
case ESM::MagicEffect::DisintegrateWeapon: case ESM::MagicEffect::DisintegrateWeapon:
@ -1280,12 +1270,9 @@ namespace MWMechanics
if (weather > 1) if (weather > 1)
damageScale *= fMagicSunBlockedMult; damageScale *= fMagicSunBlockedMult;
if (!godmode)
{
adjustDynamicStat(creatureStats, 0, -magnitude * damageScale); adjustDynamicStat(creatureStats, 0, -magnitude * damageScale);
if (magnitude * damageScale > 0.f) if (magnitude * damageScale > 0.f)
receivedMagicDamage = true; receivedMagicDamage = true;
}
break; break;
} }
@ -1294,13 +1281,9 @@ namespace MWMechanics
case ESM::MagicEffect::ShockDamage: case ESM::MagicEffect::ShockDamage:
case ESM::MagicEffect::FrostDamage: case ESM::MagicEffect::FrostDamage:
case ESM::MagicEffect::Poison: case ESM::MagicEffect::Poison:
{
if (!godmode)
{ {
adjustDynamicStat(creatureStats, 0, -magnitude); adjustDynamicStat(creatureStats, 0, -magnitude);
receivedMagicDamage = true; receivedMagicDamage = true;
}
break; break;
} }

@ -708,23 +708,22 @@ void WorldEvent::playVideo()
void WorldEvent::addAllContainers(MWWorld::CellStore* cellStore) void WorldEvent::addAllContainers(MWWorld::CellStore* cellStore)
{ {
MWWorld::CellRefList<ESM::Container> *containerList = cellStore->getContainers(); for (auto &ref : cellStore->getContainers()->mList)
for (auto &container : containerList->mList)
{ {
mwmp::WorldObject worldObject; MWWorld::Ptr ptr(&ref, 0);
worldObject.refId = container.mRef.getRefId(); addEntireContainer(ptr);
worldObject.refNumIndex = container.mRef.getRefNum().mIndex; }
worldObject.mpNum = container.mRef.getMpNum();
MWWorld::ContainerStore& containerStore = container.mClass->getContainerStore(MWWorld::Ptr(&container, 0));
for (const auto itemPtr : containerStore) for (auto &ref : cellStore->getNpcs()->mList)
{ {
addContainerItem(worldObject, itemPtr, 0); 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);
} }
} }

@ -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::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
{ {
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count); 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); return add(ref.getPtr(), count, actorPtr, true);
else
return add(ref.getPtr(), count, actorPtr, false);
} }
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)

@ -47,37 +47,45 @@ namespace MWWorld
mPlayer.mData.setPosition(playerPos); mPlayer.mData.setPosition(playerPos);
} }
void Player::saveSkillsAttributes() void Player::saveStats()
{ {
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer());
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
mSaveSkills[i] = stats.getSkill(i); mSaveSkills[i] = stats.getSkill(i);
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSaveAttributes[i] = stats.getAttribute(i); mSaveAttributes[i] = stats.getAttribute(i);
} }
void Player::restoreSkillsAttributes() void Player::restoreStats()
{ {
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);
creatureStats.setHealth(int(health.getBase() / gmst.find("fWereWolfHealth")->getFloat()));
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
stats.setSkill(i, mSaveSkills[i]); npcStats.setSkill(i, mSaveSkills[i]);
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
stats.setAttribute(i, mSaveAttributes[i]); npcStats.setAttribute(i, mSaveAttributes[i]);
} }
void Player::setWerewolfSkillsAttributes() void Player::setWerewolfStats()
{ {
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::DynamicStat<float> health = creatureStats.getDynamic(0);
creatureStats.setHealth(int(health.getBase() * gmst.find("fWereWolfHealth")->getFloat()));
for(size_t i = 0;i < ESM::Attribute::Length;++i) for(size_t i = 0;i < ESM::Attribute::Length;++i)
{ {
// Oh, Bethesda. It's "Intelligence". // Oh, Bethesda. It's "Intelligence".
std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") :
ESM::Attribute::sAttributeNames[i]); ESM::Attribute::sAttributeNames[i]);
MWMechanics::AttributeValue value = stats.getAttribute(i); MWMechanics::AttributeValue value = npcStats.getAttribute(i);
value.setBase(int(gmst.find(name)->getFloat())); 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++) 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") : std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") :
ESM::Skill::sSkillNames[i]); ESM::Skill::sSkillNames[i]);
MWMechanics::SkillValue value = stats.getSkill(i); MWMechanics::SkillValue value = npcStats.getSkill(i);
value.setBase(int(gmst.find(name)->getFloat())); 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) if (player.mObject.mNpcStats.mWerewolfDeprecatedData && player.mObject.mNpcStats.mIsWerewolf)
{ {
saveSkillsAttributes(); saveStats();
setWerewolfSkillsAttributes(); setWerewolfStats();
} }
getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear(); getPlayer().getClass().getCreatureStats(getPlayer()).getAiSequence().clear();

@ -46,7 +46,7 @@ namespace MWWorld
int mCurrentCrimeId; // the id assigned witnesses int mCurrentCrimeId; // the id assigned witnesses
int mPaidCrimeId; // the last id paid off (0 bounty) 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::SkillValue mSaveSkills[ESM::Skill::Length];
MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length]; MWMechanics::AttributeValue mSaveAttributes[ESM::Attribute::Length];
@ -56,9 +56,9 @@ namespace MWWorld
Player(const ESM::NPC *player); Player(const ESM::NPC *player);
void saveSkillsAttributes(); void saveStats();
void restoreSkillsAttributes(); void restoreStats();
void setWerewolfSkillsAttributes(); void setWerewolfStats();
// For mark/recall magic effects // For mark/recall magic effects
void markPosition (CellStore* markedCell, const ESM::Position& markedPosition); void markPosition (CellStore* markedCell, const ESM::Position& markedPosition);

@ -62,10 +62,11 @@ Wizard::MainWizard::MainWizard(QWidget *parent) :
setupInstallations(); setupInstallations();
setupPages(); setupPages();
const boost::filesystem::path& installedPath = mCfgMgr.getInstallPath(); const boost::filesystem::path& installationPath = mCfgMgr.getInstallPath();
if (!installedPath.empty()) if (!installationPath.empty())
{ {
addInstallation(toQString(installedPath)); const boost::filesystem::path& dataPath = installationPath / "Data Files";
addInstallation(toQString(dataPath));
} }
} }

@ -493,8 +493,10 @@ bool Config::GameSettings::hasMaster()
{ {
bool result = false; bool result = false;
QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey)); QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey));
for (int i = 0; i < content.count(); ++i) { for (int i = 0; i < content.count(); ++i)
if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { {
if (content.at(i).endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive) || content.at(i).endsWith(QLatin1String(".esm"), Qt::CaseInsensitive))
{
result = true; result = true;
break; break;
} }

@ -25,6 +25,8 @@ namespace SceneUtil
class ControllerFunction class ControllerFunction
{ {
public: public:
virtual ~ControllerFunction() = default;
virtual float calculate(float input) const = 0; virtual float calculate(float input) const = 0;
/// Get the "stop time" of the controller function, typically the maximum of the calculate() function. /// Get the "stop time" of the controller function, typically the maximum of the calculate() function.

@ -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. 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, 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 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. A value of 100 results in the player taking 6 times as much damage, while inflicting only 80% of the usual damage.
Values less than -500 will result in the player receiving no damage, Values below -500 will result in the player receiving no damage,
and values greater than 500 will result in the player inflicting 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. 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 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. 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 prevent merchant equipping
-------------------------- --------------------------

@ -176,12 +176,18 @@ best attack = false
# Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100). # Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100).
difficulty = 0 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 duration of magic effect and lights in the spells window.
show effect duration = false show effect duration = false
# Prevents merchants from equipping items that are sold to them. # Prevents merchants from equipping items that are sold to them.
prevent merchant equipping = false 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 # 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. # or the player. Otherwise they wait for the enemies or the player to do an attack first.
followers attack on sight = false followers attack on sight = false

Loading…
Cancel
Save