Merge branch 'master' into 'win10_ci'

# Conflicts:
#   CI/before_script.msvc.sh
pull/457/head
Bret Curtis 6 years ago
commit 739c49d59b

@ -22,6 +22,7 @@ Programmers
alexanderkjall
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Alex S (docwest)
Allofich
Andrei Kortunov (akortunov)
AnyOldName3
@ -63,6 +64,7 @@ Programmers
Evgeniy Mineev (sandstranger)
Federico Guerra (FedeWar)
Fil Krynicki (filkry)
Finbar Crago(finbar-crago)
Florian Weber (Florianjw)
Gašper Sedej
gugus/gus

@ -4,6 +4,7 @@
Bug #1990: Sunrise/sunset not set correct
Bug #2222: Fatigue's effect on selling price is backwards
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
Bug #2772: Non-existing class or faction freezes the game
Bug #2835: Player able to slowly move when overencumbered
@ -48,14 +49,17 @@
Bug #4459: NotCell dialogue condition doesn't support partial matches
Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4471: Retrieve SDL window settings instead of using magic numbers
Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long
Bug #4480: Segfalt in QuickKeysMenu when item no longer in inventory
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window
Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier

@ -452,6 +452,7 @@ fi
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
echo Done.
fi
}

@ -73,18 +73,8 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
// Expected values are (0, 1, 2, 3)
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option. Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
@ -94,6 +84,16 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
// User Interface Settings
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Other Settings
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
@ -125,16 +125,8 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
@ -147,6 +139,15 @@ void Launcher::AdvancedPage::saveSettings()
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
}
// User Interface Settings
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))

@ -201,6 +201,9 @@ void CSMPrefs::State::declare()
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Rendering");
declareInt ("framerate-limit", "FPS limit", 60).
setTooltip("Framerate limit in 3D preview windows. Zero value means \"unlimited\".").
setRange(0, 10000);
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool ("camera-ortho", "Orthographic projection for camera", false);
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).

@ -151,6 +151,9 @@ CompositeViewer::CompositeViewer()
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
mTimer.start( 10 );
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
setRunMaxFrameRate(frameRateLimit);
}
CompositeViewer &CompositeViewer::get()
@ -168,6 +171,12 @@ void CompositeViewer::update()
mSimulationTime += dt;
frame(mSimulationTime);
double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0;
if (dt < minFrameTime)
{
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-dt));
}
}
// ---------------------------------------------------
@ -376,6 +385,10 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
{
mOrbitCamControl->setConstRoll(setting->isTrue());
}
else if (*setting=="Rendering/framerate-limit")
{
CompositeViewer::get().setRunMaxFrameRate(setting->toInt());
}
else if (*setting=="Rendering/camera-fov" ||
*setting=="Rendering/camera-ortho" ||
*setting=="Rendering/camera-ortho-size")

@ -110,7 +110,11 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
int valueIndex = getValueIndex(index);
if (valueIndex != -1)
{
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
QStyleOptionViewItem itemOption(option);
#else
QStyleOptionViewItemV4 itemOption(option);
#endif
itemOption.text = mValues.at(valueIndex).second;
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
}
@ -171,5 +175,16 @@ CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)
{
mValues.push_back (std::make_pair (value, name));
auto pair = std::make_pair (value, name);
for (auto it=mValues.begin(); it!=mValues.end(); ++it)
{
if (it->second > name)
{
mValues.insert(it, pair);
return;
}
}
mValues.push_back(std::make_pair (value, name));
}

@ -381,33 +381,24 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
setWindowIcon();
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
int redSize;
int greenSize;
int blueSize;
int depthSize;
int stencilSize;
int doubleBuffer;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &redSize);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &greenSize);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blueSize);
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencilSize);
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doubleBuffer);
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
traits->red = redSize;
traits->green = greenSize;
traits->blue = blueSize;
traits->depth = depthSize;
traits->stencil = stencilSize;
traits->doubleBuffer = doubleBuffer;
traits->windowName = SDL_GetWindowTitle(mWindow);
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
traits->vsync = vsync;
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
// Here goes kcat's quote:
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
traits->red = 8;
traits->green = 8;
traits->blue = 8;
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
traits->depth = 24;
traits->stencil = 8;
traits->vsync = vsync;
traits->doubleBuffer = true;
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);

@ -773,22 +773,24 @@ namespace MWClass
float x = damage / (damage + getArmorRating(ptr));
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
if (damage < 1)
damage = 1;
damage = std::max(1.f, damage);
damageDiff = std::max(1, damageDiff);
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
{
int armorhealth = armor.getClass().getItemHealth(armor);
armorhealth -= std::min(std::max(1, damageDiff),
armorhealth);
armor.getCellRef().setCharge(armorhealth);
// Armor broken? unequip it
if (armorhealth == 0)
armor = *inv.unequipItem(armor, ptr);
if (attacker.isEmpty() || (!attacker.isEmpty() && !(object.isEmpty() && !attacker.getClass().isNpc()))) // Unarmed creature attacks don't affect armor condition
{
int armorhealth = armor.getClass().getItemHealth(armor);
armorhealth -= std::min(damageDiff, armorhealth);
armor.getCellRef().setCharge(armorhealth);
// Armor broken? unequip it
if (armorhealth == 0)
armor = *inv.unequipItem(armor, ptr);
}
if (ptr == MWMechanics::getPlayer())
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);

@ -81,7 +81,7 @@ namespace MWGui
MyGUI::Widget* getDefaultKeyFocus() override;
virtual bool exit() { return false; }
virtual bool exit() override { return false; }
bool mMarkedToDelete;

@ -33,44 +33,41 @@ namespace MWGui
QuickKeysMenu::QuickKeysMenu()
: WindowBase("openmw_quickkeys_menu.layout")
, mKey(std::vector<keyData>(10))
, mSelected(nullptr)
, mActivated(nullptr)
, mAssignDialog(0)
, mItemSelectionDialog(0)
, mMagicSelectionDialog(0)
, mSelectedIndex(-1)
, mActivatedIndex(-1)
{
getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel");
mMainWidget->setSize(mMainWidget->getWidth(),
mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
mMainWidget->getHeight() +
(mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked);
center();
for (int i = 0; i < 10; ++i)
{
ItemWidget* button;
getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1));
button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
mKey[i].index = i+1;
getWidget(mKey[i].button, "QuickKey" + MyGUI::utility::toString(i+1));
mKey[i].button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
mQuickKeyButtons.push_back(button);
mAssigned.push_back(Type_Unassigned);
unassign(button, i);
unassign(&mKey[i]);
}
}
void QuickKeysMenu::clear()
{
mActivatedIndex = -1;
mActivated = nullptr;
for (int i=0; i<10; ++i)
{
unassign(mQuickKeyButtons[i], i);
unassign(&mKey[i]);
}
}
@ -91,10 +88,7 @@ namespace MWGui
// Check if quick keys are still valid
for (int i=0; i<10; ++i)
{
ItemWidget* button = mQuickKeyButtons[i];
int type = mAssigned[i];
switch (type)
switch (mKey[i].type)
{
case Type_Unassigned:
case Type_HandToHand:
@ -103,49 +97,55 @@ namespace MWGui
case Type_Item:
case Type_MagicItem:
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
MWWorld::Ptr item = *mKey[i].button->getUserData<MWWorld::Ptr>();
// Make sure the item is available and is not broken
if (item.getRefData().getCount() < 1 ||
if (!item || item.getRefData().getCount() < 1 ||
(item.getClass().hasItemHealth(item) &&
item.getClass().getItemHealth(item) <= 0))
{
// Try searching for a compatible replacement
std::string id = item.getCellRef().getRefId();
item = store.findReplacement(mKey[i].id);
if (item)
mKey[i].button->setUserData(MWWorld::Ptr(item));
item = store.findReplacement(id);
button->setUserData(MWWorld::Ptr(item));
break;
}
}
}
}
}
void QuickKeysMenu::unassign(ItemWidget* key, int index)
void QuickKeysMenu::unassign(keyData* key)
{
key->clearUserStrings();
key->setItem(MWWorld::Ptr());
while (key->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0));
key->button->clearUserStrings();
key->button->setItem(MWWorld::Ptr());
while (key->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(key->button->getChildAt(0));
if (index == 9)
if (key->index == 10)
{
mAssigned[index] = Type_HandToHand;
key->type = Type_HandToHand;
MyGUI::ImageBox* image = key->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::ImageBox* image = key->button->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default);
image->setImageTexture("icons\\k\\stealth_handtohand.dds");
image->setNeedMouseFocus(false);
}
else
{
mAssigned[index] = Type_Unassigned;
key->type = Type_Unassigned;
key->id = "";
key->name = "";
MyGUI::TextBox* textBox = key->button->createWidgetReal<MyGUI::TextBox>("SandText",
MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
MyGUI::TextBox* textBox = key->createWidgetReal<MyGUI::TextBox>("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
textBox->setTextAlign (MyGUI::Align::Center);
textBox->setCaption (MyGUI::utility::toString(index+1));
textBox->setNeedMouseFocus (false);
textBox->setTextAlign(MyGUI::Align::Center);
textBox->setCaption(MyGUI::utility::toString(key->index));
textBox->setNeedMouseFocus(false);
}
}
@ -154,19 +154,24 @@ namespace MWGui
int index = -1;
for (int i = 0; i < 10; ++i)
{
if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i])
if (sender == mKey[i].button || sender->getParent() == mKey[i].button)
{
index = i;
break;
}
}
assert(index != -1);
mSelectedIndex = index;
mSelected = &mKey[index];
// prevent reallocation of zero key from Type_HandToHand
if(mSelected->index == 10)
return;
// open assign dialog
if (!mAssignDialog)
mAssignDialog = new QuickKeysMenuAssign(this);
mAssignDialog->setVisible (true);
mAssignDialog->setVisible(true);
}
void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)
@ -174,10 +179,9 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);
}
void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)
{
if (!mItemSelectionDialog )
if (!mItemSelectionDialog)
{
mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}");
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);
@ -187,43 +191,45 @@ namespace MWGui
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)
{
if (!mMagicSelectionDialog )
if (!mMagicSelectionDialog)
{
mMagicSelectionDialog = new MagicSelectionDialog(this);
}
mMagicSelectionDialog->setVisible(true);
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)
{
unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex);
mAssignDialog->setVisible (false);
unassign(mSelected);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)
{
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
mAssigned[mSelectedIndex] = Type_Item;
mSelected->type = Type_Item;
mSelected->id = item.getCellRef().getRefId();
mSelected->name = item.getClass().getName(item);
button->setItem(item, ItemWidget::Barter);
button->setUserString ("ToolTipType", "ItemPtr");
button->setUserData(MWWorld::Ptr(item));
mSelected->button->setItem(item, ItemWidget::Barter);
mSelected->button->setUserString("ToolTipType", "ItemPtr");
mSelected->button->setUserData(item);
if (mItemSelectionDialog)
mItemSelectionDialog->setVisible(false);
@ -234,40 +240,39 @@ namespace MWGui
mItemSelectionDialog->setVisible(false);
}
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
void QuickKeysMenu::onAssignMagicItem(MWWorld::Ptr item)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
mAssigned[mSelectedIndex] = Type_MagicItem;
mSelected->type = Type_MagicItem;
button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(item);
mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
mSelected->button->setIcon(item);
button->setUserString ("ToolTipType", "ItemPtr");
button->setUserData(MWWorld::Ptr(item));
mSelected->button->setUserString("ToolTipType", "ItemPtr");
mSelected->button->setUserData(MWWorld::Ptr(item));
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false);
}
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
void QuickKeysMenu::onAssignMagic(const std::string& spellId)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
mAssigned[mSelectedIndex] = Type_Magic;
mSelected->type = Type_Magic;
mSelected->id = spellId;
button->setItem(MWWorld::Ptr());
button->setUserString ("ToolTipType", "Spell");
button->setUserString ("Spell", spellId);
mSelected->button->setItem(MWWorld::Ptr());
mSelected->button->setUserString("ToolTipType", "Spell");
mSelected->button->setUserString("Spell", spellId);
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();
// use the icon of the first effect
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);
@ -280,14 +285,14 @@ namespace MWGui
path.insert(slashPos+1, "b_");
path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(path);
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
mSelected->button->setIcon(path);
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false);
}
void QuickKeysMenu::onAssignMagicCancel ()
void QuickKeysMenu::onAssignMagicCancel()
{
mMagicSelectionDialog->setVisible(false);
}
@ -295,18 +300,17 @@ namespace MWGui
void QuickKeysMenu::updateActivatedQuickKey()
{
// there is no delayed action, nothing to do.
if (mActivatedIndex < 0)
if (!mActivated)
return;
activateQuickKey(mActivatedIndex);
activateQuickKey(mActivated->index);
}
void QuickKeysMenu::activateQuickKey(int index)
{
assert (index-1 >= 0);
ItemWidget* button = mQuickKeyButtons[index-1];
assert(index >= 1 && index <= 10);
QuickKeyType type = mAssigned[index-1];
keyData *key = &mKey[index-1];
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
@ -319,117 +323,103 @@ namespace MWGui
|| playerStats.getHitRecovery();
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
if (isReturnNeeded && type != Type_Item)
{
return;
}
if (isDelayNeeded && type != Type_Item)
{
mActivatedIndex = index;
if (isReturnNeeded)
return;
}
else if (isDelayNeeded)
mActivated = key;
else
mActivatedIndex = -1;
mActivated = nullptr;
if (type == Type_Item || type == Type_MagicItem)
if (key->type == Type_Item || key->type == Type_MagicItem)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
// Make sure the item is available and is not broken
if (item.getRefData().getCount() < 1 ||
(item.getClass().hasItemHealth(item) &&
item.getClass().getItemHealth(item) <= 0))
MWWorld::Ptr item = *key->button->getUserData<MWWorld::Ptr>();
MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it)
{
// Try searching for a compatible replacement
std::string id = item.getCellRef().getRefId();
if (*it == item)
break;
}
if (it == store.end())
item = nullptr;
item = store.findReplacement(id);
button->setUserData(MWWorld::Ptr(item));
// check the item is available and not broken
if (!item || item.getRefData().getCount() < 1 ||
(item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))
{
item = store.findReplacement(key->id);
if (item.getRefData().getCount() < 1)
if (!item || item.getRefData().getCount() < 1)
{
// No replacement was found
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(
"#{sQuickMenu5} " + key->name);
return;
}
}
}
if (type == Type_Magic)
{
std::string spellId = button->getUserString("Spell");
// Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (!spells.hasSpell(spellId))
if (key->type == Type_Item)
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sQuickMenu5} " + spell->mName);
return;
}
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
else if (type == Type_Item)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() || item.getTypeName() == typeid(ESM::Lockpick).name();
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() ||
item.getTypeName() == typeid(ESM::Lockpick).name();
// delay weapon switching if player is busy
if (isDelayNeeded && (isWeapon || isTool))
{
mActivatedIndex = index;
return;
}
// delay weapon switching if player is busy
if (isDelayNeeded && (isWeapon || isTool))
{
return;
}
// disable weapon switching if player is dead or paralyzed
if (isReturnNeeded && (isWeapon || isTool))
{
return;
MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (rightHand != store.end() && item == *rightHand)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
}
}
MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (rightHand != store.end() && item == *rightHand)
else if (key->type == Type_MagicItem)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
// equip, if it can be equipped
if (!item.getClass().getEquipmentSlots(item).first.empty())
{
MWBase::Environment::get().getWindowManager()->useItem(item);
// make sure that item was successfully equipped
if (!store.isEquipped(item))
return;
}
store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
}
else if (type == Type_MagicItem)
else if (key->type == Type_Magic)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
std::string spellId = key->id;
// retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it)
{
if (*it == item)
{
break;
}
}
assert(it != store.end());
// Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
// equip, if it can be equipped
if (!item.getClass().getEquipmentSlots(item).first.empty())
if (!spells.hasSpell(spellId))
{
MWBase::Environment::get().getWindowManager()->useItem(item);
// make sure that item was successfully equipped
if (!store.isEquipped(item))
return;
const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName);
return;
}
store.setSelectedEnchantItem(it);
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()
->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
else if (type == Type_HandToHand)
else if (key->type == Type_HandToHand)
{
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
@ -491,9 +481,9 @@ namespace MWGui
for (int i=0; i<10; ++i)
{
ItemWidget* button = mQuickKeyButtons[i];
ItemWidget* button = mKey[i].button;
int type = mAssigned[i];
int type = mKey[i].type;
ESM::QuickKeys::QuickKey key;
key.mType = type;
@ -541,30 +531,29 @@ namespace MWGui
if (i >= 10)
return;
mSelectedIndex = i;
int keyType = it->mType;
std::string id = it->mId;
ItemWidget* button = mQuickKeyButtons[i];
mSelected = &mKey[i];
mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType;
mSelected->id = it->mId;
switch (keyType)
switch (mSelected->type)
{
case Type_Magic:
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id))
onAssignMagic(id);
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(mSelected->id))
onAssignMagic(mSelected->id);
break;
case Type_Item:
case Type_MagicItem:
{
// Find the item by id
MWWorld::Ptr item = store.findReplacement(id);
MWWorld::Ptr item = store.findReplacement(mSelected->id);
if (item.isEmpty())
unassign(button, i);
unassign(&mKey[i]);
else
{
if (keyType == Type_Item)
if (mSelected->type == Type_Item)
onAssignItem(item);
else if (keyType == Type_MagicItem)
else if (mSelected->type == Type_MagicItem)
onAssignMagicItem(item);
}
@ -572,7 +561,7 @@ namespace MWGui
}
case Type_Unassigned:
case Type_HandToHand:
unassign(button, i);
unassign(&mKey[i]);
break;
}

@ -55,23 +55,31 @@ namespace MWGui
private:
struct keyData {
int index;
ItemWidget* button;
QuickKeysMenu::QuickKeyType type;
std::string id;
std::string name;
keyData(): index(-1), button(nullptr), type(Type_Unassigned), id(""), name("") {}
};
std::vector<keyData> mKey;
keyData* mSelected;
keyData* mActivated;
MyGUI::EditBox* mInstructionLabel;
MyGUI::Button* mOkButton;
std::vector<ItemWidget*> mQuickKeyButtons;
std::vector<QuickKeyType> mAssigned;
QuickKeysMenuAssign* mAssignDialog;
ItemSelectionDialog* mItemSelectionDialog;
MagicSelectionDialog* mMagicSelectionDialog;
int mSelectedIndex;
int mActivatedIndex;
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender);
void unassign(ItemWidget* key, int index);
void unassign(keyData* key);
};
class QuickKeysMenuAssign : public WindowModal

@ -191,6 +191,9 @@ namespace MWGui
else if (type == "ItemPtr")
{
mFocusObject = *focus->getUserData<MWWorld::Ptr>();
if (!mFocusObject)
return;
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
}
else if (type == "ItemModelIndex")

@ -56,15 +56,15 @@ namespace MWGui
/*
* "Modal" windows cause the rest of the interface to be unaccessible while they are visible
* "Modal" windows cause the rest of the interface to be inaccessible while they are visible
*/
class WindowModal : public WindowBase
{
public:
WindowModal(const std::string& parLayout);
virtual void onOpen();
virtual void onClose();
virtual bool exit() {return true;}
virtual void onOpen() override;
virtual void onClose() override;
virtual bool exit() override {return true;}
};
/// A window that cannot be the target of a drag&drop action.

@ -115,14 +115,16 @@ namespace MWMechanics
if (Misc::Rng::roll0to99() < x)
{
// Reduce shield durability by incoming damage
int shieldhealth = shield->getClass().getItemHealth(*shield);
shieldhealth -= std::min(shieldhealth, int(damage));
shield->getCellRef().setCharge(shieldhealth);
if (shieldhealth == 0)
inv.unequipItem(*shield, blocker);
if (!(weapon.isEmpty() && !attacker.getClass().isNpc())) // Unarmed creature attacks don't affect armor condition
{
// Reduce shield durability by incoming damage
int shieldhealth = shield->getClass().getItemHealth(*shield);
shieldhealth -= std::min(shieldhealth, int(damage));
shield->getCellRef().setCharge(shieldhealth);
if (shieldhealth == 0)
inv.unequipItem(*shield, blocker);
}
// Reduce blocker fatigue
const float fFatigueBlockBase = gmst.find("fFatigueBlockBase")->getFloat();
const float fFatigueBlockMult = gmst.find("fFatigueBlockMult")->getFloat();

@ -328,7 +328,10 @@ namespace MWMechanics
if (race->mData.mFlags & ESM::Race::Beast)
return 0.f;
}
// Intended fall-through
else
return 0.f;
break;
// Creatures can not wear armor
case ESM::MagicEffect::BoundCuirass:
case ESM::MagicEffect::BoundGloves:

@ -608,7 +608,7 @@ namespace MWWorld
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 applyLoopingParticles(const MWWorld::Ptr& ptr);
void applyLoopingParticles(const MWWorld::Ptr& ptr) override;
const std::vector<std::string>& getContentFiles() const override;
void breakInvisibility (const MWWorld::Ptr& actor) override;

@ -22,14 +22,14 @@
<x>0</x>
<y>0</y>
<width>630</width>
<height>746</height>
<height>791</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaVerticalLayout">
<item>
<widget class="QGroupBox" name="gameGroup">
<property name="title">
<string>Game</string>
<string>Game Mechanics</string>
</property>
<layout class="QVBoxLayout" name="gameGroupVerticalLayout">
<item>
@ -62,46 +62,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showEffectDurationCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. &lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show effect duration</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showEnchantChanceCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether or not the chance of success will be displayed in the enchanting menu.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show enchant chance</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showMeleeInfoCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, melee weapons reach and speed will be showed on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show melee info</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showProjectileDamageCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, damage bonus of arrows and bolts will be showed on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show projectile damage</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rebalanceSoulGemValuesCheckBox">
<property name="toolTip">
@ -112,64 +72,6 @@
</property>
</widget>
</item>
<item alignment="Qt::AlignLeft">
<widget class="QWidget" name="showOwnedGroup" native="true">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable visual clues for items owned by NPCs when the crosshair is on the object.&lt;/p&gt;&lt;p&gt;The default value is Off.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignRight">
<widget class="QLabel" name="showOwnedLabel">
<property name="text">
<string>Show owned:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="showOwnedComboBox">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>Tool Tip Only</string>
</property>
</item>
<item>
<property name="text">
<string>Crosshair Only</string>
</property>
</item>
<item>
<property name="text">
<string>Tool Tip and Crosshair</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
@ -357,6 +259,113 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="userInterfaceGroup">
<property name="title">
<string>User Interface</string>
</property>
<layout class="QVBoxLayout" name="userInterfaceVerticalLayout">
<item>
<widget class="QCheckBox" name="showEffectDurationCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show the remaining duration of magic effects and lights if this setting is true. The remaining duration is displayed in the tooltip by hovering over the magical effect. &lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show effect duration</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showEnchantChanceCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Whether or not the chance of success will be displayed in the enchanting menu.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show enchant chance</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showMeleeInfoCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, melee weapons reach and speed will be showed on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show melee info</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showProjectileDamageCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this setting is true, damage bonus of arrows and bolts will be showed on item tooltip.&lt;/p&gt;&lt;p&gt;The default value is false.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Show projectile damage</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignLeft">
<widget class="QWidget" name="showOwnedGroup" native="true">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable visual clues for items owned by NPCs when the crosshair is on the object.&lt;/p&gt;&lt;p&gt;The default value is Off.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignRight">
<widget class="QLabel" name="showOwnedLabel">
<property name="text">
<string>Show owned:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="showOwnedComboBox">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>Tool Tip Only</string>
</property>
</item>
<item>
<property name="text">
<string>Crosshair Only</string>
</property>
</item>
<item>
<property name="text">
<string>Tool Tip and Crosshair</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="otherGroup">
<property name="title">

Loading…
Cancel
Save