#include "miscextensions.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwbase/luamanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/manualref.hpp" #include "../mwmechanics/aicast.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" #include "interpretercontext.hpp" #include "ref.hpp" namespace { void addToLevList(ESM::LevelledListBase* list, std::string_view itemId, int level) { for (auto& levelItem : list->mList) { if (levelItem.mLevel == level && itemId == levelItem.mId) return; } ESM::LevelledListBase::LevelItem item; item.mId = std::string{itemId}; item.mLevel = level; list->mList.push_back(item); } void removeFromLevList(ESM::LevelledListBase* list, std::string_view 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 { class OpMenuMode : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push (MWBase::Environment::get().getWindowManager()->isGuiMode()); } }; class OpRandom : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { Interpreter::Type_Integer limit = runtime[0].mInteger; runtime.pop(); if (limit<0) throw std::runtime_error ( "random: argument out of range (Don't be so negative!)"); auto& prng = MWBase::Environment::get().getWorld()->getPrng(); runtime.push (static_cast(::Misc::Rng::rollDice(limit, prng))); // [o, limit) } }; template class OpStartScript : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr target = R()(runtime, false); std::string_view name = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().addScript (name, target); } }; class OpScriptRunning : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { std::string_view name = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); runtime.push(MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name)); } }; class OpStopScript : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { std::string_view name = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().removeScript (name); } }; class OpGetSecondsPassed : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push (MWBase::Environment::get().getFrameDuration()); } }; template class OpEnable : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getWorld()->enable (ptr); } }; template class OpDisable : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getWorld()->disable (ptr); } }; template class OpGetDisabled : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (!ptr.getRefData().isEnabled()); } }; class OpPlayBink : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { std::string name{runtime.getStringLiteral(runtime[0].mInteger)}; runtime.pop(); bool allowSkipping = runtime[0].mInteger != 0; runtime.pop(); MWBase::Environment::get().getWindowManager()->playVideo (name, allowSkipping); } }; class OpGetPcSleep : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push (MWBase::Environment::get().getWindowManager ()->getPlayerSleeping()); } }; class OpGetPcJumping : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World* world = MWBase::Environment::get().getWorld(); runtime.push (world->getPlayer().getJumping()); } }; class OpWakeUpPc : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::Environment::get().getWindowManager ()->wakeUpPlayer(); } }; class OpXBox : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push (0); } }; template class OpOnActivate : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getRefData().onActivate()); } }; template class OpActivate : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { InterpreterContext& context = static_cast (runtime.getContext()); MWWorld::Ptr ptr = R()(runtime); if (ptr.getRefData().activateByScript() || ptr.getContainerStore()) context.executeActivation(ptr, MWMechanics::getPlayer()); } }; template class OpLock : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer lockLevel = ptr.getCellRef().getLockLevel(); if(lockLevel==0) { //no lock level was ever set, set to 100 as default lockLevel = 100; } if (arg0==1) { lockLevel = runtime[0].mInteger; runtime.pop(); } ptr.getCellRef().lock (lockLevel); // Instantly reset door to closed state // This is done when using Lock in scripts, but not when using Lock spells. if (ptr.getType() == ESM::Door::sRecordId && !ptr.getCellRef().getTeleport()) { MWBase::Environment::get().getWorld()->activateDoor(ptr, MWWorld::DoorState::Idle); } } }; template class OpUnlock : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); ptr.getCellRef().unlock (); } }; class OpToggleCollisionDebug : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug); runtime.getContext().report (enabled ? "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); } }; class OpToggleCollisionBoxes : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_CollisionDebug); runtime.getContext().report (enabled ? "Collision Mesh Rendering -> On" : "Collision Mesh Rendering -> Off"); } }; class OpToggleWireframe : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Wireframe); runtime.getContext().report (enabled ? "Wireframe Rendering -> On" : "Wireframe Rendering -> Off"); } }; class OpToggleBorders : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleBorders(); runtime.getContext().report (enabled ? "Border Rendering -> On" : "Border Rendering -> Off"); } }; class OpTogglePathgrid : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_Pathgrid); runtime.getContext().report (enabled ? "Path Grid rendering -> On" : "Path Grid Rendering -> Off"); } }; class OpFadeIn : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenIn(time, false); } }; class OpFadeOut : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(time, false); } }; class OpFadeTo : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { Interpreter::Type_Float alpha = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Float time = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWindowManager()->fadeScreenTo(static_cast(alpha), time, false); } }; class OpToggleWater : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWater() ? "Water -> On" : "Water -> Off"); } }; class OpToggleWorld : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.getContext().report(MWBase::Environment::get().getWorld()->toggleWorld() ? "World -> On" : "World -> Off"); } }; class OpDontSaveObject : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { // We are ignoring the DontSaveObject statement for now. Probably not worth // bothering with. The incompatibility we are creating should be marginal at most. } }; class OpPcForce1stPerson : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { if (!MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(true); } }; class OpPcForce3rdPerson : public Interpreter::Opcode0 { void execute (Interpreter::Runtime& runtime) override { if (MWBase::Environment::get().getWorld()->isFirstPerson()) MWBase::Environment::get().getWorld()->togglePOV(true); } }; class OpPcGet3rdPerson : public Interpreter::Opcode0 { public: void execute(Interpreter::Runtime& runtime) override { runtime.push(!MWBase::Environment::get().getWorld()->isFirstPerson()); } }; class OpToggleVanityMode : public Interpreter::Opcode0 { static bool sActivate; public: void execute(Interpreter::Runtime &runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); if (world->toggleVanityMode(sActivate)) { runtime.getContext().report(sActivate ? "Vanity Mode -> On" : "Vanity Mode -> Off"); sActivate = !sActivate; } else { runtime.getContext().report("Vanity Mode -> No"); } } }; bool OpToggleVanityMode::sActivate = true; template class OpGetLocked : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getCellRef().getLockLevel() > 0); } }; template class OpGetEffect : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view effect = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!ptr.getClass().isActor()) { runtime.push(0); return; } char *end; long key = strtol(effect.data(), &end, 10); if(key < 0 || key > 32767 || *end != '\0') key = ESM::MagicEffect::effectStringToId({effect}); const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); const MWMechanics::MagicEffects& effects = stats.getMagicEffects(); for (const auto& activeEffect : effects) { if (activeEffect.first.mId == key && activeEffect.second.getModifier() > 0) { runtime.push(1); return; } } runtime.push(0); } }; template class OpAddSoulGem : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string creature{runtime.getStringLiteral(runtime[0].mInteger)}; runtime.pop(); std::string_view gem = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!ptr.getClass().hasInventoryStore(ptr)) return; const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); store.get().find(creature); // This line throws an exception if it can't find the creature MWWorld::Ptr item = *ptr.getClass().getContainerStore(ptr).add(gem, 1, ptr); // Set the soul on just one of the gems, not the whole stack item.getContainerStore()->unstack(item, ptr); item.getCellRef().setSoul(creature); // Restack the gem with other gems with the same soul item.getContainerStore()->restack(item); } }; template class OpRemoveSoulGem : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::Ptr ptr = R()(runtime); std::string_view soul = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); // throw away additional arguments for (unsigned int i=0; igetCellRef().getSoul(), soul)) { store.remove(*it, 1, ptr); return; } } } }; template class OpDrop : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view item = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer amount = runtime[0].mInteger; runtime.pop(); if (amount<0) throw std::runtime_error ("amount must be non-negative"); // no-op if (amount == 0) return; if (!ptr.getClass().isActor()) return; if (ptr.getClass().hasInventoryStore(ptr)) { // Prefer dropping unequipped items first; re-stack if possible by unequipping items before dropping them. MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); int numNotEquipped = store.count(item); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ConstContainerStoreIterator it = store.getSlot (slot); if (it != store.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) { numNotEquipped -= it->getRefData().getCount(); } } for (int slot = 0; slot < MWWorld::InventoryStore::Slots && amount > numNotEquipped; ++slot) { MWWorld::ContainerStoreIterator it = store.getSlot (slot); if (it != store.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) { int numToRemove = std::min(amount - numNotEquipped, it->getRefData().getCount()); store.unequipItemQuantity(*it, ptr, numToRemove); numNotEquipped += numToRemove; } } for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item) && !store.isEquipped(*iter)) { int removed = store.remove(*iter, amount, ptr); MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); dropped.getCellRef().setOwner(""); amount -= removed; if (amount <= 0) break; } } } MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), item, 1); MWWorld::Ptr itemPtr(ref.getPtr()); if (amount > 0) { if (itemPtr.getClass().getScript(itemPtr).empty()) { MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, amount); } else { // Dropping one item per time to prevent making stacks of scripted items for (int i = 0; i < amount; i++) MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, 1); } } MWBase::Environment::get().getSoundManager()->playSound3D(ptr, itemPtr.getClass().getDownSoundId(itemPtr), 1.f, 1.f); } }; template class OpDropSoulGem : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view soul = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!ptr.getClass().hasInventoryStore(ptr)) return; MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().getSoul(), soul)) { MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1); store.remove(*iter, 1, ptr); break; } } } }; template class OpGetAttacked : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getCreatureStats (ptr).getAttacked ()); } }; template class OpGetWeaponDrawn : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push((ptr.getClass().hasInventoryStore(ptr) || ptr.getClass().isBipedal(ptr)) && ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState::Weapon); } }; template class OpGetSpellReadied : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getCreatureStats (ptr).getDrawState () == MWMechanics::DrawState::Spell); } }; template class OpGetSpellEffects : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view id = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!ptr.getClass().isActor()) { runtime.push(0); return; } const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); runtime.push(stats.getActiveSpells().isSpellActive(id)); } }; class OpGetCurrentTime : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push(MWBase::Environment::get().getWorld()->getTimeStamp().getHour()); } }; template class OpSetDelete : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); int parameter = runtime[0].mInteger; runtime.pop(); if (parameter == 1) MWBase::Environment::get().getWorld()->deleteObject(ptr); else if (parameter == 0) MWBase::Environment::get().getWorld()->undeleteObject(ptr); else throw std::runtime_error("SetDelete: unexpected parameter"); } }; class OpGetSquareRoot : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { float param = runtime[0].mFloat; runtime.pop(); if (param < 0) throw std::runtime_error("square root of negative number (we aren't that imaginary)"); runtime.push(std::sqrt (param)); } }; template class OpFall : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { } }; template class OpGetStandingPc : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr)); } }; template class OpGetStandingActor : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr)); } }; template class OpGetCollidingPc : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getPlayerCollidingWith(ptr)); } }; template class OpGetCollidingActor : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (MWBase::Environment::get().getWorld()->getActorCollidingWith(ptr)); } }; template class OpHurtStandingActor : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); float healthDiffPerSecond = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->hurtStandingActors(ptr, healthDiffPerSecond); } }; template class OpHurtCollidingActor : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); float healthDiffPerSecond = runtime[0].mFloat; runtime.pop(); MWBase::Environment::get().getWorld()->hurtCollidingActors(ptr, healthDiffPerSecond); } }; class OpGetWindSpeed : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { runtime.push(MWBase::Environment::get().getWorld()->getWindSpeed()); } }; template class OpHitOnMe : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view objectID = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); bool hit = ::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject()); runtime.push(hit); if(hit) stats.clearLastHitObject(); } }; template class OpHitAttemptOnMe : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view objectID = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); bool hit = ::Misc::StringUtils::ciEqual(objectID, stats.getLastHitAttemptObject()); runtime.push(hit); if(hit) stats.clearLastHitAttemptObject(); } }; template class OpEnableTeleporting : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); world->enableTeleporting(Enable); } }; template class OpEnableLevitation : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); world->enableLevitation(Enable); } }; template class OpShow : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime, false); std::string_view var = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::stringstream output; if (!ptr.isEmpty()) { std::string_view script = ptr.getClass().getScript(ptr); if (!script.empty()) { const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script); char type = locals.getType(var); std::string refId = ptr.getCellRef().getRefId(); if (refId.find(' ') != std::string::npos) refId = '"' + refId + '"'; switch (type) { case 'l': case 's': output << refId << "." << var << " = " << ptr.getRefData().getLocals().getIntVar(script, var); break; case 'f': output << refId << "." << var << " = " << ptr.getRefData().getLocals().getFloatVar(script, var); break; } } } if (output.rdbuf()->in_avail() == 0) { MWBase::World *world = MWBase::Environment::get().getWorld(); char type = world->getGlobalVariableType (var); switch (type) { case 's': output << var << " = " << runtime.getContext().getGlobalShort (var); break; case 'l': output << var << " = " << runtime.getContext().getGlobalLong (var); break; case 'f': output << var << " = " << runtime.getContext().getGlobalFloat (var); break; default: output << "unknown variable"; } } runtime.getContext().report(output.str()); } }; template class OpShowVars : public Interpreter::Opcode0 { void printLocalVars(Interpreter::Runtime &runtime, const MWWorld::Ptr &ptr) { std::stringstream str; std::string_view script = ptr.getClass().getScript(ptr); if(script.empty()) str<< ptr.getCellRef().getRefId()<<" does not have a script."; else { str<< "Local variables for "<getLocals(script); const std::vector *names = &complocals.get('s'); for(size_t i = 0;i < names->size();++i) { if(i >= locals.mShorts.size()) break; str<size();++i) { if(i >= locals.mLongs.size()) break; str<size();++i) { if(i >= locals.mFloats.size()) break; str< names = runtime.getContext().getGlobals(); for(size_t i = 0;i < names.size();++i) { char type = world->getGlobalVariableType (names[i]); str << std::endl << " " << names[i] << " = "; switch (type) { case 's': str << runtime.getContext().getGlobalShort (names[i]) << " (short)"; break; case 'l': str << runtime.getContext().getGlobalLong (names[i]) << " (long)"; break; case 'f': str << runtime.getContext().getGlobalFloat (names[i]) << " (float)"; break; default: str << ""; } } runtime.getContext().report (str.str()); } public: void execute(Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime, false); if (!ptr.isEmpty()) printLocalVars(runtime, ptr); else { // No reference, no problem. printGlobalVars(runtime); } } }; class OpToggleScripts : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleScripts(); runtime.getContext().report(enabled ? "Scripts -> On" : "Scripts -> Off"); } }; class OpToggleGodMode : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleGodMode(); runtime.getContext().report (enabled ? "God Mode -> On" : "God Mode -> Off"); } }; template class OpCast : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view spellId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::string targetId = ::Misc::StringUtils::lowerCase(runtime.getStringLiteral(runtime[0].mInteger)); runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); if (!spell) { runtime.getContext().report("spellcasting failed: cannot find spell \""+std::string(spellId)+"\""); return; } if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spell->mId); return; } if (ptr.getClass().isActor()) { if (!MWBase::Environment::get().getMechanicsManager()->isCastingSpell(ptr)) { MWMechanics::AiCast castPackage(targetId, spell->mId, true); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr); } return; } MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(targetId, false, false); if (target.isEmpty()) return; MWMechanics::CastSpell cast(ptr, target, false, true); cast.playSpellCastingEffects(spell->mId, false); cast.mHitPosition = target.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); } }; template class OpExplodeSpell : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string_view spellId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().search(spellId); if (!spell) { runtime.getContext().report("spellcasting failed: cannot find spell \""+std::string(spellId)+"\""); return; } if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getWorld()->getPlayer().setSelectedSpell(spell->mId); return; } if (ptr.getClass().isActor()) { if (!MWBase::Environment::get().getMechanicsManager()->isCastingSpell(ptr)) { MWMechanics::AiCast castPackage(ptr.getCellRef().getRefId(), spell->mId, true); ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(castPackage, ptr); } return; } MWMechanics::CastSpell cast(ptr, ptr, false, true); cast.mHitPosition = ptr.getRefData().getPosition().asVec3(); cast.mAlwaysSucceed = true; cast.cast(spell); } }; class OpGoToJail : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World* world = MWBase::Environment::get().getWorld(); world->goToJail(); } }; class OpPayFine : public Interpreter::Opcode0 { public: void execute(Interpreter::Runtime &runtime) override { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).setBounty(0); MWBase::Environment::get().getWorld()->confiscateStolenItems(player); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpPayFineThief : public Interpreter::Opcode0 { public: void execute(Interpreter::Runtime &runtime) override { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).setBounty(0); MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpGetPcInJail : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime &runtime) override { runtime.push (MWBase::Environment::get().getWorld()->isPlayerInJail()); } }; class OpGetPcTraveling : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime &runtime) override { runtime.push (MWBase::Environment::get().getWorld()->isPlayerTraveling()); } }; template class OpBetaComment : public Interpreter::Opcode1 { public: void execute(Interpreter::Runtime &runtime, unsigned int arg0) override { MWWorld::Ptr ptr = R()(runtime); std::stringstream msg; msg << "Report time: "; std::time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); msg << std::put_time(std::gmtime(¤tTime), "%Y.%m.%d %T UTC") << std::endl; msg << "Content file: " << ptr.getCellRef().getRefNum().mContentFile; if (!ptr.getCellRef().hasContentFile()) msg << " [None]" << std::endl; else { std::vector contentFiles = MWBase::Environment::get().getWorld()->getContentFiles(); msg << " [" << contentFiles.at (ptr.getCellRef().getRefNum().mContentFile) << "]" << std::endl; } msg << "RefNum: " << ptr.getCellRef().getRefNum().mIndex << std::endl; if (ptr.getRefData().isDeletedByContentFile()) msg << "[Deleted by content file]" << std::endl; if (!ptr.getRefData().getCount()) msg << "[Deleted]" << std::endl; msg << "RefID: " << ptr.getCellRef().getRefId() << std::endl; msg << "Memory address: " << ptr.getBase() << std::endl; if (ptr.isInCell()) { MWWorld::CellStore* cell = ptr.getCell(); msg << "Cell: " << MWBase::Environment::get().getWorld()->getCellName(cell) << std::endl; if (cell->getCell()->isExterior()) msg << "Grid: " << cell->getCell()->getGridX() << " " << cell->getCell()->getGridY() << std::endl; osg::Vec3f pos (ptr.getRefData().getPosition().asVec3()); msg << "Coordinates: " << pos.x() << " " << pos.y() << " " << pos.z() << std::endl; auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); std::string model = ::Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), vfs); msg << "Model: " << model << std::endl; if(!model.empty()) { const std::string archive = vfs->getArchive(model); if(!archive.empty()) msg << "(" << archive << ")" << std::endl; } if (!ptr.getClass().getScript(ptr).empty()) msg << "Script: " << ptr.getClass().getScript(ptr) << std::endl; } while (arg0 > 0) { std::string_view notes = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); if (!notes.empty()) msg << "Notes: " << notes << std::endl; --arg0; } Log(Debug::Warning) << "\n" << msg.str(); runtime.getContext().report(msg.str()); } }; class OpAddToLevCreature : public Interpreter::Opcode0 { public: void execute(Interpreter::Runtime &runtime) override { std::string_view levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::string_view 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: void execute(Interpreter::Runtime &runtime) override { std::string_view levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::string_view 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: void execute(Interpreter::Runtime &runtime) override { std::string_view levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::string_view 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: void execute(Interpreter::Runtime &runtime) override { std::string_view levId = runtime.getStringLiteral(runtime[0].mInteger); runtime.pop(); std::string_view 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); } }; template class OpShowSceneGraph : public Interpreter::Opcode1 { public: void execute(Interpreter::Runtime &runtime, unsigned int arg0) override { MWWorld::Ptr ptr = R()(runtime, false); int confirmed = 0; if (arg0==1) { confirmed = runtime[0].mInteger; runtime.pop(); } if (ptr.isEmpty() && !confirmed) runtime.getContext().report("Exporting the entire scene graph will result in a large file. Confirm this action using 'showscenegraph 1' or select an object instead."); else { const std::string& filename = MWBase::Environment::get().getWorld()->exportSceneGraph(ptr); runtime.getContext().report("Wrote '" + filename + "'"); } } }; class OpToggleNavMesh : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_NavMesh); runtime.getContext().report (enabled ? "Navigation Mesh Rendering -> On" : "Navigation Mesh Rendering -> Off"); } }; class OpToggleActorsPaths : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_ActorsPaths); runtime.getContext().report (enabled ? "Agents Paths Rendering -> On" : "Agents Paths Rendering -> Off"); } }; class OpSetNavMeshNumberToRender : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { const auto navMeshNumber = runtime[0].mInteger; runtime.pop(); if (navMeshNumber < 0) { runtime.getContext().report("Invalid navmesh number: use not less than zero values"); return; } MWBase::Environment::get().getWorld()->setNavMeshNumberToRender(static_cast(navMeshNumber)); } }; template class OpRepairedOnMe : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { // Broken in vanilla and deliberately no-op. runtime.push(0); } }; class OpToggleRecastMesh : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { bool enabled = MWBase::Environment::get().getWorld()->toggleRenderMode (MWRender::Render_RecastMesh); runtime.getContext().report (enabled ? "Recast Mesh Rendering -> On" : "Recast Mesh Rendering -> Off"); } }; class OpHelp : public Interpreter::Opcode0 { public: void execute(Interpreter::Runtime& runtime) override { std::stringstream message; message << MWBase::Environment::get().getWindowManager()->getVersionDescription() << "\n\n"; std::vector commands; MWBase::Environment::get().getScriptManager()->getExtensions().listKeywords(commands); for(const auto& command : commands) message << command << "\n"; runtime.getContext().report(message.str()); } }; class OpReloadLua : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::Environment::get().getLuaManager()->reloadAllScripts(); runtime.getContext().report("All Lua scripts are reloaded"); } }; void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5(Compiler::Misc::opcodeMenuMode); interpreter.installSegment5(Compiler::Misc::opcodeRandom); interpreter.installSegment5(Compiler::Misc::opcodeScriptRunning); interpreter.installSegment5>(Compiler::Misc::opcodeStartScript); interpreter.installSegment5>(Compiler::Misc::opcodeStartScriptExplicit); interpreter.installSegment5(Compiler::Misc::opcodeStopScript); interpreter.installSegment5(Compiler::Misc::opcodeGetSecondsPassed); interpreter.installSegment5>(Compiler::Misc::opcodeEnable); interpreter.installSegment5>(Compiler::Misc::opcodeEnableExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeDisable); interpreter.installSegment5>(Compiler::Misc::opcodeDisableExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetDisabled); interpreter.installSegment5>(Compiler::Misc::opcodeGetDisabledExplicit); interpreter.installSegment5(Compiler::Misc::opcodeXBox); interpreter.installSegment5>(Compiler::Misc::opcodeOnActivate); interpreter.installSegment5>(Compiler::Misc::opcodeOnActivateExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeActivate); interpreter.installSegment5>(Compiler::Misc::opcodeActivateExplicit); interpreter.installSegment3>(Compiler::Misc::opcodeLock); interpreter.installSegment3>(Compiler::Misc::opcodeLockExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeUnlock); interpreter.installSegment5>(Compiler::Misc::opcodeUnlockExplicit); interpreter.installSegment5(Compiler::Misc::opcodeToggleCollisionDebug); interpreter.installSegment5(Compiler::Misc::opcodeToggleCollisionBoxes); interpreter.installSegment5(Compiler::Misc::opcodeToggleWireframe); interpreter.installSegment5(Compiler::Misc::opcodeFadeIn); interpreter.installSegment5(Compiler::Misc::opcodeFadeOut); interpreter.installSegment5(Compiler::Misc::opcodeFadeTo); interpreter.installSegment5(Compiler::Misc::opcodeTogglePathgrid); interpreter.installSegment5(Compiler::Misc::opcodeToggleWater); interpreter.installSegment5(Compiler::Misc::opcodeToggleWorld); interpreter.installSegment5(Compiler::Misc::opcodeDontSaveObject); interpreter.installSegment5(Compiler::Misc::opcodePcForce1stPerson); interpreter.installSegment5(Compiler::Misc::opcodePcForce3rdPerson); interpreter.installSegment5(Compiler::Misc::opcodePcGet3rdPerson); interpreter.installSegment5(Compiler::Misc::opcodeToggleVanityMode); interpreter.installSegment5(Compiler::Misc::opcodeGetPcSleep); interpreter.installSegment5(Compiler::Misc::opcodeGetPcJumping); interpreter.installSegment5(Compiler::Misc::opcodeWakeUpPc); interpreter.installSegment5(Compiler::Misc::opcodePlayBink); interpreter.installSegment5(Compiler::Misc::opcodePayFine); interpreter.installSegment5(Compiler::Misc::opcodePayFineThief); interpreter.installSegment5(Compiler::Misc::opcodeGoToJail); interpreter.installSegment5>(Compiler::Misc::opcodeGetLocked); interpreter.installSegment5>(Compiler::Misc::opcodeGetLockedExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetEffect); interpreter.installSegment5>(Compiler::Misc::opcodeGetEffectExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeAddSoulGem); interpreter.installSegment5>(Compiler::Misc::opcodeAddSoulGemExplicit); interpreter.installSegment3>(Compiler::Misc::opcodeRemoveSoulGem); interpreter.installSegment3>(Compiler::Misc::opcodeRemoveSoulGemExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeDrop); interpreter.installSegment5>(Compiler::Misc::opcodeDropExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeDropSoulGem); interpreter.installSegment5>(Compiler::Misc::opcodeDropSoulGemExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetAttacked); interpreter.installSegment5>(Compiler::Misc::opcodeGetAttackedExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetWeaponDrawn); interpreter.installSegment5>(Compiler::Misc::opcodeGetWeaponDrawnExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetSpellReadied); interpreter.installSegment5>(Compiler::Misc::opcodeGetSpellReadiedExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetSpellEffects); interpreter.installSegment5>(Compiler::Misc::opcodeGetSpellEffectsExplicit); interpreter.installSegment5(Compiler::Misc::opcodeGetCurrentTime); interpreter.installSegment5>(Compiler::Misc::opcodeSetDelete); interpreter.installSegment5>(Compiler::Misc::opcodeSetDeleteExplicit); interpreter.installSegment5(Compiler::Misc::opcodeGetSquareRoot); interpreter.installSegment5>(Compiler::Misc::opcodeFall); interpreter.installSegment5>(Compiler::Misc::opcodeFallExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetStandingPc); interpreter.installSegment5>(Compiler::Misc::opcodeGetStandingPcExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetStandingActor); interpreter.installSegment5>(Compiler::Misc::opcodeGetStandingActorExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetCollidingPc); interpreter.installSegment5>(Compiler::Misc::opcodeGetCollidingPcExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeGetCollidingActor); interpreter.installSegment5>(Compiler::Misc::opcodeGetCollidingActorExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeHurtStandingActor); interpreter.installSegment5>(Compiler::Misc::opcodeHurtStandingActorExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeHurtCollidingActor); interpreter.installSegment5>(Compiler::Misc::opcodeHurtCollidingActorExplicit); interpreter.installSegment5(Compiler::Misc::opcodeGetWindSpeed); interpreter.installSegment5>(Compiler::Misc::opcodeHitOnMe); interpreter.installSegment5>(Compiler::Misc::opcodeHitOnMeExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeHitAttemptOnMe); interpreter.installSegment5>(Compiler::Misc::opcodeHitAttemptOnMeExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeDisableTeleporting); interpreter.installSegment5>(Compiler::Misc::opcodeEnableTeleporting); interpreter.installSegment5>(Compiler::Misc::opcodeShowVars); interpreter.installSegment5>(Compiler::Misc::opcodeShowVarsExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeShow); interpreter.installSegment5>(Compiler::Misc::opcodeShowExplicit); interpreter.installSegment5(Compiler::Misc::opcodeToggleGodMode); interpreter.installSegment5(Compiler::Misc::opcodeToggleScripts); interpreter.installSegment5>(Compiler::Misc::opcodeDisableLevitation); interpreter.installSegment5>(Compiler::Misc::opcodeEnableLevitation); interpreter.installSegment5>(Compiler::Misc::opcodeCast); interpreter.installSegment5>(Compiler::Misc::opcodeCastExplicit); interpreter.installSegment5>(Compiler::Misc::opcodeExplodeSpell); interpreter.installSegment5>(Compiler::Misc::opcodeExplodeSpellExplicit); interpreter.installSegment5(Compiler::Misc::opcodeGetPcInJail); interpreter.installSegment5(Compiler::Misc::opcodeGetPcTraveling); interpreter.installSegment3>(Compiler::Misc::opcodeBetaComment); interpreter.installSegment3>(Compiler::Misc::opcodeBetaCommentExplicit); interpreter.installSegment5(Compiler::Misc::opcodeAddToLevCreature); interpreter.installSegment5(Compiler::Misc::opcodeRemoveFromLevCreature); interpreter.installSegment5(Compiler::Misc::opcodeAddToLevItem); interpreter.installSegment5(Compiler::Misc::opcodeRemoveFromLevItem); interpreter.installSegment3>(Compiler::Misc::opcodeShowSceneGraph); interpreter.installSegment3>(Compiler::Misc::opcodeShowSceneGraphExplicit); interpreter.installSegment5(Compiler::Misc::opcodeToggleBorders); interpreter.installSegment5(Compiler::Misc::opcodeToggleNavMesh); interpreter.installSegment5(Compiler::Misc::opcodeToggleActorsPaths); interpreter.installSegment5(Compiler::Misc::opcodeSetNavMeshNumberToRender); interpreter.installSegment5>(Compiler::Misc::opcodeRepairedOnMe); interpreter.installSegment5>(Compiler::Misc::opcodeRepairedOnMeExplicit); interpreter.installSegment5(Compiler::Misc::opcodeToggleRecastMesh); interpreter.installSegment5(Compiler::Misc::opcodeHelp); interpreter.installSegment5(Compiler::Misc::opcodeReloadLua); } } }