From 374e98d665f303f2a981b4d2dfcc5601e073c48d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 23:04:02 +0300 Subject: [PATCH 01/37] Make rateWeapon more sensible and account for weapon speed --- apps/openmw/mwmechanics/weaponpriority.cpp | 71 +++++++++------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index ac01a0714..8d09bd8d1 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -21,13 +21,19 @@ namespace MWMechanics float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type, float arrowRating, float boltRating) { - if (item.getTypeName() != typeid(ESM::Weapon).name()) + if (enemy.isEmpty() || item.getTypeName() != typeid(ESM::Weapon).name()) + return 0.f; + + if (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) == 0) return 0.f; const ESM::Weapon* weapon = item.get()->mBase; if (type != -1 && weapon->mData.mType != type) return 0.f; + + const MWBase::World* world = MWBase::Environment::get().getWorld(); + const MWWorld::Store& gmst = world->getStore().get(); if (type == -1 && (weapon->mData.mType == ESM::Weapon::Arrow || weapon->mData.mType == ESM::Weapon::Bolt)) return 0.f; @@ -37,54 +43,34 @@ namespace MWMechanics if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { - // Range weapon is useless under water - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f)) - return 0.f; - - if (enemy.isEmpty()) - return 0.f; - - if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) + // Underwater ranged combat is impossible + if (world->isUnderwater(MWWorld::ConstPtr(actor), 0.75f) + || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) - rangedMult = 1.5f; + { + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); + rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + } } + const float chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) - { - float rangedDamage = weapon->mData.mChop[0] + weapon->mData.mChop[1]; - MWMechanics::adjustWeaponDamage(rangedDamage, item, actor); - - rating = rangedDamage / 2.f; - - if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown) - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); - } + rating = chop; else { - float meleeDamage = 0.f; - - for (int i=0; i<2; ++i) - { - meleeDamage += weapon->mData.mSlash[i]; - meleeDamage += weapon->mData.mThrust[i]; - meleeDamage += weapon->mData.mChop[i]; - } - - MWMechanics::adjustWeaponDamage(meleeDamage, item, actor); - rating = meleeDamage / 6.f; - - MWMechanics::resistNormalWeapon(enemy, actor, item, rating); + const float slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1]) / 2.f; + const float thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1]) / 2.f; + rating = (slash * slash + thrust * thrust + chop * chop) / (slash + thrust + chop); } - if (item.getClass().hasItemHealth(item)) - { - if (item.getClass().getItemHealth(item) == 0) - return 0.f; - } + adjustWeaponDamage(rating, item, actor); - if (weapon->mData.mType == ESM::Weapon::MarksmanBow) + if (weapon->mData.mType != ESM::Weapon::MarksmanBow && weapon->mData.mType != ESM::Weapon::MarksmanCrossbow) + resistNormalWeapon(enemy, actor, item, rating); + else if (weapon->mData.mType == ESM::Weapon::MarksmanBow) { if (arrowRating <= 0.f) rating = 0.f; @@ -101,7 +87,7 @@ namespace MWMechanics if (!weapon->mEnchant.empty()) { - const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get().find(weapon->mEnchant); + const ESM::Enchantment* enchantment = world->getStore().get().find(weapon->mEnchant); if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) { int castCost = getEffectiveEnchantmentCastCost(static_cast(enchantment->mData.mCost), actor); @@ -111,19 +97,22 @@ namespace MWMechanics } } + if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + rating *= weapon->mData.mSpeed; + if (actor.getClass().isNpc()) { int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) { int value = actor.getClass().getSkill(actor, skill); - rating *= MWMechanics::getHitChance(actor, enemy, value) / 100.f; + rating *= getHitChance(actor, enemy, value) / 100.f; } } else { MWWorld::LiveCellRef *ref = actor.get(); - rating *= MWMechanics::getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; + rating *= getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; } return rating * rangedMult; From 00c847db195e41036510681796d0279db0cbdf02 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 30 Aug 2018 23:36:47 +0300 Subject: [PATCH 02/37] Make AI Blind, Sound and Silence effect rating more logical --- apps/openmw/mwmechanics/spellpriority.cpp | 54 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index a4bf46769..b43756162 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -232,20 +232,59 @@ namespace MWMechanics case ESM::MagicEffect::CommandHumanoid: return 0.f; + case ESM::MagicEffect::Blind: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't attack + if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + return 0.f; + + // Enemy doesn't attack + if (stats.getDrawState() != MWMechanics::DrawState_Weapon) + return 0.f; + + break; + } + case ESM::MagicEffect::Sound: { if (enemy.isEmpty()) return 0.f; - // there is no need to cast sound if enemy is not able to cast spells - CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + // Enemy can't cast spells if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0) return 0.f; if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) return 0.f; + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; + + break; + } + + case ESM::MagicEffect::Silence: + { + if (enemy.isEmpty()) + return 0.f; + + const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); + + // Enemy can't cast spells + if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + return 0.f; + + // Enemy doesn't cast spells + if (stats.getDrawState() != MWMechanics::DrawState_Spell) + return 0.f; break; } @@ -353,9 +392,10 @@ namespace MWMechanics int priority = 1; if (effect.mEffectID == ESM::MagicEffect::RestoreHealth) priority = 10; - const DynamicStat& current = actor.getClass().getCreatureStats(actor). - getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); - float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration; + const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + const DynamicStat& current = stats.getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth); + const float magnitude = (effect.mMagnMin + effect.mMagnMax)/2.f; + const float toHeal = magnitude * effect.mDuration; // Effect doesn't heal more than we need, *or* we are below 1/2 health if (current.getModified() - current.getCurrent() > toHeal || current.getCurrent() < current.getModified()*0.5) @@ -363,8 +403,8 @@ namespace MWMechanics return 10000.f * priority - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion } - else - return -10000.f * priority; // Save for later + + return 0.f; } break; From 533b72eff609dc9cdc3af1de9591dcfc410981eb Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 00:36:30 +0300 Subject: [PATCH 03/37] Cache weapon type strings --- apps/openmw/mwclass/weapon.cpp | 37 ++++++++++++---------- apps/openmw/mwmechanics/spellpriority.cpp | 4 +-- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 294aebd94..466ae4716 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -268,24 +268,27 @@ namespace MWClass { text += "\n#{sType} "; - std::map > mapping; - mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); - mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); - mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); - mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); - mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); - mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); - mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); - mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); - mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + static std::map > mapping; + if (mapping.empty()) + { + mapping[ESM::Weapon::ShortBladeOneHand] = std::make_pair("sSkillShortblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeOneHand] = std::make_pair("sSkillLongblade", "sOneHanded"); + mapping[ESM::Weapon::LongBladeTwoHand] = std::make_pair("sSkillLongblade", "sTwoHanded"); + mapping[ESM::Weapon::BluntOneHand] = std::make_pair("sSkillBluntweapon", "sOneHanded"); + mapping[ESM::Weapon::BluntTwoClose] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::BluntTwoWide] = std::make_pair("sSkillBluntweapon", "sTwoHanded"); + mapping[ESM::Weapon::SpearTwoWide] = std::make_pair("sSkillSpear", "sTwoHanded"); + mapping[ESM::Weapon::AxeOneHand] = std::make_pair("sSkillAxe", "sOneHanded"); + mapping[ESM::Weapon::AxeTwoHand] = std::make_pair("sSkillAxe", "sTwoHanded"); + mapping[ESM::Weapon::MarksmanBow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanCrossbow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::MarksmanThrown] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Arrow] = std::make_pair("sSkillMarksman", ""); + mapping[ESM::Weapon::Bolt] = std::make_pair("sSkillMarksman", ""); + } - std::string type = mapping[ref->mBase->mData.mType].first; - std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; + const std::string type = mapping[ref->mBase->mData.mType].first; + const std::string oneOrTwoHanded = mapping[ref->mBase->mData.mType].second; text += store.get().find(type)->mValue.getString() + ((oneOrTwoHanded != "") ? ", " + store.get().find(oneOrTwoHanded)->mValue.getString() : ""); diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index b43756162..f55b30d7c 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -403,8 +403,8 @@ namespace MWMechanics return 10000.f * priority - (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion } - - return 0.f; + else + return -10000.f * priority; // Save for later } break; diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 8d09bd8d1..61d348e14 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -31,7 +31,7 @@ namespace MWMechanics if (type != -1 && weapon->mData.mType != type) return 0.f; - + const MWBase::World* world = MWBase::Environment::get().getWorld(); const MWWorld::Store& gmst = world->getStore().get(); From fa3e45fa7dfb4a1f2733e8b02b73c6c6aa596623 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 01:54:59 +0300 Subject: [PATCH 04/37] Slight cleanup --- apps/openmw/mwmechanics/weaponpriority.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 61d348e14..6b9616359 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -100,21 +100,21 @@ namespace MWMechanics if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) rating *= weapon->mData.mSpeed; + int value = 50.f; if (actor.getClass().isNpc()) { int skill = item.getClass().getEquipmentSkill(item); if (skill != -1) - { - int value = actor.getClass().getSkill(actor, skill); - rating *= getHitChance(actor, enemy, value) / 100.f; - } + value = actor.getClass().getSkill(actor, skill); } else { MWWorld::LiveCellRef *ref = actor.get(); - rating *= getHitChance(actor, enemy, ref->mBase->mData.mCombat) / 100.f; + value = ref->mBase->mData.mCombat; } + rating *= getHitChance(actor, enemy, value) / 100.f; + return rating * rangedMult; } From e66be02e2e2f4a6ebd8fb883e58402aa0bc17cdf Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Fri, 31 Aug 2018 02:06:29 +0300 Subject: [PATCH 05/37] Account for enemy armor rating in weapon rating --- apps/openmw/mwmechanics/weaponpriority.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6b9616359..6861a1b77 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -97,8 +97,11 @@ namespace MWMechanics } } - if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) - rating *= weapon->mData.mSpeed; + if (enemy.getClass().isNpc()) + { + static const float fCombatArmorMinMult = gmst.find("fCombatArmorMinMult")->mValue.getFloat(); + rating *= std::max(fCombatArmorMinMult, rating / (rating + enemy.getClass().getArmorRating(enemy))); + } int value = 50.f; if (actor.getClass().isNpc()) @@ -115,6 +118,9 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; + if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + rating *= weapon->mData.mSpeed; + return rating * rangedMult; } From 58bd35c70de83a46ffabacf28d7a0cc7c30be142 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:00:18 +0300 Subject: [PATCH 06/37] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab7d88d3..f84ccf050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,6 +136,9 @@ Feature #4550: Weapon priority: make ranged weapon bonus more sensible Feature #4579: Add option for applying Strength into hand to hand damage Feature #4581: Use proper logging system + Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment + Feature #4625: Weapon priority: use weighted mean for melee damage rating + Feature #4626: Weapon priority: account for weapon speed Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning From 2965373ed69c31be87aa3674c5dd035d3d82f53d Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:24:13 +0300 Subject: [PATCH 07/37] Avoid potential zero division --- apps/openmw/mwmechanics/weaponpriority.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6861a1b77..b2a9c8ded 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -52,7 +52,10 @@ namespace MWMechanics { static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); - rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + if (fAIMeleeWeaponMult != 0) + rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; + else + rangedMult = fAIRangeMeleeWeaponMult; } } From ceb6121b33536b371f7edfb07d6425761f7c0139 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 1 Sep 2018 01:44:29 +0300 Subject: [PATCH 08/37] Better checks for enemy incapacitation --- apps/openmw/mwmechanics/spellpriority.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index f55b30d7c..f9d19ca28 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -240,7 +240,7 @@ namespace MWMechanics const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); // Enemy can't attack - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't attack @@ -261,7 +261,7 @@ namespace MWMechanics if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0) return 0.f; - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't cast spells @@ -279,7 +279,7 @@ namespace MWMechanics const CreatureStats& stats = enemy.getClass().getCreatureStats(enemy); // Enemy can't cast spells - if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) + if (stats.isParalyzed() || stats.getKnockedDown()) return 0.f; // Enemy doesn't cast spells From d758b573e2c5adfd25f5b46935944c19736346de Mon Sep 17 00:00:00 2001 From: Capostrophic <21265616+Capostrophic@users.noreply.github.com> Date: Sat, 1 Sep 2018 12:07:26 +0300 Subject: [PATCH 09/37] Fix erroneous assumption that ranged weaponry has speed Restrict speed mult to melee weaponry --- apps/openmw/mwmechanics/weaponpriority.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index b2a9c8ded..6ce064fe4 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -121,7 +121,7 @@ namespace MWMechanics rating *= getHitChance(actor, enemy, value) / 100.f; - if (weapon->mData.mType <= ESM::Weapon::MarksmanThrown) + if (weapon->mData.mType < ESM::Weapon::MarksmanBow) rating *= weapon->mData.mSpeed; return rating * rangedMult; From 7e2bda459b530680887798486caf435198f0f605 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 2 Sep 2018 17:34:01 +0400 Subject: [PATCH 10/37] Check if there are textures in FlipController to avoid division by zero (bug #4614) --- CHANGELOG.md | 1 + components/nifosg/controller.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab7d88d3..72a6fa40c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice Bug #4608: Falling damage is applied twice + Bug #4614: Crash due to division by zero when FlipController has no textures Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 262966e95..83841e0e5 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -409,7 +409,7 @@ FlipController::FlipController(const FlipController ©, const osg::CopyOp &co void FlipController::apply(osg::StateSet* stateset, osg::NodeVisitor* nv) { - if (hasInput() && mDelta != 0) + if (hasInput() && mDelta != 0 && !mTextures.empty()) { int curTexture = int(getInputValue(nv) / mDelta) % mTextures.size(); stateset->setTextureAttribute(mTexSlot, mTextures[curTexture]); From 65ff346b619c5121a02f3f065dea0c4f1b1119f7 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Mon, 3 Sep 2018 16:30:21 +0300 Subject: [PATCH 11/37] Make NPC record reputation, disposition and faction rank have unsigned char type --- CHANGELOG.md | 1 + components/esm/loadnpc.hpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72a6fa40c..f964ca327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ Bug #4617: First person sneaking offset is not applied while the character is in air Bug #4618: Sneaking is possible while the character is flying Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails + Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 5f567d999..fbe1dca1f 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -90,7 +90,7 @@ struct NPC char mFactionID; unsigned short mHealth, mMana, mFatigue; - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown; int mGold; }; // 52 bytes @@ -101,7 +101,7 @@ struct NPC { short mLevel; // see above - signed char mDisposition, mReputation, mRank; + unsigned char mDisposition, mReputation, mRank; char mUnknown1, mUnknown2, mUnknown3; int mGold; }; // 12 bytes From aed7c1b2bbd47d56614ce0cdba66f38b86e6893f Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:37:43 +0400 Subject: [PATCH 12/37] Fix a couple of Clang warnings --- apps/openmw/mwscript/miscextensions.cpp | 8 +++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 3d1978d62..8d592f29c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -1064,7 +1064,13 @@ namespace MWScript runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find (spellId); - if (spell && spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) + if (!spell) + { + runtime.getContext().report("spellcasting failed: can not find spell \""+spellId+"\""); + return; + } + + if (spell->mData.mType != ESM::Spell::ST_Spell && spell->mData.mType != ESM::Spell::ST_Power) { runtime.getContext().report("spellcasting failed: you can cast only spells and powers."); return; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2f732ef97..536a40468 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -525,7 +525,7 @@ namespace MWWorld /// @note throws an exception when invoked on a teleport door void activateDoor(const MWWorld::Ptr& door, int state) override; - void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors); ///< get a list of actors standing on \a object + void getActorsStandingOn (const MWWorld::ConstPtr& object, std::vector &actors) override; ///< get a list of actors standing on \a object bool getPlayerStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if the player is standing on \a object bool getActorStandingOn (const MWWorld::ConstPtr& object) override; ///< @return true if any actor is standing on \a object bool getPlayerCollidingWith(const MWWorld::ConstPtr& object) override; ///< @return true if the player is colliding with \a object From d3defd83fc3f24968c43bfa5b53ab03377869175 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 12:58:34 +0400 Subject: [PATCH 13/37] Disable C4643 MSVC warning, caused by boost --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 678f3e16b..34e1a8f4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,7 +624,7 @@ if (WIN32) # Warnings that aren't enabled normally and don't need to be enabled # They're unneeded and sometimes completely retarded warnings that /Wall enables # Not going to bother commenting them as they tend to warn on every standard library file - 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 + 4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045 # Warnings that are thrown on standard libraries and not OpenMW @@ -643,6 +643,7 @@ if (WIN32) # caused by boost 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) + 4643 # Forward declaring 'X' in namespace std is not permitted by the C++ Standard. (in *_std_fwd.h files) # caused by MyGUI 4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception' From 5693ceca749e91d222667e930d521630dba8b256 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 17:56:19 +0400 Subject: [PATCH 14/37] Remove redundant declaration --- components/debug/debuglog.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/components/debug/debuglog.hpp b/components/debug/debuglog.hpp index 1ea18aa9b..f4a8e17be 100644 --- a/components/debug/debuglog.hpp +++ b/components/debug/debuglog.hpp @@ -43,14 +43,6 @@ public: return *this; } - template - Log& operator<<(const T& rhs) - { - if (mLevel <= Debug::CurrentDebugLevel) - std::cout << std::forward(rhs); - - return *this; - } ~Log() { From 6529883527b54409ba04b8750f304a1b284471ac Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 4 Sep 2018 18:14:51 +0400 Subject: [PATCH 15/37] Fix MSVC warning C4389 --- apps/opencs/model/tools/pathgridcheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/tools/pathgridcheck.cpp b/apps/opencs/model/tools/pathgridcheck.cpp index 6427bb119..c25845885 100644 --- a/apps/opencs/model/tools/pathgridcheck.cpp +++ b/apps/opencs/model/tools/pathgridcheck.cpp @@ -109,7 +109,7 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message pathgrid.mPoints[i].mY == pathgrid.mPoints[j].mY && pathgrid.mPoints[i].mZ == pathgrid.mPoints[j].mZ) { - std::vector::const_iterator it = find(duplList.begin(), duplList.end(), i); + std::vector::const_iterator it = find(duplList.begin(), duplList.end(), static_cast(i)); if (it == duplList.end()) { std::ostringstream ss; From 9408876b5891a38fbf7757cc9f3d4a48cf5f633f Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Thu, 6 Sep 2018 17:17:30 +0300 Subject: [PATCH 16/37] Utilize AI GMSTs for priority rating (feature #4632) Fix on-target effect rating calculation --- CHANGELOG.md | 1 + apps/openmw/mwmechanics/spellpriority.cpp | 12 +++++++++--- apps/openmw/mwmechanics/weaponpriority.cpp | 12 +++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03036e1e4..c11d92900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -141,6 +141,7 @@ Feature #4624: Spell priority: don't cast hit chance-affecting spells if the enemy is not in respective stance at the moment Feature #4625: Weapon priority: use weighted mean for melee damage rating Feature #4626: Weapon priority: account for weapon speed + Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index f9d19ca28..807b56608 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -617,13 +617,19 @@ namespace MWMechanics float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy) { // NOTE: enemy may be empty + float rating = 0.f; + float ratingMult = 1.f; // NB: this multiplier is applied to the effect rating, not the final rating + + const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); + static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->mValue.getFloat(); + static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->mValue.getFloat(); + for (std::vector::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) { - rating += rateEffect(*it, actor, enemy); + ratingMult = (it->mRange == ESM::RT_Target) ? fAIRangeMagicSpellMult : fAIMagicSpellMult; - if (it->mRange == ESM::RT_Target) - rating *= 1.5f; + rating += rateEffect(*it, actor, enemy) * ratingMult; } return rating; } diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 6ce064fe4..818065ae4 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -39,7 +39,8 @@ namespace MWMechanics return 0.f; float rating=0.f; - float rangedMult=1.f; + static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); + float ratingMult = fAIMeleeWeaponMult; if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown) { @@ -48,14 +49,11 @@ namespace MWMechanics || world->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f)) return 0.f; + // Use a higher rating multiplier if the actor is out of enemy's reach, use the normal mult otherwise if (getDistanceMinusHalfExtents(actor, enemy) >= getMaxAttackDistance(enemy)) { - static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->mValue.getFloat(); static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->mValue.getFloat(); - if (fAIMeleeWeaponMult != 0) - rangedMult = fAIRangeMeleeWeaponMult / fAIMeleeWeaponMult; - else - rangedMult = fAIRangeMeleeWeaponMult; + ratingMult = fAIRangeMeleeWeaponMult; } } @@ -124,7 +122,7 @@ namespace MWMechanics if (weapon->mData.mType < ESM::Weapon::MarksmanBow) rating *= weapon->mData.mSpeed; - return rating * rangedMult; + return rating * ratingMult; } float rateAmmo(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy, MWWorld::Ptr &bestAmmo, ESM::Weapon::Type ammoType) From bbcdfd4078275cbb5e3060e5b04645567b2297d3 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 6 Sep 2018 21:49:50 +0200 Subject: [PATCH 17/37] Implements vanilla's off-by-one error, fixing #4611 --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +++- apps/openmw/mwgui/spellcreationdialog.hpp | 1 + apps/openmw/mwmechanics/spellcasting.cpp | 9 ++++++--- apps/openmw/mwmechanics/spellcasting.hpp | 4 ++-- files/settings-default.cfg | 3 +++ 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2..bd54d47ee 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -449,11 +449,13 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + static const bool vanillaCost = Settings::Manager::getBool("expensive spells", "Game"); + for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { const ESM::ENAMstruct& effect = *it; - y += std::max(1.f, MWMechanics::calcEffectCost(effect)); + y += std::max(1.f, MWMechanics::calcEffectCost(effect, vanillaCost)); if (effect.mRange == ESM::RT_Target) y *= 1.5; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index ec90fa3ce..084c3e1e1 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "windowbase.hpp" #include "referenceinterface.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index b857c6d7e..83af249da 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -49,13 +49,13 @@ namespace MWMechanics return schoolSkillMap[school]; } - float calcEffectCost(const ESM::ENAMstruct& effect) + float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - return calcEffectCost(effect, magicEffect); + return calcEffectCost(effect, magicEffect, customSpellCost); } - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost) { int minMagn = 1; int maxMagn = 1; @@ -68,6 +68,9 @@ namespace MWMechanics int duration = 0; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; + //The wonders of vanilla spellmaking + if (customSpellCost && duration < 1) + duration = 1; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 2844e7f23..836473653 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -25,8 +25,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); - float calcEffectCost(const ESM::ENAMstruct& effect); - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); + float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost = false); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost = false); bool isSummoningEffect(int effectId); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c2ac2eb1c..352b86f65 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -229,6 +229,9 @@ barter disposition change is permanent = false # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) strength influences hand to hand = 0 + +# Makes custom spells cost the same as in vanilla +expensive spells = true [General] From 6705e5aae455219e908ffb3870f4c5d1872a5d4f Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 8 Sep 2018 11:21:43 +0200 Subject: [PATCH 18/37] forget about the setting till #2887 is implemented at least --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 +--- apps/openmw/mwgui/spellcreationdialog.hpp | 1 - apps/openmw/mwmechanics/spellcasting.cpp | 11 ++++------- apps/openmw/mwmechanics/spellcasting.hpp | 4 ++-- files/settings-default.cfg | 5 +---- 5 files changed, 8 insertions(+), 17 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index bd54d47ee..65d66f9e2 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -449,13 +449,11 @@ namespace MWGui const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - static const bool vanillaCost = Settings::Manager::getBool("expensive spells", "Game"); - for (std::vector::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) { const ESM::ENAMstruct& effect = *it; - y += std::max(1.f, MWMechanics::calcEffectCost(effect, vanillaCost)); + y += std::max(1.f, MWMechanics::calcEffectCost(effect)); if (effect.mRange == ESM::RT_Target) y *= 1.5; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 084c3e1e1..ec90fa3ce 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -3,7 +3,6 @@ #include #include -#include #include "windowbase.hpp" #include "referenceinterface.hpp" diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 83af249da..14dfac283 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -49,13 +49,13 @@ namespace MWMechanics return schoolSkillMap[school]; } - float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost) + float calcEffectCost(const ESM::ENAMstruct& effect) { const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find(effect.mEffectID); - return calcEffectCost(effect, magicEffect, customSpellCost); + return calcEffectCost(effect, magicEffect); } - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost) + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect) { int minMagn = 1; int maxMagn = 1; @@ -65,12 +65,9 @@ namespace MWMechanics maxMagn = effect.mMagnMax; } - int duration = 0; + int duration = 1; if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) duration = effect.mDuration; - //The wonders of vanilla spellmaking - if (customSpellCost && duration < 1) - duration = 1; static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore() .get().find("fEffectCostMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 836473653..2844e7f23 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -25,8 +25,8 @@ namespace MWMechanics ESM::Skill::SkillEnum spellSchoolToSkill(int school); - float calcEffectCost(const ESM::ENAMstruct& effect, const bool customSpellCost = false); - float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const bool customSpellCost = false); + float calcEffectCost(const ESM::ENAMstruct& effect); + float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect); bool isSummoningEffect(int effectId); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 352b86f65..09672aaac 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -228,10 +228,7 @@ barter disposition change is permanent = false # Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) -strength influences hand to hand = 0 - -# Makes custom spells cost the same as in vanilla -expensive spells = true +strength influences hand to hand = 0 [General] From d39c4729d205a0dee9285ea72eee389f1dc3eb23 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Sat, 8 Sep 2018 21:14:47 +0200 Subject: [PATCH 19/37] add changelog entry --- CHANGELOG.md | 1 + files/settings-default.cfg | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900..4042326d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ Bug #4604: Picking up gold from the ground only makes 1 grabbed Bug #4607: Scaling for animated collision shapes is applied twice Bug #4608: Falling damage is applied twice + Bug #4611: Instant magic effects have 0 duration in custom spell cost calculations unlike vanilla Bug #4614: Crash due to division by zero when FlipController has no textures Bug #4615: Flicker effects for light sources are handled incorrectly Bug #4617: First person sneaking offset is not applied while the character is in air diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 09672aaac..c2ac2eb1c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -228,7 +228,7 @@ barter disposition change is permanent = false # Uses the MCP formula (damage * (strength / 40)) to factor Strength into hand-to-hand combat. # (0 means it does not factor it in, 1 means it factors into werewolves damage calculation and # 2 means werewolves are ignored) -strength influences hand to hand = 0 +strength influences hand to hand = 0 [General] From 6ab42919cf3c2dbe75ac8af473fc0ebf8ff5a4d9 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 8 Sep 2018 22:40:58 +0300 Subject: [PATCH 20/37] Make sure the actor is actually crouching/running before tweaking movement speed --- CHANGELOG.md | 1 + apps/openmw/mwclass/npc.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900..28ff0ca93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,6 +115,7 @@ Bug #4618: Sneaking is possible while the character is flying Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type + Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index c247ee543..3d019ea90 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -938,8 +938,8 @@ namespace MWClass const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); + bool sneaking = MWBase::Environment::get().getMechanicsManager()->isSneaking(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool running = MWBase::Environment::get().getMechanicsManager()->isRunning(ptr) && stats.getStance(MWMechanics::CreatureStats::Stance_Run); float walkSpeed = gmst.fMinWalkSpeed->mValue.getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (gmst.fMaxWalkSpeed->mValue.getFloat() - gmst.fMinWalkSpeed->mValue.getFloat()); @@ -964,7 +964,7 @@ namespace MWClass flySpeed = std::max(0.0f, flySpeed); moveSpeed = flySpeed; } - else if(world->isSwimming(ptr)) + else if (world->isSwimming(ptr)) { float swimSpeed = walkSpeed; if(running) @@ -974,7 +974,7 @@ namespace MWClass gmst.fSwimRunAthleticsMult->mValue.getFloat(); moveSpeed = swimSpeed; } - else if(running && !sneaking) + else if (running) moveSpeed = runSpeed; else moveSpeed = walkSpeed; From 702868255a473b2a8b46f78598686e6fe74745ea Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sun, 9 Sep 2018 13:56:58 +0300 Subject: [PATCH 21/37] Use sTo GMST in spellmaking menu (feature #4636) --- CHANGELOG.md | 1 + apps/openmw/mwgui/spellcreationdialog.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4042326d7..ab0b2a9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,7 @@ Feature #4625: Weapon priority: use weighted mean for melee damage rating Feature #4626: Weapon priority: account for weapon speed Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating + Feature #4636: Use sTo GMST in spellmaking menu Task #2490: Don't open command prompt window on Release-mode builds automatically Task #4545: Enable is_pod string test Task #4605: Optimize skinning diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2..b4713291b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -147,7 +147,9 @@ namespace MWGui mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); - mMagnitudeMaxValue->setCaption("- 1"); + static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + + mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); setVisible(true); @@ -312,8 +314,9 @@ namespace MWGui } mEffect.mMagnMax = pos+1; + static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); - mMagnitudeMaxValue->setCaption("- " + MyGUI::utility::toString(pos+1)); + mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos+1)); eventEffectModified(mEffect); } From 33a66b778ffa728ac150c475e0fe5355b47ae947 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 9 Sep 2018 23:10:09 +0400 Subject: [PATCH 22/37] Disable repeating for Accept action in keyboard navigation (bug #4260) --- CHANGELOG.md | 1 + apps/openmw/mwbase/windowmanager.hpp | 2 +- apps/openmw/mwgui/keyboardnavigation.cpp | 7 ++++++- apps/openmw/mwgui/keyboardnavigation.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 6 +++--- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900..c95e45ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Bug #4230: AiTravel package issues break some Tribunal quests Bug #4231: Infected rats from the "Crimson Plague" quest rendered unconscious by change in Drain Fatigue functionality Bug #4251: Stationary NPCs do not return to their position after combat + Bug #4260: Keyboard navigation makes persuasion exploitable Bug #4271: Scamp flickers when attacking Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+ Bug #4286: Scripted animations can be interrupted diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 39eed5537..564c66e75 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -350,7 +350,7 @@ namespace MWBase virtual const MWGui::TextColours& getTextColours() = 0; - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text) = 0; + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; }; } diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index cde8a17cc..361ab407a 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -172,7 +172,7 @@ enum Direction D_Prev }; -bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) +bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { if (!mEnabled) return false; @@ -192,7 +192,12 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text) case MyGUI::KeyCode::Return: case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: + { + if (repeat) + return false; + return accept(); + } default: return false; } diff --git a/apps/openmw/mwgui/keyboardnavigation.hpp b/apps/openmw/mwgui/keyboardnavigation.hpp index 7caf25690..2a094a2df 100644 --- a/apps/openmw/mwgui/keyboardnavigation.hpp +++ b/apps/openmw/mwgui/keyboardnavigation.hpp @@ -14,7 +14,7 @@ namespace MWGui ~KeyboardNavigation(); /// @return Was the key handled by this class? - bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat); void saveFocus(int mode); void restoreFocus(int mode); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 45897b88c..6007846fa 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2060,9 +2060,9 @@ namespace MWGui return mTextColours; } - bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text) + bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { - if (!mKeyboardNavigation->injectKeyPress(key, text)) + if (!mKeyboardNavigation->injectKeyPress(key, text, repeat)) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 657548397..2e4fb8a02 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -379,7 +379,7 @@ namespace MWGui virtual const MWGui::TextColours& getTextColours(); - virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text); + virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false); private: const MWWorld::ESMStore* mStore; diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b24c8cc14..b632bf612 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -214,7 +214,7 @@ namespace MWInput break; } - MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(key, 0, false); } void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) @@ -720,7 +720,7 @@ namespace MWInput bool consumed = false; if (kc != OIS::KC_UNASSIGNED && !mInputBinder->detectingBindingState()) { - consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); + consumed = MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Enum(kc), 0, arg.repeat); if (SDL_IsTextInputActive() && // Little trick to check if key is printable ( !(SDLK_SCANCODE_MASK & arg.keysym.sym) && std::isprint(arg.keysym.sym))) consumed = true; @@ -1153,7 +1153,7 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (!SDL_IsTextInputActive() && !isLeftOrRightButton(A_Activate, mInputBinder, mFakeDeviceID, mJoystickLastUsed)) - MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0); + MWBase::Environment::get().getWindowManager()->injectKeyPress(MyGUI::KeyCode::Return, 0, false); } else if (mControlSwitch["playercontrols"]) mPlayer->activate(); From 269ef7a5596de88e4586b64c59a2407e6f9776c2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 10 Sep 2018 12:55:00 +0400 Subject: [PATCH 23/37] Disable repeating for ENTER key in GUI --- apps/openmw/mwbase/windowmanager.hpp | 1 + apps/openmw/mwgui/alchemywindow.cpp | 3 +++ apps/openmw/mwgui/countdialog.cpp | 4 +++- apps/openmw/mwgui/enchantingdialog.cpp | 3 +++ apps/openmw/mwgui/keyboardnavigation.cpp | 4 +++- apps/openmw/mwgui/savegamedialog.cpp | 3 +++ apps/openmw/mwgui/spellcreationdialog.cpp | 3 +++ apps/openmw/mwgui/textinput.cpp | 3 +++ apps/openmw/mwgui/tradewindow.cpp | 3 +++ apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++++ apps/openmw/mwgui/windowmanagerimp.hpp | 1 + 11 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 564c66e75..b2b80cca3 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -351,6 +351,7 @@ namespace MWBase virtual const MWGui::TextColours& getTextColours() = 0; virtual bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) = 0; + virtual bool injectKeyRelease(MyGUI::KeyCode key) = 0; }; } diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index 80284e9b2..6fd3d3220 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -65,6 +65,9 @@ namespace MWGui void AlchemyWindow::onAccept(MyGUI::EditBox* sender) { onCreateButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/countdialog.cpp b/apps/openmw/mwgui/countdialog.cpp index cf058caac..4292f2e04 100644 --- a/apps/openmw/mwgui/countdialog.cpp +++ b/apps/openmw/mwgui/countdialog.cpp @@ -73,8 +73,10 @@ namespace MWGui void CountDialog::onEnterKeyPressed(MyGUI::EditBox* _sender) { eventOkClicked(NULL, mSlider->getScrollPosition()+1); - setVisible(false); + + // To do not spam onEnterKeyPressed() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void CountDialog::onEditValueChanged(int value) diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 8fbfa65c6..039377dee 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -288,6 +288,9 @@ namespace MWGui void EnchantingDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 361ab407a..aaf369aa7 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -193,8 +193,10 @@ bool KeyboardNavigation::injectKeyPress(MyGUI::KeyCode key, unsigned int text, b case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: { + // We should disable repeating for activation keys + MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::None); if (repeat) - return false; + return true; return accept(); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 45790cbf5..3264e5e33 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -127,6 +127,9 @@ namespace MWGui void SaveGameDialog::onEditSelectAccept(MyGUI::EditBox *sender) { accept(); + + // To do not spam onEditSelectAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SaveGameDialog::onOpen() diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 65d66f9e2..f06f059cd 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -420,6 +420,9 @@ namespace MWGui void SpellCreationDialog::onAccept(MyGUI::EditBox *sender) { onBuyButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void SpellCreationDialog::onOpen() diff --git a/apps/openmw/mwgui/textinput.cpp b/apps/openmw/mwgui/textinput.cpp index 169c9e742..54f2d3be9 100644 --- a/apps/openmw/mwgui/textinput.cpp +++ b/apps/openmw/mwgui/textinput.cpp @@ -65,6 +65,9 @@ namespace MWGui void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender) { onOkClicked(_sender); + + // To do not spam onTextAccepted() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } std::string TextInputDialog::getTextInput() const diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 3871baa09..f461d5f50 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -367,6 +367,9 @@ namespace MWGui void TradeWindow::onAccept(MyGUI::EditBox *sender) { onOfferButtonClicked(sender); + + // To do not spam onAccept() again and again + MWBase::Environment::get().getWindowManager()->injectKeyRelease(MyGUI::KeyCode::None); } void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6007846fa..e332f4f62 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2091,6 +2091,11 @@ namespace MWGui return true; } + bool WindowManager::injectKeyRelease(MyGUI::KeyCode key) + { + return MyGUI::InputManager::getInstance().injectKeyRelease(key); + } + void WindowManager::GuiModeState::update(bool visible) { for (unsigned int i=0; i Date: Mon, 10 Sep 2018 15:18:07 +0400 Subject: [PATCH 24/37] Use drag and drop for ActionTake when InventoryWindow is active (bug #4543) --- CHANGELOG.md | 1 + apps/openmw/mwworld/actiontake.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900..7034202e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ Bug #4519: Knockdown does not discard movement in the 1st-person mode Bug #4531: Movement does not reset idle animations Bug #4539: Paper Doll is affected by GUI scaling + Bug #4543: Picking cursed items through inventory (menumode) makes it disappear Bug #4545: Creatures flee from werewolves Bug #4551: Replace 0 sound range with default range separately Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index d858859a6..a173e87fb 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -5,6 +5,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwgui/inventorywindow.hpp" + #include "class.hpp" #include "containerstore.hpp" @@ -14,6 +16,17 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + // When in GUI mode, we should use drag and drop + if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr()) + { + MWGui::GuiMode mode = MWBase::Environment::get().getWindowManager()->getMode(); + if (mode == MWGui::GM_Inventory || mode == MWGui::GM_Container) + { + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(getTarget()); + return; + } + } + MWBase::Environment::get().getMechanicsManager()->itemTaken( actor, getTarget(), MWWorld::Ptr(), getTarget().getRefData().getCount()); MWWorld::Ptr newitem = *actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); From e5a81b1f99bb6e23fb633c3fb03cb5145ba352aa Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 18:17:11 +0400 Subject: [PATCH 25/37] Fix some issues, found by Coverity Scan --- apps/openmw/mwmechanics/character.cpp | 4 +++- apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 12 +++++++++--- components/debug/debugging.hpp | 2 -- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fddd351fa..69916b37a 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2145,9 +2145,11 @@ void CharacterController::update(float duration) if (isTurning()) { // Adjust animation speed from 1.0 to 1.5 multiplier - float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); if (duration > 0) + { + float turnSpeed = std::min(1.5f, std::abs(rot.z()) / duration / static_cast(osg::PI)); mAnimation->adjustSpeedMult(mCurrentMovement, std::max(turnSpeed, 1.0f)); + } } else if (mMovementState != CharState_None && mAdjustMovementAnimSpeed) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1ace2f8b4..7410dbf03 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -219,6 +219,7 @@ namespace RemoveFinishedCallbackVisitor(int effectId) : RemoveVisitor() + , mHasMagicEffects(false) , mEffectId(effectId) { } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 48cb1dda3..9e08a2563 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1159,11 +1159,17 @@ namespace MWWorld osg::Vec3f vec(x, y, z); - CellStore *currCell = ptr.isInCell() ? ptr.getCell() : NULL; // currCell == NULL should only happen for player, during initial startup + CellStore *currCell = ptr.isInCell() ? ptr.getCell() : nullptr; // currCell == nullptr should only happen for player, during initial startup bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = isPlayer || (currCell && mWorldScene->isCellActive(*currCell)); MWWorld::Ptr newPtr = ptr; + if (!isPlayer && !currCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: current cell is nullptr"); + + if (!newCell) + throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId() + "\" to another cell: new cell is nullptr"); + if (currCell != newCell) { removeContainerScripts(ptr); @@ -1185,10 +1191,10 @@ namespace MWWorld addContainerScripts (getPlayerPtr(), newCell); newPtr = getPlayerPtr(); } - else if (currCell) + else { bool currCellActive = mWorldScene->isCellActive(*currCell); - bool newCellActive = newCell && mWorldScene->isCellActive(*newCell); + bool newCellActive = mWorldScene->isCellActive(*newCell); if (!currCellActive && newCellActive) { newPtr = currCell->moveTo(ptr, newCell); diff --git a/components/debug/debugging.hpp b/components/debug/debugging.hpp index 59536d685..f47f58e45 100644 --- a/components/debug/debugging.hpp +++ b/components/debug/debugging.hpp @@ -41,8 +41,6 @@ namespace Debug { return size; } - - char mDebugLevel; }; #if defined(_WIN32) && defined(_DEBUG) From d5bcc49079c6b4d229f403816bbacb1ddce9a64d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 19:54:22 +0400 Subject: [PATCH 26/37] Initialize missing struct fields --- apps/essimporter/converter.hpp | 5 ++++- apps/essimporter/importgame.hpp | 14 +++++++------- apps/openmw/mwsound/openal_output.cpp | 3 ++- apps/openmw/mwsound/openal_output.hpp | 4 ++-- components/esm/loadcell.hpp | 8 ++++---- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 1772e0e2d..2694ea570 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -526,7 +526,10 @@ public: class ConvertGAME : public Converter { public: - ConvertGAME() : mHasGame(false) {} + ConvertGAME() + : mHasGame(false) + { + } virtual void read(ESM::ESMReader &esm) { diff --git a/apps/essimporter/importgame.hpp b/apps/essimporter/importgame.hpp index fca7d72a0..d8051a527 100644 --- a/apps/essimporter/importgame.hpp +++ b/apps/essimporter/importgame.hpp @@ -14,13 +14,13 @@ namespace ESSImport { struct GMDT { - char mCellName[64]; - int mFogColour; - float mFogDensity; - int mCurrentWeather, mNextWeather; - int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage - float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition - int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage + char mCellName[64] {}; + int mFogColour {0}; + float mFogDensity {0.f}; + int mCurrentWeather {0}, mNextWeather {0}; + int mWeatherTransition {0}; // 0-100 transition between weathers, top 3 bytes may be garbage + float mTimeOfNextTransition {0.f}; // weather changes when gamehour == timeOfNextTransition + int mMasserPhase {0}, mSecundaPhase {0}; // top 3 bytes may be garbage }; GMDT mGMDT; diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 42dd6b5fd..833b40e38 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1483,7 +1483,8 @@ void OpenAL_Output::resumeSounds(int types) OpenAL_Output::OpenAL_Output(SoundManager &mgr) - : Sound_Output(mgr), mDevice(0), mContext(0) + : Sound_Output(mgr) + , mDevice(0), mContext(0) , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal) , mWaterFilter(0), mWaterEffect(0), mDefaultEffect(0), mEffectSlot(0) , mStreamThread(new StreamThread) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index afc869733..b6a26c99a 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -26,10 +26,10 @@ namespace MWSound struct { bool EXT_EFX : 1; bool SOFT_HRTF : 1; - } ALC; + } ALC = {false, false}; struct { bool SOFT_source_spatialize : 1; - } AL; + } AL = {false}; typedef std::deque IDDq; IDDq mFreeSources; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 249d812b1..bc5016718 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -78,14 +78,14 @@ struct Cell struct DATAstruct { - int mFlags; - int mX, mY; + int mFlags {0}; + int mX {0}, mY {0}; }; struct AMBIstruct { - Color mAmbient, mSunlight, mFog; - float mFogDensity; + Color mAmbient {0}, mSunlight {0}, mFog {0}; + float mFogDensity {0.f}; }; Cell() : mName(""), From a262e4b342a5c751311653c7a28950c4ae3472bb Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 20:20:41 +0400 Subject: [PATCH 27/37] Print warning, if can not close or remove temporary file --- components/crashcatcher/crashcatcher.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index 04b239a7f..64824b6b3 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -175,17 +175,18 @@ static void gdb_info(pid_t pid) fflush(stdout); /* Clean up */ - remove(respfile); + if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } else { /* Error creating temp file */ if(fd >= 0) { - if (close(fd) == 0) - remove(respfile); - else - Log(Debug::Warning) << "Warning: can not close and remove file '" << respfile << "': " << std::strerror(errno); + if (close(fd) != 0) + Log(Debug::Warning) << "Warning: can not close file '" << respfile << "': " << std::strerror(errno); + else if (remove(respfile) != 0) + Log(Debug::Warning) << "Warning: can not remove file '" << respfile << "': " << std::strerror(errno); } printf("!!! Could not create gdb command file\n"); } From aca6625af41957d4624944f15d2cf714b2bbde0d Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 8 Sep 2018 23:04:42 +0400 Subject: [PATCH 28/37] Avoid possible memory leak by using the unique_ptr --- components/files/constrainedfilestream.cpp | 15 +++++---------- components/files/constrainedfilestream.hpp | 8 +++++--- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/components/files/constrainedfilestream.cpp b/components/files/constrainedfilestream.cpp index b239ec6a1..419af0d6c 100644 --- a/components/files/constrainedfilestream.cpp +++ b/components/files/constrainedfilestream.cpp @@ -103,21 +103,16 @@ namespace Files }; - ConstrainedFileStream::ConstrainedFileStream(const char *filename, size_t start, size_t length) - : std::istream(new ConstrainedFileStreamBuf(filename, start, length)) + ConstrainedFileStream::ConstrainedFileStream(std::unique_ptr buf) + : std::istream(buf.get()) + , mBuf(std::move(buf)) { - } - ConstrainedFileStream::~ConstrainedFileStream() - { - delete rdbuf(); - } - - IStreamPtr openConstrainedFileStream(const char *filename, size_t start, size_t length) { - return IStreamPtr(new ConstrainedFileStream(filename, start, length)); + auto buf = std::unique_ptr(new ConstrainedFileStreamBuf(filename, start, length)); + return IStreamPtr(new ConstrainedFileStream(std::move(buf))); } } diff --git a/components/files/constrainedfilestream.hpp b/components/files/constrainedfilestream.hpp index 05ae0fbec..bf67c7b97 100644 --- a/components/files/constrainedfilestream.hpp +++ b/components/files/constrainedfilestream.hpp @@ -11,9 +11,11 @@ namespace Files class ConstrainedFileStream : public std::istream { public: - ConstrainedFileStream(const char *filename, - size_t start=0, size_t length=0xFFFFFFFF); - virtual ~ConstrainedFileStream(); + ConstrainedFileStream(std::unique_ptr buf); + virtual ~ConstrainedFileStream() {}; + +private: + std::unique_ptr mBuf; }; typedef std::shared_ptr IStreamPtr; From c2c24a76a4914af9c5a857368fcc64df6d486e68 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 9 Sep 2018 16:06:25 +0400 Subject: [PATCH 29/37] Handle MyGUI exceptions inside destructors --- apps/openmw/mwgui/keyboardnavigation.cpp | 11 ++++- apps/openmw/mwgui/layout.hpp | 14 ++++++- apps/openmw/mwgui/screenfader.cpp | 9 ++++- apps/openmw/mwgui/windowmanagerimp.cpp | 51 ++++++++++++++---------- components/fontloader/fontloader.cpp | 12 +++++- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index cde8a17cc..96970b39f 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -49,7 +51,14 @@ KeyboardNavigation::KeyboardNavigation() KeyboardNavigation::~KeyboardNavigation() { - MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + try + { + MyGUI::WidgetManager::getInstance().unregisterUnlinker(this); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void KeyboardNavigation::saveFocus(int mode) diff --git a/apps/openmw/mwgui/layout.hpp b/apps/openmw/mwgui/layout.hpp index 0e7cf3faa..ea51bf541 100644 --- a/apps/openmw/mwgui/layout.hpp +++ b/apps/openmw/mwgui/layout.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace MWGui { /** The Layout class is an utility class used to load MyGUI layouts @@ -16,7 +18,17 @@ namespace MWGui Layout(const std::string & _layout, MyGUI::Widget* _parent = nullptr) : mMainWidget(nullptr) { initialise(_layout, _parent); } - virtual ~Layout() { shutdown(); } + virtual ~Layout() + { + try + { + shutdown(); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } MyGUI::Widget* getWidget(const std::string& _name); diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index 9a9a1ae01..e75e4954e 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -99,7 +99,14 @@ namespace MWGui ScreenFader::~ScreenFader() { - MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + try + { + MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void ScreenFader::onFrameStart(float dt) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 45897b88c..6917e2e8a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -518,35 +518,42 @@ namespace MWGui WindowManager::~WindowManager() { - mKeyboardNavigation.reset(); + try + { + mKeyboardNavigation.reset(); - MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); - MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); - MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); - MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); + MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); + MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); + MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); + MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); - for (WindowBase* window : mWindows) - delete window; - mWindows.clear(); + for (WindowBase* window : mWindows) + delete window; + mWindows.clear(); - delete mMessageBoxManager; - delete mLocalMapRender; - delete mCharGen; - delete mDragAndDrop; - delete mSoulgemDialog; - delete mCursorManager; - delete mToolTips; + delete mMessageBoxManager; + delete mLocalMapRender; + delete mCharGen; + delete mDragAndDrop; + delete mSoulgemDialog; + delete mCursorManager; + delete mToolTips; - cleanupGarbage(); + cleanupGarbage(); - mFontLoader.reset(); + mFontLoader.reset(); - mGui->shutdown(); - delete mGui; + mGui->shutdown(); + delete mGui; - mGuiPlatform->shutdown(); - delete mGuiPlatform; + mGuiPlatform->shutdown(); + delete mGuiPlatform; + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } } void WindowManager::setStore(const MWWorld::ESMStore &store) diff --git a/components/fontloader/fontloader.cpp b/components/fontloader/fontloader.cpp index 790df7fa8..37214a038 100644 --- a/components/fontloader/fontloader.cpp +++ b/components/fontloader/fontloader.cpp @@ -161,7 +161,17 @@ namespace Gui mTextures.clear(); for (std::vector::iterator it = mFonts.begin(); it != mFonts.end(); ++it) - MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + { + try + { + MyGUI::ResourceManager::getInstance().removeByName((*it)->getResourceName()); + } + catch(const MyGUI::Exception& e) + { + Log(Debug::Error) << "Error in the destructor: " << e.what(); + } + } + mFonts.clear(); } From 9918212a1e4f974f243e41c0026f67bbddf8b84e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 10 Sep 2018 10:51:36 +0400 Subject: [PATCH 30/37] Set focus to Close button when opening the container window (bug #4333) --- CHANGELOG.md | 1 + apps/openmw/mwgui/container.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c11d92900..105ec9f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Bug #4307: World cleanup should remove dead bodies only if death animation is finished Bug #4311: OpenMW does not handle RootCollisionNode correctly Bug #4327: Missing animations during spell/weapon stance switching + Bug #4333: Keyboard navigation in containers is not intuitive Bug #4358: Running animation is interrupted when magic mode is toggled Bug #4368: Settings window ok button doesn't have key focus by default Bug #4378: On-self absorb spells restore stats diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 703ba309e..99d0ea0d1 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -174,6 +174,8 @@ namespace MWGui if(mDragAndDrop != NULL && mDragAndDrop->mIsOnDragAndDrop) return; + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + // transfer everything into the player's inventory ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel(); mModel->update(); @@ -219,6 +221,8 @@ namespace MWGui { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) { + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); + onTakeAllButtonClicked(mTakeButton); if (mPtr.getClass().isPersistent(mPtr)) From f0919f51e93e7dd6811074c63188563c2b88bdaa Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 11 Sep 2018 14:05:44 +0400 Subject: [PATCH 31/37] Fix integer result formatting of scripting functions --- components/compiler/lineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 602bb826f..c2c1dff9b 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -38,7 +38,7 @@ namespace Compiler { case 'l': - Generator::report (mCode, mLiterals, "%g"); + Generator::report (mCode, mLiterals, "%d"); break; case 'f': From 0440c11ccd0dbf9e12fa67805acb595f8beaa883 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 17:31:56 +0400 Subject: [PATCH 32/37] Fix swim crossbow animations --- apps/openmw/mwmechanics/character.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fddd351fa..b17b8b20c 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -423,6 +423,7 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character movementAnimName = weap->shortgroup + movementAnimName; else movementAnimName += weap->shortgroup; + if(!mAnimation->hasAnimation(movementAnimName)) { movemask = MWRender::Animation::BlendMask_LowerBody; @@ -451,6 +452,10 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character } else { + // For crossbow animations use 1h ones as fallback + if (mWeaponType == WeapType_Crossbow) + movementAnimName += "1h"; + movementAnimName.erase(swimpos, 4); if (weap != sWeaponTypeListEnd) { From 929d78d6a3195fb826e90acf670ea2198efc11c6 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 21:52:05 +0400 Subject: [PATCH 33/37] Randomize attacks for non-bipedal creatures with Weapon flag --- apps/openmw/mwmechanics/character.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index b17b8b20c..f6b6a93e6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1399,6 +1399,14 @@ bool CharacterController::updateWeaponState() MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); mAttackStrength = 0; + // Randomize attacks for non-bipedal creatures with Weapon flag + if (mPtr.getClass().getTypeName() == typeid(ESM::Creature).name() && + !mPtr.getClass().isBipedal(mPtr) && + (!mAnimation->hasAnimation(mCurrentWeapon) || isRandomAttackAnimation(mCurrentWeapon))) + { + mCurrentWeapon = chooseRandomAttackAnimation(); + } + if(mWeaponType == WeapType_Spell) { // Unset casting flag, otherwise pressing the mouse button down would From 0136f0552bebf7773a4a1f9d82a37c357060d38e Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 20 Aug 2018 22:04:02 +0400 Subject: [PATCH 34/37] Do not update mIdleState directly --- apps/openmw/mwmechanics/character.cpp | 118 ++++++++++++++++---------- apps/openmw/mwmechanics/character.hpp | 8 +- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f6b6a93e6..6b689deaa 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -243,7 +243,7 @@ std::string CharacterController::chooseRandomGroup (const std::string& prefix, i return prefix + toString(roll); } -void CharacterController::refreshHitRecoilAnims() +void CharacterController::refreshHitRecoilAnims(CharacterState& idle) { bool recovery = mPtr.getClass().getCreatureStats(mPtr).getHitRecovery(); bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown(); @@ -348,14 +348,16 @@ void CharacterController::refreshHitRecoilAnims() mAnimation->play(mCurrentHit, Priority_Knockdown, MWRender::Animation::BlendMask_All, true, 1, "loop stop", "stop", 0.0f, 0); } if (mHitState != CharState_None) - mIdleState = CharState_None; + idle = CharState_None; } -void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force) +void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force) { if(force || jump != mJumpState) { - mIdleState = CharState_None; + if (jump != JumpState_None) + idle = CharState_None; + bool startAtLoop = (jump == mJumpState); mJumpState = jump; @@ -372,6 +374,11 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState jumpmask = MWRender::Animation::BlendMask_LowerBody; jumpAnimName = "jump"; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; + // For crossbow animations use 1h ones as fallback if (mWeaponType == WeapType_Crossbow) jumpAnimName += "1h"; @@ -406,20 +413,21 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState } } -void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force) +void CharacterController::refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force) { - if(force || movement != mMovementState) + std::string movementAnimName; + MWRender::Animation::BlendMask movemask; + const StateInfo *movestate; + if(force || movement != mMovementState || idle != mIdleState) { - mMovementState = movement; - std::string movementAnimName; - MWRender::Animation::BlendMask movemask = MWRender::Animation::BlendMask_All; - const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); + movemask = MWRender::Animation::BlendMask_All; + movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(movement)); if(movestate != sMovementListEnd) { movementAnimName = movestate->groupname; if(weap != sWeaponTypeListEnd && movementAnimName.find("swim") == std::string::npos) { - if (mWeaponType == WeapType_Spell && (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)) // Spellcasting stance turning is a special case + if (mWeaponType == WeapType_Spell && (movement == CharState_TurnLeft || movement == CharState_TurnRight)) // Spellcasting stance turning is a special case movementAnimName = weap->shortgroup + movementAnimName; else movementAnimName += weap->shortgroup; @@ -429,12 +437,24 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character movemask = MWRender::Animation::BlendMask_LowerBody; movementAnimName = movestate->groupname; + // Since we apply movement only for lower body, do not reset idle animations. + // For upper body there will be idle animation. + if (idle == CharState_None) + idle = CharState_Idle; + // For crossbow animations use 1h ones as fallback if (mWeaponType == WeapType_Crossbow) movementAnimName += "1h"; } } + } + } + if(force || movement != mMovementState) + { + mMovementState = movement; + if(movestate != sMovementListEnd) + { if(!mAnimation->hasAnimation(movementAnimName)) { std::string::size_type swimpos = movementAnimName.find("swim"); @@ -532,7 +552,15 @@ void CharacterController::refreshMovementAnims(const WeaponInfo* weap, Character void CharacterController::refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force) { - if(force || idle != mIdleState || mIdleState == CharState_None || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) + // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), + // the idle animation should be displayed + if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) + || (mMovementState != CharState_None && !isTurning()) + || mHitState != CharState_None) + && !mPtr.getClass().isBipedal(mPtr)) + idle = CharState_None; + + if(force || idle != mIdleState || (!mAnimation->isPlaying(mCurrentIdle) && mAnimQueue.empty())) { mIdleState = idle; size_t numLoops = ~0ul; @@ -591,24 +619,16 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat return; if (mPtr.getClass().isActor()) - refreshHitRecoilAnims(); + refreshHitRecoilAnims(idle); const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); if (!mPtr.getClass().hasInventoryStore(mPtr)) weap = sWeaponTypeListEnd; - refreshJumpAnims(weap, jump, force); - refreshMovementAnims(weap, movement, force); + refreshJumpAnims(weap, jump, idle, force); + refreshMovementAnims(weap, movement, idle, force); // idle handled last as it can depend on the other states - // FIXME: if one of the below states is close to their last animation frame (i.e. will be disabled in the coming update), - // the idle animation should be displayed - if (((mUpperBodyState != UpperCharState_Nothing && mUpperBodyState != UpperCharState_WeapEquiped) - || (mMovementState != CharState_None && !isTurning()) - || mHitState != CharState_None) - && !mPtr.getClass().isBipedal(mPtr)) - idle = CharState_None; - refreshIdleAnims(weap, idle, force); } @@ -1200,7 +1220,7 @@ bool CharacterController::updateCarriedLeftVisible(WeaponType weaptype) const } } -bool CharacterController::updateWeaponState() +bool CharacterController::updateWeaponState(CharacterState& idle) { const MWWorld::Class &cls = mPtr.getClass(); CreatureStats &stats = cls.getCreatureStats(mPtr); @@ -1570,11 +1590,11 @@ bool CharacterController::updateWeaponState() // We should reset player's idle animation in the first-person mode. if (resetIdle && mPtr == player && MWBase::Environment::get().getWorld()->isFirstPerson()) - mIdleState = CharState_None; + idle = CharState_None; // In other cases we should not break swim and sneak animations if (resetIdle && mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) - mIdleState = CharState_None; + idle = CharState_None; animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) @@ -2085,12 +2105,18 @@ void CharacterController::update(float duration) : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); } - else if(rot.z() != 0.0f && !sneak && !(mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson())) + else if(rot.z() != 0.0f) { - if(rot.z() > rotationThreshold) - movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; - else if(rot.z() < -rotationThreshold) - movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + // It seems only bipedal actors use turning animations. + // Also do not use turning animations in the first-person view and when sneaking. + bool isFirstPlayer = mPtr == getPlayer() && MWBase::Environment::get().getWorld()->isFirstPerson(); + if (!sneak && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) + { + if(rot.z() > rotationThreshold) + movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; + else if(rot.z() < -rotationThreshold) + movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft; + } } } @@ -2108,16 +2134,21 @@ void CharacterController::update(float duration) } else { - mTurnAnimationThreshold -= duration; - if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || - movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + if (mPtr.getClass().isBipedal(mPtr)) { - mTurnAnimationThreshold = 0.05f; - } - else if (movestate == CharState_None && isTurning() - && mTurnAnimationThreshold > 0) - { - movestate = mMovementState; + if (mTurnAnimationThreshold > 0) + mTurnAnimationThreshold -= duration; + + if (movestate == CharState_TurnRight || movestate == CharState_TurnLeft || + movestate == CharState_SwimTurnRight || movestate == CharState_SwimTurnLeft) + { + mTurnAnimationThreshold = 0.05f; + } + else if (movestate == CharState_None && isTurning() + && mTurnAnimationThreshold > 0) + { + movestate = mMovementState; + } } } @@ -2126,13 +2157,12 @@ void CharacterController::update(float duration) if(mAnimQueue.empty() || inwater || sneak) { - // Note: turning animations should not interrupt idle ones. - // Also movement should not stop idle animation for spellcasting stance. + // Note: turning animations should not interrupt idle ones if (inwater) idlestate = CharState_IdleSwim; else if (sneak && !inJump) idlestate = CharState_IdleSneak; - else if (movestate != CharState_None && !isTurning() && mWeaponType != WeapType_Spell) + else if (movestate != CharState_None && !isTurning()) idlestate = CharState_None; else idlestate = CharState_Idle; @@ -2144,7 +2174,7 @@ void CharacterController::update(float duration) { // bipedal means hand-to-hand could be used (which is handled in updateWeaponState). an existing InventoryStore means an actual weapon could be used. if(cls.isBipedal(mPtr) || cls.hasInventoryStore(mPtr)) - forcestateupdate = updateWeaponState() || forcestateupdate; + forcestateupdate = updateWeaponState(idlestate) || forcestateupdate; else forcestateupdate = updateCreatureState() || forcestateupdate; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 43d26e52f..8b3c68048 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -213,14 +213,14 @@ class CharacterController : public MWRender::Animation::TextKeyListener void setAttackTypeBasedOnMovement(); void refreshCurrentAnims(CharacterState idle, CharacterState movement, JumpingState jump, bool force=false); - void refreshHitRecoilAnims(); - void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, bool force=false); - void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, bool force=false); + void refreshHitRecoilAnims(CharacterState& idle); + void refreshJumpAnims(const WeaponInfo* weap, JumpingState jump, CharacterState& idle, bool force=false); + void refreshMovementAnims(const WeaponInfo* weap, CharacterState movement, CharacterState& idle, bool force=false); void refreshIdleAnims(const WeaponInfo* weap, CharacterState idle, bool force=false); void clearAnimQueue(bool clearPersistAnims = false); - bool updateWeaponState(); + bool updateWeaponState(CharacterState& idle); bool updateCreatureState(); void updateIdleStormState(bool inwater); From 664c630ac023c5dc0b2d3cc313efb41bfeb2db14 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Wed, 12 Sep 2018 16:00:33 +0300 Subject: [PATCH 35/37] Don't make sTo strings static references --- apps/openmw/mwgui/spellcreationdialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index b4713291b..f3f104e3c 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -147,7 +147,7 @@ namespace MWGui mDurationValue->setCaption("1"); mMagnitudeMinValue->setCaption("1"); - static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); mMagnitudeMaxValue->setCaption(to + " 1"); mAreaValue->setCaption("0"); @@ -314,7 +314,7 @@ namespace MWGui } mEffect.mMagnMax = pos+1; - static const std::string &to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); + const std::string to = MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "-"); mMagnitudeMaxValue->setCaption(to + " " + MyGUI::utility::toString(pos+1)); From 275d10e1f724824adf293119a9e4fcc77c30245c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 27 Jul 2018 23:45:25 +0400 Subject: [PATCH 36/37] Add missing icons for UniversalId tables and use them in the editor menu (feature #912) --- CHANGELOG.md | 1 + apps/opencs/model/prefs/state.cpp | 1 + apps/opencs/model/world/universalid.cpp | 133 ++++----- apps/opencs/model/world/universalid.hpp | 21 +- apps/opencs/view/doc/view.cpp | 254 ++++++------------ apps/opencs/view/doc/view.hpp | 2 + files/opencs/cell.png | Bin 223 -> 1212 bytes files/opencs/debug-profile.png | Bin 0 -> 431 bytes files/opencs/edit-undo.png | Bin 650 -> 0 bytes files/opencs/error-log.png | Bin 0 -> 518 bytes files/opencs/{Info.png => info.png} | Bin files/opencs/instance.png | Bin 0 -> 618 bytes ...led-creature.png => levelled-creature.png} | Bin .../{leveled-item.png => levelled-item.png} | Bin files/opencs/magic-effect.png | Bin 325 -> 356 bytes files/opencs/menu-close.png | Bin 0 -> 438 bytes files/opencs/menu-exit.png | Bin 0 -> 257 bytes files/opencs/menu-merge.png | Bin 0 -> 219 bytes files/opencs/menu-new-addon.png | Bin 0 -> 282 bytes files/opencs/menu-new-game.png | Bin 0 -> 230 bytes files/opencs/menu-new-window.png | Bin 0 -> 213 bytes files/opencs/menu-open.png | Bin 0 -> 241 bytes files/opencs/menu-preferences.png | Bin 0 -> 385 bytes files/opencs/menu-redo.png | Bin 0 -> 323 bytes files/opencs/menu-reload.png | Bin 0 -> 447 bytes files/opencs/menu-save.png | Bin 0 -> 302 bytes files/opencs/menu-search.png | Bin 0 -> 408 bytes files/opencs/menu-status-bar.png | Bin 0 -> 188 bytes files/opencs/menu-undo.png | Bin 0 -> 323 bytes files/opencs/menu-verify.png | Bin 0 -> 487 bytes files/opencs/metadata.png | Bin 0 -> 389 bytes files/opencs/object.png | Bin 0 -> 447 bytes files/opencs/placeholder.png | Bin 2383 -> 2142 bytes files/opencs/region-map.png | Bin 0 -> 428 bytes files/opencs/region.png | Bin 320 -> 605 bytes files/opencs/resources.qrc | 36 ++- files/opencs/run-log.png | Bin 0 -> 294 bytes files/opencs/run-openmw.png | Bin 0 -> 388 bytes files/opencs/scene.png | Bin 0 -> 401 bytes files/opencs/start-script.png | Bin 0 -> 359 bytes files/opencs/stop-openmw.png | Bin 0 -> 252 bytes 41 files changed, 216 insertions(+), 232 deletions(-) create mode 100644 files/opencs/debug-profile.png delete mode 100644 files/opencs/edit-undo.png create mode 100644 files/opencs/error-log.png rename files/opencs/{Info.png => info.png} (100%) create mode 100644 files/opencs/instance.png rename files/opencs/{leveled-creature.png => levelled-creature.png} (100%) rename files/opencs/{leveled-item.png => levelled-item.png} (100%) create mode 100644 files/opencs/menu-close.png create mode 100644 files/opencs/menu-exit.png create mode 100644 files/opencs/menu-merge.png create mode 100644 files/opencs/menu-new-addon.png create mode 100644 files/opencs/menu-new-game.png create mode 100644 files/opencs/menu-new-window.png create mode 100644 files/opencs/menu-open.png create mode 100644 files/opencs/menu-preferences.png create mode 100644 files/opencs/menu-redo.png create mode 100644 files/opencs/menu-reload.png create mode 100644 files/opencs/menu-save.png create mode 100644 files/opencs/menu-search.png create mode 100644 files/opencs/menu-status-bar.png create mode 100644 files/opencs/menu-undo.png create mode 100644 files/opencs/menu-verify.png create mode 100644 files/opencs/metadata.png create mode 100644 files/opencs/object.png create mode 100644 files/opencs/region-map.png create mode 100644 files/opencs/run-log.png create mode 100644 files/opencs/run-openmw.png create mode 100644 files/opencs/scene.png create mode 100644 files/opencs/start-script.png create mode 100644 files/opencs/stop-openmw.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 44cb90afd..2ae2a38a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ Bug #4622: Recharging enchanted items with Soul Gems does not award experience if it fails Bug #4628: NPC record reputation, disposition and faction rank should have unsigned char type Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch + Feature #912: Editor: Add missing icons to UniversalId tables Feature #1645: Casting effects from objects Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index a704fb825..b6a55ea86 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -305,6 +305,7 @@ void CSMPrefs::State::declare() declareShortcut ("document-assets-videos", "Open Video Asset List", QKeySequence()); declareShortcut ("document-debug-run", "Run Debug", QKeySequence()); declareShortcut ("document-debug-shutdown", "Stop Debug", QKeySequence()); + declareShortcut ("document-debug-profiles", "Debug Profiles", QKeySequence()); declareShortcut ("document-debug-runlog", "Open Run Log", QKeySequence()); declareSubcategory ("Table"); diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 8d7a7761e..6f9eeb24f 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -18,47 +18,60 @@ namespace static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "-", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, - "Objects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, - "Instances", 0 }, - { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, - "Region Map", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", 0 }, - { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, - { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, - + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", ":./global-variable.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", ":./gmst.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", ":./skill.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", ":./class.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", ":./faction.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", ":./race.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", ":./sound.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", ":./script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", ":./region.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", ":./birthsign.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", ":./spell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", ":./dialogue-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", ":./journal-topics.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_TopicInfos, "Topic Infos", ":./dialogue-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_JournalInfos, "Journal Infos", ":./journal-topic-infos.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", ":./cell.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Enchantments, "Enchantments", ":./enchantment.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_BodyParts, "Body Parts", ":./body-part.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Objects", ":./object.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "Instances", ":./instance.png" }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, "Region Map", ":./region-map.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", ":./filter.png" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Meshes, "Meshes", ":./resources-mesh" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Icons, "Icons", ":./resources-icon" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Musics, "Music Files", ":./resources-music" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_SoundsRes, "Sound Files", ":resources-sound" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Textures, "Textures", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunGame, "Run OpenMW", ":./run-openmw.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StopGame, "Stop OpenMW", ":./stop-openmw.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "Land Textures", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Redo, "Redo", ":./menu-redo.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Undo, "Undo", ":./menu-undo.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Preferences, "Preferences", ":./menu-preferences.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Reload, "Reload", ":./menu-reload.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewWindow, "New View", ":./menu-new-window.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StatusBar, "Toggle Status Bar", ":./menu-status-bar.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewGame, "New Game", ":./menu-new-game.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewAddon, "New Addon", ":./menu-new-addon.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Open, "Open", ":./menu-open.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Save, "Save", ":./menu-save.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Verify, "Verify", ":./menu-verify.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Merge, "Merge", ":./menu-merge.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_ErrorLog, "Error Log", ":./error-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Close, "Close", ":./menu-close.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Exit, "Exit", ":./menu-exit.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; @@ -81,7 +94,7 @@ namespace { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", ":./journal-topic-infos.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Object", ":./object.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" }, @@ -93,9 +106,9 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList, - "Creature Levelled List", ":./leveled-creature.png" }, + "Creature Levelled List", ":./levelled-creature.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList, - "Item Levelled List", ":./leveled-item.png" }, + "Item Levelled List", ":./levelled-item.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous, @@ -105,35 +118,35 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, - { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Instance", ":./instance.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, - { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", ":./scene.png" }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", ":./record-preview.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Enchantment, "Enchantment", ":./enchantment.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_BodyPart, "Body Part", ":./body-part.png" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":resources-mesh"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":resources-icon"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":resources-music" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":resources-sound" }, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":resources-texture"}, - { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":resources-video"}, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Mesh, "Mesh", ":./resources-mesh"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Icon, "Icon", ":./resources-icon"}, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Music, "Music", ":./resources-music" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_SoundRes, "Sound File", ":./resources-sound" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Texture, "Texture", ":./resources-texture" }, + { CSMWorld::UniversalId::Class_Resource, CSMWorld::UniversalId::Type_Video, "Video", ":./resources-video" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", ":./debug-profile.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", ":./sound-generator.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", ":./magic-effect.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", ":./land-heightmap.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", ":./land-texture.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "Land Texture", ":./land-texture.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", ":./pathgrid.png" }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", ":./start-script.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Metadata", ":./metadata.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", 0 }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", 0 }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", ":./menu-verify.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_LoadErrorLog, "Load Error Log", ":./error-log.png" }, + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Search, "Global Search", ":./menu-search.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; } diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index accd1b78d..aa5276ba7 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -137,10 +137,27 @@ namespace CSMWorld Type_Search, Type_MetaDatas, Type_MetaData, - Type_RunLog + Type_RunLog, + Type_RunGame, + Type_StopGame, + Type_Undo, + Type_Redo, + Type_Preferences, + Type_NewWindow, + Type_StatusBar, + Type_NewGame, + Type_NewAddon, + Type_Open, + Type_Save, + Type_Verify, + Type_Merge, + Type_ErrorLog, + Type_Close, + Type_Exit, + Type_Reload }; - enum { NumberOfTypes = Type_RunLog+1 }; + enum { NumberOfTypes = Type_Reload+1 }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 3ebefd69a..97aaac4d7 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -47,58 +47,40 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("File")); - QAction *newGame = new QAction (tr ("New Game"), this); + QAction* newGame = createMenuEntry(CSMWorld::UniversalId::Type_NewGame, file, "document-file-newgame"); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); - setupShortcut("document-file-newgame", newGame); - file->addAction (newGame); - - QAction *newAddon = new QAction (tr ("New Addon"), this); + QAction* newAddon = createMenuEntry(CSMWorld::UniversalId::Type_NewAddon, file, "document-file-newaddon"); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); - setupShortcut("document-file-newaddon", newAddon); - file->addAction (newAddon); - QAction *open = new QAction (tr ("Open"), this); + QAction* open = createMenuEntry(CSMWorld::UniversalId::Type_Open, file, "document-file-open"); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); - setupShortcut("document-file-open", open); - file->addAction (open); - mSave = new QAction (tr ("Save"), this); - connect (mSave, SIGNAL (triggered()), this, SLOT (save())); - setupShortcut("document-file-save", mSave); - file->addAction (mSave); + QAction* save = createMenuEntry(CSMWorld::UniversalId::Type_Save, file, "document-file-save"); + connect (save, SIGNAL (triggered()), this, SLOT (save())); + mSave = save; - mVerify = new QAction (tr ("Verify"), this); - connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); - setupShortcut("document-file-verify", mVerify); - file->addAction (mVerify); + QAction* verify = createMenuEntry(CSMWorld::UniversalId::Type_Verify, file, "document-file-verify"); + connect (verify, SIGNAL (triggered()), this, SLOT (verify())); + mVerify = verify; - mMerge = new QAction (tr ("Merge"), this); - connect (mMerge, SIGNAL (triggered()), this, SLOT (merge())); - setupShortcut("document-file-merge", mMerge); - file->addAction (mMerge); + QAction* merge = createMenuEntry(CSMWorld::UniversalId::Type_Merge, file, "document-file-merge"); + connect (merge, SIGNAL (triggered()), this, SLOT (merge())); + mMerge = merge; - QAction *loadErrors = new QAction (tr ("Open Load Error Log"), this); + QAction* loadErrors = createMenuEntry(CSMWorld::UniversalId::Type_ErrorLog, file, "document-file-errorlog"); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); - setupShortcut("document-file-errorlog", loadErrors); - file->addAction (loadErrors); - QAction *meta = new QAction (tr ("Meta Data"), this); + QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); - setupShortcut("document-file-metadata", meta); - file->addAction (meta); - QAction *close = new QAction (tr ("Close Document"), this); + QAction* close = createMenuEntry(CSMWorld::UniversalId::Type_Close, file, "document-file-close"); connect (close, SIGNAL (triggered()), this, SLOT (close())); - setupShortcut("document-file-close", close); - file->addAction(close); - QAction *exit = new QAction (tr ("Exit Application"), this); + QAction* exit = createMenuEntry(CSMWorld::UniversalId::Type_Exit, file, "document-file-exit"); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); - connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); - setupShortcut("document-file-exit", exit); - file->addAction(exit); + connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); } namespace @@ -130,251 +112,180 @@ void CSVDoc::View::setupEditMenu() mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); setupShortcut("document-edit-undo", mUndo); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); + std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Undo).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + mUndo->setIcon(QIcon(QString::fromStdString(iconName))); + edit->addAction (mUndo); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); setupShortcut("document-edit-redo", mRedo); + iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Redo).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + mRedo->setIcon(QIcon(QString::fromStdString(iconName))); + edit->addAction (mRedo); - QAction *userSettings = new QAction (tr ("Preferences"), this); + QAction* userSettings = createMenuEntry(CSMWorld::UniversalId::Type_Preferences, edit, "document-edit-preferences"); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); - setupShortcut("document-edit-preferences", userSettings); - edit->addAction (userSettings); - QAction *search = new QAction (tr ("Search"), this); + QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); connect (search, SIGNAL (triggered()), this, SLOT (addSearchSubView())); - setupShortcut("document-edit-search", search); - edit->addAction (search); } void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("View")); - QAction *newWindow = new QAction (tr ("New View"), this); + QAction *newWindow = createMenuEntry(CSMWorld::UniversalId::Type_NewWindow, view, "document-view-newview"); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - setupShortcut("document-view-newview", newWindow); - view->addAction (newWindow); - mShowStatusBar = new QAction (tr ("Toggle Status Bar"), this); - mShowStatusBar->setCheckable (true); + mShowStatusBar = createMenuEntry(CSMWorld::UniversalId::Type_StatusBar, view, "document-view-statusbar"); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); - setupShortcut("document-view-statusbar", mShowStatusBar); - + mShowStatusBar->setCheckable (true); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); view->addAction (mShowStatusBar); - QAction *filters = new QAction (tr ("Filters"), this); + QAction *filters = createMenuEntry(CSMWorld::UniversalId::Type_Filters, view, "document-mechanics-filters"); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); - setupShortcut("document-view-filters", filters); - view->addAction (filters); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("World")); - QAction *regions = new QAction (tr ("Regions"), this); + QAction* regions = createMenuEntry(CSMWorld::UniversalId::Type_Regions, world, "document-world-regions"); connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); - setupShortcut("document-world-regions", regions); - world->addAction (regions); - QAction *cells = new QAction (tr ("Cells"), this); + QAction* cells = createMenuEntry(CSMWorld::UniversalId::Type_Cells, world, "document-world-cells"); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); - setupShortcut("document-world-cells", cells); - world->addAction (cells); - QAction *referenceables = new QAction (tr ("Objects"), this); + QAction* referenceables = createMenuEntry(CSMWorld::UniversalId::Type_Referenceables, world, "document-world-referencables"); connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView())); - setupShortcut("document-world-referencables", referenceables); - world->addAction (referenceables); - QAction *references = new QAction (tr ("Instances"), this); + QAction* references = createMenuEntry(CSMWorld::UniversalId::Type_References, world, "document-world-references"); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); - setupShortcut("document-world-references", references); - world->addAction (references); - QAction *lands = new QAction (tr ("Lands"), this); + QAction *lands = createMenuEntry(CSMWorld::UniversalId::Type_Lands, world, "document-world-lands"); connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView())); - setupShortcut("document-world-lands", lands); - world->addAction (lands); - QAction *landTextures = new QAction (tr ("Land Textures"), this); + QAction *landTextures = createMenuEntry(CSMWorld::UniversalId::Type_LandTextures, world, "document-world-landtextures"); connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView())); - setupShortcut("document-world-landtextures", landTextures); - world->addAction (landTextures); - QAction *grid = new QAction (tr ("Pathgrid"), this); + QAction *grid = createMenuEntry(CSMWorld::UniversalId::Type_Pathgrids, world, "document-world-pathgrid"); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); - setupShortcut("document-world-pathgrid", grid); - world->addAction (grid); world->addSeparator(); // items that don't represent single record lists follow here - QAction *regionMap = new QAction (tr ("Region Map"), this); + QAction *regionMap = createMenuEntry(CSMWorld::UniversalId::Type_RegionMap, world, "document-world-regionmap"); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); - setupShortcut("document-world-regionmap", regionMap); - world->addAction (regionMap); } void CSVDoc::View::setupMechanicsMenu() { QMenu *mechanics = menuBar()->addMenu (tr ("Mechanics")); - QAction *globals = new QAction (tr ("Globals"), this); + QAction* globals = createMenuEntry(CSMWorld::UniversalId::Type_Globals, mechanics, "document-mechanics-globals"); connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); - setupShortcut("document-mechanics-globals", globals); - mechanics->addAction (globals); - QAction *gmsts = new QAction (tr ("Game Settings"), this); + QAction* gmsts = createMenuEntry(CSMWorld::UniversalId::Type_Gmsts, mechanics, "document-mechanics-gamesettings"); connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView())); - setupShortcut("document-mechanics-gamesettings", gmsts); - mechanics->addAction (gmsts); - QAction *scripts = new QAction (tr ("Scripts"), this); + QAction* scripts = createMenuEntry(CSMWorld::UniversalId::Type_Scripts, mechanics, "document-mechanics-scripts"); connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView())); - setupShortcut("document-mechanics-scripts", scripts); - mechanics->addAction (scripts); - QAction *spells = new QAction (tr ("Spells"), this); + QAction* spells = createMenuEntry(CSMWorld::UniversalId::Type_Spells, mechanics, "document-mechanics-spells"); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); - setupShortcut("document-mechanics-spells", spells); - mechanics->addAction (spells); - QAction *enchantments = new QAction (tr ("Enchantments"), this); + QAction* enchantments = createMenuEntry(CSMWorld::UniversalId::Type_Enchantments, mechanics, "document-mechanics-enchantments"); connect (enchantments, SIGNAL (triggered()), this, SLOT (addEnchantmentsSubView())); - setupShortcut("document-mechanics-enchantments", enchantments); - mechanics->addAction (enchantments); - QAction *effects = new QAction (tr ("Magic Effects"), this); - connect (effects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - setupShortcut("document-mechanics-magiceffects", effects); - mechanics->addAction (effects); + QAction* magicEffects = createMenuEntry(CSMWorld::UniversalId::Type_MagicEffects, mechanics, "document-mechanics-magiceffects"); + connect (magicEffects, SIGNAL (triggered()), this, SLOT (addMagicEffectsSubView())); - QAction *startScripts = new QAction (tr ("Start Scripts"), this); + QAction* startScripts = createMenuEntry(CSMWorld::UniversalId::Type_StartScripts, mechanics, "document-mechanics-startscripts"); connect (startScripts, SIGNAL (triggered()), this, SLOT (addStartScriptsSubView())); - setupShortcut("document-mechanics-startscripts", startScripts); - mechanics->addAction (startScripts); } void CSVDoc::View::setupCharacterMenu() { QMenu *characters = menuBar()->addMenu (tr ("Characters")); - QAction *skills = new QAction (tr ("Skills"), this); + QAction* skills = createMenuEntry(CSMWorld::UniversalId::Type_Skills, characters, "document-character-skills"); connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView())); - setupShortcut("document-character-skills", skills); - characters->addAction (skills); - QAction *classes = new QAction (tr ("Classes"), this); + QAction* classes = createMenuEntry(CSMWorld::UniversalId::Type_Classes, characters, "document-character-classes"); connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView())); - setupShortcut("document-character-classes", classes); - characters->addAction (classes); - QAction *factions = new QAction (tr ("Factions"), this); + QAction* factions = createMenuEntry(CSMWorld::UniversalId::Type_Faction, characters, "document-character-factions"); connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView())); - setupShortcut("document-character-factions", factions); - characters->addAction (factions); - QAction *races = new QAction (tr ("Races"), this); + QAction* races = createMenuEntry(CSMWorld::UniversalId::Type_Races, characters, "document-character-races"); connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView())); - setupShortcut("document-character-races", races); - characters->addAction (races); - QAction *birthsigns = new QAction (tr ("Birthsigns"), this); + QAction* birthsigns = createMenuEntry(CSMWorld::UniversalId::Type_Birthsigns, characters, "document-character-birthsigns"); connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView())); - setupShortcut("document-character-birthsigns", birthsigns); - characters->addAction (birthsigns); - QAction *topics = new QAction (tr ("Topics"), this); + QAction* topics = createMenuEntry(CSMWorld::UniversalId::Type_Topics, characters, "document-character-topics"); connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); - setupShortcut("document-character-topics", topics); - characters->addAction (topics); - QAction *journals = new QAction (tr ("Journals"), this); + QAction* journals = createMenuEntry(CSMWorld::UniversalId::Type_Journals, characters, "document-character-journals"); connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); - setupShortcut("document-character-journals", journals); - characters->addAction (journals); - QAction *topicInfos = new QAction (tr ("Topic Infos"), this); + QAction* topicInfos = createMenuEntry(CSMWorld::UniversalId::Type_TopicInfos, characters, "document-character-topicinfos"); connect (topicInfos, SIGNAL (triggered()), this, SLOT (addTopicInfosSubView())); - setupShortcut("document-character-topicinfos", topicInfos); - characters->addAction (topicInfos); - QAction *journalInfos = new QAction (tr ("Journal Infos"), this); + QAction* journalInfos = createMenuEntry(CSMWorld::UniversalId::Type_JournalInfos, characters, "document-character-journalinfos"); connect (journalInfos, SIGNAL (triggered()), this, SLOT (addJournalInfosSubView())); - setupShortcut("document-character-journalinfos", journalInfos); - characters->addAction (journalInfos); - QAction *bodyParts = new QAction (tr ("Body Parts"), this); + QAction* bodyParts = createMenuEntry(CSMWorld::UniversalId::Type_BodyParts, characters, "document-character-bodyparts"); connect (bodyParts, SIGNAL (triggered()), this, SLOT (addBodyPartsSubView())); - setupShortcut("document-character-bodyparts", bodyParts); - characters->addAction (bodyParts); } void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("Assets")); - QAction *reload = new QAction (tr ("Reload"), this); + QAction* reload = createMenuEntry(CSMWorld::UniversalId::Type_Reload, assets, "document-assets-reload"); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged())); - setupShortcut("document-assets-reload", reload); - assets->addAction (reload); assets->addSeparator(); - QAction *sounds = new QAction (tr ("Sounds"), this); + QAction* sounds = createMenuEntry(CSMWorld::UniversalId::Type_Sounds, assets, "document-assets-sounds"); connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView())); - setupShortcut("document-assets-sounds", sounds); - assets->addAction (sounds); - QAction *soundGens = new QAction (tr ("Sound Generators"), this); + QAction* soundGens = createMenuEntry(CSMWorld::UniversalId::Type_SoundGens, assets, "document-assets-soundgens"); connect (soundGens, SIGNAL (triggered()), this, SLOT (addSoundGensSubView())); - setupShortcut("document-assets-soundgens", soundGens); - assets->addAction (soundGens); assets->addSeparator(); // resources follow here - QAction *meshes = new QAction (tr ("Meshes"), this); + QAction* meshes = createMenuEntry(CSMWorld::UniversalId::Type_Meshes, assets, "document-assets-meshes"); connect (meshes, SIGNAL (triggered()), this, SLOT (addMeshesSubView())); - setupShortcut("document-assets-meshes", meshes); - assets->addAction (meshes); - QAction *icons = new QAction (tr ("Icons"), this); + QAction* icons = createMenuEntry(CSMWorld::UniversalId::Type_Icons, assets, "document-assets-icons"); connect (icons, SIGNAL (triggered()), this, SLOT (addIconsSubView())); - setupShortcut("document-assets-icons", icons); - assets->addAction (icons); - QAction *musics = new QAction (tr ("Music"), this); + QAction* musics = createMenuEntry(CSMWorld::UniversalId::Type_Musics, assets, "document-assets-musics"); connect (musics, SIGNAL (triggered()), this, SLOT (addMusicsSubView())); - setupShortcut("document-assets-music", musics); - assets->addAction (musics); - QAction *soundsRes = new QAction (tr ("Sound Files"), this); - connect (soundsRes, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - setupShortcut("document-assets-soundres", soundsRes); - assets->addAction (soundsRes); + QAction* soundFiles = createMenuEntry(CSMWorld::UniversalId::Type_SoundsRes, assets, "document-assets-soundres"); + connect (soundFiles, SIGNAL (triggered()), this, SLOT (addSoundsResSubView())); - QAction *textures = new QAction (tr ("Textures"), this); + QAction* textures = createMenuEntry(CSMWorld::UniversalId::Type_Textures, assets, "document-assets-textures"); connect (textures, SIGNAL (triggered()), this, SLOT (addTexturesSubView())); - setupShortcut("document-assets-textures", textures); - assets->addAction (textures); - QAction *videos = new QAction (tr ("Videos"), this); + QAction* videos = createMenuEntry(CSMWorld::UniversalId::Type_Videos, assets, "document-assets-videos"); connect (videos, SIGNAL (triggered()), this, SLOT (addVideosSubView())); - setupShortcut("document-assets-videos", videos); - assets->addAction (videos); } void CSVDoc::View::setupDebugMenu() { QMenu *debug = menuBar()->addMenu (tr ("Debug")); - QAction *profiles = new QAction (tr ("Debug Profiles"), this); + QAction* profiles = createMenuEntry(CSMWorld::UniversalId::Type_DebugProfiles, debug, "document-debug-profiles"); connect (profiles, SIGNAL (triggered()), this, SLOT (addDebugProfilesSubView())); - debug->addAction (profiles); debug->addSeparator(); @@ -387,18 +298,31 @@ void CSVDoc::View::setupDebugMenu() QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); - setupShortcut("document-debug-run", runDebug); + std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_RunGame).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + runDebug->setIcon(QIcon(QString::fromStdString(iconName))); - mStopDebug = new QAction (tr ("Shutdown OpenMW"), this); - connect (mStopDebug, SIGNAL (triggered()), this, SLOT (stop())); - setupShortcut("document-debug-shutdown", mStopDebug); - debug->addAction (mStopDebug); + QAction* stopDebug = createMenuEntry(CSMWorld::UniversalId::Type_StopGame, debug, "document-debug-shutdown"); + connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop())); + mStopDebug = stopDebug; - QAction *runLog = new QAction (tr ("Open Run Log"), this); + QAction* runLog = createMenuEntry(CSMWorld::UniversalId::Type_RunLog, debug, "document-debug-runlog"); connect (runLog, SIGNAL (triggered()), this, SLOT (addRunLogSubView())); - setupShortcut("document-debug-runlog", runLog); - debug->addAction (runLog); +} + +QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName) +{ + const std::string title = CSMWorld::UniversalId (type).getTypeName(); + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + const std::string iconName = CSMWorld::UniversalId (type).getIcon(); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; } void CSVDoc::View::setupUi() diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 5418b7720..e767777d7 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -66,6 +66,8 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName); + void setupFileMenu(); void setupEditMenu(); diff --git a/files/opencs/cell.png b/files/opencs/cell.png index 9127dd5e5c63b41333de5a564aef7c6f77808bf0..4c3fbe25154dfc8dfb1e6cbe8422c1dec6d932ce 100644 GIT binary patch delta 1172 zcmV;F1Z(@>0lW#2BYy+HdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuva2`@ z{nsjb36|t>ImSa(@1U38$KXp6LT(b?!v!*mCZ8jlQ2X!SPXFK%F`ldo(I@W>E}LwU zj7c=xwMjO1ea;v2GkMU{{)0fJkh;D4HtjR??&c8e)Gv>E3V&tBi8IhoStRZ&et0la;7z|Q2U*GDMG_-Wt|7)?F9bL>`x4y^rTe)u6q6U_Z}hh6VA_TIMDW$S^I zr@5$~rK~d;E`NYhS2vFla62-Axt7A`2!N|OL7 zQbbo>)F4U9N{StWy95wZ2^@(Qu%=MUUJI{z)0(evMCQt{6h<(H$J3xQ<(07q>V32> zvxz%as7pozqKv`Zc)|*VaG#jE72kExUEi2HVigR!6@TW41!l8sAr{BC-qMw4g`G2& z%+~H4>I?vgklUdcRxn^ZnwdSDAQG5?V+21Lm5W+gzyLcZm8pXp#ZCwkW#ooW;MG=~ zY-$`WAi}BF0BW#xK$f)*{S$0N4`rv$l5@_x;G#<|d-c{k?|ty&jLJcS4KDZ)LJTS7 zsL@6jeSZuw#uRgsq&UJ{@+nY?DW#l|G$V9|cZNmIi!8d>#VvjbOI*^D3)-jH;)*Y! z#F9#`Qnh0HS6>6wSX0f78(OK^=9+Jz#g{qBUXv6pUleM*~Yx3*X8lA9kr1o%{*B%*hjP90C|uT^xCh~8!T6|+Ca=<7gT z?SGiaQW<@Kk3`9@P@^|6e=O;He$H#BH;&!5b#eGmPs#o?7Tpx~-~mMZpY~DzxXwKr zebIH#-ghbKTToxsO0SFku2#AN>{&Fr0_<6BbOqS6lyn8yvs&p2uxGW>wVm}`D_sHh z#a{a!*t1&c`tEwHiLURi$CPw^cRkih?_-aw=M!r;?7AOb)Kc8e_tLa$)#k(fd2bG< z-R^kwqYoY5N_v;+MWUnKQ=q?A(WmlQ>1D9LuIN+DE`$BY7`;qHM_TcBGtvLJv`ovx ze0Gum000JJOGiWiO#n>*O(_oqIgue4e+P6)O+^Rd2?h%(5uJd2d;kCdeMv+?R5;7+ zlhFylAPht=(sc?hVzzOLZljA794P&0g(wkh{q6fScQJ4Y@Zd`nUm|AETBi%=eqffW zC10Xog%ASOv1UD5yXBl4BQpyC5K&_ixn(J(rm$H*^rF7G#$l1a!5{@@7Awa7Ce3Ux mig3jGD%Y#}y2|d&1zKE!{xaOcA~d)F0000 delta 178 zcmV;j08RhA3Eu&bBa;CH7JrdRL_t(I5$#ez4!|G?gJs{-$La%_CBrmmmfd!l2LWj- z0YvUoYqe6!5%H7VVh7otNStG=LJdh(6*F_o;Oqw`tPC{;h*dKw$BaFD@3R5`xgsXw zN1WE$DAxb<`1Ctn0Hr4nL!5K3y3T~`{{kZ0DWKatDl~2`PU_+m;3qjJ%n}9`iSJkW g$g8~6yLsULT$nUR2>SeoVgLXD07*qoM6N<$g2eAn82|tP diff --git a/files/opencs/debug-profile.png b/files/opencs/debug-profile.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb01da4373c24e073eaa8780f2a1925d8e4c291 GIT binary patch literal 431 zcmV;g0Z{&lP)x>!fO`r}B#0U}7m4vO)L ze%-f*hFHKny78>*7F6o@9Wyw`w8k^_qV`xoD_*dn!6nV&UgIg6aviSFfm>|H0>X%5 zQd1npNnrg&n7}Hkw7k!d?}ls~p#o=dHZco_+K(su!yk}`Ij#M#W;5-=wLhRn(*6;! zg%6!v)B=)qB(|seE1ch2 z2}Yb(WEZv+3nAUa;B+8qvZK_S`?_ojxuzu|$ Zb6>EKPtoppBdGuY002ovPDHLkV1knC#qkSC&LBJ zfVRcM$?V1P3B6PcAX~#mZX7Dr0E-up6>u&Ai1M4+$6px*d6g)50zul{6fOZfD5 zx^(<(+NuITAZTdtF*`7sbB~Mx0Ae$D>fg&k;5u>{h0Ft(%sL2>YF!TkK|`BOGW-0q z$<55rD9F$#2!Mm7ygZedyZydz;2gN<0Fly!zty-=mBaVpcsEVu>hcW{d1#Z{npnGY zBY*fjmK0%?8^t(;YR@+XG=oH^Fxc%lEVS$r0NiT$nf{@pT|4*Ae2zV-96O_3`)`13 k>$j#pJbyuT?P=Bf1qBx3B`dxp6aWAK07*qoM6N<$g68)pi~s-t diff --git a/files/opencs/error-log.png b/files/opencs/error-log.png new file mode 100644 index 0000000000000000000000000000000000000000..df698d14531014feb9f4bd4b8f6cb7e0a6e79fe1 GIT binary patch literal 518 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_pf zf~Tj8V~B-+?_^)^!wv#%!mLx9az&?fami_EaEbY^tq9Ps@(eq|wP?W)Mx~~T&#%=v zUY(sRp0D)6-e}b$AYYtbYGHswzt4=u5fR32!1AmELWs z{eA3L=(WljtVdH$X{xm^V->5+IK=Sgkn)^^oHhsN-3>n1+t6rjvR{>XPL~|}@x%6T`X{OjY>=$&eym3zu zI(3&ND0F-5Ukg1WQH_M2m)$Dg|8G&?`1QNr_-kbx_wQRR0lw$cZUBRx!PC{xWt~$( F697;y!(jjb literal 0 HcmV?d00001 diff --git a/files/opencs/Info.png b/files/opencs/info.png similarity index 100% rename from files/opencs/Info.png rename to files/opencs/info.png diff --git a/files/opencs/instance.png b/files/opencs/instance.png new file mode 100644 index 0000000000000000000000000000000000000000..ce63e64ed66830118ed9c25e6b817be8d9f96909 GIT binary patch literal 618 zcmV-w0+s!VP)UGq2zJSnbXnj}@gMNyS!ybGM&f17qqb7rHS+D`Y8@40%+--0DA>#p^QU(d|223)fMBN$mC(w)k z4GvZmAEh}@Fb1OF2(ejk0HnZEnjQQCEEsFc_-E0<*y$JX(e8;4?*ff34gF>F=DUc>^9NrYBwnAHePvXegqyAk9fnD9R{FTuppn z(}j)$y~ah`(K!Qu0Ven-O30eiwe|+ZC-5(zmp~tw;HqP6`xZSy{wv%*KmnSxw(ghb z&ob;9JKJbI*%VEW;5KX9U~h&&@A0+jJdMG+d-I|G3k+|{pCe3}VE_OC07*qoM6N<$ Ef&wfNegFUf literal 0 HcmV?d00001 diff --git a/files/opencs/leveled-creature.png b/files/opencs/levelled-creature.png similarity index 100% rename from files/opencs/leveled-creature.png rename to files/opencs/levelled-creature.png diff --git a/files/opencs/leveled-item.png b/files/opencs/levelled-item.png similarity index 100% rename from files/opencs/leveled-item.png rename to files/opencs/levelled-item.png diff --git a/files/opencs/magic-effect.png b/files/opencs/magic-effect.png index 4901724c59a363d1ddffc137a17fa33a7c9712cc..44b682bf4ff359ce3c8076379cf27ccffa46f673 100644 GIT binary patch delta 309 zcmV-50m}Zx0^|aaIDY{aNklLCuHjy6z0As;Z-) zxq~wdyvXUgTfXz&J0^(G`?a`VoukVy{ww4o1>S55K4B{!_FZP zOGw}NUL@Hy`G@O!AGp451Pel(Q{==1D@~$!l)NT(Fs{x5aHKY|71*9DYA(}xN0D?ps*$%;ZIZLZ|NSq-Eppq5al4-Z1f{O$dr(g400000NkvXX Hu0mjf>~55q delta 278 zcmV+x0qOqa0>uK5IDY{5NklWd8N#j$ zZS9YnH2a)kVd}bK!!UHtxr9$jn?A;Pn&Cx7Hsrw&lT!zo6ze6RuLx`dC#}lJwe4Icz=Iua zkum>)5JKLdY*qaSg2^(1_1zL+KucCeDGFwB1!0C?$S6G_`HRp`7Miz+{jSojUE&u=k07*qoM6N<$f>i>0%K!iX diff --git a/files/opencs/menu-close.png b/files/opencs/menu-close.png new file mode 100644 index 0000000000000000000000000000000000000000..81bc986775089a2ab8b8113096c890a47785e84f GIT binary patch literal 438 zcmV;n0ZIOeP)_eP4v08QYouW`+TlKHqY~h g-$r@ga{kZ#4H#~PogGl%VgLXD07*qoM6N<$f?Wu@<^TWy literal 0 HcmV?d00001 diff --git a/files/opencs/menu-exit.png b/files/opencs/menu-exit.png new file mode 100644 index 0000000000000000000000000000000000000000..f583536fb695627bb749c8e742df32f37e845995 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;j%S zi(`m|;L)Iif`<%vyo1~x&RF8ZkbH=3ZcCYHl8VLdecLiWy^yWD(y6Q2cS+!%Y#@8Z zSM!a%V%(O;BsO`JOV0bbL}HSw`BWdBlPzbN0#X;7xgOJ;J>z9nzN|tI-)sxE$?|lw*K7*&LpUXO@geCy5 CkYic^ literal 0 HcmV?d00001 diff --git a/files/opencs/menu-merge.png b/files/opencs/menu-merge.png new file mode 100644 index 0000000000000000000000000000000000000000..c76288ae168580eb28605cca6c82876f8a467639 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_Wn2 z#WBP}@Mw@DUxNY<^VjMR;fMaatbV!a%-QCPCsLE&TJ4%}>Aw1B#;w;HZPGQD8ukf2 zx>Fs0vQ(K>N8;+q*)wc$cFN$M5XTG67GU&z$gXm3P*ne%c33&Br`ObTm(;sHX z2B&I1pA?od-|(3E`}>h>Z^Seb*T(MsKJ(4qc=OsMf!R|g86B&sI-#?mfc442-en9I zTI(~MChy4*O3V4?((D$wvpkC_sD5|Xw)DRo5Obi@&TU?oclyq6(>vU+-;`lY>2dcjb9C=}__=+$ cM*mmV)hqqBP323C1p14?)78&qol`;+0L{H`dH?_b literal 0 HcmV?d00001 diff --git a/files/opencs/menu-new-game.png b/files/opencs/menu-new-game.png new file mode 100644 index 0000000000000000000000000000000000000000..701daf34b179e4c2e507788fd38366f9d83c9c07 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;iE) zi(`m|;M?Fpz6J#j*K1#|%-&+5~5+}}`+Zp=FKa9hVk!j1d-CNBj$lkWtcHpe!-tP?^(cYK51uD5Gc}kTEZqVBl zDLgA>p=XQoO?FjSQZyelF{r5}E+txm0xk literal 0 HcmV?d00001 diff --git a/files/opencs/menu-new-window.png b/files/opencs/menu-new-window.png new file mode 100644 index 0000000000000000000000000000000000000000..4a42da0d1288678a5bc4bd01a4713834f3aba7a0 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_W(8 z#WBP}@a&{$p#}vGSHs)$^uoTgA7tmVnEGvoX1z%3>!%tAt}Cx-oFeqH%A{SOp(AOn zXt?jRv)ayGv32JvZ#ABcopUPXz_;0VuQ|n6A9K0&wD|l2aS6jfwst<34O1?yxy3DK zydi13!awf=jv^LL@`8(RT)7eT%${Ml{*m{uI=*idtt{Qkm|Cl~`N^Y5DWH=WJYD@< J);T3K0RY~_Pj&zR literal 0 HcmV?d00001 diff --git a/files/opencs/menu-open.png b/files/opencs/menu-open.png new file mode 100644 index 0000000000000000000000000000000000000000..3766e17549830ca741e42055f9de51faa0263226 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;jxQ zi(`m|;M-tFp#}vGm)F(5Ch65K@NVz>D)x6iTdze%m~rV@Zk-L%icB>&T|bV^hKmsy{PJL1`?c#)bIR|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;Q%fF~q`u=_FgeLk2vplYKfC7fe@ae<-uT$iT3~EtJho@4%^2p+9SuczMr`2zYsN z?VtZwt(GqNkg;~^)^E8zmPaJcE4CzmEOdCk@QL=zR*Oykm)1@{T=zVtY0vl0No6|^ z$lX7BeG^x*)07%TTaOBPkK~VA-{g21IGArL|M+i`-ckvvC!zkq>6wkDCt@O)RmGa8<0ehNE3bfxb Z|CL*E(BR zh$s~OY9M~d^p1;H?c&h7l=#PCS+ z#x9>|~QaJOhytpOOywJKnq=q3<%szi> TOZHS?05EvE`njxgN@xNAl(>K= literal 0 HcmV?d00001 diff --git a/files/opencs/menu-reload.png b/files/opencs/menu-reload.png new file mode 100644 index 0000000000000000000000000000000000000000..2b780598c4162eed952e978ae557e465278f0085 GIT binary patch literal 447 zcmV;w0YLtVP)PGS7Qaa1b3$&sEeJkZubf1&Oe51JYd%SmYmz3wBv^o(wk+$D21{nnZwrGZXc+SW z?9#EOUGQ_ZZSO}>RD<6Z{?7v9$co&fRtdfkP6Xa(l2fKC!{2Ih3qDMZ0v}@AU{6Bz z4(jNO1#)#r3uRV}v;g-K{YpLs<(!;BrWnu@NS^$@qVk*tsTRUcr#b)47VhEL^(1Fbv;Z*HtX0>tt#fj9#%sAm8G002ovPDHLkV1nW%zJve( literal 0 HcmV?d00001 diff --git a/files/opencs/menu-save.png b/files/opencs/menu-save.png new file mode 100644 index 0000000000000000000000000000000000000000..4be88765bef48fc5b571e98b8c942dcca95934f8 GIT binary patch literal 302 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkQ1F4L zi(`m|;Mw44p=Jf1Cl_Bo42|*+dhL7Uv*KOXLjgznjQ?h)nJCWA;Sg9_Cvx^rqJ+}c zYf(pM_~> zlD$gAKoo`NXTd`Jc>ocFP49~&C^lABR^qz&11?yp zm59X#wel4#Oav`$7oWhqf`y2}HYV|#T@ohljE!EHopaB*_vG$Pg!p4fyFU!WE2Y#{ zS(ZXdDT<=FNYnJduK9^oMbP{VUZ$y?B#Gn>$mii-sv7T2QOvn>6~I4mZd{t-TIPpw zJB&STup`#sJ~*2WZA?W`RC+~(-}KQA%+eTh#$^IOi~i-(3|B4j41DV;I)(3fG*Q1s zp7cu56K8{0HzZ#GpKGnx^E~gsU6M~kZD16dj<`-(sxp6z*yb!@vX4m@lh}}UGz@IP z#u)c8*>Ra@yU>TTEPGsR2Xj*`eH;YA8~LEBt(GW04_C@zZ*?@c3lHwoJtl~mq|ZdV z69!sIKBzJ6!tY4zpo_^h(CnreJhk434>vphDfrf1ZvFO7F0=P1S)EDdcINxOnVs8R&-+XBegA~lT!yPk5k(d9JH8IX@Flsj zIBh_&LUh9?6d%h9vbZUVjRqH`RNEx13Hbd+QS>C-(RgkVB$kpUSFj<%vlN1N$TxTo zt}D4*jQD|%as&YQ0bfYY9GYe9&;(c^tXo8qOj(u%xH}XCftKpr1d`0gOx4g@za{=t z0}A)JbraSt$j&lm=N9=E?{h{2$)yiTf+nWOyodOI8QVZJipM5!Hkvq1=-jYK_yOTy VK0Na~gXjPN002ovPDHLkV1lPsh4=sf literal 0 HcmV?d00001 diff --git a/files/opencs/menu-verify.png b/files/opencs/menu-verify.png new file mode 100644 index 0000000000000000000000000000000000000000..a7878ebb3f6c4c77230f8c080c0115dd7aefb765 GIT binary patch literal 487 zcmVQSK@^4eAtF{Hh@^M-7uZ;7z)DyvLBUEPpkRpF z2qM7;f{l%!2o|vj+KO8UHnC8_GL;ZWIxPf=g&`np*6&U-i&;|`yl`gbo_o%n$F5=g zGt9W(^SnX0ZPPT|9HtCNVdxbO(-+ev*uz{F?$1HP dUz_|ie*;IC(~hX{6SDvS002ovPDHLkV1m_N&5r;8 literal 0 HcmV?d00001 diff --git a/files/opencs/metadata.png b/files/opencs/metadata.png new file mode 100644 index 0000000000000000000000000000000000000000..34c78ffd61e30acaef3cc30960d59fe30979cc50 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;8rdF~q`uYVbkc!wNh-S6BXe!Er$1XZhB5lilTFn|}$k$2?+bN|AK`_H##Y%0=Pi zGbfaqOl&Nte{=o3IEKSujtOtQv4#^z&}Z)2DEnZqRMCC6Ep$|)Sbu~y#5zhbWEi_K ztmey;%ICH@yCCoX4PA+OX$h@+&Mqi-oPY6NuH=Kdf`h@*dKZdWwa+U};AYTX&E{Hb z794Q)wJO7WhcEkXcD*$Ez~FEU%PZr9_qKJsy8l!9 b=5OW&4^?AKLb7;(eq!)+^>bP0l+XkK>c5Fr literal 0 HcmV?d00001 diff --git a/files/opencs/object.png b/files/opencs/object.png new file mode 100644 index 0000000000000000000000000000000000000000..4f552151dbd78992f83ed7939f25356e38fc4f79 GIT binary patch literal 447 zcmV;w0YLtVP)7PS= z*9Sq+2ez%0S|W<&BuVxuw!$#{C_oa&ED+Zm$9V#)q-nZ_$RkFGD%iN|y6VL5Km^w4^@K7r`^EyY56&bcZxpns_ca?oa) zMDuE1;wh>l8)eQ}WImHzx3JEu8KkGNoX~NCGXr-RLQP27?L|@a;`_ehVj7<3wW;ra zb3i-_M_%Iw$G;Iy@g+(Dp$=h({#uj=#D(CDDPn=OA=c&)A7?0!F9rf);xR1~RRmpv pA(9{HoY+O&6wG>H_+t}O{0UfliPDznHH!cM002ovPDHLkV1iEIw{idg literal 0 HcmV?d00001 diff --git a/files/opencs/placeholder.png b/files/opencs/placeholder.png index 1d3df3c47d76bd213e958a44e752e4693e537f32..b9db9f18886fbefe5068cadfd31409136f20af42 100644 GIT binary patch delta 2140 zcmV-i2&4DU65bGy8Gix*007uvZqNV#00v@9M??Vs0RI60puMM)00009a7bBm000XU z000XU0RWnu7ytkO2XskIMF-*q1`!-LIsNgF0000PbVXQnLvL+uWo~o;Lvm$dbY)~9 zcWHEJAV*0}P*;Ht7XSbU#z{m$R9M69m~Bi_T^zs<2wegR5PuY3P?r>%S(=$S$O@f^ zIYBcuo%W&8hrX13uvSZ(%k{DxYpbm-O?#V_OIvDbsl%a}D{YBdzNFckpr}b^aRKr9 zaJRer0P)h(p7pfn|LNZIKm7UKbN+AV;Igx`!S5t^@SDk>4s`VB(Ty87{w_dbu{ba= zaCmt5pBN#yFn^Um005XwCYxFaf|8Syo12^G=jW}pL?V&FV7R%txw*MbOiY-~=3fAm zk&&@s!-m6$58Kr4-Me>gZmy=L#$tT?_U+Np(ZRvNI2>+iX$b%T!*FkJZ$m@Fr%#{$ z5zyw%o4?4VF6OiN265{Z3%eSfD((t>wwye9 zvcA6l-o1N22_%t7yu7>y2M29xckSBM*Vp&@^=ot_D=SMR5)BOv$>nm3T~;cUN~JPB zK7RM^-7pM4eE9HZoSM()tJP|oD-;UeH9*-vyi53?ZZ{NP%(9rPt z^Jkq-XUDQyt!A^?t^uW|r$Z1_U0qG5)3sWyg?~0NF|o6=6A5(!fgqE~mMvS>*x1reJY zqtUnql%1VzG#YDaY9I(AlgVi4A`%xD*WTU^!!Yt16B7f&@QoWcoKGcYvl)-aI}1dm zQh#G(V;?eo^}=} zFE3B6RzH6H7y#hy?QIcad_KRwzaK@6jEn>TJbn7qrOL%*GG}IHoC4x-I8jkiXen5} ze7VtRM73lxIV21+MoC3+6f%L5BiCPaA06Suh*m6sHiBNPG>(UTgbkB`?OkZXJ@AaAP9mI5)xWlThZ)-2-?j6 z0764U+uPeMeABwB*pZx^oY2tFix)3CZejQA*`w3xtlJcHh4_5F$z*!|{5e)jB9ROR zgOfA~2?=p=ag~*oHZ#`Wfc*UYVt-;{YHMq)=NXMg2!ap?8XFt4(0=*yg-j+pLnD<+ zGcz+EJa}O7Nw$IV^73B2dezg@V?A$bYKl&$0|2;O?#Rf9bgnkj9v;rm&u1_gHh)WyOeQ;j z{yd#dKXKwjb91vDZXEU~ZINs&e5j(LLa9^+1qB^He%$5)6ciM2x!lpw(b?JAg@px! z!N6j%*lf0skI(q{cw1YWMJPBvb^M41Da#)|d=Ll()|mql7=}wqN&*4`ghC;eO7-#a zfgoshcDA>-x2&w}-Me?c{(oH7+}yl#=T7^b*ZcSHf3)dN3hw{gkN-N*xpU`2LPCBc zpziMO@$vD0?eB|YuCasTwh;5 zF);xEkjZ3Eo;(qWM00aP;^JZog;HK#UQ|@%AP-3- z5(0s6?AS4EOW)PiRexDo2>>{A=1h8edRbW+4u?}H6fG?+EEY>Hm%F>WqfJPEe}7n5 zSY2IRbab?vn_Eju%Y_RU6beOcZEav+;HgumNF>tPvuDf8%Vjdzw{PDZ05KR0i{6{b zWTM$tqtUQfEaZ7$U;qFxJw1)X;XFJ%P@u-f#^T~)5{a~F(|@MH!9hG8&tNbT6B9)u z5dgqoFmN~=WZ#|2)G!RYySrmY^m;v$$wZzh6w1QFg8gdq@#DvdiHVey6dsRv=gu9o z*$l(*-Me?OmDOg)VmlA@_4RpqdBw%W0RX(byZ`{BqoZrqtfA3p>(;H~a5zIlLk=TI zrBX#iMBs2Zq<>Ex8yky>i9wu?$KxR%xwyE<<#O!-nM|gorKJ}yUMLibyu7@!va*7L z0sz3(t5@-Oyj(8N&(Bw>RE>>|0D#5CMeGkPOx4xZd3$@Kw^$Vw6+|Mjw6yf{<;%Ib zxk$lQsZ@s!9XfL4h_kf}>!-Fdf!a=pS zXx*Kp!Q=5B9v*3FX_J$aSQ^BFg%NCsxz>I-FH|bEu&{7$ZVpKk|F<81Hqc+BEpsT* S7HYBp0000DsmHZ}0aX5fB&{$e=-k zc>etPZ|_^~=!;V6ckT7{^^Ff8m!s3^*uH%`b#-+%{bVv3EnBukDwQIYN_qG09cFXW z@x@^QTCJAxTrrc9YaRaF)F`T3&l{{H@q9Xl4SR*O_BrM|u%v)N2dO%2D7 zALrDmQ@nfk?)M9rI&~`V-@k9vr+xeOOrAWM`1p9MwqCt@k(88#i;D|-y`GsfXY%63 z3!&Z5&yR@{Cvy1kVUm-RxpwWE=ok+D14D)k!OzdHNdYrw&gA6DlNbyJ3;T!>BiOQK z3wQ6{B_<|@%*;#+@&Ki!rEJ)+0i8}qQc@BF2M+vw0;WxyMsaa5A3l7r>C>f47lscX z&Vd65EbOtdu_Pxa)3$9}#*7(5L_|cRV-*z@v1!vL7A;yN{CCX~5EvMUr>7@3Z{BQl z24>BgMOIc8A3uH+Y~kVIBqStITU*PbMT>}visI6xOHGcMpPx@{Z7o4TLCqJCkdVOc z-Mdk#RJ?liO4P^S-=BW{`jMTTE!aFgJ<;iOm`o;?EnCK|TeqmKt>x3FPj-!4TwIK| zw|DadgocKqR4Tc6@giPcUQ|_8iTcf%Gl$H~Od)JarBYU}UX4G zm6g$@OBcrlNTpH|5)#PB$e_Ny9xpF1s;jH5{DOjl@bdEF#EBDvO{>)s7#PUWqenS^ z{`@bK2PiKuZ=QhBqeoL$SI6bcmyyfmxVpMpMGk=3vuBf$k-^umUj^Ir>C>sHsUagH z!y)qUeZ!Q=WL9>&1Sk{=;^X5AO1(gd4}i;GoWyGjUzR!5io;@il zDza*8R6ys>ozZHwWM*bs*u`cbDk_STCr?`Sq0wlXEg(8Nn%vx6(QLL+0rTh2XWzbk zR_k4%P*`k0YPA|4A0Mtz2dh<#IXE(a{_|dQ`OCrT~pbgIcZT z$dMyfZ8DjRZ{NNN&ykUloH=s_lgVV^=k4u{!CN;5Bqk;@fByVN_YW%p?b@{?Ha51=lU7|_ z9W7h71YqdUp%fGpi2A*K`xcc-^{d>`Xf&)`xssHW6wwd!BdnIt?Afz9ckZ0%rqO_^ zsw&iKHSk@4t(kiH@+Ce#J}8w+yNJu>a^}vRi%zFw<;s;d`?V>cO`A3Z1qHEZ&z?qK zs;;g^tya^fO&ffDeYt)6wy0luc{zD`d8}HsN|-S*C=?1NO`62v!-w(m@**xSj{EoT zH%T5q@^fkN=LjSEl0?FpGiOLoPe-HCuypBCo4#_noE0lpFm&ip^7HelsHou0n>T#= z^a&pyAN>6M2nYz^#*G^sI&_Ffj~>}Y+@=6~v2o)@`u6S1u3fuq#^@iRQmGg=Y#1IM z9=N%=(W+G|Dl02_`0yb`MMb=R{n`;4jtlVf^CKxKiG>RnHY>|)E*uu{|AN28-9HUc z0cmMzbm-9GpXTBaLV-McrTp`jR!M)Z2UP=K8@X%bhiTw&a}aTp8+^78U5DtHQoLa2E*RB-ClDZyvJfB{G(5-YBvGY}USN9)$Dk;~7Gf986hR z8P3km00ajIvwr=0K7Ra2r%s(nPfr)pg~Y@}7B61RlP6E;-@iX=)~vDUyRNQ|QKLpN zXwV?RwqU^m;Ctn6!GZ;pmzNU~62h`&%K%78Nx{v{jojQ^)~#FD^b9m0BqRidLc!w2 zi!JQ2v9aviw@=u>cI?xqtzMk0|2$Hl8xuQFl61mS=3^z;P4WHMngneg!Nuus63FJCa5 z%~VuWkeZsxwr$%ucI+6NH*W@D@7}%0WHPd|vq?`+=fsHTL^Bsg#_Y z9OlfKV|9ZglgW_FlpYv1?F3dfp;iHt}kX!rUZtv1xl#xXO414!0&U0@qnv?^=M45CNIfjVPRmCG|D z=mLT^S;hHfLLl#18)`DqYXqKo*N)?uae^rU-w_jmXWp|$${Fe2eqvU#Q+!enb~CdV z>Fp&?Y0=FWStmpSrPGJYDWGKpkaKCBc{u%y41luSs=7vL&$5c%37?6htE z`_NyTN&o>+-#$39Yzk W4f$`6a&e*n0000004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00009a7bBm000XT000XT0n*)m z`~Uy|8gxZibW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE-^7j^M67&0004?Nkl3&4Kk!Qef*>(s(8?|rTKK3e^m73jt?xw zF<~}L_cB}AWHM{*B;>as2xgJYK|UiWQ%4(pe062$w|`jX1Ntsd8QEwNPW>q-ThMA@ zqmTLq(F!iVqw)or_E6>qCVq@_i?YJXrQ++{QXiZ(2C@H7z!q4}Z(BeGt8ojJpAHe1 zy(fo=bJ_4I{`!o0o1gD-!AjM?e@)4Pr~QUVBwIK3>F)vQsWb&$S&^r&QW?aV;lvYE zu4git@ja1!AU!?f+FT$mk}i{#TY$^d;msq#$xNIH8O8}C;8g*c1MByk2>1lNhz?s& St*b}?0000;*Roco)Imd;0Jd^?ycU1meGlDguJ39An%? z$E1+#>$+A%j*2)edm{Kr;UK|Hr3j1u^a!kSOSNF48I-`Xk(sQ$%3{~vEX$(A&clothing.png container.png creature.png + debug-profile.png dialogue-info.png dialogue-journal.png dialogue-regular.png @@ -27,34 +28,60 @@ journal-topics.png door.png enchantment.png + error-log.png faction.png filter.png global-variable.png gmst.png - Info.png + info.png ingredient.png + instance.png land-heightmap.png land-texture.png - leveled-creature.png - leveled-item.png + levelled-creature.png + levelled-item.png light.png lockpick.png magic-effect.png magicrabbit.png map.png + probe.png + menu-close.png + menu-exit.png + menu-new-addon.png + menu-new-game.png + menu-new-window.png + menu-merge.png + menu-open.png + menu-preferences.png + menu-reload.png + menu-redo.png + menu-save.png + menu-search.png + menu-status-bar.png + menu-undo.png + menu-verify.png + metadata.png miscellaneous.png list-modified.png npc.png + object.png pathgrid.png potion.png - probe.png race.png random-item.png random.png list-removed.png + region.png + region-map.png repair.png + run-log.png + run-openmw.png + scene.png script.png skill.png + start-script.png + stop-openmw.png sound-generator.png sound.png spell.png @@ -64,7 +91,6 @@ record-next.png record-previous.png record-delete.png - record-revert.png record-preview.png record-clone.png record-add.png diff --git a/files/opencs/run-log.png b/files/opencs/run-log.png new file mode 100644 index 0000000000000000000000000000000000000000..463ead176d4c5e367262864fcbcf4e5a19d23ca8 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=G_YAk0{w5XjGVuN& z)o`BQMRLN785QCkyc;B4)-e8`JbCheX5LKZB)*j94l{-of+-9<%MbD>o@Q8)?Qn0C z;^~GZOH?L!-(%d!ZprqCHS3JxO>Rw1O@-*`OorT+jTHhb0;j&oP_b4FoFlGb#4yG3 k&)>hDhYYqVtY>04?6v!2)}vJ`fv#onboFyt=akR{0N)K>X#fBK literal 0 HcmV?d00001 diff --git a/files/opencs/run-openmw.png b/files/opencs/run-openmw.png new file mode 100644 index 0000000000000000000000000000000000000000..1033d62baa120ce5c3ae4e2da94d122f88051a7d GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb==d%F~q_@`Op9V_RKs6Mn;Ejwn!AzDzNdKKKJL0mxr>C+%oock}3-3zi-zsP&|7? zqUkEbf@l9X2{_+l*vM(uTp-P0#M1HfSq=Ne^2QqsvmIG@8FsU!T*%qrnCoz+F-PDc zTLbT`uc8?z#3wjrRxk^uY3oV7lA5qR$D^3>j`ahEI`$)SOC%Y-9eTdsVUF`6@qK@$ z&;BjEq*7C0^8}HO3mG;XS5|mMnK%gvaU7p8dBQ)%-;-sSTRKm&E0{(kGg@W~CkuGk ZGJNsgqSE=Fa~{x344$rjF6*2UngEJofxiF% literal 0 HcmV?d00001 diff --git a/files/opencs/scene.png b/files/opencs/scene.png new file mode 100644 index 0000000000000000000000000000000000000000..a9fc03f7699a715592edd31db78ad47ac514f006 GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCijSl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=d3- zBeIx*fm;ZK886+f`vVk|Dshb{3C>R|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zb;Z-gF~q_@IYEL|Lh-@t*V$Z0!W(|@3;h57`SWl7MU6jL1fn1C7%foY7O>~x;9$^< zV?46nan?dM8J17ifB*j7{6vn)P~L3ze@4}JjVcW#`~vyxj{+3ZSROGu*l=r^OTSW5 z+@a{usPC|cS&bod0`rpcLq{C+94D=3J@wbg?14$Z0;WjA3G&=u&!s9@b3Lg^NoPKq zx442?}xUyxj@&tLlBR3uPh;*bY?0g_T;e2NvgD_{~i9UU^lm<3m=10dDY-l)k mC|qH;aK}@(V7oL928Qm_H3#l%PWuYpUXO@geCwN2ZUMx literal 0 HcmV?d00001 diff --git a/files/opencs/start-script.png b/files/opencs/start-script.png new file mode 100644 index 0000000000000000000000000000000000000000..73ed157b9ed2c2366388723f8e5b25fff4bc3b97 GIT binary patch literal 359 zcmV-t0hs=YP)Q%iD#FbuV^VUy_zGW14pjAq*dWSMD~X{RfgUV_h! zasz4tfSa9O0ryxh=a?XB zf=o_JY^ti7sR{sWwoTKF2s#(|144HJfK!Jo%YqE1X-W|M{1Zd~{5ylcrur;`3~-I8 zuIr0ees$m;`MIkGG46U-GxZ99s|LS>;vs3-Lt1pt{7g}-u;fDPya%O?-LK1jXrETj zY5{PM3os7K82hm0O4G9UrzV_I}Un^?hcdO-#kvH8dF z#R=Wh@(Uh6k0j)d-_HyP1Xtj^h`ff5tb+0Uij%io@C{lR|DNig)We7;j%q!9Ja}7}_GuAWJGc|_p9 zW$)?Y7-Hd{{OA9FduAR3Bcnq%TO Date: Thu, 13 Sep 2018 22:15:59 +0400 Subject: [PATCH 37/37] Do not use universal IDs for menu items --- apps/opencs/model/world/universalid.cpp | 17 -------- apps/opencs/model/world/universalid.hpp | 21 +--------- apps/opencs/view/doc/view.cpp | 54 +++++++++++++------------ apps/opencs/view/doc/view.hpp | 1 + 4 files changed, 32 insertions(+), 61 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 6f9eeb24f..486f3770a 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -48,8 +48,6 @@ namespace { CSMWorld::UniversalId::Class_ResourceList, CSMWorld::UniversalId::Type_Videos, "Videos", ":./resources-video" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_DebugProfiles, "Debug Profiles", ":./debug-profile.png" }, { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", ":./run-log.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunGame, "Run OpenMW", ":./run-openmw.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StopGame, "Stop OpenMW", ":./stop-openmw.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", ":./sound-generator.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", ":./magic-effect.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", ":./land-heightmap.png" }, @@ -57,21 +55,6 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", ":./pathgrid.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", ":./start-script.png" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Metadata", ":./metadata.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Redo, "Redo", ":./menu-redo.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Undo, "Undo", ":./menu-undo.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Preferences, "Preferences", ":./menu-preferences.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Reload, "Reload", ":./menu-reload.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewWindow, "New View", ":./menu-new-window.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_StatusBar, "Toggle Status Bar", ":./menu-status-bar.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewGame, "New Game", ":./menu-new-game.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_NewAddon, "New Addon", ":./menu-new-addon.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Open, "Open", ":./menu-open.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Save, "Save", ":./menu-save.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Verify, "Verify", ":./menu-verify.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Merge, "Merge", ":./menu-merge.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_ErrorLog, "Error Log", ":./error-log.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Close, "Close", ":./menu-close.png" }, - { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_Exit, "Exit", ":./menu-exit.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index aa5276ba7..accd1b78d 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -137,27 +137,10 @@ namespace CSMWorld Type_Search, Type_MetaDatas, Type_MetaData, - Type_RunLog, - Type_RunGame, - Type_StopGame, - Type_Undo, - Type_Redo, - Type_Preferences, - Type_NewWindow, - Type_StatusBar, - Type_NewGame, - Type_NewAddon, - Type_Open, - Type_Save, - Type_Verify, - Type_Merge, - Type_ErrorLog, - Type_Close, - Type_Exit, - Type_Reload + Type_RunLog }; - enum { NumberOfTypes = Type_Reload+1 }; + enum { NumberOfTypes = Type_RunLog+1 }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 97aaac4d7..ca90b11ff 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -47,37 +47,37 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("File")); - QAction* newGame = createMenuEntry(CSMWorld::UniversalId::Type_NewGame, file, "document-file-newgame"); + QAction* newGame = createMenuEntry("New Game", ":./menu-new-game.png", file, "document-file-newgame"); connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); - QAction* newAddon = createMenuEntry(CSMWorld::UniversalId::Type_NewAddon, file, "document-file-newaddon"); + QAction* newAddon = createMenuEntry("New Addon", ":./menu-new-addon.png", file, "document-file-newaddon"); connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); - QAction* open = createMenuEntry(CSMWorld::UniversalId::Type_Open, file, "document-file-open"); + QAction* open = createMenuEntry("Open", ":./menu-open.png", file, "document-file-open"); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); - QAction* save = createMenuEntry(CSMWorld::UniversalId::Type_Save, file, "document-file-save"); + QAction* save = createMenuEntry("Save", ":./menu-save.png", file, "document-file-save"); connect (save, SIGNAL (triggered()), this, SLOT (save())); mSave = save; - QAction* verify = createMenuEntry(CSMWorld::UniversalId::Type_Verify, file, "document-file-verify"); + QAction* verify = createMenuEntry("Verify", ":./menu-verify.png", file, "document-file-verify"); connect (verify, SIGNAL (triggered()), this, SLOT (verify())); mVerify = verify; - QAction* merge = createMenuEntry(CSMWorld::UniversalId::Type_Merge, file, "document-file-merge"); + QAction* merge = createMenuEntry("Merge", ":./menu-merge.png", file, "document-file-merge"); connect (merge, SIGNAL (triggered()), this, SLOT (merge())); mMerge = merge; - QAction* loadErrors = createMenuEntry(CSMWorld::UniversalId::Type_ErrorLog, file, "document-file-errorlog"); + QAction* loadErrors = createMenuEntry("Error Log", ":./error-log.png", file, "document-file-errorlog"); connect (loadErrors, SIGNAL (triggered()), this, SLOT (loadErrorLog())); QAction* meta = createMenuEntry(CSMWorld::UniversalId::Type_MetaDatas, file, "document-file-metadata"); connect (meta, SIGNAL (triggered()), this, SLOT (addMetaDataSubView())); - QAction* close = createMenuEntry(CSMWorld::UniversalId::Type_Close, file, "document-file-close"); + QAction* close = createMenuEntry("Close", ":./menu-close.png", file, "document-file-close"); connect (close, SIGNAL (triggered()), this, SLOT (close())); - QAction* exit = createMenuEntry(CSMWorld::UniversalId::Type_Exit, file, "document-file-exit"); + QAction* exit = createMenuEntry("Exit", ":./menu-exit.png", file, "document-file-exit"); connect (exit, SIGNAL (triggered()), this, SLOT (exit())); connect (this, SIGNAL(exitApplicationRequest(CSVDoc::View *)), &mViewManager, SLOT(exitApplication(CSVDoc::View *))); @@ -112,22 +112,16 @@ void CSVDoc::View::setupEditMenu() mUndo = mDocument->getUndoStack().createUndoAction (this, tr("Undo")); setupShortcut("document-edit-undo", mUndo); connect(mUndo, SIGNAL (changed ()), this, SLOT (undoActionChanged ())); - std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Undo).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - mUndo->setIcon(QIcon(QString::fromStdString(iconName))); - + mUndo->setIcon(QIcon(QString::fromStdString(":./menu-undo.png"))); edit->addAction (mUndo); mRedo = mDocument->getUndoStack().createRedoAction (this, tr("Redo")); connect(mRedo, SIGNAL (changed ()), this, SLOT (redoActionChanged ())); setupShortcut("document-edit-redo", mRedo); - iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Redo).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - mRedo->setIcon(QIcon(QString::fromStdString(iconName))); - + mRedo->setIcon(QIcon(QString::fromStdString(":./menu-redo.png"))); edit->addAction (mRedo); - QAction* userSettings = createMenuEntry(CSMWorld::UniversalId::Type_Preferences, edit, "document-edit-preferences"); + QAction* userSettings = createMenuEntry("Preferences", ":./menu-preferences.png", edit, "document-edit-preferences"); connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); QAction* search = createMenuEntry(CSMWorld::UniversalId::Type_Search, edit, "document-edit-search"); @@ -138,10 +132,10 @@ void CSVDoc::View::setupViewMenu() { QMenu *view = menuBar()->addMenu (tr ("View")); - QAction *newWindow = createMenuEntry(CSMWorld::UniversalId::Type_NewWindow, view, "document-view-newview"); + QAction *newWindow = createMenuEntry("New View", ":./menu-new-window.png", view, "document-view-newview"); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - mShowStatusBar = createMenuEntry(CSMWorld::UniversalId::Type_StatusBar, view, "document-view-statusbar"); + mShowStatusBar = createMenuEntry("Toggle Status Bar", ":./menu-status-bar.png", view, "document-view-statusbar"); connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool))); mShowStatusBar->setCheckable (true); mShowStatusBar->setChecked (CSMPrefs::get()["Windows"]["show-statusbar"].isTrue()); @@ -248,7 +242,7 @@ void CSVDoc::View::setupAssetsMenu() { QMenu *assets = menuBar()->addMenu (tr ("Assets")); - QAction* reload = createMenuEntry(CSMWorld::UniversalId::Type_Reload, assets, "document-assets-reload"); + QAction* reload = createMenuEntry("Reload", ":./menu-reload.png", assets, "document-assets-reload"); connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged())); assets->addSeparator(); @@ -299,11 +293,9 @@ void CSVDoc::View::setupDebugMenu() QAction *runDebug = debug->addMenu (mGlobalDebugProfileMenu); runDebug->setText (tr ("Run OpenMW")); setupShortcut("document-debug-run", runDebug); - std::string iconName = CSMWorld::UniversalId (CSMWorld::UniversalId::Type_RunGame).getIcon(); - if (!iconName.empty() && iconName != ":placeholder") - runDebug->setIcon(QIcon(QString::fromStdString(iconName))); + runDebug->setIcon(QIcon(QString::fromStdString(":./run-openmw.png"))); - QAction* stopDebug = createMenuEntry(CSMWorld::UniversalId::Type_StopGame, debug, "document-debug-shutdown"); + QAction* stopDebug = createMenuEntry("Stop OpenMW", ":./stop-openmw.png", debug, "document-debug-shutdown"); connect (stopDebug, SIGNAL (triggered()), this, SLOT (stop())); mStopDebug = stopDebug; @@ -325,6 +317,18 @@ QAction* CSVDoc::View::createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* return entry; } +QAction* CSVDoc::View::createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName) +{ + QAction *entry = new QAction(QString::fromStdString(title), this); + setupShortcut(shortcutName, entry); + if (!iconName.empty() && iconName != ":placeholder") + entry->setIcon(QIcon(QString::fromStdString(iconName))); + + menu->addAction (entry); + + return entry; +} + void CSVDoc::View::setupUi() { setupFileMenu(); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index e767777d7..76c81b964 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -67,6 +67,7 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); QAction* createMenuEntry(CSMWorld::UniversalId::Type type, QMenu* menu, const char* shortcutName); + QAction* createMenuEntry(const std::string& title, const std::string& iconName, QMenu* menu, const char* shortcutName); void setupFileMenu();