From 4d7947d27c68201f5d8d544556c869829eebfbfe Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 2 Jun 2020 21:59:37 +0200 Subject: [PATCH] Mutate base records when editing AI settings (#2798) --- apps/openmw/mwbase/world.hpp | 9 ++++++ apps/openmw/mwclass/creature.cpp | 5 ++++ apps/openmw/mwclass/creature.hpp | 2 ++ apps/openmw/mwclass/npc.cpp | 5 ++++ apps/openmw/mwclass/npc.hpp | 2 ++ apps/openmw/mwmechanics/actorutil.hpp | 37 +++++++++++++++++++++++++ apps/openmw/mwscript/aiextensions.cpp | 7 +++-- apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/class.cpp | 5 ++++ apps/openmw/mwworld/class.hpp | 4 ++- apps/openmw/mwworld/esmstore.cpp | 35 +++++++++++------------ apps/openmw/mwworld/esmstore.hpp | 3 ++ apps/openmw/mwworld/worldimp.cpp | 11 ++++++++ apps/openmw/mwworld/worldimp.hpp | 8 ++++++ 14 files changed, 112 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 84f9b49844..05802c6584 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -35,6 +35,7 @@ namespace ESM struct Position; struct Cell; struct Class; + struct Creature; struct Potion; struct Spell; struct NPC; @@ -377,6 +378,14 @@ namespace MWBase ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + virtual void update (float duration, bool paused) = 0; virtual void updatePhysics (float duration, bool paused) = 0; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index d1a4395283..375b70dde7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -878,4 +878,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); scale *= ref->mBase->mScale; } + + void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 35688ed1ac..9be5c42720 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -129,6 +129,8 @@ namespace MWClass virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 297471e07f..df483962ab 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1437,4 +1437,9 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); return ref->mBase->getFactionRank(); } + + void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + MWMechanics::setBaseAISetting(id, setting, value); + } } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 9b92f63382..3d63697fb9 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -164,6 +164,8 @@ namespace MWClass virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwmechanics/actorutil.hpp b/apps/openmw/mwmechanics/actorutil.hpp index 82a904799b..cdb7873114 100644 --- a/apps/openmw/mwmechanics/actorutil.hpp +++ b/apps/openmw/mwmechanics/actorutil.hpp @@ -1,6 +1,16 @@ #ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #define OPENMW_MWMECHANICS_ACTORUTIL_H +#include +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/esmstore.hpp" + +#include "./creaturestats.hpp" + namespace MWWorld { class Ptr; @@ -11,6 +21,33 @@ namespace MWMechanics MWWorld::Ptr getPlayer(); bool isPlayerInCombat(); bool canActorMoveByZAxis(const MWWorld::Ptr& actor); + + template + void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) + { + T copy = *MWBase::Environment::get().getWorld()->getStore().get().find(id); + switch(setting) + { + case MWMechanics::CreatureStats::AiSetting::AI_Hello: + copy.mAiData.mHello = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Fight: + copy.mAiData.mFight = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Flee: + copy.mAiData.mFlee = value; + break; + case MWMechanics::CreatureStats::AiSetting::AI_Alarm: + copy.mAiData.mAlarm = value; + break; + default: + assert(0); + } + MWBase::Environment::get().getWorld()->createOverrideRecord(copy); + } + + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); + template void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value); } #endif diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 79639197d3..fdd13cfb6d 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -257,8 +257,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, - ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value); + int modified = ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value; + + ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, modified); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, modified); } }; template @@ -277,6 +279,7 @@ namespace MWScript MWMechanics::Stat stat = ptr.getClass().getCreatureStats(ptr).getAiSetting(mIndex); stat.setModified(value, 0); ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat); + ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value); } }; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 86a26212fd..560c24578d 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -460,6 +460,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str case ESM::REC_ENAB: case ESM::REC_LEVC: case ESM::REC_LEVI: + case ESM::REC_CREA: MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap); break; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 0287db56fa..07981bf2af 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -516,4 +516,9 @@ namespace MWWorld result.z() = magicEffect->mData.mBlue / 255.f; return result; } + + void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const + { + throw std::runtime_error ("class does not have creature stats"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 8c3740eddf..fb816d8109 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -10,6 +10,7 @@ #include "ptr.hpp" #include "doorstate.hpp" +#include "../mwmechanics/creaturestats.hpp" namespace ESM { @@ -28,7 +29,6 @@ namespace MWPhysics namespace MWMechanics { - class CreatureStats; class NpcStats; struct Movement; } @@ -360,6 +360,8 @@ namespace MWWorld virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; + + virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; }; } diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 1f6ed51027..f862266404 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -273,7 +273,8 @@ void ESMStore::validate() +mSpells.getDynamicSize() +mWeapons.getDynamicSize() +mCreatureLists.getDynamicSize() - +mItemLists.getDynamicSize(); + +mItemLists.getDynamicSize() + +mCreatures.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -295,6 +296,7 @@ void ESMStore::validate() mNpcs.write (writer, progress); mItemLists.write (writer, progress); mCreatureLists.write (writer, progress); + mCreatures.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) @@ -312,24 +314,8 @@ void ESMStore::validate() case ESM::REC_NPC_: case ESM::REC_LEVI: case ESM::REC_LEVC: - - { - mStores[type]->read (reader); - } - - if (type==ESM::REC_NPC_) - { - // NPC record will always be last and we know that there can be only one - // dynamic NPC record (player) -> We are done here with dynamic record loading - setUp(); - - const ESM::NPC *player = mNpcs.find ("player"); - - if (!mRaces.find (player->mRace) || - !mClasses.find (player->mClass)) - throw std::runtime_error ("Invalid player record (race or class unavailable"); - } - + case ESM::REC_CREA: + mStores[type]->read (reader); return true; case ESM::REC_DYNA: @@ -343,4 +329,15 @@ void ESMStore::validate() } } + void ESMStore::checkPlayer() + { + setUp(); + + const ESM::NPC *player = mNpcs.find ("player"); + + if (!mRaces.find (player->mRace) || + !mClasses.find (player->mClass)) + throw std::runtime_error ("Invalid player record (race or class unavailable"); + } + } // end namespace diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index d170a32c58..fe1cfc7087 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -239,6 +239,9 @@ namespace MWWorld bool readRecord (ESM::ESMReader& reader, uint32_t type); ///< \return Known type? + + // To be called when we are done with dynamic record loading + void checkPlayer(); }; template <> diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ba4a6467ac..6d20f5d455 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -403,6 +403,7 @@ namespace MWWorld reader.getHNT(mLevitationEnabled, "LEVT"); return; case ESM::REC_PLAY: + mStore.checkPlayer(); mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { @@ -1815,6 +1816,16 @@ namespace MWWorld return mStore.overrideRecord(record); } + const ESM::Creature *World::createOverrideRecord(const ESM::Creature &record) + { + return mStore.overrideRecord(record); + } + + const ESM::NPC *World::createOverrideRecord(const ESM::NPC &record) + { + return mStore.overrideRecord(record); + } + const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7b6d2afdcd..201803a481 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -490,6 +490,14 @@ namespace MWWorld ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. /// \return pointer to created record + const ESM::Creature *createOverrideRecord (const ESM::Creature& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + + const ESM::NPC *createOverrideRecord (const ESM::NPC& record) override; + ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. + /// \return pointer to created record + void update (float duration, bool paused) override; void updatePhysics (float duration, bool paused) override;