From 5468fcb29f0636bd82b56b4bb1f9a8a72f059098 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Dec 2018 15:18:33 +0400 Subject: [PATCH 01/16] Store attributes and skills values as floats (bug #4021) --- CHANGELOG.md | 1 + apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 10 ++--- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwgui/jailscreen.cpp | 4 +- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 8 ++-- apps/openmw/mwmechanics/alchemy.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 ++--- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 14 +++--- apps/openmw/mwmechanics/npcstats.cpp | 10 ++--- apps/openmw/mwmechanics/pickpocket.cpp | 4 +- apps/openmw/mwmechanics/repair.cpp | 6 +-- apps/openmw/mwmechanics/security.cpp | 4 +- apps/openmw/mwmechanics/spellresistance.cpp | 4 +- apps/openmw/mwmechanics/spellutil.cpp | 4 +- apps/openmw/mwmechanics/stat.cpp | 22 ++++----- apps/openmw/mwmechanics/stat.hpp | 22 ++++----- apps/openmw/mwscript/statsextensions.cpp | 24 +++++----- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 6 +-- components/compiler/extensions0.cpp | 12 ++--- components/esm/creaturestats.cpp | 3 +- components/esm/creaturestats.hpp | 2 +- components/esm/npcstats.cpp | 13 +++--- components/esm/npcstats.hpp | 2 +- components/esm/player.cpp | 5 ++- components/esm/player.hpp | 4 +- components/esm/savedgame.cpp | 2 +- components/esm/statstate.cpp | 45 ++++++++++++++----- components/esm/statstate.hpp | 2 +- 41 files changed, 153 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..298220699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4021: Attributes and skills are not stored as floats Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e649bba12..3f9bfb859 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -276,7 +276,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); - int armorSkill = actor.getClass().getSkill(actor, armorSkillType); + float armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 375b70dde..3feb25ca7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -605,7 +605,7 @@ namespace MWClass float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return static_cast(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5); + return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5; } int Creature::getServices(const MWWorld::ConstPtr &actor) const @@ -745,7 +745,7 @@ namespace MWClass throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } - int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const + float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 9be5c4272..3288c0d11 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -108,7 +108,7 @@ namespace MWClass virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bd61131bf..a007ad115 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -125,7 +125,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index df483962a..5efc96ca1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -127,8 +127,8 @@ namespace } // initial health - int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); - int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); + float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int multiplier = 3; @@ -1011,7 +1011,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); + float a = getSkill(ptr, ESM::Skill::Acrobatics); float b = 0.0f; if(a > 50.0f) { @@ -1136,7 +1136,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); + float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) @@ -1283,7 +1283,7 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { return getNpcStats(ptr).getSkill(skill).getModified(); } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3d63697fb..ae4f32d13 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -129,7 +129,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 4a89304b3..67124884f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -95,9 +95,9 @@ namespace MWGui MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) - value.setBase(std::min(100, value.getBase()+1)); + value.setBase(std::min(100.f, value.getBase()+1)); else - value.setBase(std::max(0, value.getBase()-1)); + value.setBase(std::max(0.f, value.getBase()-1)); } const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eb165254a..86f92db6f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -157,7 +157,7 @@ namespace MWGui mAttributeValues[i]->setEnabled(true); availableAttributes++; - int mult = pcStats.getLevelupAttributeMultiplier (i); + float mult = pcStats.getLevelupAttributeMultiplier (i); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 2a167be2d..b4de5cb50 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -22,7 +22,7 @@ namespace MWGui { MWWorld::Ptr player = MWMechanics::getPlayer(); mSourceModel = sourceModel; - int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); + float chance = player.getClass().getSkill(player, ESM::Skill::Sneak); mSourceModel->update(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 8998e3a80..4959396cc 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -159,7 +159,7 @@ namespace MWGui for (int i=0; ids[i]; ++i) if (ids[i]==id) { - setText (id, std::to_string(value.getModified())); + setText (id, std::to_string(static_cast(value.getModified()))); MyGUI::TextBox* box; getWidget(box, id); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index acc2ef72a..e4e4bae5a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -74,11 +74,11 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); // NPC can train you in his best 3 skills - std::vector< std::pair > skills; + std::vector< std::pair > skills; for (int i=0; i& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); health = 0.1f * endurance; float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); @@ -765,7 +765,7 @@ namespace MWMechanics { CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); - int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); + float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float base = 1.f; if (ptr == getPlayer()) @@ -844,7 +844,7 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) @@ -871,7 +871,7 @@ namespace MWMechanics return; // Restore fatigue - int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); + float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b490db436..116937fcd 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); + float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a332a10c..e4f870ed0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2128,7 +2128,7 @@ void CharacterController::update(float duration, bool animationOnly) cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } - const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); + const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { if (!godmode) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9698892e4..183845b8c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -101,7 +101,7 @@ namespace MWMechanics blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); - int attackerSkill = 0; + float attackerSkill = 0; if (weapon.isEmpty()) attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); else diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a64fb087c..0f11b8b2e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -126,7 +126,7 @@ namespace MWMechanics return mMagicEffects; } - void CreatureStats::setAttribute(int index, int base) + void CreatureStats::setAttribute(int index, float base) { AttributeValue current = getAttribute(index); current.setBase(base); @@ -152,10 +152,10 @@ namespace MWMechanics index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) { - int strength = getAttribute(ESM::Attribute::Strength).getModified(); - int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); - int agility = getAttribute(ESM::Attribute::Agility).getModified(); - int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); + float strength = getAttribute(ESM::Attribute::Strength).getModified(); + float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); + float agility = getAttribute(ESM::Attribute::Agility).getModified(); + float endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5a5dd3f12..b35c1e3b6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -138,7 +138,7 @@ namespace MWMechanics void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base - void setAttribute(int index, int base); + void setAttribute(int index, float base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5376ec86e..6933907db 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -688,10 +688,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); + float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); + float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); @@ -1621,8 +1621,8 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); - int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); - int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { @@ -1645,10 +1645,10 @@ namespace MWMechanics float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); - int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); - int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); - int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); + float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ee48ea7d5..1e9003a2f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { - int base = getSkill (skillIndex).getBase(); + float base = getSkill (skillIndex).getBase(); - if (base >= 100) + if (base >= 100.f) return; base += 1; @@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp() for (int i=0; i(stats.getAttribute(ESM::Attribute::Agility).getModified()); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index faa0e3b09..389d00d85 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -32,9 +32,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); float fatigueTerm = stats.getFatigueTerm(); - int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); - int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); + float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index ab286cbee..001375feb 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -19,8 +19,8 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); - mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index a187600fb..1edf14091 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -40,8 +40,8 @@ namespace MWMechanics float resistance = getEffectResistanceAttribute(effectId, magicEffects); - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index bb4953e48..8b2f5c46c 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -94,8 +94,8 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 6a559a361..7f71cf9b1 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -227,29 +227,29 @@ namespace MWMechanics } AttributeValue::AttributeValue() : - mBase(0), mModifier(0), mDamage(0) + mBase(0.f), mModifier(0.f), mDamage(0.f) { } - int AttributeValue::getModified() const + float AttributeValue::getModified() const { - return std::max(0, mBase - (int) mDamage + mModifier); + return std::max(0.f, mBase - mDamage + mModifier); } - int AttributeValue::getBase() const + float AttributeValue::getBase() const { return mBase; } - int AttributeValue::getModifier() const + float AttributeValue::getModifier() const { return mModifier; } - void AttributeValue::setBase(int base) + void AttributeValue::setBase(float base) { mBase = base; } - void AttributeValue::setModifier(int mod) + void AttributeValue::setModifier(float mod) { mModifier = mod; } @@ -275,14 +275,14 @@ namespace MWMechanics return mDamage; } - void AttributeValue::writeState (ESM::StatState& state) const + void AttributeValue::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModifier; state.mDamage = mDamage; } - void AttributeValue::readState (const ESM::StatState& state) + void AttributeValue::readState (const ESM::StatState& state) { mBase = state.mBase; mModifier = state.mMod; @@ -303,13 +303,13 @@ namespace MWMechanics mProgress = progress; } - void SkillValue::writeState (ESM::StatState& state) const + void SkillValue::writeState (ESM::StatState& state) const { AttributeValue::writeState (state); state.mProgress = mProgress; } - void SkillValue::readState (const ESM::StatState& state) + void SkillValue::readState (const ESM::StatState& state) { AttributeValue::readState (state); mProgress = state.mProgress; diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 751b80b9c..5f49da48e 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -122,20 +122,20 @@ namespace MWMechanics class AttributeValue { - int mBase; - int mModifier; + float mBase; + float mModifier; float mDamage; // needs to be float to allow continuous damage public: AttributeValue(); - int getModified() const; - int getBase() const; - int getModifier() const; + float getModified() const; + float getBase() const; + float getModifier() const; - void setBase(int base); + void setBase(float base); - void setModifier(int mod); + void setModifier(float mod); // Maximum attribute damage is limited to the modified value. // Note: I think MW applies damage directly to mModified, since you can also @@ -145,8 +145,8 @@ namespace MWMechanics float getDamage() const; - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -157,8 +157,8 @@ namespace MWMechanics float getProgress() const; void setProgress(float progress); - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8ba0cdcf2..37751c6d4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -95,7 +95,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = + Interpreter::Type_Float value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) @@ -118,7 +118,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); @@ -140,7 +140,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() @@ -155,9 +155,9 @@ namespace MWScript return; if (value < 0) - attribute.setBase(std::max(0, attribute.getBase() + value)); + attribute.setBase(std::max(0.f, attribute.getBase() + value)); else - attribute.setBase(std::min(100, attribute.getBase() + value)); + attribute.setBase(std::min(100.f, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -345,7 +345,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); + Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } @@ -364,7 +364,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); @@ -386,7 +386,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::SkillValue &skill = ptr.getClass() @@ -396,14 +396,14 @@ namespace MWScript if (value == 0) return; - if (((skill.getBase() <= 0) && (value < 0)) - || ((skill.getBase() >= 100) && (value > 0))) + if (((skill.getBase() <= 0.f) && (value < 0.f)) + || ((skill.getBase() >= 100.f) && (value > 0.f))) return; if (value < 0) - skill.setBase(std::max(0, skill.getBase() + value)); + skill.setBase(std::max(0.f, skill.getBase() + value)); else - skill.setBase(std::min(100, skill.getBase() + value)); + skill.setBase(std::min(100.f, skill.getBase() + value)); } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 07981bf2a..d7ee59ee2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -421,7 +421,7 @@ namespace MWWorld return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } - int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fb816d810..fd679c43f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -321,7 +321,7 @@ namespace MWWorld bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index a2bd84953..ada211470 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots // rate weapon for (int i = 0; i < static_cast(weaponSkillsLength); ++i) { - int max = 0; + float max = 0; int maxWeaponSkill = -1; for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + float skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { max = skillValue; @@ -399,7 +399,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c67af29c2..60b4a7451 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -423,13 +423,13 @@ namespace Compiler for (int i=0; i mAttributes[Attribute::Length]; + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index c5fa2a09e..277335e8c 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -31,8 +31,9 @@ void ESM::NpcStats::load (ESMReader &esm) mDisposition = 0; esm.getHNOT (mDisposition, "DISP"); + bool intFallback = esm.getFormat() < 11; for (int i=0; i<27; ++i) - mSkills[i].load (esm); + mSkills[i].load (esm, intFallback); mWerewolfDeprecatedData = false; if (esm.getFormat() < 8 && esm.peekNextSub("STBA")) @@ -40,17 +41,17 @@ void ESM::NpcStats::load (ESMReader &esm) // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value mWerewolfDeprecatedData = true; - std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); + std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); for (int i=0; i<27; ++i) { - ESM::StatState skill; - skill.load(esm); + ESM::StatState skill; + skill.load(esm, intFallback); skills.push_back(skill); } int i=0; - for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) + for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) { if (i%2 == 1) it = skills.erase(it); @@ -68,7 +69,7 @@ void ESM::NpcStats::load (ESMReader &esm) { ESM::StatState dummy; for (int i=0; i<8; ++i) - dummy.load(esm); + dummy.load(esm, intFallback); mWerewolfDeprecatedData = true; } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 467a099ce..3ad94b543 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -31,7 +31,7 @@ namespace ESM std::map mFactions; // lower case IDs int mDisposition; - StatState mSkills[27]; + StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 571a10a8c..3850390e9 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -43,12 +43,13 @@ void ESM::Player::load (ESMReader &esm) checkPrevItems = false; } + bool intFallback = esm.getFormat() < 11; if (esm.hasMoreSubs()) { for (int i=0; i mSaveAttributes[ESM::Attribute::Length]; - StatState mSaveSkills[ESM::Skill::Length]; + StatState mSaveAttributes[ESM::Attribute::Length]; + StatState mSaveSkills[ESM::Skill::Length]; typedef std::map PreviousItems; // previous equipped items, needed for bound spells PreviousItems mPreviousItems; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..063ae86ed 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 10; +int ESM::SavedGame::sCurrentFormat = 11; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/statstate.cpp b/components/esm/statstate.cpp index c17bedd81..b9ddc3efd 100644 --- a/components/esm/statstate.cpp +++ b/components/esm/statstate.cpp @@ -9,19 +9,44 @@ namespace ESM StatState::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {} template - void StatState::load(ESMReader &esm) + void StatState::load(ESMReader &esm, bool intFallback) { - esm.getHNT(mBase, "STBA"); + // We changed stats values from integers to floats; ensure backwards compatibility + if (intFallback) + { + int base = 0; + esm.getHNT(base, "STBA"); + mBase = static_cast(base); - mMod = 0; - esm.getHNOT(mMod, "STMO"); - mCurrent = 0; - esm.getHNOT(mCurrent, "STCU"); + int mod = 0; + esm.getHNOT(mod, "STMO"); + mMod = static_cast(mod); - // mDamage was changed to a float; ensure backwards compatibility - T oldDamage = 0; - esm.getHNOT(oldDamage, "STDA"); - mDamage = static_cast(oldDamage); + int current = 0; + esm.getHNOT(current, "STCU"); + mCurrent = static_cast(current); + + // mDamage was changed to a float; ensure backwards compatibility + int oldDamage = 0; + esm.getHNOT(oldDamage, "STDA"); + mDamage = static_cast(oldDamage); + } + else + { + mBase = 0; + esm.getHNT(mBase, "STBA"); + + mMod = 0; + esm.getHNOT(mMod, "STMO"); + + mCurrent = 0; + esm.getHNOT(mCurrent, "STCU"); + + mDamage = 0; + esm.getHNOT(mDamage, "STDF"); + + mProgress = 0; + } esm.getHNOT(mDamage, "STDF"); diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 47aeb0331..d81d24a61 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -20,7 +20,7 @@ namespace ESM StatState(); - void load (ESMReader &esm); + void load (ESMReader &esm, bool intFallback = false); void save (ESMWriter &esm) const; }; } From d35ccc39c6f95b0957e3e8b943f7694559b8f3c5 Mon Sep 17 00:00:00 2001 From: elsid Date: Mon, 1 Jun 2020 13:51:56 +0200 Subject: [PATCH 02/16] Fix build tests with double precision bullet --- apps/openmw/CMakeLists.txt | 4 ---- components/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..a6bd85b1d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -15,10 +15,6 @@ set(GAME_HEADER engine.hpp ) -if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) -endif() - source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f626fe714..507b25cd1 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -287,5 +287,5 @@ endif() set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) if (BULLET_USE_DOUBLES) - add_definitions(-DBT_USE_DOUBLE_PRECISION) + target_compile_definitions(components PUBLIC BT_USE_DOUBLE_PRECISION) endif() From 86c1d0f4beffe86f367a74fd32b53d9fca5a4343 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:36:55 +0000 Subject: [PATCH 03/16] Warn about fake stub Python --- CI/before_script.msvc.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 2f40aef9c..8f70e5369 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -13,7 +13,16 @@ MISSINGTOOLS=0 command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } -command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; } + +MISSINGPYTHON=0 +if ! command -v python >/dev/null 2>&1; then + echo "Warning: Python is not on the path, automatic Qt installation impossible." + MISSINGPYTHON=1 +elif ! python --version >/dev/null 2>&1; then + echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible." + echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings." + MISSINGPYTHON=1 +fi if [ $MISSINGTOOLS -ne 0 ]; then wrappedExit 1 @@ -745,6 +754,11 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then + if [ $MISSINGTOOLS -ne 0 ]; then + echo "Can't be automatically installed without Python." + wrappedExit 1 + fi + pushd "$DEPS" > /dev/null if ! [ -d 'aqt-venv' ]; then echo " Creating Virtualenv for aqt..." From 6e267e398e642e8cd200dd6cbcd2eb7c85ecaeb2 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 3 Jun 2020 22:38:08 +0000 Subject: [PATCH 04/16] Fix copy-paste snafu --- CI/before_script.msvc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_script.msvc.sh b/CI/before_script.msvc.sh index 8f70e5369..75bde1f81 100644 --- a/CI/before_script.msvc.sh +++ b/CI/before_script.msvc.sh @@ -754,7 +754,7 @@ fi if [ -d 'Qt/5.15.0' ]; then printf "Exists. " elif [ -z $SKIP_EXTRACT ]; then - if [ $MISSINGTOOLS -ne 0 ]; then + if [ $MISSINGPYTHON -ne 0 ]; then echo "Can't be automatically installed without Python." wrappedExit 1 fi From da8ea9d8c7a94326307ec1122572d54caee33c09 Mon Sep 17 00:00:00 2001 From: elsid Date: Tue, 2 Jun 2020 21:30:46 +0200 Subject: [PATCH 05/16] Mark not changing AiPackages fields as const --- apps/openmw/mwmechanics/aiactivate.hpp | 2 +- apps/openmw/mwmechanics/aiavoiddoor.hpp | 2 +- apps/openmw/mwmechanics/aicast.cpp | 18 ++++++++--- apps/openmw/mwmechanics/aicast.hpp | 8 ++--- apps/openmw/mwmechanics/aiescort.cpp | 13 +++----- apps/openmw/mwmechanics/aiescort.hpp | 16 ++++----- apps/openmw/mwmechanics/aiface.hpp | 3 +- apps/openmw/mwmechanics/aifollow.cpp | 13 ++++---- apps/openmw/mwmechanics/aifollow.hpp | 16 ++++----- apps/openmw/mwmechanics/aitravel.hpp | 8 ++--- apps/openmw/mwmechanics/aiwander.cpp | 43 +++++++++++++------------ apps/openmw/mwmechanics/aiwander.hpp | 12 +++---- 12 files changed, 80 insertions(+), 74 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a9e3d6d8..5a96f4cdb 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -34,7 +34,7 @@ namespace MWMechanics void writeState(ESM::AiSequence::AiSequence& sequence) const final; private: - std::string mObjectId; + const std::string mObjectId; }; } #endif // GAME_MWMECHANICS_AIACTIVATE_H diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index cc02c4de1..fdbf7ebc7 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -33,7 +33,7 @@ namespace MWMechanics private: float mDuration; - MWWorld::ConstPtr mDoorPtr; + const MWWorld::ConstPtr mDoorPtr; osg::Vec3f mLastPos; int mDirection; diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index de61851cd..cc4c03bf1 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -10,12 +10,22 @@ #include "creaturestats.hpp" #include "steering.hpp" +namespace MWMechanics +{ + namespace + { + float getInitialDistance(const std::string& spellId) + { + ActionSpell action = ActionSpell(spellId); + bool isRanged; + return action.getCombatRange(isRanged); + } + } +} + MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) - : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0) + : mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId)) { - ActionSpell action = ActionSpell(spellId); - bool isRanged; - mDistance = action.getCombatRange(isRanged); } bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index 21b629f24..cdf7db2bf 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -27,11 +27,11 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - std::string mTargetId; - std::string mSpellId; + const std::string mTargetId; + const std::string mSpellId; bool mCasting; - bool mManual; - float mDistance; + const bool mManual; + const float mDistance; }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 5e1d38331..216547f58 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -26,7 +26,6 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z) @@ -35,24 +34,20 @@ namespace MWMechanics , mCellY(std::numeric_limits::max()) { mTargetActorRefId = actorId; - mMaxDist = 450; } AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort) : mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ) - , mMaxDist(450) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(escort->mRemainingDuration > 0) , mRemainingDuration(escort->mRemainingDuration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { mTargetActorRefId = escort->mTargetId; mTargetActorId = escort->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 9f5ac2f42..c12de1fac 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -45,16 +45,16 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - std::string mCellId; - float mX; - float mY; - float mZ; - float mMaxDist; - float mDuration; // In hours + const std::string mCellId; + const float mX; + const float mY; + const float mZ; + float mMaxDist = 450; + const float mDuration; // In hours float mRemainingDuration; // In hours - int mCellX; - int mCellY; + const int mCellX; + const int mCellY; }; } #endif diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index ce1c9847b..516dd18dc 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -20,7 +20,8 @@ namespace MWMechanics bool shouldCancelPreviousAi() const final { return false; } private: - float mTargetX, mTargetY; + const float mTargetX; + const float mTargetY; }; } diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index bffa238d5..eb5b595ab 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -58,18 +58,17 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration) + : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. + // The exact value of mDuration only matters for repeating packages. + // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. + , mDuration(follow->mRemainingDuration) + , mRemainingDuration(follow->mRemainingDuration) , mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ) , mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = follow->mTargetId; mTargetActorId = follow->mTargetActorId; - // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. - // The exact value of mDuration only matters for repeating packages. - if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. - mDuration = 1; - else - mDuration = 0; } bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 39a10294b..865f4171b 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -86,16 +86,16 @@ namespace MWMechanics private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ - bool mAlwaysFollow; - bool mCommanded; - float mDuration; // Hours + const bool mAlwaysFollow; + const bool mCommanded; + const float mDuration; // Hours float mRemainingDuration; // Hours - float mX; - float mY; - float mZ; - std::string mCellId; + const float mX; + const float mY; + const float mZ; + const std::string mCellId; bool mActive; // have we spotted the target? - int mFollowIndex; + const int mFollowIndex; static int mFollowIndexCounter; }; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index beaf2819f..4f785e237 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -37,11 +37,11 @@ namespace MWMechanics osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } private: - float mX; - float mY; - float mZ; + const float mX; + const float mY; + const float mZ; - bool mHidden; + const bool mHidden; }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 50a46432b..584131bbe 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -1,5 +1,7 @@ #include "aiwander.hpp" +#include + #include #include #include @@ -33,6 +35,8 @@ namespace MWMechanics // distance must be long enough that NPC will need to move to get there. static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; + static const std::size_t MAX_IDLE_SIZE = 8; + const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { std::string("idle2"), @@ -94,25 +98,28 @@ namespace MWMechanics { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; } + + std::vector getInitialIdle(const std::vector& idle) + { + std::vector result(MAX_IDLE_SIZE, 0); + std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin()); + return result; + } + + std::vector getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE]) + { + return std::vector(std::begin(idle), std::end(idle)); + } } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): - mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), + mDistance(std::max(0, distance)), + mDuration(std::max(0, duration)), + mRemainingDuration(duration), mTimeOfDay(timeOfDay), + mIdle(getInitialIdle(idle)), mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { - mIdle.resize(8, 0); - init(); - } - - void AiWander::init() - { - // NOTE: mDistance and mDuration must be set already - - if(mDistance < 0) - mDistance = 0; - if(mDuration < 0) - mDuration = 0; } /* @@ -235,7 +242,6 @@ namespace MWMechanics stopWalking(actor); // Reset package so it can be used again mRemainingDuration=mDuration; - init(); return true; } @@ -879,10 +885,11 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(wander->mData.mDistance) - , mDuration(wander->mData.mDuration) + : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) + , mIdle(getInitialIdle(wander->mData.mIdle)) , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) @@ -891,11 +898,7 @@ namespace MWMechanics { if (mStoredInitialActorPosition) mInitialActorPosition = wander->mInitialActorPosition; - for (int i=0; i<8; ++i) - mIdle.push_back(wander->mData.mIdle[i]); if (mRemainingDuration <= 0 || mRemainingDuration >= 24) mRemainingDuration = mDuration; - - init(); } } diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index bb5872eef..8eb735205 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -114,8 +114,6 @@ namespace MWMechanics } private: - // NOTE: mDistance and mDuration must be set already - void init(); void stopWalking(const MWWorld::Ptr& actor); /// Have the given actor play an idle animation @@ -136,12 +134,12 @@ namespace MWMechanics bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); - int mDistance; // how far the actor can wander from the spawn point - int mDuration; + const int mDistance; // how far the actor can wander from the spawn point + const int mDuration; float mRemainingDuration; - int mTimeOfDay; - std::vector mIdle; - bool mRepeat; + const int mTimeOfDay; + const std::vector mIdle; + const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell From 81805b726337c8c2da9c104af72ecb428a86de0b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 3 Jun 2020 11:32:28 +0400 Subject: [PATCH 06/16] Introduce a separate class to control world date and time --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 19 +- apps/openmw/mwgui/waitdialog.cpp | 9 +- apps/openmw/mwstate/statemanagerimp.cpp | 6 +- apps/openmw/mwworld/datetimemanager.cpp | 227 ++++++++++++++++++++++++ apps/openmw/mwworld/datetimemanager.hpp | 43 +++++ apps/openmw/mwworld/globals.cpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 197 ++++---------------- apps/openmw/mwworld/worldimp.hpp | 30 +--- components/esm/defs.hpp | 8 + components/esm/savedgame.cpp | 1 - components/esm/savedgame.hpp | 12 +- 12 files changed, 338 insertions(+), 219 deletions(-) create mode 100644 apps/openmw/mwworld/datetimemanager.cpp create mode 100644 apps/openmw/mwworld/datetimemanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 68e3949ba..9072deb48 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -71,7 +71,7 @@ add_openmw_dir (mwworld actionequip timestamp actionalchemy cellstore actionapply actioneat store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager - cellpreloader + cellpreloader datetimemanager ) add_openmw_dir (mwphysics diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6bf4cbaae..627e2f2b7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -48,6 +48,7 @@ namespace ESM struct EffectList; struct CreatureLevList; struct ItemLevList; + struct TimeStamp; } namespace MWRender @@ -204,24 +205,14 @@ namespace MWBase virtual void advanceTime (double hours, bool incremental = false) = 0; ///< Advance in-game time. - virtual void setHour (double hour) = 0; - ///< Set in-game time hour. - - virtual void setMonth (int month) = 0; - ///< Set in-game time month. - - virtual void setDay (int day) = 0; - ///< Set in-game time day. - - virtual int getDay() const = 0; - virtual int getMonth() const = 0; - virtual int getYear() const = 0; - virtual std::string getMonthName (int month = -1) const = 0; ///< Return name of month (-1: current month) virtual MWWorld::TimeStamp getTimeStamp() const = 0; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0; + ///< Return current in-game date and time. virtual bool toggleSky() = 0; ///< \return Resulting mode diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 64f912298..18cc187c1 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -150,11 +150,10 @@ namespace MWGui if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - std::string dateTimeText = - MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " - + month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) - + ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); - + ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp(); + int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay(); + std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"; + std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour); mDateTimeText->setCaptionWithReplacing (dateTimeText); } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index db83f72c1..9974b8f16 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mPlayerClassId = classId; profile.mPlayerCell = world.getCellName(); - - profile.mInGameTime.mGameHour = world.getTimeStamp().getHour(); - profile.mInGameTime.mDay = world.getDay(); - profile.mInGameTime.mMonth = world.getMonth(); - profile.mInGameTime.mYear = world.getYear(); + profile.mInGameTime = world.getEpochTimeStamp(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; diff --git a/apps/openmw/mwworld/datetimemanager.cpp b/apps/openmw/mwworld/datetimemanager.cpp new file mode 100644 index 000000000..0894c974d --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.cpp @@ -0,0 +1,227 @@ +#include "datetimemanager.hpp" + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "esmstore.hpp" +#include "globals.hpp" +#include "timestamp.hpp" + +namespace +{ + static int getDaysPerMonth(int month) + { + switch (month) + { + case 0: return 31; + case 1: return 28; + case 2: return 31; + case 3: return 30; + case 4: return 31; + case 5: return 30; + case 6: return 31; + case 7: return 31; + case 8: return 30; + case 9: return 31; + case 10: return 30; + case 11: return 31; + } + + throw std::runtime_error ("month out of range"); + } +} + +namespace MWWorld +{ + void DateTimeManager::setup(Globals& globalVariables) + { + mGameHour = globalVariables["gamehour"].getFloat(); + mDaysPassed = globalVariables["dayspassed"].getInteger(); + mDay = globalVariables["day"].getInteger(); + mMonth = globalVariables["month"].getInteger(); + mYear = globalVariables["year"].getInteger(); + mTimeScale = globalVariables["timescale"].getFloat(); + } + + void DateTimeManager::setHour(double hour) + { + if (hour < 0) + hour = 0; + + int days = static_cast(hour / 24); + hour = std::fmod(hour, 24); + mGameHour = static_cast(hour); + + if (days > 0) + setDay(days + mDay); + } + + void DateTimeManager::setDay(int day) + { + if (day < 1) + day = 1; + + int month = mMonth; + while (true) + { + int days = getDaysPerMonth(month); + if (day <= days) + break; + + if (month < 11) + { + ++month; + } + else + { + month = 0; + mYear++; + } + + day -= days; + } + + mDay = day; + mMonth = month; + } + + TimeStamp DateTimeManager::getTimeStamp() const + { + return TimeStamp(mGameHour, mDaysPassed); + } + + float DateTimeManager::getTimeScaleFactor() const + { + return mTimeScale; + } + + ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const + { + ESM::EpochTimeStamp timeStamp; + timeStamp.mGameHour = mGameHour; + timeStamp.mDay = mDay; + timeStamp.mMonth = mMonth; + timeStamp.mYear = mYear; + return timeStamp; + } + + void DateTimeManager::setMonth(int month) + { + if (month < 0) + month = 0; + + int years = month / 12; + month = month % 12; + + int days = getDaysPerMonth(month); + if (mDay > days) + mDay = days; + + mMonth = month; + + if (years > 0) + mYear += years; + } + + void DateTimeManager::advanceTime(double hours, Globals& globalVariables) + { + hours += mGameHour; + setHour(hours); + + int days = static_cast(hours / 24); + if (days > 0) + mDaysPassed += days; + + globalVariables["gamehour"].setFloat(mGameHour); + globalVariables["dayspassed"].setInteger(mDaysPassed); + globalVariables["day"].setInteger(mDay); + globalVariables["month"].setInteger(mMonth); + globalVariables["year"].setInteger(mYear); + } + + std::string DateTimeManager::getMonthName(int month) const + { + if (month == -1) + month = mMonth; + + const int months = 12; + if (month < 0 || month >= months) + return std::string(); + + static const char *monthNames[months] = + { + "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", + "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", + "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" + }; + + const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get().find(monthNames[month]); + return setting->mValue.getString(); + } + + bool DateTimeManager::updateGlobalFloat(const std::string& name, float value) + { + if (name=="gamehour") + { + setHour(value); + return true; + } + else if (name=="day") + { + setDay(static_cast(value)); + return true; + } + else if (name=="month") + { + setMonth(static_cast(value)); + return true; + } + else if (name=="year") + { + mYear = static_cast(value); + } + else if (name=="timescale") + { + mTimeScale = value; + } + else if (name=="dayspassed") + { + mDaysPassed = static_cast(value); + } + + return false; + } + + bool DateTimeManager::updateGlobalInt(const std::string& name, int value) + { + if (name=="gamehour") + { + setHour(static_cast(value)); + return true; + } + else if (name=="day") + { + setDay(value); + return true; + } + else if (name=="month") + { + setMonth(value); + return true; + } + else if (name=="year") + { + mYear = value; + } + else if (name=="timescale") + { + mTimeScale = static_cast(value); + } + else if (name=="dayspassed") + { + mDaysPassed = value; + } + + return false; + } +} diff --git a/apps/openmw/mwworld/datetimemanager.hpp b/apps/openmw/mwworld/datetimemanager.hpp new file mode 100644 index 000000000..b460be746 --- /dev/null +++ b/apps/openmw/mwworld/datetimemanager.hpp @@ -0,0 +1,43 @@ +#ifndef GAME_MWWORLD_DATETIMEMANAGER_H +#define GAME_MWWORLD_DATETIMEMANAGER_H + +#include + +namespace ESM +{ + struct EpochTimeStamp; +} + +namespace MWWorld +{ + class Globals; + class TimeStamp; + + class DateTimeManager + { + int mDaysPassed = 0; + int mDay = 0; + int mMonth = 0; + int mYear = 0; + float mGameHour = 0.f; + float mTimeScale = 0.f; + + void setHour(double hour); + void setDay(int day); + void setMonth(int month); + + public: + std::string getMonthName(int month) const; + TimeStamp getTimeStamp() const; + ESM::EpochTimeStamp getEpochTimeStamp() const; + float getTimeScaleFactor() const; + + void advanceTime(double hours, Globals& globalVariables); + + void setup(Globals& globalVariables); + bool updateGlobalInt(const std::string& name, int value); + bool updateGlobalFloat(const std::string& name, float value); + }; +} + +#endif diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 69ec5563b..8a481334e 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -2,10 +2,9 @@ #include -#include - #include #include +#include #include "esmstore.hpp" diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index aee98e7ea..c51266bab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -60,6 +60,7 @@ #include "../mwphysics/object.hpp" #include "../mwphysics/constants.hpp" +#include "datetimemanager.hpp" #include "player.hpp" #include "manualref.hpp" #include "cellstore.hpp" @@ -121,33 +122,11 @@ namespace MWWorld LoadersContainer mLoaders; }; - int World::getDaysPerMonth (int month) const - { - switch (month) - { - case 0: return 31; - case 1: return 28; - case 2: return 31; - case 3: return 30; - case 4: return 31; - case 5: return 30; - case 6: return 31; - case 7: return 31; - case 8: return 30; - case 9: return 31; - case 10: return 30; - case 11: return 31; - } - - throw std::runtime_error ("month out of range"); - } - void World::adjustSky() { if (mSky && (isCellExterior() || isCellQuasiExterior())) { - mRendering->skySetDate (mDay->getInteger(), mMonth->getInteger()); - + updateSkyDate(); mRendering->setSkyEnabled(true); } else @@ -193,6 +172,8 @@ namespace MWWorld if (mEsm[0].getFormat() == 0) ensureNeededRecords(); + mCurrentDate.reset(new DateTimeManager()); + fillGlobalVariables(); mStore.setUp(true); @@ -227,13 +208,7 @@ namespace MWWorld void World::fillGlobalVariables() { mGlobalVariables.fill (mStore); - - mGameHour = &mGlobalVariables["gamehour"]; - mDaysPassed = &mGlobalVariables["dayspassed"]; - mDay = &mGlobalVariables["day"]; - mMonth = &mGlobalVariables["month"]; - mYear = &mGlobalVariables["year"]; - mTimeScale = &mGlobalVariables["timescale"]; + mCurrentDate->setup(mGlobalVariables); } void World::startNewGame (bool bypass) @@ -310,6 +285,7 @@ namespace MWWorld mPhysics->toggleCollisionMode(); MWBase::Environment::get().getWindowManager()->updatePlayer(); + mCurrentDate->setup(mGlobalVariables); } void World::clear() @@ -639,26 +615,20 @@ namespace MWWorld void World::setGlobalInt (const std::string& name, int value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay (value); - else if (name=="month") - setMonth (value); - else - mGlobalVariables[name].setInteger (value); + bool dateUpdated = mCurrentDate->updateGlobalInt(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setInteger (value); } void World::setGlobalFloat (const std::string& name, float value) { - if (name=="gamehour") - setHour (value); - else if (name=="day") - setDay(static_cast(value)); - else if (name=="month") - setMonth(static_cast(value)); - else - mGlobalVariables[name].setFloat (value); + bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value); + if (dateUpdated) + updateSkyDate(); + + mGlobalVariables[name].setFloat(value); } int World::getGlobalInt (const std::string& name) const @@ -676,6 +646,11 @@ namespace MWWorld return mGlobalVariables.getType (name); } + std::string World::getMonthName (int month) const + { + return mCurrentDate->getMonthName(month); + } + std::string World::getCellName (const MWWorld::CellStore *cell) const { if (!cell) @@ -894,130 +869,29 @@ namespace MWWorld } mWeatherManager->advanceTime (hours, incremental); + mCurrentDate->advanceTime(hours, mGlobalVariables); + updateSkyDate(); if (!incremental) { mRendering->notifyWorldSpaceChanged(); mProjectileManager->clear(); } - - hours += mGameHour->getFloat(); - - setHour (hours); - - int days = static_cast(hours / 24); - - if (days>0) - mDaysPassed->setInteger ( - days + mDaysPassed->getInteger()); - } - - void World::setHour (double hour) - { - if (hour<0) - hour = 0; - - int days = static_cast(hour / 24); - - hour = std::fmod (hour, 24); - - mGameHour->setFloat(static_cast(hour)); - - if (days>0) - setDay (days + mDay->getInteger()); - } - - void World::setDay (int day) - { - if (day<1) - day = 1; - - int month = mMonth->getInteger(); - - while (true) - { - int days = getDaysPerMonth (month); - if (day<=days) - break; - - if (month<11) - { - ++month; - } - else - { - month = 0; - mYear->setInteger(mYear->getInteger()+1); - } - - day -= days; - } - - mDay->setInteger(day); - mMonth->setInteger(month); - - mRendering->skySetDate(day, month); - } - - void World::setMonth (int month) - { - if (month<0) - month = 0; - - int years = month / 12; - month = month % 12; - - int days = getDaysPerMonth (month); - - if (mDay->getInteger()>days) - mDay->setInteger (days); - - mMonth->setInteger (month); - - if (years>0) - mYear->setInteger (years+mYear->getInteger()); - - mRendering->skySetDate (mDay->getInteger(), month); } - int World::getDay() const - { - return mDay->getInteger(); - } - - int World::getMonth() const - { - return mMonth->getInteger(); - } - - int World::getYear() const + float World::getTimeScaleFactor() const { - return mYear->getInteger(); + return mCurrentDate->getTimeScaleFactor(); } - std::string World::getMonthName (int month) const + TimeStamp World::getTimeStamp() const { - if (month==-1) - month = getMonth(); - - const int months = 12; - - if (month<0 || month>=months) - return ""; - - static const char *monthNames[months] = - { - "sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand", - "sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed", - "sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar" - }; - - return mStore.get().find (monthNames[month])->mValue.getString(); + return mCurrentDate->getTimeStamp(); } - TimeStamp World::getTimeStamp() const + ESM::EpochTimeStamp World::getEpochTimeStamp() const { - return TimeStamp (mGameHour->getFloat(), mDaysPassed->getInteger()); + return mCurrentDate->getEpochTimeStamp(); } bool World::toggleSky() @@ -1042,11 +916,6 @@ namespace MWWorld mRendering->skySetMoonColour (red); } - float World::getTimeScaleFactor() const - { - return mTimeScale->getFloat(); - } - void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent) { mPhysics->clearQueuedMovement(); @@ -1089,6 +958,8 @@ namespace MWWorld changeToExteriorCell (position, adjustPlayerPos, changeEvent); else changeToInteriorCell (cellId.mWorldspace, position, adjustPlayerPos, changeEvent); + + mCurrentDate->setup(mGlobalVariables); } void World::markCellAsUnchanged() @@ -3968,4 +3839,10 @@ namespace MWWorld mNavigator->reportStats(frameNumber, stats); mPhysics->reportStats(frameNumber, stats); } + + void World::updateSkyDate() + { + ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp(); + mRendering->skySetDate(currentDate.mDay, currentDate.mMonth); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index fa2f7778b..aeb6bbae4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -69,6 +69,7 @@ namespace MWPhysics namespace MWWorld { + class DateTimeManager; class WeatherManager; class Player; class ProjectileManager; @@ -85,13 +86,6 @@ namespace MWWorld LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; - ESM::Variant* mGameHour; - ESM::Variant* mDaysPassed; - ESM::Variant* mDay; - ESM::Variant* mMonth; - ESM::Variant* mYear; - ESM::Variant* mTimeScale; - Cells mCells; std::string mCurrentWorldSpace; @@ -102,6 +96,7 @@ namespace MWWorld std::unique_ptr mRendering; std::unique_ptr mWorldScene; std::unique_ptr mWeatherManager; + std::unique_ptr mCurrentDate; std::shared_ptr mProjectileManager; bool mSky; @@ -139,7 +134,6 @@ namespace MWWorld World& operator= (const World&); void updateWeather(float duration, bool paused = false); - int getDaysPerMonth (int month) const; void rotateObjectImp (const Ptr& ptr, const osg::Vec3f& rot, MWBase::RotationFlags flags); @@ -173,6 +167,8 @@ namespace MWWorld void fillGlobalVariables(); + void updateSkyDate(); + /** * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) * @param fileCollections- Container which holds content file names and their paths @@ -318,24 +314,14 @@ namespace MWWorld void advanceTime (double hours, bool incremental = false) override; ///< Advance in-game time. - void setHour (double hour) override; - ///< Set in-game time hour. - - void setMonth (int month) override; - ///< Set in-game time month. - - void setDay (int day) override; - ///< Set in-game time day. - - int getDay() const override; - int getMonth() const override; - int getYear() const override; - std::string getMonthName (int month = -1) const override; ///< Return name of month (-1: current month) TimeStamp getTimeStamp() const override; - ///< Return current in-game time stamp. + ///< Return current in-game time and number of day since new game start. + + ESM::EpochTimeStamp getEpochTimeStamp() const override; + ///< Return current in-game date and time. bool toggleSky() override; ///< \return Resulting mode diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 0f0478faa..6c0c33526 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -14,6 +14,14 @@ struct TimeStamp int mDay; }; +struct EpochTimeStamp +{ + float mGameHour; + int mDay; + int mMonth; + int mYear; +}; + // Pixel color value. Standard four-byte rr,gg,bb,aa format. typedef uint32_t Color; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..b5bf6e406 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -2,7 +2,6 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -#include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; int ESM::SavedGame::sCurrentFormat = 10; diff --git a/components/esm/savedgame.hpp b/components/esm/savedgame.hpp index aa0429657..26efae824 100644 --- a/components/esm/savedgame.hpp +++ b/components/esm/savedgame.hpp @@ -4,6 +4,8 @@ #include #include +#include "defs.hpp" + namespace ESM { class ESMReader; @@ -17,14 +19,6 @@ namespace ESM static int sCurrentFormat; - struct TimeStamp - { - float mGameHour; - int mDay; - int mMonth; - int mYear; - }; - std::vector mContentFiles; std::string mPlayerName; int mPlayerLevel; @@ -36,7 +30,7 @@ namespace ESM std::string mPlayerClassName; std::string mPlayerCell; - TimeStamp mInGameTime; + EpochTimeStamp mInGameTime; double mTimePlayed; std::string mDescription; std::vector mScreenshot; // raw jpg-encoded data From 5b34ef224bfe31e07c32c343f34e1f9a490f72dc Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 16 May 2020 21:08:39 +0200 Subject: [PATCH 07/16] Replace AiPackage virtual methods by options --- apps/openmw/mwmechanics/aiactivate.cpp | 5 -- apps/openmw/mwmechanics/aiactivate.hpp | 3 +- apps/openmw/mwmechanics/aiavoiddoor.cpp | 10 ---- apps/openmw/mwmechanics/aiavoiddoor.hpp | 16 ++++--- apps/openmw/mwmechanics/aibreathe.cpp | 10 ---- apps/openmw/mwmechanics/aibreathe.hpp | 15 +++--- apps/openmw/mwmechanics/aicast.cpp | 10 ---- apps/openmw/mwmechanics/aicast.hpp | 14 ++++-- apps/openmw/mwmechanics/aicombat.cpp | 10 ---- apps/openmw/mwmechanics/aicombat.hpp | 14 ++++-- apps/openmw/mwmechanics/aiescort.cpp | 5 -- apps/openmw/mwmechanics/aiescort.hpp | 12 +++-- apps/openmw/mwmechanics/aiface.cpp | 10 ---- apps/openmw/mwmechanics/aiface.hpp | 16 ++++--- apps/openmw/mwmechanics/aifollow.cpp | 24 ++++------ apps/openmw/mwmechanics/aifollow.hpp | 16 ++++--- apps/openmw/mwmechanics/aipackage.cpp | 29 ++---------- apps/openmw/mwmechanics/aipackage.hpp | 55 +++++++++++++++++----- apps/openmw/mwmechanics/aipursue.cpp | 5 -- apps/openmw/mwmechanics/aipursue.hpp | 14 ++++-- apps/openmw/mwmechanics/aisequence.cpp | 8 +++- apps/openmw/mwmechanics/aitravel.cpp | 38 +++++++++++---- apps/openmw/mwmechanics/aitravel.hpp | 35 +++++++++++--- apps/openmw/mwmechanics/aiwander.cpp | 19 ++------ apps/openmw/mwmechanics/aiwander.hpp | 15 +++--- apps/openmw/mwmechanics/typedaipackage.hpp | 10 ++++ 26 files changed, 220 insertions(+), 198 deletions(-) diff --git a/apps/openmw/mwmechanics/aiactivate.cpp b/apps/openmw/mwmechanics/aiactivate.cpp index 6764eba23..b4ddf0c03 100644 --- a/apps/openmw/mwmechanics/aiactivate.cpp +++ b/apps/openmw/mwmechanics/aiactivate.cpp @@ -44,11 +44,6 @@ namespace MWMechanics return false; } - int AiActivate::getTypeId() const - { - return TypeIdActivate; - } - void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr activate(new ESM::AiSequence::AiActivate()); diff --git a/apps/openmw/mwmechanics/aiactivate.hpp b/apps/openmw/mwmechanics/aiactivate.hpp index 5a96f4cdb..b263e74a6 100644 --- a/apps/openmw/mwmechanics/aiactivate.hpp +++ b/apps/openmw/mwmechanics/aiactivate.hpp @@ -29,7 +29,8 @@ namespace MWMechanics AiActivate(const ESM::AiSequence::AiActivate* activate); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdActivate; } void writeState(ESM::AiSequence::AiSequence& sequence) const final; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index 9cdb8d90b..d8517c5c9 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -72,16 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont return false; } -int MWMechanics::AiAvoidDoor::getTypeId() const -{ - return TypeIdAvoidDoor; -} - -unsigned int MWMechanics::AiAvoidDoor::getPriority() const -{ - return 2; -} - bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const { return (actorPos - mLastPos).length2() < 10 * 10; diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index fdbf7ebc7..72cde1026 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -24,12 +24,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: float mDuration; diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp index 5cb81b3d8..15251e125 100644 --- a/apps/openmw/mwmechanics/aibreathe.cpp +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -31,13 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro return true; } - -int MWMechanics::AiBreathe::getTypeId() const -{ - return TypeIdBreathe; -} - -unsigned int MWMechanics::AiBreathe::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp index 6e3bb912a..2a04ab2ad 100644 --- a/apps/openmw/mwmechanics/aibreathe.hpp +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -12,13 +12,16 @@ namespace MWMechanics public: bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdBreathe; } - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } }; } #endif - diff --git a/apps/openmw/mwmechanics/aicast.cpp b/apps/openmw/mwmechanics/aicast.cpp index cc4c03bf1..9ad7b4c56 100644 --- a/apps/openmw/mwmechanics/aicast.cpp +++ b/apps/openmw/mwmechanics/aicast.cpp @@ -89,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const return target; } - -int MWMechanics::AiCast::getTypeId() const -{ - return AiPackage::TypeIdCast; -} - -unsigned int MWMechanics::AiCast::getPriority() const -{ - return 3; -} diff --git a/apps/openmw/mwmechanics/aicast.hpp b/apps/openmw/mwmechanics/aicast.hpp index cdf7db2bf..22575c7bc 100644 --- a/apps/openmw/mwmechanics/aicast.hpp +++ b/apps/openmw/mwmechanics/aicast.hpp @@ -17,14 +17,18 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCast; } MWWorld::Ptr getTarget() const final; - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 3; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const std::string mTargetId; diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 4a3c7aee6..883a8cc1c 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -406,16 +406,6 @@ namespace MWMechanics } } - int AiCombat::getTypeId() const - { - return TypeIdCombat; - } - - unsigned int AiCombat::getPriority() const - { - return 1; - } - MWWorld::Ptr AiCombat::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 2ef0298fc..ef8782ae1 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -104,18 +104,22 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdCombat; } - unsigned int getPriority() const final; + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 1; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } ///Returns target ID MWWorld::Ptr getTarget() const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final; - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } - private: /// Returns true if combat should end bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 216547f58..5dc1e44db 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -95,11 +95,6 @@ namespace MWMechanics return false; } - int AiEscort::getTypeId() const - { - return TypeIdEscort; - } - void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const { std::unique_ptr escort(new ESM::AiSequence::AiEscort()); diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index c12de1fac..42558bf7c 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -32,11 +32,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdEscort; } - bool useVariableSpeed() const final { return true; } - - bool sideWithTarget() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; diff --git a/apps/openmw/mwmechanics/aiface.cpp b/apps/openmw/mwmechanics/aiface.cpp index 0bfd00c87..17b18babc 100644 --- a/apps/openmw/mwmechanics/aiface.cpp +++ b/apps/openmw/mwmechanics/aiface.cpp @@ -14,13 +14,3 @@ bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::Charac osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f)); } - -int MWMechanics::AiFace::getTypeId() const -{ - return AiPackage::TypeIdFace; -} - -unsigned int MWMechanics::AiFace::getPriority() const -{ - return 2; -} diff --git a/apps/openmw/mwmechanics/aiface.hpp b/apps/openmw/mwmechanics/aiface.hpp index 516dd18dc..3a9a482c0 100644 --- a/apps/openmw/mwmechanics/aiface.hpp +++ b/apps/openmw/mwmechanics/aiface.hpp @@ -12,12 +12,16 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - unsigned int getPriority() const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } + static constexpr TypeId getTypeId() { return TypeIdFace; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mPriority = 2; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } private: const float mTargetX; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index eb5b595ab..a9e43b3c3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -16,25 +16,24 @@ namespace MWMechanics { - int AiFollow::mFollowIndexCounter = 0; AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actorId; } AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -42,7 +41,7 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, } AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z) -: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) +: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) , mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -50,7 +49,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float d } AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) -: mAlwaysFollow(true), mCommanded(commanded), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) +: TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!commanded)) +, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) { mTargetActorRefId = actor.getCellRef().getRefId(); @@ -58,7 +58,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) } AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) - : mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded) + : TypedAiPackage(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded)) + , mAlwaysFollow(follow->mAlwaysFollow) // mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration. // The exact value of mDuration only matters for repeating packages. // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves. @@ -200,14 +201,9 @@ std::string AiFollow::getFollowedActor() return mTargetActorRefId; } -int AiFollow::getTypeId() const -{ - return TypeIdFollow; -} - bool AiFollow::isCommanded() const { - return mCommanded; + return !mOptions.mShouldCancelPreviousAi; } void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const @@ -221,7 +217,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const follow->mRemainingDuration = mRemainingDuration; follow->mCellId = mCellId; follow->mAlwaysFollow = mAlwaysFollow; - follow->mCommanded = mCommanded; + follow->mCommanded = isCommanded(); follow->mActive = mActive; ESM::AiSequence::AiPackageContainer package; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 865f4171b..b4cf88be8 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -53,15 +53,18 @@ namespace MWMechanics AiFollow(const ESM::AiSequence::AiFollow* follow); - bool sideWithTarget() const final { return true; } - bool followTargetThroughDoors() const final { return true; } - bool shouldCancelPreviousAi() const final { return !mCommanded; } - bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdFollow; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mSideWithTarget = true; + options.mFollowTargetThroughDoors = true; + return options; + } /// Returns the actor being followed std::string getFollowedActor(); @@ -87,7 +90,6 @@ namespace MWMechanics /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ const bool mAlwaysFollow; - const bool mCommanded; const float mDuration; // Hours float mRemainingDuration; // Hours const float mX; diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index dca882a3b..66b41db54 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -24,7 +24,9 @@ #include -MWMechanics::AiPackage::AiPackage() : +MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) : + mTypeId(typeId), + mOptions(options), mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTargetActorRefId(""), mTargetActorId(-1), @@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const return MWWorld::Ptr(); } -bool MWMechanics::AiPackage::sideWithTarget() const -{ - return false; -} - -bool MWMechanics::AiPackage::followTargetThroughDoors() const -{ - return false; -} - -bool MWMechanics::AiPackage::canCancel() const -{ - return true; -} - -bool MWMechanics::AiPackage::shouldCancelPreviousAi() const -{ - return true; -} - -bool MWMechanics::AiPackage::getRepeat() const -{ - return false; -} - void MWMechanics::AiPackage::reset() { // reset all members diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 873ad1c29..c32fb93aa 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -55,11 +55,39 @@ namespace MWMechanics TypeIdCast = 11 }; - ///Default constructor - AiPackage(); + struct Options + { + unsigned int mPriority = 0; + bool mUseVariableSpeed = false; + bool mSideWithTarget = false; + bool mFollowTargetThroughDoors = false; + bool mCanCancel = true; + bool mShouldCancelPreviousAi = true; + bool mRepeat = false; + bool mAlwaysActive = false; + + constexpr Options withRepeat(bool value) + { + mRepeat = value; + return *this; + } + + constexpr Options withShouldCancelPreviousAi(bool value) + { + mShouldCancelPreviousAi = value; + return *this; + } + }; + + AiPackage(TypeId typeId, const Options& options); virtual ~AiPackage() = default; + static constexpr Options makeDefaultOptions() + { + return Options{}; + } + ///Clones the package virtual std::unique_ptr clone() const = 0; @@ -69,13 +97,13 @@ namespace MWMechanics /// Returns the TypeID of the AiPackage /// \see enum TypeId - virtual int getTypeId() const = 0; + TypeId getTypeId() const { return mTypeId; } /// Higher number is higher priority (0 being the lowest) - virtual unsigned int getPriority() const {return 0;} + unsigned int getPriority() const { return mOptions.mPriority; } /// Check if package use movement with variable speed - virtual bool useVariableSpeed() const { return false;} + bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; } virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} @@ -89,24 +117,24 @@ namespace MWMechanics virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); }; /// Return true if having this AiPackage makes the actor side with the target in fights (default false) - virtual bool sideWithTarget() const; + bool sideWithTarget() const { return mOptions.mSideWithTarget; } /// Return true if the actor should follow the target through teleport doors (default false) - virtual bool followTargetThroughDoors() const; + bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; } /// Can this Ai package be canceled? (default true) - virtual bool canCancel() const; + bool canCancel() const { return mOptions.mCanCancel; } /// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)? - virtual bool shouldCancelPreviousAi() const; + bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; } /// Return true if this package should repeat. Currently only used for Wander packages. - virtual bool getRepeat() const; + bool getRepeat() const { return mOptions.mRepeat; } virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); } - // Return true if any loaded actor with this AI package must be active. - virtual bool alwaysActive() const { return false; } + /// Return true if any loaded actor with this AI package must be active. + bool alwaysActive() const { return mOptions.mAlwaysActive; } /// Reset pathfinding state void reset(); @@ -139,6 +167,9 @@ namespace MWMechanics DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; + const TypeId mTypeId; + const Options mOptions; + // TODO: all this does not belong here, move into temporary storage PathFinder mPathFinder; ObstacleCheck mObstacleCheck; diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 76b4ac0c9..7aa2a9554 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -66,11 +66,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte return false; } -int AiPursue::getTypeId() const -{ - return TypeIdPursue; -} - MWWorld::Ptr AiPursue::getTarget() const { return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 6898a8dd3..6031f84fb 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -27,14 +27,20 @@ namespace MWMechanics AiPursue(const ESM::AiSequence::AiPursue* pursue); bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + + static constexpr TypeId getTypeId() { return TypeIdPursue; } + + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mCanCancel = false; + options.mShouldCancelPreviousAi = false; + return options; + } MWWorld::Ptr getTarget() const final; void writeState (ESM::AiSequence::AiSequence& sequence) const final; - - bool canCancel() const final { return false; } - bool shouldCancelPreviousAi() const final { return false; } }; } #endif diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 9f5d4ccaf..4a23dc788 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -338,7 +338,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo dest = actor.getRefData().getPosition().asVec3(); } - MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true); + MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z()); stack(travelPackage, actor, false); } @@ -478,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence) } case ESM::AiSequence::Ai_Travel: { - package.reset(new AiTravel(static_cast(it->mPackage))); + const auto source = static_cast(it->mPackage); + if (source->mHidden) + package.reset(new AiInternalTravel(source)); + else + package.reset(new AiTravel(source)); break; } case ESM::AiSequence::Ai_Escort: diff --git a/apps/openmw/mwmechanics/aitravel.cpp b/apps/openmw/mwmechanics/aitravel.cpp index ea65f4670..b2a506ca6 100644 --- a/apps/openmw/mwmechanics/aitravel.cpp +++ b/apps/openmw/mwmechanics/aitravel.cpp @@ -27,14 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2) namespace MWMechanics { - AiTravel::AiTravel(float x, float y, float z, bool hidden) - : mX(x),mY(y),mZ(z),mHidden(hidden) + AiTravel::AiTravel(float x, float y, float z, AiTravel*) + : mX(x), mY(y), mZ(z), mHidden(false) + { + } + + AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived) + : TypedAiPackage(derived), mX(x), mY(y), mZ(z), mHidden(true) + { + } + + AiTravel::AiTravel(float x, float y, float z) + : AiTravel(x, y, z, this) { } AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel) - : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(travel->mHidden) + : mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false) { + // Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type + assert(!travel->mHidden); } bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) @@ -78,11 +90,6 @@ namespace MWMechanics return false; } - int AiTravel::getTypeId() const - { - return mHidden ? TypeIdInternalTravel : TypeIdTravel; - } - void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) { if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) @@ -107,5 +114,20 @@ namespace MWMechanics package.mPackage = travel.release(); sequence.mPackages.push_back(package); } + + AiInternalTravel::AiInternalTravel(float x, float y, float z) + : AiTravel(x, y, z, this) + { + } + + AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel) + : AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this) + { + } + + std::unique_ptr AiInternalTravel::clone() const + { + return std::make_unique(*this); + } } diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index 4f785e237..3049801cd 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -13,12 +13,18 @@ namespace AiSequence namespace MWMechanics { + struct AiInternalTravel; + /// \brief Causes the AI to travel to the specified point - class AiTravel final : public TypedAiPackage + class AiTravel : public TypedAiPackage { public: - /// Default constructor - AiTravel(float x, float y, float z, bool hidden = false); + AiTravel(float x, float y, float z, AiTravel* derived); + + AiTravel(float x, float y, float z, AiInternalTravel* derived); + + AiTravel(float x, float y, float z); + AiTravel(const ESM::AiSequence::AiTravel* travel); /// Simulates the passing of time @@ -28,11 +34,15 @@ namespace MWMechanics bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; - - bool useVariableSpeed() const final { return true; } + static constexpr TypeId getTypeId() { return TypeIdTravel; } - bool alwaysActive() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mAlwaysActive = true; + return options; + } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } @@ -43,6 +53,17 @@ namespace MWMechanics const bool mHidden; }; + + struct AiInternalTravel final : public AiTravel + { + AiInternalTravel(float x, float y, float z); + + explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel); + + static constexpr TypeId getTypeId() { return TypeIdInternalTravel; } + + std::unique_ptr clone() const final; + }; } #endif diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 584131bbe..fd978717e 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -113,11 +113,12 @@ namespace MWMechanics } AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector& idle, bool repeat): + TypedAiPackage(makeDefaultOptions().withRepeat(repeat)), mDistance(std::max(0, distance)), mDuration(std::max(0, duration)), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(getInitialIdle(idle)), - mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), + mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) { } @@ -309,11 +310,6 @@ namespace MWMechanics return false; // AiWander package not yet completed } - bool AiWander::getRepeat() const - { - return mRepeat; - } - osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const { if (mHasDestination) @@ -599,11 +595,6 @@ namespace MWMechanics } } - int AiWander::getTypeId() const - { - return TypeIdWander; - } - void AiWander::stopWalking(const MWWorld::Ptr& actor) { mPathFinder.clearPath(); @@ -873,7 +864,7 @@ namespace MWMechanics assert (mIdle.size() == 8); for (int i=0; i<8; ++i) wander->mData.mIdle[i] = mIdle[i]; - wander->mData.mShouldRepeat = mRepeat; + wander->mData.mShouldRepeat = mOptions.mRepeat; wander->mStoredInitialActorPosition = mStoredInitialActorPosition; if (mStoredInitialActorPosition) wander->mInitialActorPosition = mInitialActorPosition; @@ -885,12 +876,12 @@ namespace MWMechanics } AiWander::AiWander (const ESM::AiSequence::AiWander* wander) - : mDistance(std::max(static_cast(0), wander->mData.mDistance)) + : TypedAiPackage(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0)) + , mDistance(std::max(static_cast(0), wander->mData.mDistance)) , mDuration(std::max(static_cast(0), wander->mData.mDuration)) , mRemainingDuration(wander->mDurationData.mRemainingDuration) , mTimeOfDay(wander->mData.mTimeOfDay) , mIdle(getInitialIdle(wander->mData.mIdle)) - , mRepeat(wander->mData.mShouldRepeat != 0) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mHasDestination(false) , mDestination(osg::Vec3f(0, 0, 0)) diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 8eb735205..8171107d9 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -93,16 +93,20 @@ namespace MWMechanics bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; - int getTypeId() const final; + static constexpr TypeId getTypeId() { return TypeIdWander; } - bool useVariableSpeed() const final { return true; } + static constexpr Options makeDefaultOptions() + { + AiPackage::Options options; + options.mUseVariableSpeed = true; + options.mRepeat = false; + return options; + } void writeState(ESM::AiSequence::AiSequence &sequence) const final; void fastForward(const MWWorld::Ptr& actor, AiState& state) final; - bool getRepeat() const final; - osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; osg::Vec3f getDestination() const final @@ -139,7 +143,6 @@ namespace MWMechanics float mRemainingDuration; const int mTimeOfDay; const std::vector mIdle; - const bool mRepeat; bool mStoredInitialActorPosition; osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell @@ -174,7 +177,7 @@ namespace MWMechanics static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static int OffsetToPreventOvercrowding(); - }; + }; } #endif diff --git a/apps/openmw/mwmechanics/typedaipackage.hpp b/apps/openmw/mwmechanics/typedaipackage.hpp index e2b5f8688..c959f4d68 100644 --- a/apps/openmw/mwmechanics/typedaipackage.hpp +++ b/apps/openmw/mwmechanics/typedaipackage.hpp @@ -8,6 +8,16 @@ namespace MWMechanics template struct TypedAiPackage : public AiPackage { + TypedAiPackage() : + AiPackage(T::getTypeId(), T::makeDefaultOptions()) {} + + TypedAiPackage(const Options& options) : + AiPackage(T::getTypeId(), options) {} + + template + TypedAiPackage(Derived*) : + AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {} + virtual std::unique_ptr clone() const override { return std::make_unique(*static_cast(this)); From 6de97e6bc2f0308a3a437d875b6eef01757c96a4 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sat, 6 Jun 2020 14:10:24 +0400 Subject: [PATCH 08/16] Remove redundant variables from RenderingManager --- apps/openmw/mwrender/renderingmanager.cpp | 12 ++++-------- apps/openmw/mwrender/renderingmanager.hpp | 2 -- components/terrain/world.hpp | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6b41854e0..3eb71db03 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -213,10 +213,8 @@ namespace MWRender , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) , mDistantFog(false) - , mDistantTerrain(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) - , mBorders(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -290,9 +288,7 @@ namespace MWRender DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); @@ -302,7 +298,7 @@ namespace MWRender mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); - if (mDistantTerrain) + if (Settings::Manager::getBool("distant terrain", "Terrain")) { const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); @@ -558,9 +554,9 @@ namespace MWRender bool RenderingManager::toggleBorders() { - mBorders = !mBorders; - mTerrain->setBordersVisible(mBorders); - return mBorders; + bool borders = !mTerrain->getBordersVisible(); + mTerrain->setBordersVisible(borders); + return borders; } bool RenderingManager::toggleRenderMode(RenderMode mode) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 09cff26f1..30826fb38 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -299,12 +299,10 @@ namespace MWRender float mNearClip; float mViewDistance; bool mDistantFog : 1; - bool mDistantTerrain : 1; bool mFieldOfViewOverridden : 1; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; - bool mBorders; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index a69d03ca9..618095a60 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -139,6 +139,7 @@ namespace Terrain virtual void enable(bool enabled) {} virtual void setBordersVisible(bool visible); + virtual bool getBordersVisible() { return mBorderVisible; } /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. From 48758116d6f228dfab4ec82e9c26975e7bcefdb4 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Sat, 6 Jun 2020 14:24:33 +0200 Subject: [PATCH 09/16] Make sure the skill level up message box displays the value correctly Fixes regression introduced in 204d2acf2570073d9d9736493d79e9a1326d6136. --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 1e9003a2f..0e4b3f44c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas MWBase::Environment::get().getWindowManager()->playSound("skillraise"); std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""); - message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), base); + message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast(base)); if (readBook) message = "#{sBookSkillMessage}\n" + message; From 456f0f9f445f3984b131e9880990c70d13bdba0b Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 19:09:18 +0300 Subject: [PATCH 10/16] Clean up magic bolts of actors that are gone (#5451) --- CHANGELOG.md | 1 + apps/openmw/mwworld/projectilemanager.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..a136994ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Bug #5427: GetDistance unknown ID error is misleading Bug #5435: Enemies can't hurt the player when collision is off Bug #5441: Enemies can't push a player character when in critical strike stance + Bug #5451: Magic projectiles don't disappear with the caster Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5445: Handle NiLines diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 38458fdb9..6ace82ea1 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -387,6 +387,18 @@ namespace MWWorld { for (std::vector::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();) { + // If the actor caster is gone, the magic bolt needs to be removed from the scene during the next frame. + MWWorld::Ptr caster = it->getCaster(); + if (!caster.isEmpty() && caster.getClass().isActor()) + { + if (caster.getRefData().getCount() <= 0 || caster.getClass().getCreatureStats(caster).isDead()) + { + cleanupMagicBolt(*it); + it = mMagicBolts.erase(it); + continue; + } + } + osg::Quat orient = it->mNode->getAttitude(); static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get() .find("fTargetSpellMaxSpeed")->mValue.getFloat(); @@ -405,8 +417,6 @@ namespace MWWorld update(*it, duration); - MWWorld::Ptr caster = it->getCaster(); - // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. std::vector targetActors; if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) From e02b66cdf4b0ea50fd50203d153c5c88ef92c319 Mon Sep 17 00:00:00 2001 From: Capostrophic Date: Sat, 6 Jun 2020 20:04:09 +0300 Subject: [PATCH 11/16] Ignore bogus string arguments for Disable/Enable again --- components/compiler/extensions0.cpp | 4 ++-- components/compiler/lineparser.cpp | 10 +--------- components/compiler/lineparser.hpp | 3 +-- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 60b4a7451..3989ef0f4 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -247,8 +247,8 @@ namespace Compiler extensions.registerInstruction ("startscript", "c", opcodeStartScript, opcodeStartScriptExplicit); extensions.registerInstruction ("stopscript", "c", opcodeStopScript); extensions.registerFunction ("getsecondspassed", 'f', "", opcodeGetSecondsPassed); - extensions.registerInstruction ("enable", "", opcodeEnable, opcodeEnableExplicit); - extensions.registerInstruction ("disable", "", opcodeDisable, opcodeDisableExplicit); + extensions.registerInstruction ("enable", "x", opcodeEnable, opcodeEnableExplicit); + extensions.registerInstruction ("disable", "x", opcodeDisable, opcodeDisableExplicit); extensions.registerFunction ("getdisabled", 'l', "x", opcodeGetDisabled, opcodeGetDisabledExplicit); extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("onactivate", 'l', "", opcodeOnActivate, opcodeOnActivateExplicit); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 326b5f9f6..829ad6f08 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -86,13 +86,6 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { - if (mState==PotentialEndState) - { - getErrorHandler().warning ("Stray string argument", loc); - mState = EndState; - return true; - } - if (mState==SetState) { std::string name2 = Misc::StringUtils::lowerCase (name); @@ -445,8 +438,7 @@ namespace Compiler return true; } - if (code==Scanner::S_newline && - (mState==EndState || mState==BeginState || mState==PotentialEndState)) + if (code==Scanner::S_newline && (mState==EndState || mState==BeginState)) return false; if (code==Scanner::S_comma && mState==MessageState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index 8f7f64bf2..cc32b9592 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -25,8 +25,7 @@ namespace Compiler SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, - EndState, PotentialEndState /* may have a stray string argument */, - PotentialExplicitState, ExplicitState, MemberState + EndState, PotentialExplicitState, ExplicitState, MemberState }; Locals& mLocals; From 5209f5ff6d2139b5d3d3a8af797bcdb5703741dd Mon Sep 17 00:00:00 2001 From: elsid Date: Sat, 6 Jun 2020 23:00:53 +0200 Subject: [PATCH 12/16] Mark all derived classes from ESM::ObjectsState and overriden functions as final --- components/esm/containerstate.hpp | 10 +++++----- components/esm/creaturelevliststate.hpp | 10 +++++----- components/esm/creaturestate.hpp | 10 +++++----- components/esm/doorstate.hpp | 10 +++++----- components/esm/npcstate.hpp | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/components/esm/containerstate.hpp b/components/esm/containerstate.hpp index 33818d4f1..b0c4da2d1 100644 --- a/components/esm/containerstate.hpp +++ b/components/esm/containerstate.hpp @@ -8,18 +8,18 @@ namespace ESM { // format 0, saved games only - struct ContainerState : public ObjectState + struct ContainerState final : public ObjectState { InventoryState mInventory; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual ContainerState& asContainerState() + ContainerState& asContainerState() final { return *this; } - virtual const ContainerState& asContainerState() const + const ContainerState& asContainerState() const final { return *this; } diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp index 4d0b726a0..ec0bc8667 100644 --- a/components/esm/creaturelevliststate.hpp +++ b/components/esm/creaturelevliststate.hpp @@ -7,19 +7,19 @@ namespace ESM { // format 0, saved games only - struct CreatureLevListState : public ObjectState + struct CreatureLevListState final : public ObjectState { int mSpawnActorId; bool mSpawn; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureLevListState& asCreatureLevListState() + CreatureLevListState& asCreatureLevListState() final { return *this; } - virtual const CreatureLevListState& asCreatureLevListState() const + const CreatureLevListState& asCreatureLevListState() const final { return *this; } diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index ca0d2601a..3e9a44d56 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -9,7 +9,7 @@ namespace ESM { // format 0, saved games only - struct CreatureState : public ObjectState + struct CreatureState final : public ObjectState { InventoryState mInventory; CreatureStats mCreatureStats; @@ -17,14 +17,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual CreatureState& asCreatureState() + CreatureState& asCreatureState() final { return *this; } - virtual const CreatureState& asCreatureState() const + const CreatureState& asCreatureState() const final { return *this; } diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp index 1251b9059..04ad110d6 100644 --- a/components/esm/doorstate.hpp +++ b/components/esm/doorstate.hpp @@ -7,18 +7,18 @@ namespace ESM { // format 0, saved games only - struct DoorState : public ObjectState + struct DoorState final : public ObjectState { int mDoorState = 0; - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual DoorState& asDoorState() + DoorState& asDoorState() final { return *this; } - virtual const DoorState& asDoorState() const + const DoorState& asDoorState() const final { return *this; } diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index 4ae026da6..6c0469050 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -10,7 +10,7 @@ namespace ESM { // format 0, saved games only - struct NpcState : public ObjectState + struct NpcState final : public ObjectState { InventoryState mInventory; NpcStats mNpcStats; @@ -19,14 +19,14 @@ namespace ESM /// Initialize to default state void blank(); - virtual void load (ESMReader &esm); - virtual void save (ESMWriter &esm, bool inInventory = false) const; + void load (ESMReader &esm) final; + void save (ESMWriter &esm, bool inInventory = false) const final; - virtual NpcState& asNpcState() + NpcState& asNpcState() final { return *this; } - virtual const NpcState& asNpcState() const + const NpcState& asNpcState() const final { return *this; } From 75e7a3e8b1cbe4d72859ca75b082a8f93e377667 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 09:25:46 +0400 Subject: [PATCH 13/16] Do not store object position, if it is the same as in CellRef --- components/esm/defs.hpp | 20 ++++++++++++++++++++ components/esm/objectstate.cpp | 3 ++- components/esm/savedgame.cpp | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 6c0c33526..61b7b161b 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -57,6 +57,26 @@ struct Position }; #pragma pack(pop) +bool inline operator== (const Position& left, const Position& right) noexcept +{ + return left.pos[0] == right.pos[0] && + left.pos[1] == right.pos[1] && + left.pos[2] == right.pos[2] && + left.rot[0] == right.rot[0] && + left.rot[1] == right.rot[1] && + left.rot[2] == right.rot[2]; +} + +bool inline operator!= (const Position& left, const Position& right) noexcept +{ + return left.pos[0] != right.pos[0] || + left.pos[1] != right.pos[1] || + left.pos[2] != right.pos[2] || + left.rot[0] != right.rot[0] || + left.rot[1] != right.rot[1] || + left.rot[2] != right.rot[2]; +} + template struct FourCC { diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index a7a452c82..9709bf4ff 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -26,6 +26,7 @@ void ESM::ObjectState::load (ESMReader &esm) mCount = 1; esm.getHNOT (mCount, "COUN"); + mPosition = mRef.mPos; esm.getHNOT (mPosition, "POS_", 24); if (esm.isNextSub("LROT")) @@ -61,7 +62,7 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mCount!=1) esm.writeHNT ("COUN", mCount); - if (!inInventory) + if (!inInventory && mPosition != mRef.mPos) esm.writeHNT ("POS_", mPosition, 24); if (mFlags != 0) diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index d87607e44..76695dbe8 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -4,7 +4,7 @@ #include "esmwriter.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 11; +int ESM::SavedGame::sCurrentFormat = 12; void ESM::SavedGame::load (ESMReader &esm) { From 5d17a4c66e881d78ba4d3ebbd5149ebf14cc5cde Mon Sep 17 00:00:00 2001 From: foobar13372 Date: Sun, 7 Jun 2020 10:10:33 +0200 Subject: [PATCH 14/16] Update CHANGELOG.md Correctly name change (was probably a copy paste error) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 298220699..50103697e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -299,7 +299,7 @@ Feature #5147: Show spell magicka cost in spell buying window Feature #5170: Editor: Land shape editing, land selection Feature #5172: Editor: Delete instances/references with keypress in scene window - Feature #5193: Weapon sheathing + Feature #5193: Shields sheathing Feature #5201: Editor: Show tool outline in scene view, when using editmodes Feature #5219: Impelement TestCells console command Feature #5224: Handle NiKeyframeController for NiTriShape From 4afc332a0ccc07db0d737a8d57f966d4a2b826fd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 7 Jun 2020 10:35:33 +0400 Subject: [PATCH 15/16] Add a FogManager --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/fogmanager.cpp | 104 ++++++++++++++++++++++ apps/openmw/mwrender/fogmanager.hpp | 39 ++++++++ apps/openmw/mwrender/renderingmanager.cpp | 86 +++--------------- apps/openmw/mwrender/renderingmanager.hpp | 14 +-- 5 files changed, 157 insertions(+), 88 deletions(-) create mode 100644 apps/openmw/mwrender/fogmanager.cpp create mode 100644 apps/openmw/mwrender/fogmanager.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a6b68df86..99bd1c2cd 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/fogmanager.cpp b/apps/openmw/mwrender/fogmanager.cpp new file mode 100644 index 000000000..837e6ad04 --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.cpp @@ -0,0 +1,104 @@ +#include "fogmanager.hpp" + +#include + +#include +#include +#include +#include + +namespace +{ + float DLLandFogStart; + float DLLandFogEnd; + float DLUnderwaterFogStart; + float DLUnderwaterFogEnd; + float DLInteriorFogStart; + float DLInteriorFogEnd; +} + +namespace MWRender +{ + FogManager::FogManager() + : mLandFogStart(0.f) + , mLandFogEnd(std::numeric_limits::max()) + , mUnderwaterFogStart(0.f) + , mUnderwaterFogEnd(std::numeric_limits::max()) + , mFogColor(osg::Vec4f()) + , mDistantFog(Settings::Manager::getBool("use distant fog", "Fog")) + , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) + , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) + , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) + { + DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); + DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); + DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); + DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); + DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); + DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); + } + + void FogManager::configure(float viewDistance, const ESM::Cell *cell) + { + osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); + + if (mDistantFog) + { + float density = std::max(0.2f, cell->mAmbi.mFogDensity); + mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density; + mLandFogEnd = DLInteriorFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + mFogColor = color; + } + else + configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + } + + void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) + { + if (mDistantFog) + { + mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd); + mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd; + mUnderwaterFogStart = DLUnderwaterFogStart; + mUnderwaterFogEnd = DLUnderwaterFogEnd; + } + else + { + if (fogDepth == 0.0) + { + mLandFogStart = 0.0f; + mLandFogEnd = std::numeric_limits::max(); + } + else + { + mLandFogStart = viewDistance * (1 - fogDepth); + mLandFogEnd = viewDistance; + } + mUnderwaterFogStart = std::min(viewDistance, 6666.f) * (1 - underwaterFog); + mUnderwaterFogEnd = std::min(viewDistance, 6666.f); + } + mFogColor = color; + } + + float FogManager::getFogStart(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogStart : mLandFogStart; + } + + float FogManager::getFogEnd(bool isUnderwater) const + { + return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd; + } + + osg::Vec4f FogManager::getFogColor(bool isUnderwater) const + { + if (isUnderwater) + { + return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight); + } + + return mFogColor; + } +} diff --git a/apps/openmw/mwrender/fogmanager.hpp b/apps/openmw/mwrender/fogmanager.hpp new file mode 100644 index 000000000..c3efd06ab --- /dev/null +++ b/apps/openmw/mwrender/fogmanager.hpp @@ -0,0 +1,39 @@ +#ifndef OPENMW_MWRENDER_FOGMANAGER_H +#define OPENMW_MWRENDER_FOGMANAGER_H + +#include + +namespace ESM +{ + struct Cell; +} + +namespace MWRender +{ + class FogManager + { + public: + FogManager(); + + void configure(float viewDistance, const ESM::Cell *cell); + void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color); + + osg::Vec4f getFogColor(bool isUnderwater) const; + float getFogStart(bool isUnderwater) const; + float getFogEnd(bool isUnderwater) const; + + private: + float mLandFogStart; + float mLandFogEnd; + float mUnderwaterFogStart; + float mUnderwaterFogEnd; + osg::Vec4f mFogColor; + bool mDistantFog; + + osg::Vec4f mUnderwaterColor; + float mUnderwaterWeight; + float mUnderwaterIndoorFog; + }; +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3eb71db03..462f9fbb6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -69,16 +69,7 @@ #include "navmesh.hpp" #include "actorspaths.hpp" #include "recastmesh.hpp" - -namespace -{ - float DLLandFogStart; - float DLLandFogEnd; - float DLUnderwaterFogStart; - float DLUnderwaterFogEnd; - float DLInteriorFogStart; - float DLInteriorFogEnd; -} +#include "fogmanager.hpp" namespace MWRender { @@ -204,15 +195,7 @@ namespace MWRender , mWorkQueue(workQueue) , mUnrefQueue(new SceneUtil::UnrefQueue) , mNavigator(navigator) - , mLandFogStart(0.f) - , mLandFogEnd(std::numeric_limits::max()) - , mUnderwaterFogStart(0.f) - , mUnderwaterFogEnd(std::numeric_limits::max()) - , mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor")) - , mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight")) - , mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog")) , mNightEyeFactor(0.f) - , mDistantFog(false) , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { @@ -282,14 +265,6 @@ namespace MWRender mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); - DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog"); - DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog"); - DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog"); - DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog"); - DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog"); - DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog"); - mDistantFog = Settings::Manager::getBool("use distant fog", "Fog"); - const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders"); @@ -345,8 +320,9 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); - mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); + mFog.reset(new FogManager()); + mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mSky->setCamera(mViewer->getCamera()); mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); @@ -602,46 +578,12 @@ namespace MWRender void RenderingManager::configureFog(const ESM::Cell *cell) { - osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); - - if(mDistantFog) - { - float density = std::max(0.2f, cell->mAmbi.mFogDensity); - mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density); - mLandFogEnd = DLInteriorFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - mFogColor = color; - } - else - configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color); + mFog->configure(mViewDistance, cell); } void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) { - if(mDistantFog) - { - mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd); - mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd; - mUnderwaterFogStart = DLUnderwaterFogStart; - mUnderwaterFogEnd = DLUnderwaterFogEnd; - } - else - { - if(fogDepth == 0.0) - { - mLandFogStart = 0.0f; - mLandFogEnd = std::numeric_limits::max(); - } - else - { - mLandFogStart = mViewDistance * (1 - fogDepth); - mLandFogEnd = mViewDistance; - } - mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog); - mUnderwaterFogEnd = std::min(mViewDistance, 6666.f); - } - mFogColor = color; + mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color); } SkyManager* RenderingManager::getSkyManager() @@ -670,19 +612,11 @@ namespace MWRender osg::Vec3f focal, cameraPos; mCamera->getPosition(focal, cameraPos); mCurrentCameraPos = cameraPos; - if (mWater->isUnderwater(cameraPos)) - { - setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(mUnderwaterFogStart); - mStateUpdater->setFogEnd(mUnderwaterFogEnd); - } - else - { - setFogColor(mFogColor); - mStateUpdater->setFogStart(mLandFogStart); - mStateUpdater->setFogEnd(mLandFogEnd); - } + bool isUnderwater = mWater->isUnderwater(cameraPos); + mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater)); + mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater)); + setFogColor(mFog->getFogColor(isUnderwater)); } void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) @@ -1331,7 +1265,7 @@ namespace MWRender else if (it->first == "Camera" && it->second == "viewing distance") { mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); - if(!mDistantFog) + if(!Settings::Manager::getBool("use distant fog", "Fog")) mStateUpdater->setFogEnd(mViewDistance); updateProjectionMatrix(); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 30826fb38..13f09b359 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -73,6 +73,7 @@ namespace MWRender class StateUpdater; class EffectManager; + class FogManager; class SkyManager; class NpcAnimation; class Pathgrid; @@ -275,6 +276,7 @@ namespace MWRender std::unique_ptr mTerrain; TerrainStorage* mTerrainStorage; std::unique_ptr mSky; + std::unique_ptr mFog; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; osg::ref_ptr mPlayerAnimation; @@ -284,22 +286,12 @@ namespace MWRender osg::ref_ptr mStateUpdater; - float mLandFogStart; - float mLandFogEnd; - float mUnderwaterFogStart; - float mUnderwaterFogEnd; - osg::Vec4f mUnderwaterColor; - float mUnderwaterWeight; - float mUnderwaterIndoorFog; - osg::Vec4f mFogColor; - osg::Vec4f mAmbientColor; float mNightEyeFactor; float mNearClip; float mViewDistance; - bool mDistantFog : 1; - bool mFieldOfViewOverridden : 1; + bool mFieldOfViewOverridden; float mFieldOfViewOverride; float mFieldOfView; float mFirstPersonFieldOfView; From 3dd4023e8dbd74d6c0ed18acf2d74e9faab8a360 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 8 Jun 2020 11:29:38 +0400 Subject: [PATCH 16/16] Update active spells during rest --- apps/openmw/mwmechanics/actors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 6c88592f4..aa5aac80a 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -2051,6 +2051,8 @@ namespace MWMechanics for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) { + iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); + if (iter->first.getClass().getCreatureStats(iter->first).isDead()) continue;