From 31d28e727f004d944c40c58e5f3822415192a265 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 17 Dec 2014 01:05:32 +0100 Subject: [PATCH] Implement leveled list script functions (Fixes #1546) --- apps/openmw/mwbase/world.hpp | 10 +++ apps/openmw/mwscript/docs/vmformat.txt | 6 +- apps/openmw/mwscript/miscextensions.cpp | 112 ++++++++++++++++++++++++ apps/openmw/mwstate/statemanagerimp.cpp | 2 + apps/openmw/mwworld/esmstore.cpp | 8 +- apps/openmw/mwworld/esmstore.hpp | 15 ++++ apps/openmw/mwworld/store.hpp | 10 +-- apps/openmw/mwworld/worldimp.cpp | 10 +++ apps/openmw/mwworld/worldimp.hpp | 8 ++ components/compiler/extensions0.cpp | 4 + components/compiler/opcodes.hpp | 4 + 11 files changed, 182 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 34be298d3a..32beadf18a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -40,6 +40,8 @@ namespace ESM struct Enchantment; struct Book; struct EffectList; + struct CreatureLevList; + struct ItemLevList; } namespace MWRender @@ -359,6 +361,14 @@ namespace MWBase ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record + virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& 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::ItemLevList *createOverrideRecord (const ESM::ItemLevList& 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 MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 800c6e2c7b..172e1b528a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -438,5 +438,9 @@ op 0x20002f7: PCForce3rdPerson op 0x20002f8: PCGet3rdPerson op 0x20002f9: HitAttemptOnMe op 0x20002fa: HitAttemptOnMe, explicit +op 0x20002fb: AddToLevCreature +op 0x20002fc: RemoveFromLevCreature +op 0x20002fd: AddToLevItem +op 0x20002fe: RemoveFromLevItem -opcodes 0x20002fb-0x3ffffff unused +opcodes 0x20002ff-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index c92acff820..f20c9967df 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -31,6 +31,42 @@ #include "interpretercontext.hpp" #include "ref.hpp" +namespace +{ + + void addToLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + { + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + { + if (it->mLevel == level && itemId == it->mId) + return; + } + + ESM::LeveledListBase::LevelItem item; + item.mId = itemId; + item.mLevel = level; + list->mList.push_back(item); + } + + void removeFromLevList(ESM::LeveledListBase* list, const std::string& itemId, int level) + { + // level of -1 removes all items with that itemId + for (std::vector::iterator it = list->mList.begin(); it != list->mList.end();) + { + if (level != -1 && it->mLevel != level) + { + ++it; + continue; + } + if (Misc::StringUtils::ciEqual(itemId, it->mId)) + it = list->mList.erase(it); + else + ++it; + } + } + +} + namespace MWScript { namespace Misc @@ -1032,6 +1068,78 @@ namespace MWScript } }; + class OpAddToLevCreature : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + addToLevList(&listCopy, creatureId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpRemoveFromLevCreature : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& creatureId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::CreatureLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + removeFromLevList(&listCopy, creatureId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpAddToLevItem : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + addToLevList(&listCopy, itemId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + + class OpRemoveFromLevItem : public Interpreter::Opcode0 + { + public: + virtual void execute(Interpreter::Runtime &runtime) + { + const std::string& levId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + const std::string& itemId = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + int level = runtime[0].mInteger; + runtime.pop(); + + ESM::ItemLevList listCopy = *MWBase::Environment::get().getWorld()->getStore().get().find(levId); + removeFromLevList(&listCopy, itemId, level); + MWBase::Environment::get().getWorld()->createOverrideRecord(listCopy); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox); @@ -1121,6 +1229,10 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); interpreter.installSegment5 (Compiler::Misc::opcodeBetaComment, new OpBetaComment); interpreter.installSegment5 (Compiler::Misc::opcodeBetaCommentExplicit, new OpBetaComment); + interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevCreature, new OpAddToLevCreature); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevCreature, new OpRemoveFromLevCreature); + interpreter.installSegment5 (Compiler::Misc::opcodeAddToLevItem, new OpAddToLevItem); + interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem); } } } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 18ebe11cee..9746202dc3 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -352,6 +352,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_PROJ: case ESM::REC_MPRJ: case ESM::REC_ENAB: + case ESM::REC_LEVC: + case ESM::REC_LEVI: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 56cb05c646..2a3fd9179e 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -149,7 +149,9 @@ void ESMStore::setUp() +mEnchants.getDynamicSize() +mNpcs.getDynamicSize() +mSpells.getDynamicSize() - +mWeapons.getDynamicSize(); + +mWeapons.getDynamicSize() + +mCreatureLists.getDynamicSize() + +mItemLists.getDynamicSize(); } void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -170,6 +172,8 @@ void ESMStore::setUp() mSpells.write (writer, progress); mWeapons.write (writer, progress); mNpcs.write (writer, progress); + mItemLists.write (writer, progress); + mCreatureLists.write (writer, progress); } bool ESMStore::readRecord (ESM::ESMReader& reader, int32_t type) @@ -185,6 +189,8 @@ void ESMStore::setUp() case ESM::REC_SPEL: case ESM::REC_WEAP: case ESM::REC_NPC_: + case ESM::REC_LEVI: + case ESM::REC_LEVC: mStores[type]->read (reader); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 83e911174d..5d794db895 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -167,6 +167,7 @@ namespace MWWorld throw std::runtime_error("Storage for this type not exist"); } + /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records) template const T *insert(const T &x) { std::ostringstream id; @@ -191,6 +192,20 @@ namespace MWWorld return ptr; } + /// Insert a record with set ID, and allow it to override a pre-existing static record. + template + const T *overrideRecord(const T &x) { + Store &store = const_cast &>(get()); + + T *ptr = store.insert(x); + for (iterator it = mStores.begin(); it != mStores.end(); ++it) { + if (it->second == &store) { + mIds[ptr->mId] = it->first; + } + } + return ptr; + } + template const T *insertStatic(const T &x) { std::ostringstream id; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index c4070f0327..a0d34b228d 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -145,17 +145,17 @@ namespace MWWorld T item; item.mId = Misc::StringUtils::lowerCase(id); + typename Dynamic::const_iterator dit = mDynamic.find(item.mId); + if (dit != mDynamic.end()) { + return &dit->second; + } + typename std::map::const_iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } - typename Dynamic::const_iterator dit = mDynamic.find(item.mId); - if (dit != mDynamic.end()) { - return &dit->second; - } - return 0; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e24e99c305..17a45f9f12 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1490,6 +1490,16 @@ namespace MWWorld return mStore.insert(record); } + const ESM::CreatureLevList *World::createOverrideRecord(const ESM::CreatureLevList &record) + { + return mStore.overrideRecord(record); + } + + const ESM::ItemLevList *World::createOverrideRecord(const ESM::ItemLevList &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 c8e254b2f9..2a0da917b9 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -420,6 +420,14 @@ namespace MWWorld ///< Create a new record (of type book) in the ESM store. /// \return pointer to created record + virtual const ESM::CreatureLevList *createOverrideRecord (const ESM::CreatureLevList& record); + ///< 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::ItemLevList *createOverrideRecord (const ESM::ItemLevList& record); + ///< 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); virtual MWWorld::Ptr placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 690e589f73..d08a2bb320 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -308,6 +308,10 @@ namespace Compiler extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); extensions.registerInstruction ("betacomment", "S", opcodeBetaComment, opcodeBetaCommentExplicit); extensions.registerInstruction ("bc", "S", opcodeBetaComment, opcodeBetaCommentExplicit); + extensions.registerInstruction ("addtolevcreature", "ccl", opcodeAddToLevCreature); + extensions.registerInstruction ("removefromlevcreature", "ccl", opcodeRemoveFromLevCreature); + extensions.registerInstruction ("addtolevitem", "ccl", opcodeAddToLevItem); + extensions.registerInstruction ("removefromlevitem", "ccl", opcodeRemoveFromLevItem); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index da79555e22..bbafd6b13b 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -284,6 +284,10 @@ namespace Compiler const int opcodeExplodeSpellExplicit = 0x200022a; const int opcodeGetPcInJail = 0x200023e; const int opcodeGetPcTraveling = 0x200023f; + const int opcodeAddToLevCreature = 0x20002fb; + const int opcodeRemoveFromLevCreature = 0x20002fc; + const int opcodeAddToLevItem = 0x20002fd; + const int opcodeRemoveFromLevItem = 0x20002fe; } namespace Sky