Implement difficulty scaling (Fixes #1505)

deque
scrawl 11 years ago
parent 5bbf07976f
commit 28a0899d2b

@ -69,7 +69,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle autocalcspell disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling
) )
add_openmw_dir (mwstate add_openmw_dir (mwstate

@ -9,6 +9,7 @@
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/disease.hpp" #include "../mwmechanics/disease.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -412,6 +413,9 @@ namespace MWClass
if(ishealth) if(ishealth)
{ {
if (!attacker.isEmpty())
damage = scaleDamage(damage, attacker, ptr);
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker); setActorHealth(ptr, health, attacker);

@ -23,6 +23,7 @@
#include "../mwmechanics/disease.hpp" #include "../mwmechanics/disease.hpp"
#include "../mwmechanics/combat.hpp" #include "../mwmechanics/combat.hpp"
#include "../mwmechanics/autocalcspell.hpp" #include "../mwmechanics/autocalcspell.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp" #include "../mwworld/actiontalk.hpp"
@ -788,6 +789,9 @@ namespace MWClass
if(ishealth) if(ishealth)
{ {
if (!attacker.isEmpty())
damage = scaleDamage(damage, attacker, ptr);
if(damage > 0.0f) if(damage > 0.0f)
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;

@ -157,6 +157,8 @@ namespace MWGui
{ {
configureWidgets(mMainWidget); configureWidgets(mMainWidget);
setTitle("#{sOptions}");
getWidget(mOkButton, "OkButton"); getWidget(mOkButton, "OkButton");
getWidget(mResolutionList, "ResolutionList"); getWidget(mResolutionList, "ResolutionList");
getWidget(mFullscreenButton, "FullscreenButton"); getWidget(mFullscreenButton, "FullscreenButton");
@ -174,6 +176,7 @@ namespace MWGui
getWidget(mControlsBox, "ControlsBox"); getWidget(mControlsBox, "ControlsBox");
getWidget(mResetControlsButton, "ResetControlsButton"); getWidget(mResetControlsButton, "ResetControlsButton");
getWidget(mRefractionButton, "RefractionButton"); getWidget(mRefractionButton, "RefractionButton");
getWidget(mDifficultySlider, "DifficultySlider");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled); mShaderModeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShaderModeToggled);
@ -227,6 +230,10 @@ namespace MWGui
MyGUI::TextBox* fovText; MyGUI::TextBox* fovText;
getWidget(fovText, "FovText"); getWidget(fovText, "FovText");
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("field of view", "General"))) + ")"); fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("field of view", "General"))) + ")");
MyGUI::TextBox* diffText;
getWidget(diffText, "DifficultyText");
diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("difficulty", "Game"))) + ")");
} }
void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender) void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)
@ -406,6 +413,12 @@ namespace MWGui
getWidget(fovText, "FovText"); getWidget(fovText, "FovText");
fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(value)) + ")"); fovText->setCaption("Field of View (" + boost::lexical_cast<std::string>(int(value)) + ")");
} }
if (scroller == mDifficultySlider)
{
MyGUI::TextBox* diffText;
getWidget(diffText, "DifficultyText");
diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast<std::string>(int(value)) + ")");
}
} }
else else
{ {

@ -30,6 +30,7 @@ namespace MWGui
MyGUI::Button* mVSyncButton; MyGUI::Button* mVSyncButton;
MyGUI::Button* mFPSButton; MyGUI::Button* mFPSButton;
MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mFOVSlider;
MyGUI::ScrollBar* mDifficultySlider;
MyGUI::ScrollBar* mAnisotropySlider; MyGUI::ScrollBar* mAnisotropySlider;
MyGUI::ComboBox* mTextureFilteringButton; MyGUI::ComboBox* mTextureFilteringButton;
MyGUI::TextBox* mAnisotropyLabel; MyGUI::TextBox* mAnisotropyLabel;

@ -10,6 +10,7 @@
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/difficultyscaling.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
@ -292,6 +293,10 @@ namespace MWMechanics
static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fElementalShieldMult")->getFloat(); static const float fElementalShieldMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fElementalShieldMult")->getFloat();
x = fElementalShieldMult * magnitude * (1.f - 0.01f * x); x = fElementalShieldMult * magnitude * (1.f - 0.01f * x);
// Note swapped victim and attacker, since the attacker takes the damage here.
x = scaleDamage(x, victim, attacker);
MWMechanics::DynamicStat<float> health = attackerStats.getHealth(); MWMechanics::DynamicStat<float> health = attackerStats.getHealth();
health.setCurrent(health.getCurrent() - x); health.setCurrent(health.getCurrent() - x);
attackerStats.setHealth(health); attackerStats.setHealth(health);

@ -0,0 +1,38 @@
#include "difficultyscaling.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp"
#include <components/settings/settings.hpp>
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim)
{
const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr();
// [-100, 100]
int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDifficultyMult")->getFloat();
float difficultyTerm = 0.01f * difficultySetting;
float x = 0;
if (victim == player)
{
if (difficultyTerm > 0)
x = fDifficultyMult * difficultyTerm;
else
x = difficultyTerm / fDifficultyMult;
}
else if (attacker == player)
{
if (difficultyTerm > 0)
x = -difficultyTerm / fDifficultyMult;
else
x = fDifficultyMult * (-difficultyTerm);
}
damage *= 1 + x;
return damage;
}

@ -0,0 +1,12 @@
#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
namespace MWWorld
{
class Ptr;
}
/// Scales damage dealt to an actor based on difficulty setting
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
#endif

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="Layout"> <MyGUI type="Layout">
<Widget type="Window" skin="MW_Window_NoCaption" layer="Windows" position="0 0 400 426" name="_Main"> <Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 400 426" name="_Main">
<Property key="MinSize" value="400 446"/> <Property key="MinSize" value="400 446"/>
<Property key="MaxSize" value="400 446"/> <Property key="MaxSize" value="400 446"/>
@ -12,98 +12,115 @@
<Widget type="TabItem" skin="" position="4 28 360 312"> <Widget type="TabItem" skin="" position="4 28 360 312">
<Property key="Caption" value=" #{sPrefs} "/> <Property key="Caption" value=" #{sPrefs} "/>
<Widget type="TextBox" skin="NormalText" position="4 4 352 18" align="Left Top"> <Widget type="Widget" position="4 4 352 54" align="Left Top">
<Property key="Caption" value="#{sTransparency_Menu}"/> <Widget type="TextBox" skin="NormalText" position="0 0 352 16" align="Left Top">
</Widget> <Property key="Caption" value="#{sTransparency_Menu}"/>
<Widget type="MWScrollBar" skin="MW_HScroll" position="4 28 352 18" align="Left Top" name="MenuTransparencySlider"> </Widget>
<Property key="Range" value="10000"/> <Widget type="MWScrollBar" skin="MW_HScroll" position="0 20 352 14" align="Left Top" name="MenuTransparencySlider">
<Property key="Page" value="300"/> <Property key="Range" value="10000"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="300"/>
<UserString key="SettingCategory" value="GUI"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingName" value="menu transparency"/> <UserString key="SettingCategory" value="GUI"/>
<UserString key="SettingValueType" value="Float"/> <UserString key="SettingName" value="menu transparency"/>
</Widget> <UserString key="SettingValueType" value="Float"/>
<Widget type="TextBox" skin="SandText" position="4 52 352 18" align="Left Top"> </Widget>
<Property key="Caption" value="#{sFull}"/> <Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="TextAlign" value="Left"/> <Property key="Caption" value="#{sFull}"/>
</Widget> <Property key="TextAlign" value="Left"/>
<Widget type="TextBox" skin="SandText" position="4 52 352 18" align="Left Top"> </Widget>
<Property key="Caption" value="#{sNone}"/> <Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="TextAlign" value="Right"/> <Property key="Caption" value="#{sNone}"/>
<Property key="TextAlign" value="Right"/>
</Widget>
</Widget> </Widget>
<Widget type="TextBox" skin="NormalText" position="4 78 352 18" align="Left Top"> <Widget type="Widget" position="4 64 352 54" align="Left Top">
<Property key="Caption" value="#{sMenu_Help_Delay}"/> <Widget type="TextBox" skin="NormalText" position="0 0 352 16" align="Left Top">
</Widget> <Property key="Caption" value="#{sMenu_Help_Delay}"/>
<Widget type="MWScrollBar" skin="MW_HScroll" position="4 102 352 18" align="Left Top" name="ToolTipDelaySlider"> </Widget>
<Property key="Range" value="10000"/> <Widget type="MWScrollBar" skin="MW_HScroll" position="0 20 352 14" align="Left Top" name="ToolTipDelaySlider">
<Property key="Page" value="300"/> <Property key="Range" value="10000"/>
<UserString key="SettingType" value="Slider"/> <Property key="Page" value="300"/>
<UserString key="SettingCategory" value="GUI"/> <UserString key="SettingType" value="Slider"/>
<UserString key="SettingName" value="tooltip delay"/> <UserString key="SettingCategory" value="GUI"/>
<UserString key="SettingValueType" value="Float"/> <UserString key="SettingName" value="tooltip delay"/>
</Widget> <UserString key="SettingValueType" value="Float"/>
<Widget type="TextBox" skin="SandText" position="4 126 352 18" align="Left Top"> </Widget>
<Property key="Caption" value="#{sFast}"/> <Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="TextAlign" value="Left"/> <Property key="Caption" value="#{sFast}"/>
</Widget> <Property key="TextAlign" value="Left"/>
<Widget type="TextBox" skin="SandText" position="4 126 352 18" align="Left Top"> </Widget>
<Property key="Caption" value="#{sSlow}"/> <Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="TextAlign" value="Right"/> <Property key="Caption" value="#{sSlow}"/>
<Property key="TextAlign" value="Right"/>
</Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 170 260 24"> <Widget type="Widget" position="4 124 352 54" align="Left Top">
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="BestAttackButton"> <Widget type="TextBox" skin="NormalText" position="0 0 352 16" align="Left Top" name="DifficultyText">
<Property key="Caption" value="#{sDifficulty}"/>
</Widget>
<Widget type="MWScrollBar" skin="MW_HScroll" position="0 20 352 14" align="Left Top" name="DifficultySlider">
<Property key="Range" value="10000"/>
<Property key="Page" value="300"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Game"/> <UserString key="SettingCategory" value="Game"/>
<UserString key="SettingName" value="best attack"/> <UserString key="SettingName" value="difficulty"/>
<UserString key="SettingType" value="CheckButton"/> <UserString key="SettingValueType" value="Float"/>
<UserString key="SettingMin" value="-100"/>
<UserString key="SettingMax" value="100"/>
</Widget> </Widget>
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top"> <Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="Caption" value="#{sBestAttack}"/> <Property key="Caption" value="#{sEasy}"/>
<Property key="TextAlign" value="Left"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
<Property key="Caption" value="#{sHard}"/>
<Property key="TextAlign" value="Right"/>
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 200 260 24"> <Widget type="HBox" skin="" position="4 200 260 24">
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="SubtitlesButton"> <Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="AutosaveButton">
<UserString key="SettingCategory" value="GUI"/> <UserString key="SettingCategory" value="Saves"/>
<UserString key="SettingName" value="subtitles"/> <UserString key="SettingName" value="autosave"/>
<UserString key="SettingType" value="CheckButton"/> <UserString key="SettingType" value="CheckButton"/>
</Widget> </Widget>
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top"> <Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
<Property key="Caption" value="#{sSubtitles}"/> <Property key="Caption" value="#{sQuick_Save}"/>
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 230 260 24"> <Widget type="HBox" skin="" position="4 230 260 24">
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="CrosshairButton"> <Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="BestAttackButton">
<UserString key="SettingCategory" value="HUD"/> <UserString key="SettingCategory" value="Game"/>
<UserString key="SettingName" value="crosshair"/> <UserString key="SettingName" value="best attack"/>
<UserString key="SettingType" value="CheckButton"/> <UserString key="SettingType" value="CheckButton"/>
</Widget> </Widget>
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top"> <Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
<Property key="Caption" value="#{sCursorOff}"/> <Property key="Caption" value="#{sBestAttack}"/>
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 260 260 24"> <Widget type="HBox" skin="" position="4 260 260 24">
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="GrabCursorButton"> <Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="SubtitlesButton">
<UserString key="SettingCategory" value="Input"/> <UserString key="SettingCategory" value="GUI"/>
<UserString key="SettingName" value="grab cursor"/> <UserString key="SettingName" value="subtitles"/>
<UserString key="SettingType" value="CheckButton"/> <UserString key="SettingType" value="CheckButton"/>
</Widget> </Widget>
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top"> <Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
<Property key="Caption" value="Grab cursor"/> <Property key="Caption" value="#{sSubtitles}"/>
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin="" position="4 290 260 24"> <Widget type="HBox" skin="" position="4 290 260 24">
<Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="AutosaveButton"> <Widget type="AutoSizedButton" skin="MW_Button" align="Left Top" name="CrosshairButton">
<UserString key="SettingCategory" value="Saves"/> <UserString key="SettingCategory" value="HUD"/>
<UserString key="SettingName" value="autosave"/> <UserString key="SettingName" value="crosshair"/>
<UserString key="SettingType" value="CheckButton"/> <UserString key="SettingType" value="CheckButton"/>
</Widget> </Widget>
<Widget type="AutoSizedTextBox" skin="SandText" align="Left Top"> <Widget type="AutoSizedTextBox" skin="SandText" align="Left Top">
<Property key="Caption" value="#{sQuick_Save}"/> <Property key="Caption" value="#{sCursorOff}"/>
</Widget> </Widget>
</Widget> </Widget>

@ -170,6 +170,8 @@ always run = false
# Always use the most powerful attack when striking with a weapon (chop, slash or thrust) # Always use the most powerful attack when striking with a weapon (chop, slash or thrust)
best attack = false best attack = false
difficulty = 0
[Saves] [Saves]
character = character =
# Save when resting # Save when resting

Loading…
Cancel
Save