diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fde57f39..b1f00cccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,8 @@ Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused Bug #4497: File names starting with x or X are not classified as animation Bug #4503: Cast and ExplodeSpell commands increase alteration skill + Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute + Bug #4519: Knockdown does not discard movement in the 1st-person mode Feature #2606: Editor: Implemented (optional) case sensitive global search Feature #3083: Play animation when NPC is casting spell via script Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results @@ -78,7 +80,10 @@ Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically Feature #4444: Per-group KF-animation files support Feature #4466: Editor: Add option to ignore "Base" records when running verifier + Feature #4488: Make water shader rougher during rain Feature #4012: Editor: Write a log file if OpenCS crashes + Feature #4509: Show count of enchanted items in stack in the spells list + Feature #4512: Editor: Use markers for lights and creatures levelled lists Task #2490: Don't open command prompt window on Release-mode builds automatically 0.44.0 diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 204bf4c2f..961b6c1c1 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -102,6 +102,14 @@ void CSVRender::Object::update() if (recordType == CSMWorld::UniversalId::Type_Light) { light = &dynamic_cast& >(referenceables.getRecord(index)).get(); + if (model.empty()) + model = "marker_light.nif"; + } + + if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList) + { + if (model.empty()) + model = "marker_creature.nif"; } if (model.empty()) diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index 9db16d593..f0c364bc2 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -32,7 +32,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps () while (it != mIcons.end()) { - mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) ); + mPixmaps.push_back (std::make_pair (it->mValue, it->mIcon.pixmap (mIconSize) ) ); ++it; } } @@ -142,9 +142,23 @@ void CSVWorld::DataDisplayDelegate::settingChanged (const CSMPrefs::Setting *set void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, const QString& enumName, const QString& iconFilename) { - mIcons.push_back (std::make_pair(enumValue, QIcon(iconFilename))); EnumDelegateFactory::add(enumValue, enumName); + Icon icon; + icon.mValue = enumValue; + icon.mName = enumName; + icon.mIcon = QIcon(iconFilename); + + for (auto it=mIcons.begin(); it!=mIcons.end(); ++it) + { + if (it->mName > enumName) + { + mIcons.insert(it, icon); + return; + } + } + + mIcons.push_back(icon); } CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index 540216d78..f8e775369 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -11,11 +11,18 @@ namespace CSMPrefs namespace CSVWorld { + struct Icon + { + int mValue; + QIcon mIcon; + QString mName; + }; + class DataDisplayDelegate : public EnumDelegate { public: - typedef std::vector < std::pair < int, QIcon > > IconList; + typedef std::vector IconList; typedef std::vector > ValueList; protected: diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index edac34db2..dc630178a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -652,6 +652,9 @@ namespace MWClass float Creature::getSpeed(const MWWorld::Ptr &ptr) const { MWMechanics::CreatureStats& stats = getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) + return 0.f; + const GMST& gmst = getGmst(); float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01f * stats.getAttribute(ESM::Attribute::Speed).getModified() diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index b069c9037..433ccc8ed 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1096,6 +1096,10 @@ namespace MWClass float Npc::getSpeed(const MWWorld::Ptr& ptr) const { + const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) + return 0.f; + const MWBase::World *world = MWBase::Environment::get().getWorld(); const GMST& gmst = getGmst(); @@ -1104,8 +1108,8 @@ namespace MWClass const float normalizedEncumbrance = getNormalizedEncumbrance(ptr); - bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); - bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); + bool sneaking = stats.getStance(MWMechanics::CreatureStats::Stance_Sneak); + bool running = stats.getStance(MWMechanics::CreatureStats::Stance_Run); float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat()); diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index a73d343f9..a2710b633 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -80,6 +80,7 @@ namespace MWGui newSpell.mSelected = (MWBase::Environment::get().getWindowManager()->getSelectedSpell() == spell->mId); newSpell.mActive = true; + newSpell.mCount = 1; mSpells.push_back(newSpell); } @@ -109,6 +110,7 @@ namespace MWGui newSpell.mItem = item; newSpell.mId = item.getCellRef().getRefId(); newSpell.mName = item.getClass().getName(item); + newSpell.mCount = item.getRefData().getCount(); newSpell.mType = Spell::Type_EnchantedItem; newSpell.mSelected = invStore.getSelectedEnchantItem() == it; diff --git a/apps/openmw/mwgui/spellmodel.hpp b/apps/openmw/mwgui/spellmodel.hpp index 6b10f7127..8e29707ae 100644 --- a/apps/openmw/mwgui/spellmodel.hpp +++ b/apps/openmw/mwgui/spellmodel.hpp @@ -20,6 +20,7 @@ namespace MWGui std::string mCostColumn; // Cost/chance or Cost/charge std::string mId; // Item ID or spell ID MWWorld::Ptr mItem; // Only for Type_EnchantedItem + int mCount; // Only for Type_EnchantedItem bool mSelected; // Is this the currently selected spell/item (only one can be selected at a time) bool mActive; // (Items only) is the item equipped? diff --git a/apps/openmw/mwgui/spellview.cpp b/apps/openmw/mwgui/spellview.cpp index 4268b33a0..758e6b306 100644 --- a/apps/openmw/mwgui/spellview.cpp +++ b/apps/openmw/mwgui/spellview.cpp @@ -7,6 +7,8 @@ #include +#include "tooltips.hpp" + namespace MWGui { @@ -103,11 +105,12 @@ namespace MWGui } const std::string skin = spell.mActive ? "SandTextButton" : "SpellTextUnequipped"; + const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount); Gui::SharedStateButton* t = mScrollView->createWidget(skin, MyGUI::IntCoord(0, 0, 0, spellHeight), MyGUI::Align::Left | MyGUI::Align::Top); t->setNeedKeyFocus(true); - t->setCaption(spell.mName); + t->setCaption(spell.mName + captionSuffix); t->setTextAlign(MyGUI::Align::Left); adjustSpellWidget(spell, i, t); @@ -163,7 +166,8 @@ namespace MWGui // more checking for major change. const Spell& spell = mModel->getItem(spellIndex); - if (nameButton->getCaption() != spell.mName) + const std::string captionSuffix = MWGui::ToolTips::getCountString(spell.mCount); + if (nameButton->getCaption() != (spell.mName + captionSuffix)) { fullUpdateRequired = true; break; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5e9f0c925..56a145522 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1289,12 +1289,17 @@ bool CharacterController::updateWeaponState() && mUpperBodyState != UpperCharState_UnEquipingWeap && !isStillWeapon) { - // Note: we do not disable unequipping animation automatically to avoid body desync - getWeaponGroup(mWeaponType, weapgroup); - mAnimation->play(weapgroup, priorityWeapon, - MWRender::Animation::BlendMask_All, false, - 1.0f, "unequip start", "unequip stop", 0.0f, 0); - mUpperBodyState = UpperCharState_UnEquipingWeap; + // We can not play un-equip animation when we switch to HtH + // because we already un-equipped weapon + if (weaptype != WeapType_HandToHand || mWeaponType == WeapType_Spell) + { + // Note: we do not disable unequipping animation automatically to avoid body desync + getWeaponGroup(mWeaponType, weapgroup); + mAnimation->play(weapgroup, priorityWeapon, + MWRender::Animation::BlendMask_All, false, + 1.0f, "unequip start", "unequip stop", 0.0f, 0); + mUpperBodyState = UpperCharState_UnEquipingWeap; + } if(!downSoundId.empty()) { diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index ce329dbfe..ac93b5b9a 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -168,7 +168,7 @@ namespace MWMechanics int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); - float currentToBaseRatio = (fatigue.getCurrent() / fatigue.getBase()); + float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; fatigue.setModified(fatigue.getModified() + diff, 0); fatigue.setCurrent(fatigue.getBase() * currentToBaseRatio); setFatigue(fatigue); diff --git a/components/crashcatcher/crashcatcher.cpp b/components/crashcatcher/crashcatcher.cpp index af42eb695..e5377b64f 100644 --- a/components/crashcatcher/crashcatcher.cpp +++ b/components/crashcatcher/crashcatcher.cpp @@ -32,6 +32,10 @@ namespace bfs = boost::filesystem; #include #endif +#if defined(__APPLE__) +#include +#endif + #define UNUSED(x) (void)(x) static const char crash_switch[] = "--cc-handle-crash"; @@ -460,6 +464,7 @@ int crashCatcherInstallHandlers(int argc, char **argv, int num_signals, int *sig static bool is_debugger_present() { +#if !defined (__APPLE__) bfs::ifstream file((bfs::path("/proc/self/status"))); while (!file.eof()) { @@ -472,6 +477,35 @@ static bool is_debugger_present() } } return false; +#else + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + + return (info.kp_proc.p_flag & P_TRACED) != 0; +#endif } void crashCatcherInstall(int argc, char **argv, const std::string &crashLogPath) diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 0b220c795..75113755e 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -13,14 +13,19 @@ const float BIG_WAVES_Y = 0.1; const float MID_WAVES_X = 0.1; // strength of middle sized waves const float MID_WAVES_Y = 0.1; +const float MID_WAVES_RAIN_X = 0.2; +const float MID_WAVES_RAIN_Y = 0.2; const float SMALL_WAVES_X = 0.1; // strength of small waves const float SMALL_WAVES_Y = 0.1; +const float SMALL_WAVES_RAIN_X = 0.3; +const float SMALL_WAVES_RAIN_Y = 0.3; const float WAVE_CHOPPYNESS = 0.05; // wave choppyness const float WAVE_SCALE = 75.0; // overall wave scale const float BUMP = 0.5; // overall water surface bumpiness +const float BUMP_RAIN = 2.5; const float REFL_BUMP = 0.10; // reflection distortion amount const float REFR_BUMP = 0.07; // refraction distortion amount @@ -183,22 +188,27 @@ void main(void) vec3 rippleAdd = rainRipple.xyz * rainRipple.w * 10.0; - vec3 normal = (normal0 * BIG_WAVES_X + normal1 * BIG_WAVES_Y + - normal2 * MID_WAVES_X + normal3 * MID_WAVES_Y + - normal4 * SMALL_WAVES_X + normal5 * SMALL_WAVES_Y + + vec2 bigWaves = vec2(BIG_WAVES_X,BIG_WAVES_Y); + vec2 midWaves = mix(vec2(MID_WAVES_X,MID_WAVES_Y),vec2(MID_WAVES_RAIN_X,MID_WAVES_RAIN_Y),rainIntensity); + vec2 smallWaves = mix(vec2(SMALL_WAVES_X,SMALL_WAVES_Y),vec2(SMALL_WAVES_RAIN_X,SMALL_WAVES_RAIN_Y),rainIntensity); + float bump = mix(BUMP,BUMP_RAIN,rainIntensity); + + vec3 normal = (normal0 * bigWaves.x + normal1 * bigWaves.y + + normal2 * midWaves.x + normal3 * midWaves.y + + normal4 * smallWaves.x + normal5 * smallWaves.y + rippleAdd); - normal = normalize(vec3(normal.x * BUMP, normal.y * BUMP, normal.z)); + normal = normalize(vec3(normal.x * bump, normal.y * bump, normal.z)); normal = vec3(-normal.x, -normal.y, normal.z); // normal for sunlight scattering - vec3 lNormal = (normal0 * BIG_WAVES_X*0.5 + normal1 * BIG_WAVES_Y*0.5 + - normal2 * MID_WAVES_X*0.2 + normal3 * MID_WAVES_Y*0.2 + - normal4 * SMALL_WAVES_X*0.1 + normal5 * SMALL_WAVES_Y*0.1 + + vec3 lNormal = (normal0 * bigWaves.x * 0.5 + normal1 * bigWaves.y * 0.5 + + normal2 * midWaves.x * 0.2 + normal3 * midWaves.y * 0.2 + + normal4 * smallWaves.x * 0.1 + normal5 * smallWaves.y * 0.1 + rippleAdd).xyz; - lNormal = normalize(vec3(lNormal.x * BUMP, lNormal.y * BUMP, lNormal.z)); + lNormal = normalize(vec3(lNormal.x * bump, lNormal.y * bump, lNormal.z)); lNormal = vec3(-lNormal.x, -lNormal.y, lNormal.z); vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz); diff --git a/readthedocs.yml b/readthedocs.yml new file mode 100644 index 000000000..e53e54b78 --- /dev/null +++ b/readthedocs.yml @@ -0,0 +1,2 @@ +# Don't build any extra formats +formats: [] \ No newline at end of file