mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-30 03:32:36 +00:00
Merge branch 'master' of https://gitlab.com/OpenMW/openmw.git into fix/osg-animation-rename-update-order-sucks-this-took-too-long
This commit is contained in:
commit
df0a7a849b
@ -184,6 +184,8 @@
|
||||
Bug #8005: F3 stats bars are sorted not according to their place in the timeline
|
||||
Bug #8018: Potion effects should never explode and always apply on self
|
||||
Bug #8021: Player's scale doesn't reset when starting a new game
|
||||
Bug #8048: Actors can generate negative collision extents and have no collision
|
||||
Bug #8064: Lua move360 script doesn't respect the enableZoom/disableZoom Camera interface setting
|
||||
Feature #1415: Infinite fall failsafe
|
||||
Feature #2566: Handle NAM9 records for manual cell references
|
||||
Feature #3537: Shader-based water ripples
|
||||
@ -249,6 +251,7 @@
|
||||
Feature #7971: Make save's Time Played value display hours instead of days
|
||||
Feature #7985: Support dark mode on Windows
|
||||
Feature #8034: (Lua) Containers should have respawning/organic flags
|
||||
Feature #8067: Support Game Mode on macOS
|
||||
Task #5896: Do not use deprecated MyGUI properties
|
||||
Task #6085: Replace boost::filesystem with std::filesystem
|
||||
Task #6149: Dehardcode Lua API_REVISION
|
||||
|
@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 49)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
set(OPENMW_LUA_API_REVISION 62)
|
||||
set(OPENMW_LUA_API_REVISION 64)
|
||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
@ -1364,4 +1364,28 @@ namespace
|
||||
|
||||
EXPECT_EQ(*result, expected);
|
||||
}
|
||||
|
||||
TEST_F(TestBulletNifLoader, dont_assign_invalid_bounding_box_extents)
|
||||
{
|
||||
copy(mTransform, mNiTriShape.mTransform);
|
||||
mNiTriShape.mTransform.mScale = 10;
|
||||
mNiTriShape.mParents.push_back(&mNiNode);
|
||||
|
||||
mNiTriShape2.mName = "Bounding Box";
|
||||
mNiTriShape2.mBounds.mType = Nif::BoundingVolume::Type::BOX_BV;
|
||||
mNiTriShape2.mBounds.mBox.mExtents = osg::Vec3f(-1, -2, -3);
|
||||
mNiTriShape2.mParents.push_back(&mNiNode);
|
||||
|
||||
mNiNode.mChildren = Nif::NiAVObjectList{ Nif::NiAVObjectPtr(&mNiTriShape), Nif::NiAVObjectPtr(&mNiTriShape2) };
|
||||
|
||||
Nif::NIFFile file("test.nif");
|
||||
file.mRoots.push_back(&mNiNode);
|
||||
|
||||
const auto result = mLoader.load(file);
|
||||
|
||||
const bool extentsUnassigned
|
||||
= std::ranges::all_of(result->mCollisionBox.mExtents._v, [](float extent) { return extent == 0.f; });
|
||||
|
||||
EXPECT_EQ(extentsUnassigned, true);
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +319,6 @@ bool Launcher::SettingsPage::loadSettings()
|
||||
// Miscellaneous
|
||||
{
|
||||
// Saves
|
||||
loadSettingBool(Settings::saves().mTimeplayed, *timePlayedCheckbox);
|
||||
loadSettingInt(Settings::saves().mMaxQuicksaves, *maximumQuicksavesComboBox);
|
||||
|
||||
// Other Settings
|
||||
@ -512,7 +511,6 @@ void Launcher::SettingsPage::saveSettings()
|
||||
// Miscellaneous
|
||||
{
|
||||
// Saves Settings
|
||||
saveSettingBool(*timePlayedCheckbox, Settings::saves().mTimeplayed);
|
||||
saveSettingInt(*maximumQuicksavesComboBox, Settings::saves().mMaxQuicksaves);
|
||||
|
||||
// Other Settings
|
||||
|
@ -1430,16 +1430,6 @@
|
||||
<string>Saves</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="savesGroupVerticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="timePlayedCheckbox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add "Time Played" to Saves</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="maximumQuicksavesLayout">
|
||||
<item>
|
||||
|
@ -71,7 +71,7 @@ bool CSVWorld::DataDisplayDelegate::eventFilter(QObject* target, QEvent* event)
|
||||
QColor themeColor = QApplication::palette().text().color();
|
||||
if (themeColor != mPixmapsColor)
|
||||
{
|
||||
mPixmapsColor = themeColor;
|
||||
mPixmapsColor = std::move(themeColor);
|
||||
|
||||
buildPixmaps();
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ void CSVWorld::ScriptSubView::useHint(const std::string& hint)
|
||||
if (hint.empty())
|
||||
return;
|
||||
|
||||
unsigned line = 0, column = 0;
|
||||
int line = 0, column = 0;
|
||||
char c;
|
||||
std::istringstream stream(hint.c_str() + 1);
|
||||
switch (hint[0])
|
||||
@ -222,8 +222,8 @@ void CSVWorld::ScriptSubView::useHint(const std::string& hint)
|
||||
{
|
||||
QModelIndex index = mModel->getModelIndex(getUniversalId().getId(), mColumn);
|
||||
QString source = mModel->data(index).toString();
|
||||
unsigned stringSize = source.length();
|
||||
unsigned pos, dummy;
|
||||
int stringSize = static_cast<int>(source.length());
|
||||
int pos, dummy;
|
||||
if (!(stream >> c >> dummy >> pos))
|
||||
return;
|
||||
|
||||
@ -234,7 +234,7 @@ void CSVWorld::ScriptSubView::useHint(const std::string& hint)
|
||||
pos = stringSize;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i <= pos; ++i)
|
||||
for (int i = 0; i <= pos; ++i)
|
||||
{
|
||||
if (source[i] == '\n')
|
||||
{
|
||||
|
@ -245,7 +245,7 @@ bool OMW::Engine::frame(unsigned frameNumber, float frametime)
|
||||
|
||||
if (mStateManager->getState() != MWBase::StateManager::State_NoGame)
|
||||
{
|
||||
if (!mWindowManager->containsMode(MWGui::GM_MainMenu))
|
||||
if (!mWindowManager->containsMode(MWGui::GM_MainMenu) || !paused)
|
||||
{
|
||||
if (mWorld->getScriptsEnabled())
|
||||
{
|
||||
|
@ -435,7 +435,7 @@ namespace MWGui
|
||||
mCurrentSlot->mProfile.mInGameTime.mMonth)
|
||||
<< " " << hour << " " << (pm ? "#{Calendar:pm}" : "#{Calendar:am}");
|
||||
|
||||
if (Settings::saves().mTimeplayed)
|
||||
if (mCurrentSlot->mProfile.mTimePlayed > 0)
|
||||
{
|
||||
text << "\n"
|
||||
<< "#{OMWEngine:TimePlayed}: " << formatTimeplayed(mCurrentSlot->mProfile.mTimePlayed);
|
||||
|
@ -1,22 +1,28 @@
|
||||
#include "itemdata.hpp"
|
||||
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/loadench.hpp>
|
||||
|
||||
#include "context.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
#include "objectvariant.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwmechanics/spellutil.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using SelfObject = MWLua::SelfObject;
|
||||
using Index = const SelfObject::CachedStat::Index&;
|
||||
|
||||
constexpr std::array properties = { "condition", /*"enchantmentCharge", "soul", "owner", etc..*/ };
|
||||
constexpr std::array properties = { "condition", "enchantmentCharge", "soul" };
|
||||
|
||||
void invalidPropErr(std::string_view prop, const MWWorld::Ptr& ptr)
|
||||
void valueErr(std::string_view prop, std::string type)
|
||||
{
|
||||
throw std::runtime_error("'" + std::string(prop) + "'" + " property does not exist for item "
|
||||
+ std::string(ptr.getClass().getName(ptr)) + "(" + std::string(ptr.getTypeDescription()) + ")");
|
||||
throw std::logic_error("'" + std::string(prop) + "'" + " received invalid value type (" + type + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,26 +60,56 @@ namespace MWLua
|
||||
if (it != self->mStatsCache.end())
|
||||
return it->second;
|
||||
}
|
||||
return sol::make_object(context.mLua->sol(), getValue(context, prop));
|
||||
return sol::make_object(context.mLua->sol(), getValue(context, prop, mObject.ptr()));
|
||||
}
|
||||
|
||||
void set(const Context& context, std::string_view prop, const sol::object& value) const
|
||||
{
|
||||
SelfObject* obj = mObject.asSelfObject();
|
||||
addStatUpdateAction(context.mLuaManager, *obj);
|
||||
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value;
|
||||
if (mObject.isGObject())
|
||||
setValue({}, prop, mObject.ptr(), value);
|
||||
else if (mObject.isSelfObject())
|
||||
{
|
||||
SelfObject* obj = mObject.asSelfObject();
|
||||
addStatUpdateAction(context.mLuaManager, *obj);
|
||||
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Only global or self scripts can set the value");
|
||||
}
|
||||
|
||||
sol::object getValue(const Context& context, std::string_view prop) const
|
||||
static sol::object getValue(const Context& context, std::string_view prop, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (prop == "condition")
|
||||
{
|
||||
MWWorld::Ptr o = mObject.ptr();
|
||||
if (o.mRef->getType() == ESM::REC_LIGH)
|
||||
return sol::make_object(context.mLua->sol(), o.getClass().getRemainingUsageTime(o));
|
||||
else if (o.getClass().hasItemHealth(o))
|
||||
return sol::make_object(
|
||||
context.mLua->sol(), o.getClass().getItemHealth(o) + o.getCellRef().getChargeIntRemainder());
|
||||
if (ptr.mRef->getType() == ESM::REC_LIGH)
|
||||
return sol::make_object(context.mLua->sol(), ptr.getClass().getRemainingUsageTime(ptr));
|
||||
else if (ptr.getClass().hasItemHealth(ptr))
|
||||
return sol::make_object(context.mLua->sol(),
|
||||
ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder());
|
||||
}
|
||||
else if (prop == "enchantmentCharge")
|
||||
{
|
||||
const ESM::RefId& enchantmentName = ptr.getClass().getEnchantment(ptr);
|
||||
|
||||
if (enchantmentName.empty())
|
||||
return sol::lua_nil;
|
||||
|
||||
float charge = ptr.getCellRef().getEnchantmentCharge();
|
||||
const auto& store = MWBase::Environment::get().getESMStore();
|
||||
const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
|
||||
|
||||
if (charge == -1) // return the full charge
|
||||
return sol::make_object(context.mLua->sol(), MWMechanics::getEnchantmentCharge(*enchantment));
|
||||
|
||||
return sol::make_object(context.mLua->sol(), charge);
|
||||
}
|
||||
else if (prop == "soul")
|
||||
{
|
||||
ESM::RefId soul = ptr.getCellRef().getSoul();
|
||||
if (soul.empty())
|
||||
return sol::lua_nil;
|
||||
|
||||
return sol::make_object(context.mLua->sol(), soul.serializeText());
|
||||
}
|
||||
|
||||
return sol::lua_nil;
|
||||
@ -83,17 +119,48 @@ namespace MWLua
|
||||
{
|
||||
if (prop == "condition")
|
||||
{
|
||||
float cond = LuaUtil::cast<float>(value);
|
||||
if (ptr.mRef->getType() == ESM::REC_LIGH)
|
||||
ptr.getClass().setRemainingUsageTime(ptr, cond);
|
||||
else if (ptr.getClass().hasItemHealth(ptr))
|
||||
if (value.get_type() == sol::type::number)
|
||||
{
|
||||
// if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0
|
||||
ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond)));
|
||||
ptr.getCellRef().setCharge(std::max(0.f, cond));
|
||||
float cond = LuaUtil::cast<float>(value);
|
||||
if (ptr.mRef->getType() == ESM::REC_LIGH)
|
||||
ptr.getClass().setRemainingUsageTime(ptr, cond);
|
||||
else if (ptr.getClass().hasItemHealth(ptr))
|
||||
{
|
||||
// if the value set is less than 0, chargeInt and chargeIntRemainder is set to 0
|
||||
ptr.getCellRef().setChargeIntRemainder(std::max(0.f, std::modf(cond, &cond)));
|
||||
ptr.getCellRef().setCharge(std::max(0.f, cond));
|
||||
}
|
||||
}
|
||||
else
|
||||
invalidPropErr(prop, ptr);
|
||||
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
|
||||
}
|
||||
else if (prop == "enchantmentCharge")
|
||||
{
|
||||
if (value.get_type() == sol::type::lua_nil)
|
||||
ptr.getCellRef().setEnchantmentCharge(-1);
|
||||
else if (value.get_type() == sol::type::number)
|
||||
ptr.getCellRef().setEnchantmentCharge(std::max(0.0f, LuaUtil::cast<float>(value)));
|
||||
else
|
||||
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
|
||||
}
|
||||
else if (prop == "soul")
|
||||
{
|
||||
if (value.get_type() == sol::type::lua_nil)
|
||||
ptr.getCellRef().setSoul(ESM::RefId{});
|
||||
else if (value.get_type() == sol::type::string)
|
||||
{
|
||||
std::string_view souldId = LuaUtil::cast<std::string_view>(value);
|
||||
ESM::RefId creature = ESM::RefId::deserializeText(souldId);
|
||||
const auto& store = *MWBase::Environment::get().getESMStore();
|
||||
|
||||
// TODO: Add Support for NPC Souls
|
||||
if (store.get<ESM::Creature>().search(creature))
|
||||
ptr.getCellRef().setSoul(creature);
|
||||
else
|
||||
throw std::runtime_error("Cannot use non-existent creature as a soul: " + std::string(souldId));
|
||||
}
|
||||
else
|
||||
valueErr(prop, sol::type_name(value.lua_state(), value.get_type()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "localscripts.hpp"
|
||||
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/esm3/loadweap.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
@ -13,6 +14,7 @@
|
||||
#include "../mwmechanics/aisequence.hpp"
|
||||
#include "../mwmechanics/aitravel.hpp"
|
||||
#include "../mwmechanics/aiwander.hpp"
|
||||
#include "../mwmechanics/attacktype.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
@ -63,6 +65,11 @@ namespace MWLua
|
||||
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
|
||||
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
|
||||
selfAPI["ATTACK_TYPE"]
|
||||
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWMechanics::AttackType>(
|
||||
{ { "NoAttack", MWMechanics::AttackType::NoAttack }, { "Any", MWMechanics::AttackType::Any },
|
||||
{ "Chop", MWMechanics::AttackType::Chop }, { "Slash", MWMechanics::AttackType::Slash },
|
||||
{ "Thrust", MWMechanics::AttackType::Thrust } }));
|
||||
|
||||
using AiPackage = MWMechanics::AiPackage;
|
||||
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadacti.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -51,7 +52,8 @@ namespace MWLua
|
||||
record["model"] = sol::readonly_property([](const ESM::Activator& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctMeshPath(rec.mModel);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Activator& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Activator& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadappa.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -39,8 +40,9 @@ namespace MWLua
|
||||
record["model"] = sol::readonly_property([](const ESM::Apparatus& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctMeshPath(rec.mModel);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Apparatus& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Apparatus& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadarmo.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -98,10 +99,10 @@ namespace MWLua
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
record["enchant"]
|
||||
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mEnchant.serializeText(); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["enchant"] = sol::readonly_property(
|
||||
[](const ESM::Armor& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mEnchant); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Armor& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["weight"] = sol::readonly_property([](const ESM::Armor& rec) -> float { return rec.mData.mWeight; });
|
||||
record["value"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mValue; });
|
||||
record["type"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mType; });
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <components/esm3/loadbook.hpp>
|
||||
#include <components/esm3/loadskil.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -104,14 +105,14 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Book& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Book& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; });
|
||||
record["enchant"]
|
||||
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mEnchant.serializeText(); });
|
||||
record["enchant"] = sol::readonly_property(
|
||||
[](const ESM::Book& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mEnchant); });
|
||||
record["isScroll"] = sol::readonly_property([](const ESM::Book& rec) -> bool { return rec.mData.mIsScroll; });
|
||||
record["value"] = sol::readonly_property([](const ESM::Book& rec) -> int { return rec.mData.mValue; });
|
||||
record["weight"] = sol::readonly_property([](const ESM::Book& rec) -> float { return rec.mData.mWeight; });
|
||||
@ -119,9 +120,7 @@ namespace MWLua
|
||||
= sol::readonly_property([](const ESM::Book& rec) -> float { return rec.mData.mEnchant * 0.1f; });
|
||||
record["skill"] = sol::readonly_property([](const ESM::Book& rec) -> sol::optional<std::string> {
|
||||
ESM::RefId skill = ESM::Skill::indexToRefId(rec.mData.mSkillId);
|
||||
if (!skill.empty())
|
||||
return skill.serializeText();
|
||||
return sol::nullopt;
|
||||
return LuaUtil::serializeRefId(skill);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadclot.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -93,10 +94,12 @@ namespace MWLua
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
record["enchant"] = sol::readonly_property(
|
||||
[](const ESM::Clothing& rec) -> std::string { return rec.mEnchant.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Clothing& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["enchant"] = sol::readonly_property([](const ESM::Clothing& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mEnchant);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Clothing& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["weight"] = sol::readonly_property([](const ESM::Clothing& rec) -> float { return rec.mData.mWeight; });
|
||||
record["value"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mValue; });
|
||||
record["type"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mType; });
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadcont.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -51,8 +52,9 @@ namespace MWLua
|
||||
record["model"] = sol::readonly_property([](const ESM::Container& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctMeshPath(rec.mModel);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Container& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Container& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["weight"] = sol::readonly_property([](const ESM::Container& rec) -> float { return rec.mWeight; });
|
||||
record["isOrganic"] = sol::readonly_property(
|
||||
[](const ESM::Container& rec) -> bool { return rec.mFlags & ESM::Container::Organic; });
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -36,8 +37,9 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Creature& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Creature& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Creature& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Creature& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["baseCreature"] = sol::readonly_property(
|
||||
[](const ESM::Creature& rec) -> std::string { return rec.mOriginal.serializeText(); });
|
||||
record["soulValue"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mSoul; });
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loaddoor.hpp>
|
||||
#include <components/esm4/loaddoor.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/lua/utilpackage.hpp>
|
||||
#include <components/misc/convert.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
@ -64,8 +65,8 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Door& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Door& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["openSound"] = sol::readonly_property(
|
||||
[](const ESM::Door& rec) -> std::string { return rec.mOpenSound.serializeText(); });
|
||||
record["closeSound"] = sol::readonly_property(
|
||||
|
@ -34,8 +34,9 @@ namespace MWLua
|
||||
record["model"] = sol::readonly_property([](const ESM::Ingredient& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctMeshPath(rec.mModel);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Ingredient& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Ingredient& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ namespace MWLua
|
||||
{
|
||||
void addItemBindings(sol::table item, const Context& context)
|
||||
{
|
||||
// Deprecated. Moved to itemData; should be removed later
|
||||
item["getEnchantmentCharge"] = [](const Object& object) -> sol::optional<float> {
|
||||
float charge = object.ptr().getCellRef().getEnchantmentCharge();
|
||||
if (charge == -1)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadligh.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -96,8 +97,8 @@ namespace MWLua
|
||||
});
|
||||
record["sound"]
|
||||
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Light& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["weight"] = sol::readonly_property([](const ESM::Light& rec) -> float { return rec.mData.mWeight; });
|
||||
record["value"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mValue; });
|
||||
record["duration"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mTime; });
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadlock.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -31,8 +32,9 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Lockpick& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Lockpick& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Lockpick& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <components/esm3/loadcrea.hpp>
|
||||
#include <components/esm3/loadmisc.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -55,6 +56,7 @@ namespace MWLua
|
||||
addRecordFunctionBinding<ESM::Miscellaneous>(miscellaneous, context);
|
||||
miscellaneous["createRecordDraft"] = tableToMisc;
|
||||
|
||||
// Deprecated. Moved to itemData; should be removed later
|
||||
miscellaneous["setSoul"] = [](const GObject& object, std::string_view soulId) {
|
||||
ESM::RefId creature = ESM::RefId::deserializeText(soulId);
|
||||
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
|
||||
@ -69,12 +71,10 @@ namespace MWLua
|
||||
};
|
||||
miscellaneous["getSoul"] = [](const Object& object) -> sol::optional<std::string> {
|
||||
ESM::RefId soul = object.ptr().getCellRef().getSoul();
|
||||
if (soul.empty())
|
||||
return sol::nullopt;
|
||||
else
|
||||
return soul.serializeText();
|
||||
return LuaUtil::serializeRefId(soul);
|
||||
};
|
||||
miscellaneous["soul"] = miscellaneous["getSoul"]; // for compatibility; should be removed later
|
||||
|
||||
sol::usertype<ESM::Miscellaneous> record
|
||||
= context.mLua->sol().new_usertype<ESM::Miscellaneous>("ESM3_Miscellaneous");
|
||||
record[sol::meta_function::to_string]
|
||||
@ -85,8 +85,9 @@ namespace MWLua
|
||||
record["model"] = sol::readonly_property([](const ESM::Miscellaneous& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctMeshPath(rec.mModel);
|
||||
});
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property([](const ESM::Miscellaneous& rec) -> sol::optional<std::string> {
|
||||
return LuaUtil::serializeRefId(rec.mScript);
|
||||
});
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -85,8 +85,8 @@ namespace MWLua
|
||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mRace.serializeText(); });
|
||||
record["class"]
|
||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mClass.serializeText(); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::NPC& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["hair"]
|
||||
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.serializeText(); });
|
||||
record["baseDisposition"]
|
||||
|
@ -80,8 +80,8 @@ namespace MWLua
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Potion& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; });
|
||||
record["value"] = sol::readonly_property([](const ESM::Potion& rec) -> int { return rec.mData.mValue; });
|
||||
record["effects"] = sol::readonly_property([context](const ESM::Potion& rec) -> sol::table {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadprob.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -31,8 +32,8 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Probe& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Probe& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadrepa.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -31,8 +32,8 @@ namespace MWLua
|
||||
record["name"] = sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mName; });
|
||||
record["model"] = sol::readonly_property(
|
||||
[](const ESM::Repair& rec) -> std::string { return Misc::ResourceHelpers::correctMeshPath(rec.mModel); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Repair& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <components/esm3/loadweap.hpp>
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/util.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
@ -132,9 +133,9 @@ namespace MWLua
|
||||
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
|
||||
});
|
||||
record["enchant"] = sol::readonly_property(
|
||||
[](const ESM::Weapon& rec) -> std::string { return rec.mEnchant.serializeText(); });
|
||||
record["mwscript"]
|
||||
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mScript.serializeText(); });
|
||||
[](const ESM::Weapon& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mEnchant); });
|
||||
record["mwscript"] = sol::readonly_property(
|
||||
[](const ESM::Weapon& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
|
||||
record["isMagical"] = sol::readonly_property(
|
||||
[](const ESM::Weapon& rec) -> bool { return rec.mData.mFlags & ESM::Weapon::Magical; });
|
||||
record["isSilver"] = sol::readonly_property(
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "aifollow.hpp"
|
||||
#include "aipursue.hpp"
|
||||
#include "aiwander.hpp"
|
||||
#include "attacktype.hpp"
|
||||
#include "character.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
@ -239,6 +240,23 @@ namespace MWMechanics
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string_view attackTypeName(AttackType attackType)
|
||||
{
|
||||
switch (attackType)
|
||||
{
|
||||
case AttackType::NoAttack:
|
||||
case AttackType::Any:
|
||||
return {};
|
||||
case AttackType::Chop:
|
||||
return "chop";
|
||||
case AttackType::Slash:
|
||||
return "slash";
|
||||
case AttackType::Thrust:
|
||||
return "thrust";
|
||||
}
|
||||
throw std::logic_error("Invalid attack type value: " + std::to_string(static_cast<int>(attackType)));
|
||||
}
|
||||
|
||||
float getTimeToDestination(const AiPackage& package, const osg::Vec3f& position, float speed, float duration,
|
||||
const osg::Vec3f& halfExtents)
|
||||
{
|
||||
@ -363,7 +381,11 @@ namespace MWMechanics
|
||||
mov.mSpeedFactor = osg::Vec2(controls.mMovement, controls.mSideMovement).length();
|
||||
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Run, controls.mRun);
|
||||
stats.setMovementFlag(MWMechanics::CreatureStats::Flag_Sneak, controls.mSneak);
|
||||
stats.setAttackingOrSpell((controls.mUse & 1) == 1);
|
||||
|
||||
AttackType attackType = static_cast<AttackType>(controls.mUse);
|
||||
stats.setAttackingOrSpell(attackType != AttackType::NoAttack);
|
||||
stats.setAttackType(attackTypeName(attackType));
|
||||
|
||||
controls.mChanged = false;
|
||||
}
|
||||
// For the player we don't need to copy these values to Lua because mwinput doesn't change them.
|
||||
|
@ -455,27 +455,37 @@ namespace MWMechanics
|
||||
void AiWander::doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration,
|
||||
MWWorld::MovementDirectionFlags supportedMovementDirections, AiWanderStorage& storage)
|
||||
{
|
||||
switch (storage.mState)
|
||||
// Attempt to fast forward to the next state instead of remaining in an intermediate state for a frame
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
case AiWanderStorage::Wander_IdleNow:
|
||||
onIdleStatePerFrameActions(actor, duration, storage);
|
||||
break;
|
||||
switch (storage.mState)
|
||||
{
|
||||
case AiWanderStorage::Wander_IdleNow:
|
||||
{
|
||||
onIdleStatePerFrameActions(actor, duration, storage);
|
||||
if (storage.mState != AiWanderStorage::Wander_ChooseAction)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
case AiWanderStorage::Wander_Walking:
|
||||
onWalkingStatePerFrameActions(actor, duration, supportedMovementDirections, storage);
|
||||
return;
|
||||
|
||||
case AiWanderStorage::Wander_Walking:
|
||||
onWalkingStatePerFrameActions(actor, duration, supportedMovementDirections, storage);
|
||||
break;
|
||||
case AiWanderStorage::Wander_ChooseAction:
|
||||
{
|
||||
onChooseActionStatePerFrameActions(actor, storage);
|
||||
if (storage.mState != AiWanderStorage::Wander_IdleNow)
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
case AiWanderStorage::Wander_MoveNow:
|
||||
return; // nothing to do
|
||||
|
||||
case AiWanderStorage::Wander_ChooseAction:
|
||||
onChooseActionStatePerFrameActions(actor, storage);
|
||||
break;
|
||||
|
||||
case AiWanderStorage::Wander_MoveNow:
|
||||
break; // nothing to do
|
||||
|
||||
default:
|
||||
// should never get here
|
||||
assert(false);
|
||||
break;
|
||||
default:
|
||||
// should never get here
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
16
apps/openmw/mwmechanics/attacktype.hpp
Normal file
16
apps/openmw/mwmechanics/attacktype.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef OPENMW_MWMECHANICS_ATTACKTYPE_H
|
||||
#define OPENMW_MWMECHANICS_ATTACKTYPE_H
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
enum class AttackType
|
||||
{
|
||||
NoAttack,
|
||||
Any,
|
||||
Chop,
|
||||
Slash,
|
||||
Thrust
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1674,7 +1674,12 @@ namespace MWMechanics
|
||||
}
|
||||
}
|
||||
else if (aiInactive)
|
||||
mAttackType = getRandomAttackType();
|
||||
{
|
||||
mAttackType = getDesiredAttackType();
|
||||
if (mAttackType == "")
|
||||
mAttackType = getRandomAttackType();
|
||||
}
|
||||
|
||||
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat
|
||||
startKey = mAttackType + ' ' + startKey;
|
||||
stopKey = mAttackType + " max attack";
|
||||
@ -3003,6 +3008,11 @@ namespace MWMechanics
|
||||
return mPtr.getClass().getCreatureStats(mPtr).getAttackingOrSpell();
|
||||
}
|
||||
|
||||
std::string_view CharacterController::getDesiredAttackType() const
|
||||
{
|
||||
return mPtr.getClass().getCreatureStats(mPtr).getAttackType();
|
||||
}
|
||||
|
||||
void CharacterController::setActive(int active) const
|
||||
{
|
||||
mAnimation->setActive(active);
|
||||
|
@ -247,6 +247,8 @@ namespace MWMechanics
|
||||
bool getAttackingOrSpell() const;
|
||||
void setAttackingOrSpell(bool attackingOrSpell) const;
|
||||
|
||||
std::string_view getDesiredAttackType() const;
|
||||
|
||||
void prepareHit();
|
||||
|
||||
public:
|
||||
|
@ -97,6 +97,7 @@ namespace MWMechanics
|
||||
protected:
|
||||
int mLevel;
|
||||
bool mAttackingOrSpell;
|
||||
std::string mAttackType;
|
||||
|
||||
public:
|
||||
CreatureStats();
|
||||
@ -130,6 +131,7 @@ namespace MWMechanics
|
||||
const MagicEffects& getMagicEffects() const;
|
||||
|
||||
bool getAttackingOrSpell() const { return mAttackingOrSpell; }
|
||||
std::string_view getAttackType() const { return mAttackType; }
|
||||
|
||||
int getLevel() const;
|
||||
|
||||
@ -156,6 +158,8 @@ namespace MWMechanics
|
||||
|
||||
void setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; }
|
||||
|
||||
void setAttackType(std::string_view attackType) { mAttackType = attackType; }
|
||||
|
||||
void setLevel(int level);
|
||||
|
||||
void setAiSetting(AiSetting index, Stat<int> value);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
|
||||
#include "postprocessor.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
void DistortionCallback::drawImplementation(
|
||||
@ -10,6 +12,11 @@ namespace MWRender
|
||||
osg::State* state = renderInfo.getState();
|
||||
size_t frameId = state->getFrameStamp()->getFrameNumber() % 2;
|
||||
|
||||
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
|
||||
|
||||
if (!postProcessor || bin->getStage()->getFrameBufferObject() != postProcessor->getPrimaryFbo(frameId))
|
||||
return;
|
||||
|
||||
mFBO[frameId]->apply(*state);
|
||||
|
||||
const osg::Texture* tex
|
||||
|
@ -630,6 +630,12 @@ namespace MWWorld
|
||||
}
|
||||
void Store<ESM::Cell>::clearDynamic()
|
||||
{
|
||||
for (const auto& [_, cell] : mDynamicExt)
|
||||
mCells.erase(cell->mId);
|
||||
mDynamicExt.clear();
|
||||
for (const auto& [_, cell] : mDynamicInt)
|
||||
mCells.erase(cell->mId);
|
||||
mDynamicInt.clear();
|
||||
setUp();
|
||||
}
|
||||
|
||||
|
@ -473,8 +473,8 @@ namespace MWWorld
|
||||
|
||||
mStore.write(writer, progress); // dynamic Store must be written (and read) before Cells, so that
|
||||
// references to custom made records will be recognized
|
||||
mWorldModel.write(writer, progress); // the player's cell needs to be loaded before the player
|
||||
mPlayer->write(writer, progress);
|
||||
mWorldModel.write(writer, progress);
|
||||
mGlobalVariables.write(writer, progress);
|
||||
mWeatherManager->write(writer, progress);
|
||||
mProjectileManager->write(writer, progress);
|
||||
|
@ -101,6 +101,24 @@ namespace MWWorld
|
||||
return Cell(*cell);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CellStore* getOrCreateExterior(const ESM::ExteriorCellLocation& location,
|
||||
std::map<ESM::ExteriorCellLocation, MWWorld::CellStore*>& exteriors, ESMStore& store,
|
||||
ESM::ReadersCache& readers, std::unordered_map<ESM::RefId, CellStore>& cells, bool triggerEvent)
|
||||
{
|
||||
if (const auto it = exteriors.find(location); it != exteriors.end())
|
||||
{
|
||||
assert(it->second != nullptr);
|
||||
return it->second;
|
||||
}
|
||||
auto [cell, created] = createExteriorCell(location, store);
|
||||
const ESM::RefId id = cell.getId();
|
||||
CellStore* const cellStore = &emplaceCellStore(id, std::move(cell), store, readers, cells);
|
||||
exteriors.emplace(location, cellStore);
|
||||
if (created && triggerEvent)
|
||||
MWBase::Environment::get().getLuaManager()->exteriorCreated(*cellStore);
|
||||
return cellStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,23 +196,7 @@ namespace MWWorld
|
||||
{
|
||||
CellStore& WorldModel::getExterior(ESM::ExteriorCellLocation location, bool forceLoad) const
|
||||
{
|
||||
const auto it = mExteriors.find(location);
|
||||
CellStore* cellStore = nullptr;
|
||||
|
||||
if (it == mExteriors.end())
|
||||
{
|
||||
auto [cell, created] = createExteriorCell(location, mStore);
|
||||
const ESM::RefId id = cell.getId();
|
||||
cellStore = &emplaceCellStore(id, std::move(cell), mStore, mReaders, mCells);
|
||||
mExteriors.emplace(location, cellStore);
|
||||
if (created)
|
||||
MWBase::Environment::get().getLuaManager()->exteriorCreated(*cellStore);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(it->second != nullptr);
|
||||
cellStore = it->second;
|
||||
}
|
||||
CellStore* cellStore = getOrCreateExterior(location, mExteriors, mStore, mReaders, mCells, true);
|
||||
|
||||
if (forceLoad && cellStore->getState() != CellStore::State_Loaded)
|
||||
cellStore->load();
|
||||
@ -447,17 +449,26 @@ void MWWorld::WorldModel::write(ESM::ESMWriter& writer, Loading::Listener& progr
|
||||
}
|
||||
}
|
||||
|
||||
struct GetCellStoreCallback : public MWWorld::CellStore::GetCellStoreCallback
|
||||
struct MWWorld::WorldModel::GetCellStoreCallback : public CellStore::GetCellStoreCallback
|
||||
{
|
||||
public:
|
||||
GetCellStoreCallback(MWWorld::WorldModel& worldModel)
|
||||
GetCellStoreCallback(WorldModel& worldModel)
|
||||
: mWorldModel(worldModel)
|
||||
{
|
||||
}
|
||||
|
||||
MWWorld::WorldModel& mWorldModel;
|
||||
WorldModel& mWorldModel;
|
||||
|
||||
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override { return mWorldModel.findCell(cellId); }
|
||||
CellStore* getCellStore(const ESM::RefId& cellId) override
|
||||
{
|
||||
if (const auto* exteriorId = cellId.getIf<ESM::ESM3ExteriorCellRefId>())
|
||||
{
|
||||
ESM::ExteriorCellLocation location(exteriorId->getX(), exteriorId->getY(), ESM::Cell::sDefaultWorldspaceId);
|
||||
return getOrCreateExterior(
|
||||
location, mWorldModel.mExteriors, mWorldModel.mStore, mWorldModel.mReaders, mWorldModel.mCells, false);
|
||||
}
|
||||
return mWorldModel.findCell(cellId);
|
||||
}
|
||||
};
|
||||
|
||||
bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
@ -467,7 +478,10 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
ESM::CellState state;
|
||||
state.mId = reader.getCellId();
|
||||
|
||||
CellStore* const cellStore = findCell(state.mId);
|
||||
GetCellStoreCallback callback(*this);
|
||||
|
||||
CellStore* const cellStore = callback.getCellStore(state.mId);
|
||||
|
||||
if (cellStore == nullptr)
|
||||
{
|
||||
Log(Debug::Warning) << "Dropping state for cell " << state.mId << " (cell no longer exists)";
|
||||
@ -484,8 +498,6 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||
if (cellStore->getState() != CellStore::State_Loaded)
|
||||
cellStore->load();
|
||||
|
||||
GetCellStoreCallback callback(*this);
|
||||
|
||||
cellStore->readReferences(reader, &callback);
|
||||
|
||||
return true;
|
||||
|
@ -104,6 +104,8 @@ namespace MWWorld
|
||||
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||
|
||||
private:
|
||||
struct GetCellStoreCallback;
|
||||
|
||||
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last
|
||||
|
||||
MWWorld::ESMStore& mStore;
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "fileparser.hpp"
|
||||
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
|
||||
#include "scanner.hpp"
|
||||
#include "tokenloc.hpp"
|
||||
|
||||
@ -12,11 +14,6 @@ namespace Compiler
|
||||
{
|
||||
}
|
||||
|
||||
std::string FileParser::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
Interpreter::Program FileParser::getProgram() const
|
||||
{
|
||||
return mScriptParser.getProgram();
|
||||
@ -39,7 +36,7 @@ namespace Compiler
|
||||
if (mState == EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName != name)
|
||||
if (!Misc::StringUtils::ciEqual(mName, name))
|
||||
reportWarning("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
@ -79,7 +76,7 @@ namespace Compiler
|
||||
if (mState == EndNameState)
|
||||
{
|
||||
// optional repeated name after end statement
|
||||
if (mName != loc.mLiteral)
|
||||
if (!Misc::StringUtils::ciEqual(mName, loc.mLiteral))
|
||||
reportWarning("Names for script " + mName + " do not match", loc);
|
||||
|
||||
mState = EndCompleteState;
|
||||
|
@ -28,9 +28,6 @@ namespace Compiler
|
||||
public:
|
||||
FileParser(ErrorHandler& errorHandler, Context& context);
|
||||
|
||||
std::string getName() const;
|
||||
///< Return script name.
|
||||
|
||||
Interpreter::Program getProgram() const;
|
||||
|
||||
const Locals& getLocals() const;
|
||||
|
@ -2,6 +2,11 @@
|
||||
#define COMPONENTS_LUA_UTIL_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include <components/esm/refid.hpp>
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
@ -15,6 +20,13 @@ namespace LuaUtil
|
||||
{
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
inline sol::optional<std::string> serializeRefId(ESM::RefId id)
|
||||
{
|
||||
if (id.empty())
|
||||
return sol::nullopt;
|
||||
return id.serializeText();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -241,7 +241,7 @@ namespace LuaUtil
|
||||
return std::make_tuple(angles.x(), angles.z());
|
||||
};
|
||||
transMType["getAnglesZYX"] = [](const TransformM& m) {
|
||||
osg::Vec3f angles = Misc::toEulerAnglesXZ(m.mM);
|
||||
osg::Vec3f angles = Misc::toEulerAnglesZYX(m.mM);
|
||||
return std::make_tuple(angles.z(), angles.y(), angles.x());
|
||||
};
|
||||
|
||||
@ -277,7 +277,7 @@ namespace LuaUtil
|
||||
return std::make_tuple(angles.x(), angles.z());
|
||||
};
|
||||
transQType["getAnglesZYX"] = [](const TransformQ& q) {
|
||||
osg::Vec3f angles = Misc::toEulerAnglesXZ(q.mQ);
|
||||
osg::Vec3f angles = Misc::toEulerAnglesZYX(q.mQ);
|
||||
return std::make_tuple(angles.z(), angles.y(), angles.x());
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,12 @@ namespace Misc
|
||||
return QIcon();
|
||||
|
||||
QFile iconFile(fileName);
|
||||
iconFile.open(QIODevice::ReadOnly);
|
||||
if (!iconFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Failed to open icon file:" << fileName;
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
auto content = iconFile.readAll();
|
||||
if (!content.startsWith("<?xml"))
|
||||
return QIcon(fileName);
|
||||
|
@ -85,7 +85,8 @@ namespace NifBullet
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(node.mName, "Bounding Box"))
|
||||
{
|
||||
if (node.mBounds.mType == Nif::BoundingVolume::Type::BOX_BV)
|
||||
if (node.mBounds.mType == Nif::BoundingVolume::Type::BOX_BV
|
||||
&& std::ranges::all_of(node.mBounds.mBox.mExtents._v, [](float extent) { return extent > 0.f; }))
|
||||
{
|
||||
mShape->mCollisionBox.mExtents = node.mBounds.mBox.mExtents;
|
||||
mShape->mCollisionBox.mCenter = node.mBounds.mBox.mCenter;
|
||||
|
@ -43,7 +43,12 @@ namespace Platform
|
||||
setStyle("windows");
|
||||
|
||||
QFile file(getStyleSheetPath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Failed to open style sheet file:" << getStyleSheetPath();
|
||||
return;
|
||||
}
|
||||
|
||||
setStyleSheet(file.readAll());
|
||||
}
|
||||
}
|
||||
@ -60,7 +65,12 @@ namespace Platform
|
||||
setStyle("windows");
|
||||
|
||||
QFile file(getStyleSheetPath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "Failed to open style sheet file:" << getStyleSheetPath();
|
||||
return;
|
||||
}
|
||||
|
||||
setStyleSheet(file.readAll());
|
||||
}
|
||||
else
|
||||
|
@ -37,7 +37,8 @@ namespace SceneUtil
|
||||
|
||||
osg::StateSet* stateset = node.getOrCreateStateSet();
|
||||
|
||||
stateset->setRenderBinDetails(14, "Distortion", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
stateset->setNestRenderBins(false);
|
||||
stateset->setRenderBinDetails(14, "Distortion", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
|
||||
stateset->addUniform(new osg::Uniform("distortionStrength", distortionStrength));
|
||||
|
||||
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
|
||||
|
@ -20,7 +20,6 @@ namespace Settings
|
||||
|
||||
SettingValue<std::string> mCharacter{ mIndex, "Saves", "character" };
|
||||
SettingValue<bool> mAutosave{ mIndex, "Saves", "autosave" };
|
||||
SettingValue<bool> mTimeplayed{ mIndex, "Saves", "timeplayed" };
|
||||
SettingValue<int> mMaxQuicksaves{ mIndex, "Saves", "max quicksaves", makeMaxSanitizerInt(1) };
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
parse_cmake
|
||||
sphinx==1.8.5
|
||||
docutils==0.17.1
|
||||
sphinx==7.1.2
|
||||
docutils==0.18.1
|
||||
jinja2==3.1.4
|
||||
sphinx_rtd_theme
|
||||
sphinx_rtd_theme==1.3.0
|
||||
|
@ -55,7 +55,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'OpenMW'
|
||||
copyright = u'2023, OpenMW Team'
|
||||
copyright = u'2024, OpenMW Team'
|
||||
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -21,19 +21,6 @@ This setting determines whether the game will be automatically saved when the ch
|
||||
|
||||
This setting can be toggled in game with the Auto-Save when Rest button in the Prefs panel of the Options menu.
|
||||
|
||||
timeplayed
|
||||
----------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
:Default: False
|
||||
|
||||
This setting determines whether the amount of the time the player has spent playing will be displayed
|
||||
for each saved game in the Load menu. Currently, the counter includes time spent in menus, including the pause menu,
|
||||
but does not include time spent with the game window minimized.
|
||||
|
||||
This setting can only be configured by editing the settings configuration file.
|
||||
|
||||
max quicksaves
|
||||
--------------
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
local self = require('openmw.self')
|
||||
local interfaces = require('openmw.interfaces')
|
||||
local types = require('openmw.types')
|
||||
local util = require('openmw.util')
|
||||
|
||||
local function startPackage(args)
|
||||
@ -10,6 +11,7 @@ local function startPackage(args)
|
||||
self:_startAiCombat(args.target, cancelOther)
|
||||
elseif args.type == 'Pursue' then
|
||||
if not args.target then error("target required") end
|
||||
if not types.Player.objectIsInstance(args.target) then error("target must be a player") end
|
||||
self:_startAiPursue(args.target, cancelOther)
|
||||
elseif args.type == 'Follow' then
|
||||
if not args.target then error("target required") end
|
||||
|
@ -35,7 +35,8 @@ local function processZoom3rdPerson()
|
||||
not Player.getControlSwitch(self, Player.CONTROL_SWITCH.ViewMode) or
|
||||
not Player.getControlSwitch(self, Player.CONTROL_SWITCH.Controls) or
|
||||
input.getBooleanActionValue('TogglePOV') or
|
||||
not I.Camera.isModeControlEnabled()
|
||||
not I.Camera.isModeControlEnabled() or
|
||||
not I.Camera.isZoomEnabled()
|
||||
then
|
||||
return
|
||||
end
|
||||
|
@ -448,7 +448,6 @@ local function resetPlayerGroups()
|
||||
if not menuGroups[groupKey] then
|
||||
if groupElements[pageKey][groupKey] then
|
||||
groupElements[pageKey][groupKey]:destroy()
|
||||
print(string.format('destroyed group element %s %s', pageKey, groupKey))
|
||||
groupElements[pageKey][groupKey] = nil
|
||||
end
|
||||
page[groupKey] = nil
|
||||
|
@ -920,10 +920,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Saves</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>JPG</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -1415,10 +1411,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Can Zoom on Maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add "Time Played" to Saves</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Notify on Saved Screenshot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
|
@ -1363,14 +1363,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Saves</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add "Time Played" to Saves</source>
|
||||
<translation></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Maximum Quicksaves</source>
|
||||
<translation></translation>
|
||||
|
@ -920,10 +920,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Saves</source>
|
||||
<translation>Sauvegardes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></source>
|
||||
<translation><html><body><p>Cette option affiche le temps de jeu de chaque sauvegarde dans leur menu de sélection.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>JPG</source>
|
||||
<translation>JPG</translation>
|
||||
@ -1418,10 +1414,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Can Zoom on Maps</source>
|
||||
<translation>Permettre le zoom sur la carte</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add "Time Played" to Saves</source>
|
||||
<translation>Ajoute le temps de jeu aux sauvegardes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Notify on Saved Screenshot</source>
|
||||
<translation>Notifier l'enregistrement des captures d'écran</translation>
|
||||
|
@ -1140,14 +1140,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov
|
||||
<source>Saves</source>
|
||||
<translation>Сохранения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></source>
|
||||
<translation><html><head/><body><p>Эта настройка определяет, будет ли отображаться время с начала новой игры для выбранного сохранения в меню загрузки.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add "Time Played" to Saves</source>
|
||||
<translation>Выводить "Время в игре" в сохранениях</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>JPG</source>
|
||||
<translation>JPG</translation>
|
||||
|
@ -933,10 +933,6 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin
|
||||
<source>Saves</source>
|
||||
<translation>Sparfiler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><html><head/><body><p>This setting determines whether the amount of the time the player has spent playing will be displayed for each saved game in the Load menu.</p></body></html></source>
|
||||
<translation><html><head/><body><p>Denna inställning avgör huruvida mängden tid spelaren har spenderat i spelet kommer visas för varje sparat spel i Ladda spel-menyn.</p></body></html></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>JPG</source>
|
||||
<translation>JPG</translation>
|
||||
@ -1434,10 +1430,6 @@ de ordinarie fonterna i Morrowind. Bocka denna ruta om du ändå föredrar ordin
|
||||
<source>Can Zoom on Maps</source>
|
||||
<translation>Kan zooma på kartor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add "Time Played" to Saves</source>
|
||||
<translation>Lägg till spelad tid i sparfiler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Notify on Saved Screenshot</source>
|
||||
<translation>Ge notis vid sparad skärmdump</translation>
|
||||
|
@ -349,6 +349,7 @@
|
||||
|
||||
---
|
||||
-- @type ActiveSpellEffect
|
||||
-- @field #number index Index of this effect within the original list of @{#MagicEffectWithParams} of the spell/enchantment/potion this effect came from.
|
||||
-- @field #string affectedSkill Optional skill ID
|
||||
-- @field #string affectedAttribute Optional attribute ID
|
||||
-- @field #string id Magic effect id
|
||||
@ -729,7 +730,6 @@
|
||||
-- @field #number magnitude current magnitude of the effect. Will be set to 0 when effect is removed or expires.
|
||||
-- @field #number magnitudeBase
|
||||
-- @field #number magnitudeModifier
|
||||
-- @field #number index Index of this effect within the original list of @{#MagicEffectWithParams} of the spell/enchantment/potion this effect came from.
|
||||
|
||||
--- @{#Sound}: Sounds and Speech
|
||||
-- @field [parent=#core] #Sound sound
|
||||
|
@ -37,7 +37,15 @@
|
||||
-- @field [parent=#ActorControls] #boolean run true - run, false - walk
|
||||
-- @field [parent=#ActorControls] #boolean sneak If true - sneak
|
||||
-- @field [parent=#ActorControls] #boolean jump If true - initiate a jump
|
||||
-- @field [parent=#ActorControls] #number use if 1 - activates the readied weapon/spell. For weapons, keeping at 1 will charge the attack until set to 0.
|
||||
-- @field [parent=#ActorControls] #ATTACK_TYPE use Activates the readied weapon/spell according to a provided value. For weapons, keeping this value modified will charge the attack until set to @{#ATTACK_TYPE.NoAttack}. If an @{#ATTACK_TYPE} not appropriate for a currently equipped weapon provided - an appropriate @{#ATTACK_TYPE} will be used instead.
|
||||
|
||||
---
|
||||
-- @type ATTACK_TYPE
|
||||
-- @field #number NoAttack
|
||||
-- @field #number Any
|
||||
-- @field #number Chop
|
||||
-- @field #number Swing
|
||||
-- @field #number Thrust
|
||||
|
||||
---
|
||||
-- Enables or disables standard AI (enabled by default).
|
||||
|
@ -750,7 +750,7 @@
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Get this item's current enchantment charge.
|
||||
-- (DEPRECATED, use itemData(item).enchantmentCharge) Get this item's current enchantment charge.
|
||||
-- @function [parent=#Item] getEnchantmentCharge
|
||||
-- @param openmw.core#GameObject item
|
||||
-- @return #number The charge remaining. `nil` if the enchantment has never been used, implying the charge is full. Unenchanted items will always return a value of `nil`.
|
||||
@ -763,7 +763,7 @@
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Set this item's enchantment charge.
|
||||
-- (DEPRECATED, use itemData(item).enchantmentCharge) Set this item's enchantment charge.
|
||||
-- @function [parent=#Item] setEnchantmentCharge
|
||||
-- @param openmw.core#GameObject item
|
||||
-- @param #number charge Can be `nil` to reset the unused state / full
|
||||
@ -777,14 +777,16 @@
|
||||
-- @return #boolean
|
||||
|
||||
---
|
||||
-- Set of properties that differentiates one item from another of the same record type.
|
||||
-- Set of properties that differentiates one item from another of the same record type; can be used by any script, but only global and self scripts can change values.
|
||||
-- @function [parent=#Item] itemData
|
||||
-- @param openmw.core#GameObject item
|
||||
-- @return #ItemData
|
||||
|
||||
---
|
||||
-- @type ItemData
|
||||
-- @field #number condition The item's current condition. Time remaining for lights. Uses left for lockpicks and probes. Current health for weapons and armor.
|
||||
-- @field #number condition The item's current condition. Time remaining for lights. Uses left for repairs, lockpicks and probes. Current health for weapons and armor.
|
||||
-- @field #number enchantmentCharge The item's current enchantment charge. Unenchanted items will always return a value of `nil`. Setting this to `nil` will reset the charge of the item.
|
||||
-- @field #string soul The recordId of the item's current soul. Items without soul will always return a value of `nil`. Setting this to `nil` will remove the soul from the item.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- @{#Creature} functions
|
||||
@ -835,7 +837,7 @@
|
||||
-- @field #string name
|
||||
-- @field #string baseCreature Record id of a base creature, which was modified to create this one
|
||||
-- @field #string model VFS path to the creature's model
|
||||
-- @field #string mwscript
|
||||
-- @field #string mwscript MWScript on this creature (can be nil)
|
||||
-- @field #number soulValue The soul value of the creature record
|
||||
-- @field #number type The @{#Creature.TYPE} of the creature
|
||||
-- @field #number baseGold The base barter gold of the creature
|
||||
@ -1115,7 +1117,7 @@
|
||||
-- @field #string race
|
||||
-- @field #string class Name of the NPC's class (e. g. Acrobat)
|
||||
-- @field #string model Path to the model associated with this NPC, used for animations.
|
||||
-- @field #string mwscript MWScript that is attached to this NPC
|
||||
-- @field #string mwscript MWScript on this NPC (can be nil)
|
||||
-- @field #string hair Path to the hair body part model
|
||||
-- @field #string head Path to the head body part model
|
||||
-- @field #number baseGold The base barter gold of the NPC
|
||||
@ -1325,9 +1327,9 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this armor (can be empty)
|
||||
-- @field #string mwscript MWScript on this armor (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #string enchant The enchantment ID of this armor (can be empty)
|
||||
-- @field #string enchant The enchantment ID of this armor (can be nil)
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
-- @field #number type See @{#Armor.TYPE}
|
||||
@ -1414,9 +1416,9 @@
|
||||
-- @field #string id The record ID of the book
|
||||
-- @field #string name Name of the book
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this book (can be empty)
|
||||
-- @field #string mwscript MWScript on this book (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #string enchant The enchantment ID of this book (can be empty)
|
||||
-- @field #string enchant The enchantment ID of this book (can be nil)
|
||||
-- @field #string text The text content of the book
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
@ -1492,9 +1494,9 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Name of the clothing
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this clothing (can be empty)
|
||||
-- @field #string mwscript MWScript on this clothing (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #string enchant The enchantment ID of this clothing (can be empty)
|
||||
-- @field #string enchant The enchantment ID of this clothing (can be nil)
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
-- @field #number type See @{#Clothing.TYPE}
|
||||
@ -1535,7 +1537,7 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this potion (can be empty)
|
||||
-- @field #string mwscript MWScript on this potion (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
@ -1641,7 +1643,7 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this light (can be empty)
|
||||
-- @field #string mwscript MWScript on this light (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #string sound VFS path to the sound
|
||||
-- @field #number weight
|
||||
@ -1689,7 +1691,7 @@
|
||||
-- @return #MiscellaneousRecord
|
||||
|
||||
---
|
||||
-- Returns the read-only soul of a miscellaneous item
|
||||
-- (DEPRECATED, use itemData(item).soul) Returns the read-only soul of a miscellaneous item
|
||||
-- @function [parent=#Miscellaneous] getSoul
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @return #string
|
||||
@ -1702,7 +1704,7 @@
|
||||
-- @return #MiscellaneousRecord A strongly typed Miscellaneous record.
|
||||
|
||||
---
|
||||
-- Sets the soul of a miscellaneous item, intended for soul gem objects; Must be used in a global script.
|
||||
-- (DEPRECATED, use itemData(item).soul) Sets the soul of a miscellaneous item, intended for soul gem objects; Must be used in a global script.
|
||||
-- @function [parent=#Miscellaneous] setSoul
|
||||
-- @param openmw.core#GameObject object
|
||||
-- @param #string soulId Record ID for the soul of the creature to use
|
||||
@ -1712,7 +1714,7 @@
|
||||
-- @field #string id The record ID of the miscellaneous item
|
||||
-- @field #string name The name of the miscellaneous item
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this miscellaneous item (can be empty)
|
||||
-- @field #string mwscript MWScript on this miscellaneous item (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
@ -1757,7 +1759,7 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this potion (can be empty)
|
||||
-- @field #string mwscript MWScript on this potion (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number weight
|
||||
-- @field #number value
|
||||
@ -1817,9 +1819,9 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this weapon (can be empty)
|
||||
-- @field #string mwscript MWScript on this weapon (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #string enchant
|
||||
-- @field #string enchant The enchantment ID of this weapon (can be nil)
|
||||
-- @field #boolean isMagical
|
||||
-- @field #boolean isSilver
|
||||
-- @field #number weight
|
||||
@ -1886,7 +1888,7 @@
|
||||
-- @field #string id The record ID of the apparatus
|
||||
-- @field #string name The name of the apparatus
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this apparatus (can be empty)
|
||||
-- @field #string mwscript MWScript on this apparatus (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number type The type of apparatus. See @{#Apparatus.TYPE}
|
||||
-- @field #number weight
|
||||
@ -1925,7 +1927,7 @@
|
||||
-- @field #string id The record ID of the lockpick
|
||||
-- @field #string name The name of the lockpick
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this lockpick (can be empty)
|
||||
-- @field #string mwscript MWScript on this lockpick (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number maxCondition The maximum number of uses of this lockpick
|
||||
-- @field #number weight
|
||||
@ -1964,7 +1966,7 @@
|
||||
-- @field #string id The record ID of the probe
|
||||
-- @field #string name The name of the probe
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this probe (can be empty)
|
||||
-- @field #string mwscript MWScript on this probe (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number maxCondition The maximum number of uses of this probe
|
||||
-- @field #number weight
|
||||
@ -2003,7 +2005,7 @@
|
||||
-- @field #string id The record ID of the repair tool
|
||||
-- @field #string name The name of the repair tool
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this repair tool (can be empty)
|
||||
-- @field #string mwscript MWScript on this repair tool (can be nil)
|
||||
-- @field #string icon VFS path to the icon
|
||||
-- @field #number maxCondition The maximum number of uses of this repair tool
|
||||
-- @field #number weight
|
||||
@ -2040,7 +2042,7 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this activator (can be empty)
|
||||
-- @field #string mwscript MWScript on this activator (can be nil)
|
||||
|
||||
---
|
||||
-- Creates a @{#ActivatorRecord} without adding it to the world database.
|
||||
@ -2107,7 +2109,7 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this container (can be empty)
|
||||
-- @field #string mwscript MWScript on this container (can be nil)
|
||||
-- @field #number weight capacity of this container
|
||||
-- @field #boolean isOrganic Whether items can be placed in the container
|
||||
-- @field #boolean isRespawning Whether the container respawns its contents
|
||||
@ -2169,10 +2171,9 @@
|
||||
-- @field #string id Record id
|
||||
-- @field #string name Human-readable name
|
||||
-- @field #string model VFS path to the model
|
||||
-- @field #string mwscript MWScript on this door (can be empty)
|
||||
-- @field #string openSound VFS path to the sound of opening
|
||||
-- @field #string closeSound VFS path to the sound of closing
|
||||
|
||||
-- @field #string mwscript MWScript on this door (can be nil)
|
||||
-- @field #string openSound The sound id for door opening
|
||||
-- @field #string closeSound The sound id for door closing
|
||||
|
||||
|
||||
--- Functions for @{#Static} objects
|
||||
|
@ -8,8 +8,8 @@
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>openmw-launcher</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openmw.openmw</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openmw.openmw</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
@ -20,14 +20,16 @@
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${OPENMW_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${OPENMW_VERSION}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${OPENMW_VERSION}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.role-playing-games</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string></string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
|
@ -566,9 +566,6 @@ character =
|
||||
# Automatically save the game whenever the player rests.
|
||||
autosave = true
|
||||
|
||||
# Display the time played on each save file in the load menu.
|
||||
timeplayed = false
|
||||
|
||||
# The maximum number of quick (or auto) save slots to have.
|
||||
# If all slots are used, the oldest save is reused
|
||||
max quicksaves = 1
|
||||
|
@ -13,18 +13,88 @@ types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.Magic, false)
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.VanityMode, false)
|
||||
types.Player.setControlSwitch(self, types.Player.CONTROL_SWITCH.ViewMode, false)
|
||||
|
||||
testing.registerLocalTest('playerRotation',
|
||||
function()
|
||||
local endTime = core.getSimulationTime() + 1
|
||||
while core.getSimulationTime() < endTime do
|
||||
self.controls.jump = false
|
||||
self.controls.run = true
|
||||
self.controls.movement = 0
|
||||
self.controls.sideMovement = 0
|
||||
self.controls.yawChange = util.normalizeAngle(math.rad(90) - self.rotation:getYaw()) * 0.5
|
||||
coroutine.yield()
|
||||
local function rotate(object, targetPitch, targetYaw)
|
||||
local endTime = core.getSimulationTime() + 1
|
||||
while core.getSimulationTime() < endTime do
|
||||
object.controls.jump = false
|
||||
object.controls.run = true
|
||||
object.controls.movement = 0
|
||||
object.controls.sideMovement = 0
|
||||
if targetPitch ~= nil then
|
||||
object.controls.pitchChange = util.normalizeAngle(targetPitch - object.rotation:getPitch()) * 0.5
|
||||
end
|
||||
testing.expectEqualWithDelta(self.rotation:getYaw(), math.rad(90), 0.05, 'Incorrect rotation')
|
||||
if targetYaw ~= nil then
|
||||
object.controls.yawChange = util.normalizeAngle(targetYaw - object.rotation:getYaw()) * 0.5
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
local function rotateByYaw(object, target)
|
||||
rotate(object, nil, target)
|
||||
end
|
||||
|
||||
local function rotateByPitch(object, target)
|
||||
rotate(object, target, nil)
|
||||
end
|
||||
|
||||
testing.registerLocalTest('playerYawRotation',
|
||||
function()
|
||||
local initialAlphaXZ, initialGammaXZ = self.rotation:getAnglesXZ()
|
||||
local initialAlphaZYX, initialBetaZYX, initialGammaZYX = self.rotation:getAnglesZYX()
|
||||
|
||||
local targetYaw = math.rad(90)
|
||||
rotateByYaw(self, targetYaw)
|
||||
|
||||
testing.expectEqualWithDelta(self.rotation:getYaw(), targetYaw, 0.05, 'Incorrect yaw rotation')
|
||||
|
||||
local alpha1, gamma1 = self.rotation:getAnglesXZ()
|
||||
testing.expectEqualWithDelta(alpha1, initialAlphaXZ, 0.05, 'Alpha rotation in XZ convention should not change')
|
||||
testing.expectEqualWithDelta(gamma1, targetYaw, 0.05, 'Incorrect gamma rotation in XZ convention')
|
||||
|
||||
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
|
||||
testing.expectEqualWithDelta(alpha2, targetYaw, 0.05, 'Incorrect alpha rotation in ZYX convention')
|
||||
testing.expectEqualWithDelta(beta2, initialBetaZYX, 0.05, 'Beta rotation in ZYX convention should not change')
|
||||
testing.expectEqualWithDelta(gamma2, initialGammaZYX, 0.05, 'Gamma rotation in ZYX convention should not change')
|
||||
end)
|
||||
|
||||
testing.registerLocalTest('playerPitchRotation',
|
||||
function()
|
||||
local initialAlphaXZ, initialGammaXZ = self.rotation:getAnglesXZ()
|
||||
local initialAlphaZYX, initialBetaZYX, initialGammaZYX = self.rotation:getAnglesZYX()
|
||||
|
||||
local targetPitch = math.rad(90)
|
||||
rotateByPitch(self, targetPitch)
|
||||
|
||||
testing.expectEqualWithDelta(self.rotation:getPitch(), targetPitch, 0.05, 'Incorrect pitch rotation')
|
||||
|
||||
local alpha1, gamma1 = self.rotation:getAnglesXZ()
|
||||
testing.expectEqualWithDelta(alpha1, targetPitch, 0.05, 'Incorrect alpha rotation in XZ convention')
|
||||
testing.expectEqualWithDelta(gamma1, initialGammaXZ, 0.05, 'Gamma rotation in XZ convention should not change')
|
||||
|
||||
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
|
||||
testing.expectEqualWithDelta(alpha2, initialAlphaZYX, 0.05, 'Alpha rotation in ZYX convention should not change')
|
||||
testing.expectEqualWithDelta(beta2, initialBetaZYX, 0.05, 'Beta rotation in ZYX convention should not change')
|
||||
testing.expectEqualWithDelta(gamma2, targetPitch, 0.05, 'Incorrect gamma rotation in ZYX convention')
|
||||
end)
|
||||
|
||||
testing.registerLocalTest('playerPitchAndYawRotation',
|
||||
function()
|
||||
local targetPitch = math.rad(-30)
|
||||
local targetYaw = math.rad(-60)
|
||||
rotate(self, targetPitch, targetYaw)
|
||||
|
||||
testing.expectEqualWithDelta(self.rotation:getPitch(), targetPitch, 0.05, 'Incorrect pitch rotation')
|
||||
testing.expectEqualWithDelta(self.rotation:getYaw(), targetYaw, 0.05, 'Incorrect yaw rotation')
|
||||
|
||||
local alpha1, gamma1 = self.rotation:getAnglesXZ()
|
||||
testing.expectEqualWithDelta(alpha1, targetPitch, 0.05, 'Incorrect alpha rotation in XZ convention')
|
||||
testing.expectEqualWithDelta(gamma1, targetYaw, 0.05, 'Incorrect gamma rotation in XZ convention')
|
||||
|
||||
local alpha2, beta2, gamma2 = self.rotation:getAnglesZYX()
|
||||
testing.expectEqualWithDelta(alpha2, math.rad(-56), 0.05, 'Incorrect alpha rotation in ZYX convention')
|
||||
testing.expectEqualWithDelta(beta2, math.rad(-25), 0.05, 'Incorrect beta rotation in ZYX convention')
|
||||
testing.expectEqualWithDelta(gamma2, math.rad(-16), 0.05, 'Incorrect gamma rotation in ZYX convention')
|
||||
end)
|
||||
|
||||
testing.registerLocalTest('playerForwardRunning',
|
||||
|
@ -44,7 +44,17 @@ local function testTeleport()
|
||||
testing.expectEqualWithDelta(player.position.x, 100, 1, 'incorrect position after teleporting')
|
||||
testing.expectEqualWithDelta(player.position.y, 50, 1, 'incorrect position after teleporting')
|
||||
testing.expectEqualWithDelta(player.position.z, 500, 1, 'incorrect position after teleporting')
|
||||
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(90), 0.05, 'incorrect rotation after teleporting')
|
||||
testing.expectEqualWithDelta(player.rotation:getYaw(), math.rad(90), 0.05, 'incorrect yaw rotation after teleporting')
|
||||
testing.expectEqualWithDelta(player.rotation:getPitch(), math.rad(0), 0.05, 'incorrect pitch rotation after teleporting')
|
||||
|
||||
local rotationX1, rotationZ1 = player.rotation:getAnglesXZ()
|
||||
testing.expectEqualWithDelta(rotationX1, math.rad(0), 0.05, 'incorrect x rotation from getAnglesXZ after teleporting')
|
||||
testing.expectEqualWithDelta(rotationZ1, math.rad(90), 0.05, 'incorrect z rotation from getAnglesXZ after teleporting')
|
||||
|
||||
local rotationZ2, rotationY2, rotationX2 = player.rotation:getAnglesZYX()
|
||||
testing.expectEqualWithDelta(rotationZ2, math.rad(90), 0.05, 'incorrect z rotation from getAnglesZYX after teleporting')
|
||||
testing.expectEqualWithDelta(rotationY2, math.rad(0), 0.05, 'incorrect y rotation from getAnglesZYX after teleporting')
|
||||
testing.expectEqualWithDelta(rotationX2, math.rad(0), 0.05, 'incorrect x rotation from getAnglesZYX after teleporting')
|
||||
|
||||
player:teleport('', player.position, {rotation=util.transform.rotateZ(math.rad(-90)), onGround=true})
|
||||
coroutine.yield()
|
||||
@ -193,9 +203,17 @@ end
|
||||
|
||||
tests = {
|
||||
{'timers', testTimers},
|
||||
{'playerRotation', function()
|
||||
{'rotating player with controls.yawChange should change rotation', function()
|
||||
initPlayer()
|
||||
testing.runLocalTest(player, 'playerRotation')
|
||||
testing.runLocalTest(player, 'playerYawRotation')
|
||||
end},
|
||||
{'rotating player with controls.pitchChange should change rotation', function()
|
||||
initPlayer()
|
||||
testing.runLocalTest(player, 'playerPitchRotation')
|
||||
end},
|
||||
{'rotating player with controls.pitchChange and controls.yawChange should change rotation', function()
|
||||
initPlayer()
|
||||
testing.runLocalTest(player, 'playerPitchAndYawRotation')
|
||||
end},
|
||||
{'playerForwardRunning', function()
|
||||
initPlayer()
|
||||
|
Loading…
x
Reference in New Issue
Block a user