#include "statsextensions.hpp" #include #include #include "../mwworld/esmstore.hpp" #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/spellcasting.hpp" #include "ref.hpp" namespace { std::string getDialogueActorFaction(const MWWorld::ConstPtr& actor) { std::string factionId = actor.getClass().getPrimaryFaction(actor); if (factionId.empty()) throw std::runtime_error ( "failed to determine dialogue actors faction (because actor is factionless)"); return factionId; } void modStat(MWMechanics::AttributeValue& stat, float amount) { float base = stat.getBase(); float modifier = stat.getModifier() - stat.getDamage(); float modified = base + modifier; if(modified <= 0.f && amount < 0.f) amount = 0.f; else if(amount < 0.f && modified + amount < 0.f) amount = -modified; else if((modifier <= 0.f || base >= 100.f) && amount > 0.f) amount = std::clamp(100.f - modified, 0.f, amount); stat.setBase(std::min(base + amount, 100.f), true); modifier += base - stat.getBase() + amount; stat.setModifier(modifier); } } namespace MWScript { namespace Stats { template class OpGetLevel : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass() .getCreatureStats (ptr) .getLevel(); runtime.push (value); } }; template class OpSetLevel : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); ptr.getClass() .getCreatureStats (ptr) .setLevel(value); } }; template class OpGetAttribute : public Interpreter::Opcode0 { int mIndex; public: OpGetAttribute (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) .getModified(); runtime.push (value); } }; template class OpSetAttribute : public Interpreter::Opcode0 { int mIndex; public: OpSetAttribute (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); attribute.setBase(value, true); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; template class OpModAttribute : public Interpreter::Opcode0 { int mIndex; public: OpModAttribute (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() .getCreatureStats(ptr) .getAttribute(mIndex); modStat(attribute, value); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; template class OpGetDynamic : public Interpreter::Opcode0 { int mIndex; public: OpGetDynamic (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value; if (mIndex==0 && ptr.getClass().hasItemHealth (ptr)) { // health is a special case value = static_cast(ptr.getClass().getItemMaxHealth(ptr)); } else { value = ptr.getClass() .getCreatureStats(ptr) .getDynamic(mIndex) .getCurrent(); // GetMagicka shouldn't return negative values if(mIndex == 1 && value < 0) value = 0; } runtime.push (value); } }; template class OpSetDynamic : public Interpreter::Opcode0 { int mIndex; public: OpSetDynamic (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); stat.setModified (value, 0); stat.setCurrent(value); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpModDynamic : public Interpreter::Opcode0 { int mIndex; public: OpModDynamic (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { int peek = R::implicit ? 0 : runtime[0].mInteger; MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); // workaround broken endgame scripts that kill dagoth ur if (!R::implicit && ::Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), "dagoth_ur_1")) { runtime.push (peek); if (R()(runtime, false, true).isEmpty()) { Log(Debug::Warning) << "Warning: Compensating for broken script in Morrowind.esm by " << "ignoring remote access to dagoth_ur_1"; return; } } MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); stat.setModified (diff + stat.getModified(), 0); stat.setCurrentModified (diff + stat.getCurrentModified()); stat.setCurrent (diff + current); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpModCurrentDynamic : public Interpreter::Opcode0 { int mIndex; public: OpModCurrentDynamic (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); MWMechanics::DynamicStat stat (ptr.getClass().getCreatureStats (ptr) .getDynamic (mIndex)); bool allowDecreaseBelowZero = false; if (mIndex == 2) // Fatigue-specific logic { // For fatigue, a negative current value is allowed and means the actor will be knocked down allowDecreaseBelowZero = true; // Knock down the actor immediately if a non-positive new value is the case if (diff + current <= 0.f) ptr.getClass().getCreatureStats(ptr).setKnockedDown(true); } stat.setCurrent (diff + current, allowDecreaseBelowZero); ptr.getClass().getCreatureStats (ptr).setDynamic (mIndex, stat); } }; template class OpGetDynamicGetRatio : public Interpreter::Opcode0 { int mIndex; public: OpGetDynamicGetRatio (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); Interpreter::Type_Float value = 0; Interpreter::Type_Float max = stats.getDynamic(mIndex).getModified(); if (max>0) value = stats.getDynamic(mIndex).getCurrent() / max; runtime.push (value); } }; template class OpGetSkill : public Interpreter::Opcode0 { int mIndex; public: OpGetSkill (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } }; template class OpSetSkill : public Interpreter::Opcode0 { int mIndex; public: OpSetSkill (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); stats.getSkill(mIndex).setBase(value, true); } }; template class OpModSkill : public Interpreter::Opcode0 { int mIndex; public: OpModSkill (int index) : mIndex (index) {} void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::SkillValue &skill = ptr.getClass() .getNpcStats(ptr) .getSkill(mIndex); modStat(skill, value); } }; class OpGetPCCrimeLevel : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); runtime.push (static_cast (player.getClass().getNpcStats (player).getBounty())); } }; class OpSetPCCrimeLevel : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); int bounty = static_cast(runtime[0].mFloat); runtime.pop(); player.getClass().getNpcStats (player).setBounty(bounty); if (bounty == 0) MWBase::Environment::get().getWorld()->getPlayer().recordCrimeId(); } }; class OpModPCCrimeLevel : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayerPtr(); player.getClass().getNpcStats(player).setBounty(static_cast(runtime[0].mFloat) + player.getClass().getNpcStats(player).getBounty()); runtime.pop(); } }; template class OpAddSpell : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find (id); MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); creatureStats.getSpells().add(id); ESM::Spell::SpellType type = static_cast(spell->mData.mType); if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Power) { // Add spell effect to *this actor's* queue immediately creatureStats.getActiveSpells().addSpell(spell, ptr); // Apply looping particles immediately for constant effects MWBase::Environment::get().getWorld()->applyLoopingParticles(ptr); } } }; template class OpRemoveSpell : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); creatureStats.getSpells().remove (id); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); if (ptr == MWMechanics::getPlayer() && id == wm->getSelectedSpell()) { wm->unsetSelectedSpell(); } } }; template class OpRemoveSpellEffects : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string spellid = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().removeEffects(ptr, spellid); } }; template class OpRemoveEffects : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer effectId = runtime[0].mInteger; runtime.pop(); ptr.getClass().getCreatureStats (ptr).getActiveSpells().purgeEffect(ptr, effectId); } }; template class OpGetSpell : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer value = 0; if (ptr.getClass().isActor() && ptr.getClass().getCreatureStats(ptr).getSpells().hasSpell(id)) value = 1; runtime.push (value); } }; template class OpPCJoinFaction : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).joinFaction(factionID); } } }; template class OpPCRaiseRank : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) == player.getClass().getNpcStats(player).getFactionRanks().end()) { player.getClass().getNpcStats(player).joinFaction(factionID); } else { player.getClass().getNpcStats(player).raiseRank(factionID); } } } }; template class OpPCLowerRank : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr actor = R()(runtime, false); std::string factionID = ""; if(arg0==0) { factionID = getDialogueActorFaction(actor); } else { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); if(factionID != "") { MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats(player).lowerRank(factionID); } } }; template class OpGetPCRank : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::lowerCaseInPlace(factionID); // Make sure this faction exists MWBase::Environment::get().getWorld()->getStore().get().find(factionID); MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { if(player.getClass().getNpcStats(player).getFactionRanks().find(factionID) != player.getClass().getNpcStats(player).getFactionRanks().end()) { runtime.push(player.getClass().getNpcStats(player).getFactionRanks().at(factionID)); } else { runtime.push(-1); } } else { runtime.push(-1); } } }; template class OpModDisposition : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); if (ptr.getClass().isNpc()) ptr.getClass().getNpcStats (ptr).setBaseDisposition (ptr.getClass().getNpcStats (ptr).getBaseDisposition() + value); // else: must not throw exception (used by an Almalexia dialogue script) } }; template class OpSetDisposition : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); if (ptr.getClass().isNpc()) ptr.getClass().getNpcStats (ptr).setBaseDisposition (value); } }; template class OpGetDisposition : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); if (!ptr.getClass().isNpc()) runtime.push(0); else runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); } }; class OpGetDeadCount : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { std::string id = runtime.getStringLiteral (runtime[0].mInteger); runtime[0].mInteger = MWBase::Environment::get().getMechanicsManager()->countDeaths (id); } }; template class OpGetPCFacRep : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); runtime.push ( player.getClass().getNpcStats (player).getFactionReputation (factionId)); } }; template class OpSetPCFacRep : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, value); } }; template class OpModPCFacRep : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); std::string factionId; if (arg0==1) { factionId = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionId = getDialogueActorFaction(ptr); } if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); ::Misc::StringUtils::lowerCaseInPlace (factionId); MWWorld::Ptr player = MWMechanics::getPlayer(); player.getClass().getNpcStats (player).setFactionReputation (factionId, player.getClass().getNpcStats (player).getFactionReputation (factionId)+ value); } }; template class OpGetCommonDisease : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getClass().getCreatureStats (ptr).hasCommonDisease()); } }; template class OpGetBlightDisease : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push (ptr.getClass().getCreatureStats (ptr).hasBlightDisease()); } }; template class OpGetRace : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::ConstPtr ptr = R()(runtime); std::string race = runtime.getStringLiteral(runtime[0].mInteger); ::Misc::StringUtils::lowerCaseInPlace(race); runtime.pop(); std::string npcRace = ptr.get()->mBase->mRace; ::Misc::StringUtils::lowerCaseInPlace(npcRace); runtime.push (npcRace == race); } }; class OpGetWerewolfKills : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayerPtr(); runtime.push (ptr.getClass().getNpcStats (ptr).getWerewolfKills ()); } }; template class OpPcExpelled : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } ::Misc::StringUtils::lowerCaseInPlace(factionID); MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { runtime.push(player.getClass().getNpcStats(player).getExpelled(factionID)); } else { runtime.push(0); } } }; template class OpPcExpell : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") { player.getClass().getNpcStats(player).expell(factionID); } } }; template class OpPcClearExpelled : public Interpreter::Opcode1 { public: void execute (Interpreter::Runtime& runtime, unsigned int arg0) override { MWWorld::ConstPtr ptr = R()(runtime, false); std::string factionID = ""; if(arg0 >0 ) { factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } else { factionID = ptr.getClass().getPrimaryFaction(ptr); } MWWorld::Ptr player = MWMechanics::getPlayer(); if(factionID!="") player.getClass().getNpcStats(player).clearExpelled(factionID); } }; template class OpRaiseRank : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string factionID = ptr.getClass().getPrimaryFaction(ptr); if(factionID.empty()) return; MWWorld::Ptr player = MWMechanics::getPlayer(); // no-op when executed on the player if (ptr == player) return; // If we already changed rank for this NPC, modify current rank in the NPC stats. // Otherwise take rank from base NPC record, increase it and put it to NPC data. int currentRank = ptr.getClass().getNpcStats(ptr).getFactionRank(factionID); if (currentRank >= 0) ptr.getClass().getNpcStats(ptr).raiseRank(factionID); else { int rank = ptr.getClass().getPrimaryFactionRank(ptr); rank++; ptr.getClass().getNpcStats(ptr).joinFaction(factionID); for (int i=0; i class OpLowerRank : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); std::string factionID = ptr.getClass().getPrimaryFaction(ptr); if(factionID.empty()) return; MWWorld::Ptr player = MWMechanics::getPlayer(); // no-op when executed on the player if (ptr == player) return; // If we already changed rank for this NPC, modify current rank in the NPC stats. // Otherwise take rank from base NPC record, decrease it and put it to NPC data. int currentRank = ptr.getClass().getNpcStats(ptr).getFactionRank(factionID); if (currentRank == 0) return; else if (currentRank > 0) ptr.getClass().getNpcStats(ptr).lowerRank(factionID); else { int rank = ptr.getClass().getPrimaryFactionRank(ptr); rank--; ptr.getClass().getNpcStats(ptr).joinFaction(factionID); for (int i=0; i class OpOnDeath : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).hasDied(); if (value) ptr.getClass().getCreatureStats (ptr).clearHasDied(); runtime.push (value); } }; template class OpOnMurder : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).hasBeenMurdered(); if (value) ptr.getClass().getCreatureStats (ptr).clearHasBeenMurdered(); runtime.push (value); } }; template class OpOnKnockout : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); Interpreter::Type_Integer value = ptr.getClass().getCreatureStats (ptr).getKnockedDownOneFrame(); runtime.push (value); } }; template class OpIsWerewolf : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); runtime.push(ptr.getClass().getNpcStats(ptr).isWerewolf()); } }; template class OpSetWerewolf : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getMechanicsManager()->setWerewolf(ptr, set); } }; template class OpSetWerewolfAcrobatics : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); MWBase::Environment::get().getMechanicsManager()->applyWerewolfAcrobatics(ptr); } }; template class OpResurrect : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); if (ptr == MWMechanics::getPlayer()) { MWBase::Environment::get().getMechanicsManager()->resurrect(ptr); if (MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Ended) MWBase::Environment::get().getStateManager()->resumeGame(); } else if (ptr.getClass().getCreatureStats(ptr).isDead()) { bool wasEnabled = ptr.getRefData().isEnabled(); MWBase::Environment::get().getWorld()->undeleteObject(ptr); auto windowManager = MWBase::Environment::get().getWindowManager(); bool wasOpen = windowManager->containsMode(MWGui::GM_Container); windowManager->onDeleteCustomData(ptr); // HACK: disable/enable object to re-add it to the scene properly (need a new Animation). MWBase::Environment::get().getWorld()->disable(ptr); if (wasOpen && !windowManager->containsMode(MWGui::GM_Container)) { // Reopen the loot GUI if it was closed because we resurrected the actor we were looting MWBase::Environment::get().getMechanicsManager()->resurrect(ptr); windowManager->forceLootMode(ptr); } else { MWBase::Environment::get().getWorld()->removeContainerScripts(ptr); // resets runtime state such as inventory, stats and AI. does not reset position in the world ptr.getRefData().setCustomData(nullptr); } if (wasEnabled) MWBase::Environment::get().getWorld()->enable(ptr); } } }; template class OpGetStat : public Interpreter::Opcode0 { public: void execute (Interpreter::Runtime& runtime) override { // dummy runtime.push(0); } }; template class OpGetMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpGetMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } void execute (Interpreter::Runtime& runtime) override { MWWorld::Ptr ptr = R()(runtime); const MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.get(mPositiveEffect).getMagnitude(); if (mNegativeEffect != -1) currentValue -= effects.get(mNegativeEffect).getMagnitude(); // GetResist* should take in account elemental shields if (mPositiveEffect == ESM::MagicEffect::ResistFire) currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude(); if (mPositiveEffect == ESM::MagicEffect::ResistShock) currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude(); if (mPositiveEffect == ESM::MagicEffect::ResistFrost) currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude(); int ret = static_cast(currentValue); runtime.push(ret); } }; template class OpSetMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpSetMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } void execute(Interpreter::Runtime &runtime) override { MWWorld::Ptr ptr = R()(runtime); MWMechanics::MagicEffects& effects = ptr.getClass().getCreatureStats(ptr).getMagicEffects(); float currentValue = effects.get(mPositiveEffect).getMagnitude(); if (mNegativeEffect != -1) currentValue -= effects.get(mNegativeEffect).getMagnitude(); // SetResist* should take in account elemental shields if (mPositiveEffect == ESM::MagicEffect::ResistFire) currentValue += effects.get(ESM::MagicEffect::FireShield).getMagnitude(); if (mPositiveEffect == ESM::MagicEffect::ResistShock) currentValue += effects.get(ESM::MagicEffect::LightningShield).getMagnitude(); if (mPositiveEffect == ESM::MagicEffect::ResistFrost) currentValue += effects.get(ESM::MagicEffect::FrostShield).getMagnitude(); int arg = runtime[0].mInteger; runtime.pop(); effects.modifyBase(mPositiveEffect, (arg - static_cast(currentValue))); } }; template class OpModMagicEffect : public Interpreter::Opcode0 { int mPositiveEffect; int mNegativeEffect; public: OpModMagicEffect (int positiveEffect, int negativeEffect) : mPositiveEffect(positiveEffect) , mNegativeEffect(negativeEffect) { } void execute(Interpreter::Runtime &runtime) override { MWWorld::Ptr ptr = R()(runtime); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); int arg = runtime[0].mInteger; runtime.pop(); stats.getMagicEffects().modifyBase(mPositiveEffect, arg); } }; struct MagicEffect { int mPositiveEffect; int mNegativeEffect; }; void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i>(Compiler::Stats::opcodeGetAttribute + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeGetAttributeExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetAttribute + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetAttributeExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModAttribute + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModAttributeExplicit + i, i); } for (int i=0; i>(Compiler::Stats::opcodeGetDynamic + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeGetDynamicExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetDynamic + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetDynamicExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModDynamic + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModDynamicExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModCurrentDynamic + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModCurrentDynamicExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeGetDynamicGetRatio + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeGetDynamicGetRatioExplicit + i, i); } for (int i=0; i>(Compiler::Stats::opcodeGetSkill + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeGetSkillExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetSkill + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeSetSkillExplicit + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModSkill + i, i); interpreter.installSegment5>(Compiler::Stats::opcodeModSkillExplicit + i, i); } interpreter.installSegment5(Compiler::Stats::opcodeGetPCCrimeLevel); interpreter.installSegment5(Compiler::Stats::opcodeSetPCCrimeLevel); interpreter.installSegment5(Compiler::Stats::opcodeModPCCrimeLevel); interpreter.installSegment5>(Compiler::Stats::opcodeAddSpell); interpreter.installSegment5>(Compiler::Stats::opcodeAddSpellExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveSpell); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveSpellExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveSpellEffects); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveSpellEffectsExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeResurrect); interpreter.installSegment5>(Compiler::Stats::opcodeResurrectExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveEffects); interpreter.installSegment5>(Compiler::Stats::opcodeRemoveEffectsExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetSpell); interpreter.installSegment5>(Compiler::Stats::opcodeGetSpellExplicit); interpreter.installSegment3>(Compiler::Stats::opcodePCRaiseRank); interpreter.installSegment3>(Compiler::Stats::opcodePCLowerRank); interpreter.installSegment3>(Compiler::Stats::opcodePCJoinFaction); interpreter.installSegment3>(Compiler::Stats::opcodePCRaiseRankExplicit); interpreter.installSegment3>(Compiler::Stats::opcodePCLowerRankExplicit); interpreter.installSegment3>(Compiler::Stats::opcodePCJoinFactionExplicit); interpreter.installSegment3>(Compiler::Stats::opcodeGetPCRank); interpreter.installSegment3>(Compiler::Stats::opcodeGetPCRankExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeModDisposition); interpreter.installSegment5>(Compiler::Stats::opcodeModDispositionExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeSetDisposition); interpreter.installSegment5>(Compiler::Stats::opcodeSetDispositionExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetDisposition); interpreter.installSegment5>(Compiler::Stats::opcodeGetDispositionExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetLevel); interpreter.installSegment5>(Compiler::Stats::opcodeGetLevelExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeSetLevel); interpreter.installSegment5>(Compiler::Stats::opcodeSetLevelExplicit); interpreter.installSegment5(Compiler::Stats::opcodeGetDeadCount); interpreter.installSegment3>(Compiler::Stats::opcodeGetPCFacRep); interpreter.installSegment3>(Compiler::Stats::opcodeGetPCFacRepExplicit); interpreter.installSegment3>(Compiler::Stats::opcodeSetPCFacRep); interpreter.installSegment3>(Compiler::Stats::opcodeSetPCFacRepExplicit); interpreter.installSegment3>(Compiler::Stats::opcodeModPCFacRep); interpreter.installSegment3>(Compiler::Stats::opcodeModPCFacRepExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetCommonDisease); interpreter.installSegment5>(Compiler::Stats::opcodeGetCommonDiseaseExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetBlightDisease); interpreter.installSegment5>(Compiler::Stats::opcodeGetBlightDiseaseExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetRace); interpreter.installSegment5>(Compiler::Stats::opcodeGetRaceExplicit); interpreter.installSegment5(Compiler::Stats::opcodeGetWerewolfKills); interpreter.installSegment3>(Compiler::Stats::opcodePcExpelled); interpreter.installSegment3>(Compiler::Stats::opcodePcExpelledExplicit); interpreter.installSegment3>(Compiler::Stats::opcodePcExpell); interpreter.installSegment3>(Compiler::Stats::opcodePcExpellExplicit); interpreter.installSegment3>(Compiler::Stats::opcodePcClearExpelled); interpreter.installSegment3>(Compiler::Stats::opcodePcClearExpelledExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeRaiseRank); interpreter.installSegment5>(Compiler::Stats::opcodeRaiseRankExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeLowerRank); interpreter.installSegment5>(Compiler::Stats::opcodeLowerRankExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeOnDeath); interpreter.installSegment5>(Compiler::Stats::opcodeOnDeathExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeOnMurder); interpreter.installSegment5>(Compiler::Stats::opcodeOnMurderExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeOnKnockout); interpreter.installSegment5>(Compiler::Stats::opcodeOnKnockoutExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeIsWerewolf); interpreter.installSegment5>(Compiler::Stats::opcodeIsWerewolfExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeBecomeWerewolf); interpreter.installSegment5>(Compiler::Stats::opcodeBecomeWerewolfExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeUndoWerewolf); interpreter.installSegment5>(Compiler::Stats::opcodeUndoWerewolfExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeSetWerewolfAcrobatics); interpreter.installSegment5>(Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit); interpreter.installSegment5>(Compiler::Stats::opcodeGetStat); interpreter.installSegment5>(Compiler::Stats::opcodeGetStatExplicit); static const MagicEffect sMagicEffects[] = { { ESM::MagicEffect::ResistMagicka, ESM::MagicEffect::WeaknessToMagicka }, { ESM::MagicEffect::ResistFire, ESM::MagicEffect::WeaknessToFire }, { ESM::MagicEffect::ResistFrost, ESM::MagicEffect::WeaknessToFrost }, { ESM::MagicEffect::ResistShock, ESM::MagicEffect::WeaknessToShock }, { ESM::MagicEffect::ResistCommonDisease, ESM::MagicEffect::WeaknessToCommonDisease }, { ESM::MagicEffect::ResistBlightDisease, ESM::MagicEffect::WeaknessToBlightDisease }, { ESM::MagicEffect::ResistCorprusDisease, ESM::MagicEffect::WeaknessToCorprusDisease }, { ESM::MagicEffect::ResistPoison, ESM::MagicEffect::WeaknessToPoison }, { ESM::MagicEffect::ResistParalysis, -1 }, { ESM::MagicEffect::ResistNormalWeapons, ESM::MagicEffect::WeaknessToNormalWeapons }, { ESM::MagicEffect::WaterBreathing, -1 }, { ESM::MagicEffect::Chameleon, -1 }, { ESM::MagicEffect::WaterWalking, -1 }, { ESM::MagicEffect::SwiftSwim, -1 }, { ESM::MagicEffect::Jump, -1 }, { ESM::MagicEffect::Levitate, -1 }, { ESM::MagicEffect::Shield, -1 }, { ESM::MagicEffect::Sound, -1 }, { ESM::MagicEffect::Silence, -1 }, { ESM::MagicEffect::Blind, -1 }, { ESM::MagicEffect::Paralyze, -1 }, { ESM::MagicEffect::Invisibility, -1 }, { ESM::MagicEffect::FortifyAttack, -1 }, { ESM::MagicEffect::Sanctuary, -1 }, }; for (int i=0; i<24; ++i) { int positive = sMagicEffects[i].mPositiveEffect; int negative = sMagicEffects[i].mNegativeEffect; interpreter.installSegment5>(Compiler::Stats::opcodeGetMagicEffect + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeGetMagicEffectExplicit + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeSetMagicEffect + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeSetMagicEffectExplicit + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeModMagicEffect + i, positive, negative); interpreter.installSegment5>(Compiler::Stats::opcodeModMagicEffectExplicit + i, positive, negative); } } } }