diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index c41ffb50b..a4883b77d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -51,6 +51,7 @@ set(GAMESCRIPT mwscript/soundextensions.cpp mwscript/skyextensions.cpp mwscript/statsextensions.cpp + mwscript/containerextensions.cpp mwscript/extensions.cpp mwscript/globalscripts.cpp ) @@ -65,6 +66,7 @@ set(GAMESCRIPT_HEADER mwscript/soundextensions.hpp mwscript/skyextensions.hpp mwscript/statsextensions.hpp + mwscript/containerextensions.hpp mwscript/extensions.hpp mwscript/globalscripts.hpp ) @@ -93,6 +95,7 @@ set(GAMEWORLD_HEADER mwworld/nullaction.hpp mwworld/actionteleport.hpp mwworld/containerstore.hpp + mwworld/manualref.hpp ) source_group(apps\\openmw\\mwworld FILES ${GAMEWORLD} ${GAMEWORLD_HEADER}) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp new file mode 100644 index 000000000..73111145b --- /dev/null +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -0,0 +1,88 @@ + +#include "containerextensions.hpp" + +#include + +#include +#include +#include + +#include "../mwworld/manualref.hpp" +#include "../mwworld/class.hpp" + +#include "interpretercontext.hpp" + +namespace MWScript +{ + namespace Container + { + class OpAddItem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWScript::InterpreterContext& context + = static_cast (runtime.getContext()); + + std::string item = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer count = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Ptr ptr = context.getReference(); + + MWWorld::ManualRef ref (context.getWorld().getStore(), item); + + ref.getPtr().getRefData().setCount (count); + + MWWorld::Class::get (ref.getPtr()).insertIntoContainer (ref.getPtr(), + MWWorld::Class::get (ptr).getContainerStore (ptr)); + } + }; + + class OpAddItemExplicit : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWScript::InterpreterContext& context + = static_cast (runtime.getContext()); + + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string item = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer count = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Ptr ptr = context.getWorld().getPtr (id, false); + + MWWorld::ManualRef ref (context.getWorld().getStore(), item); + + ref.getPtr().getRefData().setCount (count); + + MWWorld::Class::get (ref.getPtr()).insertIntoContainer (ref.getPtr(), + MWWorld::Class::get (ptr).getContainerStore (ptr)); + } + }; + + const int opcodeAddItem = 0x2000076; + const int opcodeAddItemExplicit = 0x2000077; + + void registerExtensions (Compiler::Extensions& extensions) + { + extensions.registerInstruction ("additem", "cl", opcodeAddItem, opcodeAddItemExplicit); + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + interpreter.installSegment5 (opcodeAddItem, new OpAddItem); + interpreter.installSegment5 (opcodeAddItemExplicit, new OpAddItemExplicit); + } + } +} diff --git a/apps/openmw/mwscript/containerextensions.hpp b/apps/openmw/mwscript/containerextensions.hpp new file mode 100644 index 000000000..f99a71b4d --- /dev/null +++ b/apps/openmw/mwscript/containerextensions.hpp @@ -0,0 +1,25 @@ +#ifndef GAME_SCRIPT_CONTAINEREXTENSIONS_H +#define GAME_SCRIPT_CONTAINEREXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWScript +{ + /// \brief stats-related script functionality (creatures and NPCs) + namespace Container + { + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); + } +} + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 5807f26d4..e2de2cbc1 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -77,4 +77,6 @@ op 0x200006c-0x200006e: ModDynamic (health, magicka, fatigue), explicit referenc op 0x200006f-0x2000071: GetDynamic (health, magicka, fatigue) op 0x2000072-0x2000074: GetDynamic (health, magicka, fatigue), explicit reference op 0x2000075: Activate -opcodes 0x2000076-0x3ffffff unused +op 0x2000076: AddItem +op 0x2000077: AddItem, explicit reference +opcodes 0x2000078-0x3ffffff unused diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index 3abaac58c..1b3d3df14 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -10,6 +10,7 @@ #include "guiextensions.hpp" #include "skyextensions.hpp" #include "statsextensions.hpp" +#include "containerextensions.hpp" namespace MWScript { @@ -21,8 +22,9 @@ namespace MWScript Sound::registerExtensions (extensions); Sky::registerExtensions (extensions); Stats::registerExtensions (extensions); + Container::registerExtensions (extensions); } - + void installOpcodes (Interpreter::Interpreter& interpreter) { Interpreter::installOpcodes (interpreter); @@ -32,6 +34,6 @@ namespace MWScript Sound::installOpcodes (interpreter); Sky::installOpcodes (interpreter); Stats::installOpcodes (interpreter); - } + Container::installOpcodes (interpreter); + } } - diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp new file mode 100644 index 000000000..4b7edfa43 --- /dev/null +++ b/apps/openmw/mwworld/manualref.hpp @@ -0,0 +1,86 @@ +#ifndef GAME_MWWORLD_MANUALREF_H +#define GAME_MWWORLD_MANUALREF_H + +#include + +#include +#include + +#include "ptr.hpp" + +namespace MWWorld +{ + /// \brief Manually constructed live cell ref + class ManualRef + { + boost::any mRef; + Ptr mPtr; + + ManualRef (const ManualRef&); + ManualRef& operator= (const ManualRef&); + + template + bool create (const ESMS::RecListT& list, const std::string& name) + { + if (const T *instance = list.search (name)) + { + ESMS::LiveCellRef ref; + ref.base = instance; + + mRef = ref; + mPtr = Ptr (&boost::any_cast&> (mRef), 0); + + return true; + } + + return false; + } + + public: + + ManualRef (const ESMS::ESMStore& store, const std::string& name) + { + // create + if (!create (store.activators, name) && + !create (store.potions, name) && + !create (store.appas, name) && + !create (store.armors, name) && + !create (store.books, name) && + !create (store.clothes, name) && + !create (store.containers, name) && + !create (store.creatures, name) && + !create (store.doors, name) && + !create (store.ingreds, name) && + !create (store.creatureLists, name) && + !create (store.itemLists, name) && + !create (store.lights, name) && + !create (store.lockpicks, name) && + !create (store.miscItems, name) && + !create (store.npcs, name) && + !create (store.probes, name) && + !create (store.repairs, name) && + !create (store.statics, name) && + !create (store.weapons, name)) + throw std::logic_error ("failed to create manual cell ref for " + name); + + // initialise + ESM::CellRef& cellRef = mPtr.getCellRef(); + cellRef.refnum = -1; + cellRef.scale = 1; + cellRef.factIndex = 0; + cellRef.charge = 0; + cellRef.intv = 0; + cellRef.nam9 = 0; + cellRef.teleport = false; + cellRef.lockLevel = 0; + cellRef.unam = 0; + } + + const Ptr& getPtr() const + { + return mPtr; + } + }; +} + +#endif