mirror of
https://github.com/OpenMW/openmw.git
synced 2025-12-27 20:13:08 +00:00
Merge branch openmw:master into master
This commit is contained in:
commit
3600a5c716
33 changed files with 253 additions and 166 deletions
|
|
@ -242,11 +242,36 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
|||
{
|
||||
if (mode == Settings::WindowMode::Fullscreen || mode == Settings::WindowMode::WindowedFullscreen)
|
||||
{
|
||||
QString customSizeMessage = tr("Custom window size is available only in Windowed mode.");
|
||||
QString windowBorderMessage = tr("Window border is available only in Windowed mode.");
|
||||
|
||||
standardRadioButton->toggle();
|
||||
customRadioButton->setEnabled(false);
|
||||
customWidthSpinBox->setEnabled(false);
|
||||
customHeightSpinBox->setEnabled(false);
|
||||
windowBorderCheckBox->setEnabled(false);
|
||||
windowBorderCheckBox->setToolTip(windowBorderMessage);
|
||||
customWidthSpinBox->setToolTip(customSizeMessage);
|
||||
customHeightSpinBox->setToolTip(customSizeMessage);
|
||||
customRadioButton->setToolTip(customSizeMessage);
|
||||
}
|
||||
|
||||
if (mode == Settings::WindowMode::Fullscreen)
|
||||
{
|
||||
resolutionComboBox->setEnabled(true);
|
||||
resolutionComboBox->setToolTip("");
|
||||
standardRadioButton->setToolTip("");
|
||||
}
|
||||
else if (mode == Settings::WindowMode::WindowedFullscreen)
|
||||
{
|
||||
QString fullScreenMessage = tr("Windowed Fullscreen mode always uses the native display resolution.");
|
||||
|
||||
resolutionComboBox->setEnabled(false);
|
||||
resolutionComboBox->setToolTip(fullScreenMessage);
|
||||
standardRadioButton->setToolTip(fullScreenMessage);
|
||||
|
||||
// Assume that a first item is a native screen resolution
|
||||
resolutionComboBox->setCurrentIndex(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -254,6 +279,13 @@ void Launcher::GraphicsPage::handleWindowModeChange(Settings::WindowMode mode)
|
|||
customWidthSpinBox->setEnabled(true);
|
||||
customHeightSpinBox->setEnabled(true);
|
||||
windowBorderCheckBox->setEnabled(true);
|
||||
resolutionComboBox->setEnabled(true);
|
||||
resolutionComboBox->setToolTip("");
|
||||
standardRadioButton->setToolTip("");
|
||||
windowBorderCheckBox->setToolTip("");
|
||||
customWidthSpinBox->setToolTip("");
|
||||
customHeightSpinBox->setToolTip("");
|
||||
customRadioButton->setToolTip("");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -360,16 +360,12 @@ namespace MWClass
|
|||
{
|
||||
stats.setAttacked(true);
|
||||
|
||||
// No retaliation for totally static creatures (they have no movement or attacks anyway)
|
||||
if (isMobile(ptr))
|
||||
{
|
||||
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
||||
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
||||
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
||||
setOnPcHitMe = false;
|
||||
else
|
||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
}
|
||||
bool complain = sourceType == MWMechanics::DamageSourceType::Melee;
|
||||
bool supportFriendlyFire = sourceType != MWMechanics::DamageSourceType::Ranged;
|
||||
if (supportFriendlyFire && MWMechanics::friendlyHit(attacker, ptr, complain))
|
||||
setOnPcHitMe = false;
|
||||
else
|
||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||
}
|
||||
|
||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||
|
|
|
|||
|
|
@ -270,14 +270,14 @@ namespace MWClass
|
|||
|
||||
std::pair<int, std::string_view> Weapon::canBeEquipped(const MWWorld::ConstPtr& ptr, const MWWorld::Ptr& npc) const
|
||||
{
|
||||
if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)
|
||||
return { 0, "#{sInventoryMessage1}" };
|
||||
|
||||
// Do not allow equip weapons from inventory during attack
|
||||
if (npc.isInCell() && MWBase::Environment::get().getWindowManager()->isGuiMode()
|
||||
&& MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
||||
return { 0, "#{sCantEquipWeapWarning}" };
|
||||
|
||||
if (hasItemHealth(ptr) && getItemHealth(ptr) == 0)
|
||||
return { 0, "#{sInventoryMessage1}" };
|
||||
|
||||
std::pair<std::vector<int>, bool> slots_ = getEquipmentSlots(ptr);
|
||||
|
||||
if (slots_.first.empty())
|
||||
|
|
|
|||
|
|
@ -614,36 +614,25 @@ namespace MWGui
|
|||
}
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
auto type = ptr.getType();
|
||||
bool isWeaponOrArmor = type == ESM::Weapon::sRecordId || type == ESM::Armor::sRecordId;
|
||||
bool isBroken = ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0;
|
||||
|
||||
// early-out for items that need to be equipped, but can't be equipped: we don't want to set OnPcEquip in that
|
||||
// case
|
||||
if (!ptr.getClass().getEquipmentSlots(ptr).first.empty())
|
||||
// In vanilla, broken armor or weapons cannot be equipped
|
||||
// tools with 0 charges is equippable
|
||||
if (isBroken && isWeaponOrArmor)
|
||||
{
|
||||
if (ptr.getClass().hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
auto canEquip = ptr.getClass().canBeEquipped(ptr, player);
|
||||
|
||||
if (canEquip.first == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
}
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
return;
|
||||
}
|
||||
|
||||
bool canEquip = ptr.getClass().canBeEquipped(ptr, mPtr).first != 0;
|
||||
bool shouldSetOnPcEquip = canEquip || force;
|
||||
|
||||
// If the item has a script, set OnPCEquip or PCSkipEquip to 1
|
||||
if (!script.empty())
|
||||
if (!script.empty() && shouldSetOnPcEquip)
|
||||
{
|
||||
// Ingredients, books and repair hammers must not have OnPCEquip set to 1 here
|
||||
auto type = ptr.getType();
|
||||
bool isBook = type == ESM::Book::sRecordId;
|
||||
if (!isBook && type != ESM::Ingredient::sRecordId && type != ESM::Repair::sRecordId)
|
||||
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
|
||||
|
|
@ -653,7 +642,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
std::unique_ptr<MWWorld::Action> action = ptr.getClass().use(ptr, force);
|
||||
action->execute(player);
|
||||
action->execute(player, !canEquip);
|
||||
|
||||
// Handles partial equipping (final part)
|
||||
if (mEquippedStackableCount.has_value())
|
||||
|
|
@ -684,6 +673,14 @@ namespace MWGui
|
|||
{
|
||||
MWWorld::Ptr ptr = mDragAndDrop->mItem.mBase;
|
||||
|
||||
auto [canEquipRes, canEquipMsg] = ptr.getClass().canBeEquipped(ptr, mPtr);
|
||||
if (canEquipRes == 0) // cannot equip
|
||||
{
|
||||
mDragAndDrop->drop(mTradeModel, mItemView); // also plays down sound
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquipMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
mDragAndDrop->finish();
|
||||
|
||||
if (mDragAndDrop->mSourceModel != mTradeModel)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ namespace MWGui
|
|||
: WindowBase("openmw_jail_screen.layout")
|
||||
, mDays(1)
|
||||
, mFadeTimeRemaining(0)
|
||||
, mTimeAdvancer(0.01f)
|
||||
{
|
||||
getWidget(mProgressBar, "ProgressBar");
|
||||
|
||||
|
|
|
|||
|
|
@ -90,9 +90,8 @@ namespace MWGui
|
|||
case ESM::QuickKeys::Type::MagicItem:
|
||||
{
|
||||
MWWorld::Ptr item = *mKey[index].button->getUserData<MWWorld::Ptr>();
|
||||
// Make sure the item is available and is not broken
|
||||
if (item.isEmpty() || item.getCellRef().getCount() < 1
|
||||
|| (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))
|
||||
// Make sure the item is available
|
||||
if (item.isEmpty() || item.getCellRef().getCount() < 1)
|
||||
{
|
||||
// Try searching for a compatible replacement
|
||||
item = store.findReplacement(mKey[index].id);
|
||||
|
|
@ -259,6 +258,8 @@ namespace MWGui
|
|||
|
||||
if (mItemSelectionDialog)
|
||||
mItemSelectionDialog->setVisible(false);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound(item.getClass().getDownSoundId(item));
|
||||
}
|
||||
|
||||
void QuickKeysMenu::onAssignItemCancel()
|
||||
|
|
@ -395,23 +396,16 @@ namespace MWGui
|
|||
if (it == store.end())
|
||||
item = nullptr;
|
||||
|
||||
// check the item is available and not broken
|
||||
if (item.isEmpty() || item.getCellRef().getCount() < 1
|
||||
|| (item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))
|
||||
// check the quickkey item is available
|
||||
if (item.isEmpty() || item.getCellRef().getCount() < 1)
|
||||
{
|
||||
item = store.findReplacement(key->id);
|
||||
|
||||
if (item.isEmpty() || item.getCellRef().getCount() < 1)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name);
|
||||
|
||||
return;
|
||||
}
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + key->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key->type == ESM::QuickKeys::Type::Item)
|
||||
{
|
||||
if (!store.isEquipped(item))
|
||||
if (!store.isEquipped(item.getCellRef().getRefId()))
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
MWWorld::ConstContainerStoreIterator rightHand
|
||||
= store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ namespace MWGui
|
|||
&& !base.get<ESM::Book>()->mBase->mData.mIsScroll)
|
||||
return false;
|
||||
|
||||
if ((mFilter & Filter_OnlyUsableItems) && base.getClass().getScript(base).empty())
|
||||
if ((mFilter & Filter_OnlyUsableItems))
|
||||
{
|
||||
std::unique_ptr<MWWorld::Action> actionOnUse = base.getClass().use(base);
|
||||
if (!actionOnUse || actionOnUse->isNullAction())
|
||||
|
|
|
|||
|
|
@ -1,14 +1,19 @@
|
|||
#include "timeadvancer.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Time per hour tick
|
||||
constexpr float kProgressStepDelay = 1.0f / 60.0f;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
TimeAdvancer::TimeAdvancer(float delay)
|
||||
TimeAdvancer::TimeAdvancer()
|
||||
: mRunning(false)
|
||||
, mCurHour(0)
|
||||
, mHours(1)
|
||||
, mInterruptAt(-1)
|
||||
, mDelay(delay)
|
||||
, mRemainingTime(delay)
|
||||
, mRemainingTime(kProgressStepDelay)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +22,7 @@ namespace MWGui
|
|||
mHours = hours;
|
||||
mCurHour = 0;
|
||||
mInterruptAt = interruptAt;
|
||||
mRemainingTime = mDelay;
|
||||
mRemainingTime = kProgressStepDelay;
|
||||
|
||||
mRunning = true;
|
||||
}
|
||||
|
|
@ -43,7 +48,7 @@ namespace MWGui
|
|||
|
||||
while (mRemainingTime <= 0)
|
||||
{
|
||||
mRemainingTime += mDelay;
|
||||
mRemainingTime += kProgressStepDelay;
|
||||
++mCurHour;
|
||||
|
||||
if (mCurHour <= mHours)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace MWGui
|
|||
class TimeAdvancer
|
||||
{
|
||||
public:
|
||||
TimeAdvancer(float delay);
|
||||
TimeAdvancer();
|
||||
|
||||
void run(int hours, int interruptAt = -1);
|
||||
void stop();
|
||||
|
|
@ -32,7 +32,6 @@ namespace MWGui
|
|||
int mHours;
|
||||
int mInterruptAt;
|
||||
|
||||
float mDelay;
|
||||
float mRemainingTime;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ namespace MWGui
|
|||
|
||||
TrainingWindow::TrainingWindow()
|
||||
: WindowBase("openmw_trainingwindow.layout")
|
||||
, mTimeAdvancer(0.05f)
|
||||
{
|
||||
getWidget(mTrainingOptions, "TrainingOptions");
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ namespace MWGui
|
|||
|
||||
WaitDialog::WaitDialog()
|
||||
: WindowBase("openmw_wait_dialog.layout")
|
||||
, mTimeAdvancer(0.05f)
|
||||
, mSleeping(false)
|
||||
, mHours(1)
|
||||
, mManualHours(1)
|
||||
|
|
|
|||
|
|
@ -273,13 +273,14 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
bool updateSpellWindow = false;
|
||||
bool playNonLooping = false;
|
||||
if (ptr.getClass().hasInventoryStore(ptr)
|
||||
&& !(creatureStats.isDead() && !creatureStats.isDeathAnimationFinished()))
|
||||
{
|
||||
auto& store = ptr.getClass().getInventoryStore(ptr);
|
||||
if (store.getInvListener() != nullptr)
|
||||
{
|
||||
bool playNonLooping = !store.isFirstEquip();
|
||||
playNonLooping = !store.isFirstEquip();
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
for (int slotIndex = 0; slotIndex < MWWorld::InventoryStore::Slots; slotIndex++)
|
||||
{
|
||||
|
|
@ -307,9 +308,6 @@ namespace MWMechanics
|
|||
applyPurges(ptr);
|
||||
ActiveSpellParams& params = mSpells.emplace_back(ActiveSpellParams{ *slot, enchantment, ptr });
|
||||
params.setActiveSpellId(MWBase::Environment::get().getESMStore()->generateId());
|
||||
for (const auto& effect : params.mEffects)
|
||||
MWMechanics::playEffects(
|
||||
ptr, *world->getStore().get<ESM::MagicEffect>().find(effect.mEffectId), playNonLooping);
|
||||
updateSpellWindow = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -327,7 +325,7 @@ namespace MWMechanics
|
|||
std::optional<ActiveSpellParams> reflected;
|
||||
for (auto it = spellIt->mEffects.begin(); it != spellIt->mEffects.end();)
|
||||
{
|
||||
auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration);
|
||||
auto result = applyMagicEffect(ptr, caster, *spellIt, *it, duration, playNonLooping);
|
||||
if (result.mType == MagicApplicationResult::Type::REFLECTED)
|
||||
{
|
||||
if (!reflected)
|
||||
|
|
|
|||
|
|
@ -605,10 +605,6 @@ namespace MWMechanics
|
|||
void Actors::engageCombat(
|
||||
const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, SidingCache& cachedAllies, bool againstPlayer) const
|
||||
{
|
||||
// No combat for totally static creatures
|
||||
if (!actor1.getClass().isMobile(actor1))
|
||||
return;
|
||||
|
||||
CreatureStats& creatureStats1 = actor1.getClass().getCreatureStats(actor1);
|
||||
if (creatureStats1.isDead() || creatureStats1.getAiSequence().isInCombat(actor2))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -104,10 +104,10 @@ namespace MWMechanics
|
|||
bool AiCombat::execute(
|
||||
const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
{
|
||||
// get or create temporary storage
|
||||
// Get or create temporary storage
|
||||
AiCombatStorage& storage = state.get<AiCombatStorage>();
|
||||
|
||||
// General description
|
||||
// No combat for dead creatures
|
||||
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||
return true;
|
||||
|
||||
|
|
@ -124,6 +124,13 @@ namespace MWMechanics
|
|||
if (actor == target) // This should never happen.
|
||||
return true;
|
||||
|
||||
// No actions for totally static creatures
|
||||
if (!actor.getClass().isMobile(actor))
|
||||
{
|
||||
storage.mFleeState = AiCombatStorage::FleeState_Idle;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!storage.isFleeing())
|
||||
{
|
||||
if (storage.mCurrentAction.get()) // need to wait to init action with its attack range
|
||||
|
|
|
|||
|
|
@ -914,7 +914,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
|
||||
ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt)
|
||||
ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt, bool playNonLooping)
|
||||
{
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
bool invalid = false;
|
||||
|
|
@ -1032,9 +1032,12 @@ namespace MWMechanics
|
|||
oldMagnitude = effect.mMagnitude;
|
||||
else
|
||||
{
|
||||
if (!spellParams.hasFlag(ESM::ActiveSpells::Flag_Equipment)
|
||||
&& !spellParams.hasFlag(ESM::ActiveSpells::Flag_Lua))
|
||||
playEffects(target, *magicEffect, spellParams.hasFlag(ESM::ActiveSpells::Flag_Temporary));
|
||||
bool isTemporary = spellParams.hasFlag(ESM::ActiveSpells::Flag_Temporary);
|
||||
bool isEquipment = spellParams.hasFlag(ESM::ActiveSpells::Flag_Equipment);
|
||||
|
||||
if (!spellParams.hasFlag(ESM::ActiveSpells::Flag_Lua))
|
||||
playEffects(target, *magicEffect, (isTemporary || (isEquipment && playNonLooping)));
|
||||
|
||||
if (effect.mEffectId == ESM::MagicEffect::Soultrap && !target.getClass().isNpc()
|
||||
&& target.getType() == ESM::Creature::sRecordId
|
||||
&& target.get<ESM::Creature>()->mBase->mData.mSoul == 0 && caster == getPlayer())
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ namespace MWMechanics
|
|||
|
||||
// Applies a tick of a single effect. Returns true if the effect should be removed immediately
|
||||
MagicApplicationResult applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
|
||||
ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt);
|
||||
ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, float dt,
|
||||
bool playNonLoopingEffect = true);
|
||||
|
||||
// Undoes permanent effects created by ESM::MagicEffect::AppliedOnce
|
||||
void onMagicEffectRemoved(
|
||||
|
|
|
|||
|
|
@ -280,15 +280,6 @@ namespace MWRender
|
|||
{
|
||||
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||
|
||||
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
|
||||
{
|
||||
state.setActiveTextureUnit(0);
|
||||
state.applyTextureAttribute(0,
|
||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
.getTexture());
|
||||
ext->glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
lastApplied = pass.mRenderTarget->getHandle(state.getContextID());
|
||||
}
|
||||
else if (pass.mResolve && index == filtered.back())
|
||||
|
|
@ -325,6 +316,15 @@ namespace MWRender
|
|||
|
||||
drawGeometry(renderInfo);
|
||||
|
||||
if (pass.mRenderTarget && pass.mRenderTexture->getNumMipmapLevels() > 0)
|
||||
{
|
||||
state.setActiveTextureUnit(0);
|
||||
state.applyTextureAttribute(0,
|
||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
.getTexture());
|
||||
ext->glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
state.popStateSet();
|
||||
state.apply();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1146,9 +1146,18 @@ namespace MWScript
|
|||
{
|
||||
void printLocalVars(Interpreter::Runtime& runtime, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::stringstream str;
|
||||
std::ostringstream str;
|
||||
|
||||
const ESM::RefId& script = ptr.getClass().getScript(ptr);
|
||||
|
||||
auto printVariables = [&str](const auto& names, const auto& values, std::string_view type) {
|
||||
size_t size = std::min(names.size(), values.size());
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
str << "\n " << names[i] << " = " << values[i] << " (" << type << ")";
|
||||
}
|
||||
};
|
||||
|
||||
if (script.empty())
|
||||
str << ptr.getCellRef().getRefId() << " does not have a script.";
|
||||
else
|
||||
|
|
@ -1159,27 +1168,9 @@ namespace MWScript
|
|||
const Compiler::Locals& complocals
|
||||
= MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||
|
||||
const std::vector<std::string>* names = &complocals.get('s');
|
||||
for (size_t i = 0; i < names->size(); ++i)
|
||||
{
|
||||
if (i >= locals.mShorts.size())
|
||||
break;
|
||||
str << std::endl << " " << (*names)[i] << " = " << locals.mShorts[i] << " (short)";
|
||||
}
|
||||
names = &complocals.get('l');
|
||||
for (size_t i = 0; i < names->size(); ++i)
|
||||
{
|
||||
if (i >= locals.mLongs.size())
|
||||
break;
|
||||
str << std::endl << " " << (*names)[i] << " = " << locals.mLongs[i] << " (long)";
|
||||
}
|
||||
names = &complocals.get('f');
|
||||
for (size_t i = 0; i < names->size(); ++i)
|
||||
{
|
||||
if (i >= locals.mFloats.size())
|
||||
break;
|
||||
str << std::endl << " " << (*names)[i] << " = " << locals.mFloats[i] << " (float)";
|
||||
}
|
||||
printVariables(complocals.get('s'), locals.mShorts, "short");
|
||||
printVariables(complocals.get('l'), locals.mLongs, "long");
|
||||
printVariables(complocals.get('f'), locals.mFloats, "float");
|
||||
}
|
||||
|
||||
runtime.getContext().report(str.str());
|
||||
|
|
@ -1187,50 +1178,43 @@ namespace MWScript
|
|||
|
||||
void printGlobalVars(Interpreter::Runtime& runtime)
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "Global variables:";
|
||||
std::ostringstream str;
|
||||
str << "Global Variables:";
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
std::vector<std::string> names = runtime.getContext().getGlobals();
|
||||
auto& context = runtime.getContext();
|
||||
std::vector<std::string> names = context.getGlobals();
|
||||
|
||||
// sort for user convenience
|
||||
std::sort(names.begin(), names.end());
|
||||
|
||||
for (size_t i = 0; i < names.size(); ++i)
|
||||
{
|
||||
char type = world->getGlobalVariableType(names[i]);
|
||||
str << std::endl << " " << names[i] << " = ";
|
||||
std::sort(names.begin(), names.end(), ::Misc::StringUtils::ciLess);
|
||||
|
||||
auto printVariable = [&str, &context](const std::string& name, char type) {
|
||||
str << "\n " << name << " = ";
|
||||
switch (type)
|
||||
{
|
||||
case 's':
|
||||
|
||||
str << runtime.getContext().getGlobalShort(names[i]) << " (short)";
|
||||
str << context.getGlobalShort(name) << " (short)";
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
|
||||
str << runtime.getContext().getGlobalLong(names[i]) << " (long)";
|
||||
str << context.getGlobalLong(name) << " (long)";
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
|
||||
str << runtime.getContext().getGlobalFloat(names[i]) << " (float)";
|
||||
str << context.getGlobalFloat(name) << " (float)";
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
str << "<unknown type>";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
runtime.getContext().report(str.str());
|
||||
for (const auto& name : names)
|
||||
printVariable(name, world->getGlobalVariableType(name));
|
||||
|
||||
context.report(str.str());
|
||||
}
|
||||
|
||||
void printGlobalScriptsVars(Interpreter::Runtime& runtime)
|
||||
{
|
||||
std::stringstream str;
|
||||
str << std::endl << "Global Scripts:";
|
||||
std::ostringstream str;
|
||||
str << "\nGlobal Scripts:";
|
||||
|
||||
const auto& scripts = MWBase::Environment::get().getScriptManager()->getGlobalScripts().getScripts();
|
||||
|
||||
|
|
@ -1238,12 +1222,11 @@ namespace MWScript
|
|||
std::map<ESM::RefId, std::shared_ptr<GlobalScriptDesc>> globalScripts(scripts.begin(), scripts.end());
|
||||
|
||||
auto printVariables
|
||||
= [&str](const ESM::RefId& scptName, const auto& names, const auto& values, std::string_view type) {
|
||||
= [&str](std::string_view scptName, const auto& names, const auto& values, std::string_view type) {
|
||||
size_t size = std::min(names.size(), values.size());
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
str << std::endl
|
||||
<< " " << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")";
|
||||
str << "\n " << scptName << "->" << names[i] << " = " << values[i] << " (" << type << ")";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1253,18 +1236,20 @@ namespace MWScript
|
|||
if (!script->mRunning)
|
||||
continue;
|
||||
|
||||
std::string_view scptName = refId.getRefIdString();
|
||||
|
||||
const Compiler::Locals& complocals
|
||||
= MWBase::Environment::get().getScriptManager()->getLocals(refId);
|
||||
const Locals& locals
|
||||
= MWBase::Environment::get().getScriptManager()->getGlobalScripts().getLocals(refId);
|
||||
|
||||
if (locals.isEmpty())
|
||||
str << std::endl << " No variables in script " << refId;
|
||||
str << "\n No variables in script " << scptName;
|
||||
else
|
||||
{
|
||||
printVariables(refId, complocals.get('s'), locals.mShorts, "short");
|
||||
printVariables(refId, complocals.get('l'), locals.mLongs, "long");
|
||||
printVariables(refId, complocals.get('f'), locals.mFloats, "float");
|
||||
printVariables(scptName, complocals.get('s'), locals.mShorts, "short");
|
||||
printVariables(scptName, complocals.get('l'), locals.mLongs, "long");
|
||||
printVariables(scptName, complocals.get('f'), locals.mFloats, "float");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@ namespace MWWorld
|
|||
MWWorld::Ptr object = getTarget();
|
||||
MWWorld::InventoryStore& invStore = actor.getClass().getInventoryStore(actor);
|
||||
|
||||
if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0)
|
||||
if (actor != MWMechanics::getPlayer())
|
||||
{
|
||||
if (actor == MWMechanics::getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sInventoryMessage1}");
|
||||
|
||||
return;
|
||||
// player logic is handled in InventoryWindow::useItem
|
||||
if (object.getClass().hasItemHealth(object) && object.getCellRef().getCharge() == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mForce)
|
||||
|
|
|
|||
|
|
@ -732,6 +732,16 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::ConstPtr& item)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MWWorld::InventoryStore::isEquipped(const ESM::RefId& id)
|
||||
{
|
||||
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||
{
|
||||
if (getSlot(i) != end() && getSlot(i)->getCellRef().getRefId() == id)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MWWorld::InventoryStore::isFirstEquip()
|
||||
{
|
||||
bool first = mFirstAutoEquip;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ namespace MWWorld
|
|||
///< \warning \a iterator can not be an end()-iterator, use unequip function instead
|
||||
|
||||
bool isEquipped(const MWWorld::ConstPtr& item);
|
||||
bool isEquipped(const ESM::RefId& id);
|
||||
///< Utility function, returns true if the given item is equipped in any slot
|
||||
|
||||
void setSelectedEnchantItem(const ContainerStoreIterator& iterator);
|
||||
|
|
|
|||
|
|
@ -76,15 +76,15 @@ namespace ESM
|
|||
break;
|
||||
case fourCC("QSTN"):
|
||||
mQuestStatus = QS_Name;
|
||||
esm.skipRecord();
|
||||
esm.skipHSub();
|
||||
break;
|
||||
case fourCC("QSTF"):
|
||||
mQuestStatus = QS_Finished;
|
||||
esm.skipRecord();
|
||||
esm.skipHSub();
|
||||
break;
|
||||
case fourCC("QSTR"):
|
||||
mQuestStatus = QS_Restart;
|
||||
esm.skipRecord();
|
||||
esm.skipHSub();
|
||||
break;
|
||||
case SREC_DELE:
|
||||
esm.skipHSub();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@
|
|||
white-space: normal;
|
||||
}
|
||||
|
||||
html:has(#luadoc) {
|
||||
scroll-padding-top: 4.0rem;
|
||||
}
|
||||
|
||||
#content #luadoc code a:not(.toc-backref) {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
--muted: 223 27% 14%;
|
||||
--card: 220 14% 9%;
|
||||
|
||||
--link: #ffffff;
|
||||
--link-hover: #ffffff;
|
||||
--link: #95b1dd;
|
||||
--link-hover: #dde2eb;
|
||||
}
|
||||
|
||||
.contents ul li a.reference:hover, .sd-dropdown .toctree-wrapper ul li a.reference, details.sd-dropdown .sd-summary-title {
|
||||
|
|
@ -143,6 +143,10 @@ tbody tr:hover {
|
|||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#content table p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ WaterShaderTextureQuality: "Качество текстуры воды"
|
|||
WindowBorder: "Рамка окна"
|
||||
WindowMode: "Режим окна"
|
||||
WindowModeFullscreen: "Полный экран"
|
||||
WindowModeHint: "Подсказка: режим Оконный без полей\nвсегда использует родное разрешение экрана."
|
||||
WindowModeHint: "Подсказка: режим \"Оконный без полей\"\nвсегда использует родное разрешение экрана."
|
||||
WindowModeWindowed: "Оконный"
|
||||
WindowModeWindowedFullscreen: "Оконный без полей"
|
||||
WobblyShores: "Колеблющиеся берега"
|
||||
|
|
|
|||
|
|
@ -1,36 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Dialog" position="0 0 370 230" layer="Modal" align="Center" name="_Main">
|
||||
<Widget type="Window" skin="MW_Dialog" position="0 0 380 224" layer="Modal" align="Center" name="_Main">
|
||||
|
||||
<Widget type="TextBox" skin="SandText" position="8 8 354 18">
|
||||
<Widget type="TextBox" skin="SandText" position="8 7 356 18">
|
||||
<Property key="Caption" value="#{sQuickMenuTitle}"/>
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="EditBox" skin="SandText" position="8 26 354 18" name="InstructionLabel" align="Left Top VStretch">
|
||||
<Widget type="EditBox" skin="SandText" position="23 26 354 18" name="InstructionLabel" align="Left Top VStretch">
|
||||
<Property key="Caption" value="#{sQuickMenuInstruc}"/>
|
||||
<Property key="MultiLine" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="Static" value="true"/>
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="Widget" skin="" position="15 55 332 128" align="Left Bottom HCenter">
|
||||
<Widget type="Widget" skin="" position="20 49 332 128" align="Left Bottom HCenter">
|
||||
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="0 0 60 60" name="QuickKey1"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="68 0 60 60" name="QuickKey2"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="136 0 60 60" name="QuickKey3"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="204 0 60 60" name="QuickKey4"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="272 0 60 60" name="QuickKey5"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="0 67 60 60" name="QuickKey6"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="68 67 60 60" name="QuickKey7"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="136 67 60 60" name="QuickKey8"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="204 67 60 60" name="QuickKey9"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="272 67 60 60" name="QuickKey10"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="0 68 60 60" name="QuickKey6"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="68 68 60 60" name="QuickKey7"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="136 68 60 60" name="QuickKey8"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="204 68 60 60" name="QuickKey9"/>
|
||||
<Widget type="ItemWidget" skin="MW_ItemIconBox" position="272 68 60 60" name="QuickKey10"/>
|
||||
|
||||
</Widget>
|
||||
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="OKButton" position="315 190 32 24" align="Right Bottom">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="OKButton" position="330 185 32 24" align="Right Bottom">
|
||||
<Property key="ExpandDirection" value="Left"/>
|
||||
<Property key="Caption" value="#{Interface:OK}"/>
|
||||
</Widget>
|
||||
|
|
|
|||
|
|
@ -425,6 +425,18 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
|||
<source><br><b>SDL_GetDisplayMode failed:</b><br><br></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom window size is available only in Windowed mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Window border is available only in Windowed mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Windowed Fullscreen mode always uses the native display resolution.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Launcher::ImportPage</name>
|
||||
|
|
|
|||
|
|
@ -425,6 +425,18 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
|||
<source><br><b>SDL_GetDisplayMode failed:</b><br><br></source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom window size is available only in Windowed mode.</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Window border is available only in Windowed mode.</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Windowed Fullscreen mode always uses the native display resolution.</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Launcher::ImportPage</name>
|
||||
|
|
|
|||
|
|
@ -425,6 +425,18 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
|||
<source><br><b>SDL_GetDisplayMode failed:</b><br><br></source>
|
||||
<translation><br><b>SDL_GetDisplayMode failed:</b><br><br></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom window size is available only in Windowed mode.</source>
|
||||
<translation>La taille personalisée de fenêtre n'est disponible qu'en mode fenêtré.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Window border is available only in Windowed mode.</source>
|
||||
<translation>Les bordures de fenêtres ne sont disponibles qu'en mode fenêtré.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Windowed Fullscreen mode always uses the native display resolution.</source>
|
||||
<translation>Le mode "Fenêtré plein écran" utilise toujours la résolution native de l'écran.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Launcher::ImportPage</name>
|
||||
|
|
|
|||
|
|
@ -427,6 +427,18 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
|||
<source><br><b>SDL_GetDisplayMode failed:</b><br><br></source>
|
||||
<translation><br><b>Вызов SDL_GetDisplayMode завершился с ошибкой:</b><br><br></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom window size is available only in Windowed mode.</source>
|
||||
<translation>Особый размер окна доступен только в оконном режиме.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Window border is available only in Windowed mode.</source>
|
||||
<translation>Рамка окна доступна только в оконном режиме.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Windowed Fullscreen mode always uses the native display resolution.</source>
|
||||
<translation>Режим "Оконный без полей" всегда использует родное разрешение экрана.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Launcher::ImportPage</name>
|
||||
|
|
|
|||
|
|
@ -428,6 +428,18 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin
|
|||
<source><br><b>SDL_GetDisplayMode failed:</b><br><br></source>
|
||||
<translation><br><b>SDL_GetDisplayMode misslyckades:</b><br><br></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Custom window size is available only in Windowed mode.</source>
|
||||
<translation>Anpassad fönsterstorlek finns endast tillgänglig i fönsterläge.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Window border is available only in Windowed mode.</source>
|
||||
<translation>Fönsterram finns endast tillgänglig i fönsterläge</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Windowed Fullscreen mode always uses the native display resolution.</source>
|
||||
<translation>Helskärm i fönsterläge använder alltid skärmens nativa upplösning.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Launcher::ImportPage</name>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ void main()
|
|||
vec2 adjustedUV = (gl_TextureMatrix[0] * vec4(uv, 0.0, 1.0)).xy;
|
||||
|
||||
#if @parallax
|
||||
adjustedUV += getParallaxOffset(transpose(normalToViewMatrix) * normalize(-passViewPos), texture2D(normalMap, adjustedUV).a, 1.f);
|
||||
adjustedUV += getParallaxOffset(transpose(normalToViewMatrix) * normalize(-passViewPos), texture2D(normalMap, adjustedUV).a, -1.0f);
|
||||
#endif
|
||||
vec4 diffuseTex = texture2D(diffuseMap, adjustedUV);
|
||||
gl_FragData[0] = vec4(diffuseTex.xyz, 1.0);
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ void main(void)
|
|||
normalToViewMatrix = gl_NormalMatrix;
|
||||
|
||||
#if @normalMap
|
||||
mat3 tbnMatrix = generateTangentSpace(vec4(1.0, 0.0, 0.0, 1.0), passNormal);
|
||||
tbnMatrix[0] = normalize(cross(tbnMatrix[2], tbnMatrix[1])); // note, now we need to re-cross to derive tangent again because it wasn't orthonormal
|
||||
mat3 tbnMatrix = generateTangentSpace(vec4(1.0, 0.0, 0.0, -1.0), passNormal);
|
||||
tbnMatrix[0] = -normalize(cross(tbnMatrix[2], tbnMatrix[1])); // our original tangent was not at a 90 degree angle to the normal, so we need to rederive it
|
||||
normalToViewMatrix *= tbnMatrix;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue