Add OpenMW commits up to 2 May 2019

# Conflicts:
#	CMakeLists.txt
#	apps/openmw/mwmechanics/mechanicsmanagerimp.cpp
#	apps/openmw/mwscript/statsextensions.cpp
pull/541/head
David Cernat 5 years ago
commit 5181c601c0

@ -126,6 +126,7 @@ Programmers
Michael Hogan (Xethik)
Michael Mc Donnell
Michael Papageorgiou (werdanith)
Michael Stopa (Stomy)
Michał Ściubidło (mike-sc)
Michał Bień (Glorf)
Michał Moroz (dragonee)
@ -135,6 +136,7 @@ Programmers
Mitchell Schwitzer (schwitzerm)
naclander
Narmo
Nat Meo (Utopium)
Nathan Jeffords (blunted2night)
NeveHanter
Nialsy
@ -182,7 +184,6 @@ Programmers
Stanislaw Halik (sthalik)
Star-Demon
stil-t
Stomy
svaante
Sylvain Thesnieres (Garvek)
t6

@ -6,25 +6,32 @@
Bug #3006: 'else if' operator breaks script compilation
Bug #3109: SetPos/Position handles actors differently
Bug #3282: Unintended behaviour when assigning F3 and Windows keys
Bug #3623: Fix HiDPI on Windows
Bug #3623: Display scaling breaks mouse recognition
Bug #3725: Using script function in a non-conditional expression breaks script compilation
Bug #3733: Normal maps are inverted on mirrored UVs
Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation
Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled
Bug #4329: Removed birthsign abilities are restored after reloading the save
Bug #4383: Bow model obscures crosshair when arrow is drawn
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons
Bug #4411: Reloading a saved game while falling prevents damage in some cases
Bug #4540: Rain delay when exiting water
Bug #4701: PrisonMarker record is not hardcoded like other markers
Bug #4703: Editor: it's possible to preview levelled list records
Bug #4705: Editor: unable to open exterior cell views from Instances table
Bug #4714: Crash upon game load in the repair menu while the "Your repair failed!" message is active
Bug #4715: "Cannot get class of an empty object" exception after pressing ESC in the dialogue mode
Bug #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation
Bug #4723: ResetActors command works incorrectly
Bug #4745: Editor: Interior cell lighting field values are not displayed as colors
Bug #4736: LandTexture records overrides do not work
Bug #4745: Editor: Interior cell lighting field values are not displayed as colors
Bug #4746: Non-solid player can't run or sneak
Bug #4747: Bones are not read from X.NIF file for NPC animation
Bug #4748: Editor: Cloned, moved, added instances re-use RefNum indices
Bug #4750: Sneaking doesn't work in first person view if the player is in attack ready state
Bug #4756: Animation issues with VAOs
Bug #4757: Content selector: files can be cleared when there aren't any files to clear
Bug #4768: Fallback numerical value recovery chokes on invalid arguments
Bug #4775: Slowfall effect resets player jumping flag
Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken
@ -32,15 +39,19 @@
Bug #4800: Standing collisions are not updated immediately when an object is teleported without a cell change
Bug #4803: Stray special characters before begin statement break script compilation
Bug #4804: Particle system with the "Has Sizes = false" causes an exception
Bug #4805: NPC movement speed calculations do not take race Weight into account
Bug #4810: Raki creature broken in OpenMW
Bug #4813: Creatures with known file but no "Sound Gen Creature" assigned use default sounds
Bug #4815: "Finished" journal entry with lower index doesn't close journal, SetJournalIndex closes journal
Bug #4820: Spell absorption is broken
Bug #4823: Jail progress bar works incorrectly
Bug #4826: Uninitialized memory in unit test
Bug #4827: NiUVController is handled incorrectly
Bug #4828: Potion looping effects VFX are not shown for NPCs
Bug #4837: CTD when a mesh with NiLODNode root node with particles is loaded
Bug #4841: Russian localization ignores implicit keywords
Bug #4841: Russian localization ignores implicit keywords
Bug #4847: Idle animation reset oddities
Bug #4851: No shadows since switch to OSG
Bug #4860: Actors outside of processing range visible for one frame after spawning
Bug #4867: Arbitrary text after local variable declarations breaks script compilation
Bug #4876: AI ratings handling inconsistencies
@ -49,15 +60,39 @@
Bug #4896: Title screen music doesn't loop
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.
Bug #4918: Abilities don't play looping VFX when they're initially applied
Bug #4920: Combat AI uses incorrect invisibility check
Bug #4922: Werewolves can not attack if the transformation happens during attack
Bug #4927: Spell effect having both a skill and an attribute assigned is a fatal error
Bug #4932: Invalid records matching when loading save with edited plugin
Bug #4933: Field of View not equal with Morrowind
Bug #4938: Strings from subrecords with actually empty headers can't be empty
Bug #4942: Hand-to-Hand attack type is chosen randomly when "always use best attack" is turned off
Bug #4945: Poor random magic magnitude distribution
Bug #4947: Player character doesn't use lip animation
Bug #4948: Footstep sounds while levitating on ground level
Bug #4963: Enchant skill progress is incorrect
Bug #4964: Multiple effect spell projectile sounds play louder than vanilla
Bug #4965: Global light attenuation settings setup is lacking
Bug #4969: "Miss" sound plays for any actor
Bug #4971: OpenMW-CS: Make rotations display as degrees instead of radians
Bug #4972: Player is able to use quickkeys while disableplayerfighting is active
Bug #4979: AiTravel maximum range depends on "actors processing range" setting
Bug #4980: Drowning mechanics is applied for actors indifferently from distance to player
Bug #4984: "Friendly hits" feature should be used only for player's followers
Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent
Bug #4990: Dead bodies prevent you from hitting
Bug #5004: Werewolves shield their eyes during storm
Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis
Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller
Feature #4001: Toggle sneak controller shortcut
Feature #4209: Editor: Faction rank sub-table
Feature #4360: Improve default controller bindings
Feature #4673: Weapon sheathing
Feature #4675: Support for NiRollController
Feature #4730: Native animated containers support
@ -66,8 +101,14 @@
Feature #4859: Make water reflections more configurable
Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable
Feature #4958: Support eight blood types
Feature #4962: Add casting animations for magic items
Feature #4968: Scalable UI widget skins
Feature #4994: Persistent pinnable windows hiding
Feature #5000: Compressed BSA format support
Task #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption
Task #4721: Add NMake support to the Windows prebuild script
0.45.0
------

@ -0,0 +1,109 @@
*** PLEASE PUT YOUR ISSUE DESCRIPTION FOR DUMMIES HERE FOR REVIEW ***
- I'm just a placeholder description (#1337)
- I'm also just a placeholder description, but I'm a more recent one (#42)
***
0.46.0
------
The OpenMW team is proud to announce the release of version 0.46.0! Grab it from our Downloads Page for all operating systems. ***short summary: shadows, recastnavigation, etc.***
Check out the release video (***add link***) and the OpenMW-CS release video (***add link***) by the ***add flattering adjective*** Atahualpa, and see below for the full list of changes.
Known Issues:
- There's currently no way to redirect the logging output to the command prompt on Windows Release builds -- this will be resolved in version 0.46.0
- To use generic Linux binaries, Qt4 and libpng12 must be installed on your system
- On macOS, launching OpenMW from OpenMW-CS requires OpenMW.app and OpenMW-CS.app to be siblings
New Features:
- NIF files which contain an "AvoidNode" are ignored by the pathfinding algorithm (#1724)
- Navmeshes are used for AI pathfinding which should resolve most related issues (#2229)
- Movement input from gamepad joysticks is transformed into analogue values (#3025)
- Sane default values for openmw.cfg file to overcome the original morrowind.ini file (#3442)
- Option to invert x-axis for controllers (#3610)
- Local variables of objects selected in the console can now be directly read and set without explicitly stating the object (#3893)
- In-game option to enable or disable controllers (#3980)
- Sneak mode can be toggled using a controller (#4001)
- Controllers use original engine's default key bindings (#4360)
- Support for sheathing animations, including weapon holstering, scabbards (except for throwing weapons), and quivers for projectiles (#4673)
- Support for "NiRollController" in NIF files to ensure correct rotation of models in "Weapon Sheathing" mod (#4675)
- Support for native animated containers (#4730)
- Support for VAO ("Vertex Array Objects") from OSG 3.5.6 or later (#4756)
- Support for "NiSwitchNode" in NIF files to allow future implementation of native support for extended features like harvestable plants or glowing - windows (#4812)
- Native support for glowing windows (and other daytime-dependent meshes) by adding internal day-night-mode switch (#4836)
- Shadows (#4851)
- More configuration options for in-game water reflections (#4859)
- Command line option to specify a random seed to be used by the game's random-number generator ("RNG") for debugging purposes (#4887)
- Configuration options for distant terrain to adjust quality and performance impact (#4890)
New Editor Features:
- "Faction Ranks" table for "Faction" records (#4209)
Bug Fixes:
- Scripted Items cannot be stacked anymore to avoid multiple script execution (#2969)
- Stray text after an "else" statement is now ignored, like in the original engine, to handle mods which erroneously use "else if" statements (#3006)
- "SetPos" and "SetPosition" commands now more closely replicate the original engine's behaviour (#3109)
- "Reserved keys [F3], [F4], [F10], and [F11] cannot be assigned to in-game controls anymore (#3282)
- Windows: Reserved [Windows] key cannot be assigned to in-game controls anymore (#3282)"
- Windows: Windows-internal display scaling no longer breaks main menu (#3623)
- Normal maps on mirrored UVs are no longer inverted (#3733)
- Teleporting attempts are now also detected if teleporting is disabled to ensure compatibility with certain mods (#3765)
- Throwing weapons are now correctly rotated during throwing animation when using the "Improved Thrown Weapon Projectiles" mod (#3778)
- Birthsign abilities are no longer restored upon loading to ensure mod compatibility (#4329)
- Player character's model is no longer scaled in first-person mode to prevent issues with arrows obscuring the crosshair (#4383)
- Optional: Ranged attacks now bypass normal weapon resistance or weakness if ammunition and/or bow are appropriate (#4384)
- Fall damage is now also applied when first reloading a savegame and when your character is near the ground in the loaded game (#4411)
- Rain drops are no longer delayed when your character emerges from water (#4540)
- ESM record for prison markers is now hardcoded like, e.g., door markers or temple markers (#4701)
- Loading a savegame which includes active messages no longer crashes the game (#4714)
- An empty pointer actor no longer throws an exception upon exiting the dialogue menu (#4715)
- Inventory paper doll no longer simultaneously displays shield and two-handed weapon during drawing and holstering animations (#4720)
- "Reset actors" command ("ra") no longer tries to reset actors originating from inactive cells, e.g., followers (#4723)
- "Reset actors" command ("ra") now traces reset actors to the ground and also resets fall damage (#4723)"
- Land texture records can now be overwritten by content files to create mods like "Winter in Morrowind" (#4736)
- Disabling collision no longer forces your character to walking speed, but also allows them to run or sneak (#4746)
- NPCs now also use the skeleton associated with their specified model, not only the animations (#4747)
- Sneaking und swimming idle animations are no longer interrupted if your character is in attack-ready state in first-person view (#4750)
- Numerical fallback values with invalid values (e.g., stray text) in the openmw.cfg file no longer crash or break the game (#4768)
- Character's "jumping" flag is no longer unnecessarily reset to ensure compatibility with certain mods, e.g., "Sotha Sil Expanded" (#4775)
- Calling "GetSpellEffects", "GetEffect", or "GetSpell" function on non-actor objects now returns 0, fixing issues with "Sotha Sil Expanded" - (#4778)
- AI values for actors without AIDT ("AI Data") subrecord are now set to zero upon loading instead of filling in "random" values (#4778)
- Running and sneaking are now also considered in in-game checks when your character is in midair, fixing an issue with the "Reign of Fire" mod - (#4797)
- Collision checks are now immediately updated when an object is moved to ensure compatibility with "Sotha Sil Expanded" (#4800)
- Stray special characters before the "begin" statement of a script are now ignored to ensure, once again, compatibility with "Sotha Sil Expanded" - (#4803)
- Particle nodes with an empty "sizes" array are now correctly loaded and no longer cause an exception (#4804)
- Handling of "root bone" and "bip01" nodes in NIF files now matches the original engine's behaviour to ensure compatibility with "Skyrim: Home of - the Nords" (#4810)
- Creatures without specified sound files now fallback to the sounds of the first creature sharing the same model (#4813)
- "Journal" command now also closes a quest when the specified "finish quest" entry has a lower value than the current one for that quest (#4815)
- Spell effects are no longer applied when a spell is successfully absorbed (#4820)
- World state is no longer updated for every in-game hour your character is in jail but only once, which should significantly reduce loading times - (#4823)
- "NiUVController" in NIF files now only affects textures which use the specified "UV Set" index, usually 0; ensures compatibility with "Glow in the Dahrk" (#4827)
- Visual effects ("VFX") for magic effects are now played immediately after the effect is triggered to not accidentally skip the VFX, e.g., when actors drink potions in battle (#4828)
- Meshes with "NiLODNode" or "NiSwitchNode" no longer cause crashes when they contain particles (#4837)
- Localisations can now make use of implicit keywords to create hyperlinks in dialogue text (#4841)
- Actors outside of the processing range no longer appear for one frame when they are spawned (#4860)
- Stray text after a local-variable declaration is now ignored to ensure mod compatibility (#4867)
- Range and default values of AI data fields now match the original engine's ones (#4876)
- "Startup" scripts are now always run once upon starting OpenMW (#4877)
- Stray explicit reference calls for global variables are now ignored to ensure mod compatibility, e.g., with "Sotha Sil Expanded" (#4888)
- Title screen music now loops (#4896)
- "Specular power" is no longer hardcoded but uses the specified value in the shader code (#4916)
- Werewolves can now also attack if their transformation happened during an attack move (#4922)
- Plug-ins with valid empty subrecords are now correctly loaded, which fixes issues with the "DC - Return of Great House Dagoth" mod (#4938)
- Hand-to-hand attacks are now movement-based when the "always use best attack" option is turned off, like in the original engine (#4942)
Editor Bug Fixes:
- Certain numerical fields now only accept unsigned 8-bit integers to avoid overflows (#2987)
- Preview option is now disabled for levelled lists (#4703)
- Opening the "Scene" view from the "Instances" table now also works for exterior cells (#4705)
- Colour fields in interior-cell records now also use the colour picker widget (#4745)
- Cloned, added, or moved instances no longer disappear at load-time (#4748)
- "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757)
- Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911)
Miscellaneous:
- Upgraded to FFMPEG3 for media decoding (#4686)
- Optimised terrain code to drastically increase performance with distant terrain enabled (#4695)
- Windows: Added support for NMake to the prebuild script (#4721)

@ -19,7 +19,7 @@ DATE=$(date +'%d%m%Y')
SHORT_COMMIT=$(git rev-parse --short "${TRAVIS_COMMIT}")
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
if ! ssh -p "$OSX_DEPLOY_PORT" -i "$SSH_KEY_PATH" "$OSX_DEPLOY_HOST" sh -c "ls \"$REMOTE_PATH\"" | grep "$SHORT_COMMIT" > /dev/null; then
if ! ssh -p "$OSX_DEPLOY_PORT" -i "$SSH_KEY_PATH" "$OSX_DEPLOY_HOST" "ls \"$REMOTE_PATH\"" | grep "$SHORT_COMMIT" > /dev/null; then
scp -P "$OSX_DEPLOY_PORT" -i "$SSH_KEY_PATH" ./*.dmg "$OSX_DEPLOY_HOST:$REMOTE_PATH/$TARGET_FILENAME"
else
echo "An existing nightly build for commit ${SHORT_COMMIT} has been found, skipping upload."

@ -311,7 +311,7 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
endif()
find_package(MyGUI 3.2.1 REQUIRED)
find_package(MyGUI 3.2.2 REQUIRED)
find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED)
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
@ -429,7 +429,7 @@ endif()
# CXX Compiler settings
set(CMAKE_CXX_STANDARD 14)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++14 -pedantic -Wno-long-long")
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
if (APPLE)

@ -704,29 +704,25 @@ std::string creatureFlags(int flags)
{
std::string properties;
if (flags == 0) properties += "[None] ";
if (flags & ESM::Creature::None) properties += "All ";
if (flags & ESM::Creature::Base) properties += "Base ";
if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon ";
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
if (flags & ESM::Creature::Metal) properties += "Metal ";
if (flags & ESM::Creature::Essential) properties += "Essential ";
int unused = (0xFFFFFFFF ^
(ESM::Creature::None|
int unused = (0xFF ^
(ESM::Creature::Base|
ESM::Creature::Walks|
ESM::Creature::Swims|
ESM::Creature::Flies|
ESM::Creature::Bipedal|
ESM::Creature::Respawn|
ESM::Creature::Weapon|
ESM::Creature::Skeleton|
ESM::Creature::Metal|
ESM::Creature::Essential));
if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
properties += str(boost::format("(0x%02X)") % flags);
return properties;
}
@ -828,33 +824,21 @@ std::string npcFlags(int flags)
{
std::string properties;
if (flags == 0) properties += "[None] ";
// Mythicmods and the ESM component differ. Mythicmods says
// 0x8=None and 0x10=AutoCalc, while our code previously defined
// 0x8 as AutoCalc. The former seems to be correct. All Bethesda
// records have bit 0x8 set. Previously, suspiciously large portion
// of females had autocalc turned off.
if (flags & 0x00000008) properties += "Unknown ";
if (flags & ESM::NPC::Base) properties += "Base ";
if (flags & ESM::NPC::Autocalc) properties += "Autocalc ";
if (flags & ESM::NPC::Female) properties += "Female ";
if (flags & ESM::NPC::Respawn) properties += "Respawn ";
if (flags & ESM::NPC::Essential) properties += "Essential ";
// These two flags do not appear on any NPCs and may have been
// confused with the flags for creatures.
if (flags & ESM::NPC::Skeleton) properties += "Skeleton ";
if (flags & ESM::NPC::Metal) properties += "Metal ";
// Whether corpses persist is a bit that is unaccounted for,
// however the only unknown bit occurs on ALL records, and
// relatively few NPCs have this bit set.
int unused = (0xFFFFFFFF ^
(0x00000008|
// however relatively few NPCs have this bit set.
int unused = (0xFF ^
(ESM::NPC::Base|
ESM::NPC::Autocalc|
ESM::NPC::Female|
ESM::NPC::Respawn|
ESM::NPC::Essential|
ESM::NPC::Skeleton|
ESM::NPC::Metal));
ESM::NPC::Essential));
if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags);
properties += str(boost::format("(0x%02X)") % flags);
return properties;
}

@ -618,7 +618,8 @@ void Record<ESM::Creature>::print()
std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Flags: " << creatureFlags(mData.mFlags) << std::endl;
std::cout << " Flags: " << creatureFlags((int)mData.mFlags) << std::endl;
std::cout << " Blood Type: " << mData.mBloodType+1 << std::endl;
std::cout << " Original: " << mData.mOriginal << std::endl;
std::cout << " Scale: " << mData.mScale << std::endl;
@ -1022,7 +1023,9 @@ void Record<ESM::NPC>::print()
std::cout << " Script: " << mData.mScript << std::endl;
if (!mData.mFaction.empty())
std::cout << " Faction: " << mData.mFaction << std::endl;
std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl;
std::cout << " Flags: " << npcFlags((int)mData.mFlags) << std::endl;
if (mData.mBloodType != 0)
std::cout << " Blood Type: " << mData.mBloodType+1 << std::endl;
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{

@ -74,13 +74,14 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
int unarmedFactorsStrengthIndex = mEngineSettings.getInt("strength influences hand to hand", "Game");
if (unarmedFactorsStrengthIndex >= 0 && unarmedFactorsStrengthIndex <= 2)
unarmedFactorsStrengthComboBox->setCurrentIndex(unarmedFactorsStrengthIndex);
loadSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
loadSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
// Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
@ -134,13 +135,14 @@ void Launcher::AdvancedPage::saveSettings()
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
saveSettingBool(classicReflectedAbsorbSpellsCheckBox, "classic reflected absorb spells behavior", "Game");
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
int unarmedFactorsStrengthIndex = unarmedFactorsStrengthComboBox->currentIndex();
if (unarmedFactorsStrengthIndex != mEngineSettings.getInt("strength influences hand to hand", "Game"))
mEngineSettings.setInt("strength influences hand to hand", "Game", unarmedFactorsStrengthIndex);
saveSettingBool(requireAppropriateAmmunitionCheckBox, "only appropriate ammunition bypasses resistance", "Game");
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
saveSettingBool(normaliseRaceSpeedCheckBox, "normalise race speed", "Game");
// Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");

@ -230,9 +230,19 @@ MwIniImporter::MwIniImporter()
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture 3",
"Blood:Texture 4",
"Blood:Texture 5",
"Blood:Texture 6",
"Blood:Texture 7",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
"Blood:Texture Name 3",
"Blood:Texture Name 4",
"Blood:Texture Name 5",
"Blood:Texture Name 6",
"Blood:Texture Name 7",
// movies
"Movies:Company Logo",
@ -624,17 +634,6 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish",
"Moons:Script Color",
// blood
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
// werewolf (Bloodmoon)
"General:Werewolf FOV",

@ -21,12 +21,13 @@ using namespace Fallback;
CS::Editor::Editor (int argc, char **argv)
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
mViewManager (mDocumentManager), mPid(""),
mLock(), mMerge (mDocumentManager),
mPid(""), mLock(), mMerge (mDocumentManager),
mIpcServerName ("org.openmw.OpenCS"), mServer(nullptr), mClientSocket(nullptr)
{
{
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
mViewManager = new CSVDoc::ViewManager(mDocumentManager);
setupDataFiles (config.first);
NifOsg::Loader::setShowMarkers(true);
@ -44,11 +45,11 @@ CS::Editor::Editor (int argc, char **argv)
connect (&mDocumentManager, SIGNAL (lastDocumentDeleted()),
this, SLOT (lastDocumentDeleted()));
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (&mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));
connect (mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
connect (mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
connect (mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ()));
connect (mViewManager, SIGNAL (mergeDocument (CSMDoc::Document *)), this, SLOT (mergeDocument (CSMDoc::Document *)));
connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ()));
connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ()));
@ -69,6 +70,8 @@ CS::Editor::Editor (int argc, char **argv)
CS::Editor::~Editor ()
{
delete mViewManager;
mPidFile.close();
if(mServer && boost::filesystem::exists(mPid))
@ -107,7 +110,9 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc, quiet);
mCfgMgr.readConfiguration(variables, desc, false);
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
const std::string encoding = variables["encoding"].as<Files::EscapeHashString>().toStdString();
mDocumentManager.setEncoding (ToUTF8::calculateEncoding (encoding));
@ -115,8 +120,6 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap);
if (variables["script-blacklist-use"].as<bool>())
mDocumentManager.setBlacklistedScripts (
variables["script-blacklist"].as<Files::EscapeStringVector>().toStdStringVector());
@ -367,7 +370,7 @@ int CS::Editor::run()
void CS::Editor::documentAdded (CSMDoc::Document *document)
{
mViewManager.addView (document);
mViewManager->addView (document);
}
void CS::Editor::documentAboutToBeRemoved (CSMDoc::Document *document)

@ -42,7 +42,6 @@ namespace CS
Files::ConfigurationManager mCfgMgr;
CSMPrefs::State mSettingsState;
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame;
CSVPrefs::Dialogue mSettings;
@ -54,6 +53,7 @@ namespace CS
boost::filesystem::ofstream mPidFile;
bool mFsStrict;
CSVTools::Merge mMerge;
CSVDoc::ViewManager* mViewManager;
void setupDataFiles (const Files::PathContainer& dataDirs);

@ -273,18 +273,16 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files,bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
const Fallback::Map* fallback,
ToUTF8::FromType encoding,
const std::vector<std::string>& blacklistedScripts,
ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, fallback, resDir),
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, resDir),
mTools (*this, encoding),
mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")),
mSavingOperation (*this, mProjectPath, encoding),
mSaving (&mSavingOperation),
mResDir(resDir), mFallbackMap(fallback),
mRunner (mProjectPath), mDirty (false), mIdCompletionManager(mData)
mResDir(resDir), mRunner (mProjectPath),
mDirty (false), mIdCompletionManager(mData)
{
if (mContentFiles.empty())
throw std::runtime_error ("Empty content file sequence");

@ -69,7 +69,6 @@ namespace CSMDoc
Saving mSavingOperation;
OperationHolder mSaving;
boost::filesystem::path mResDir;
const Fallback::Map* mFallbackMap;
Blacklist mBlacklist;
Runner mRunner;
bool mDirty;
@ -105,8 +104,7 @@ namespace CSMDoc
Document (const Files::ConfigurationManager& configuration,
const std::vector< boost::filesystem::path >& files, bool new_,
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
const Fallback::Map* fallback, ToUTF8::FromType encoding,
const std::vector<std::string>& blacklistedScripts,
ToUTF8::FromType encoding, const std::vector<std::string>& blacklistedScripts,
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
~Document();

@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_)
{
return new Document (mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
return new Document (mConfiguration, files, new_, savePath, mResDir, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
}
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
@ -98,11 +98,6 @@ void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& par
mResDir = boost::filesystem::system_complete(parResDir);
}
void CSMDoc::DocumentManager::setFallbackMap(const std::map<std::string, std::string>& fallbackMap)
{
mFallbackMap = Fallback::Map(fallbackMap);
}
void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
{
mEncoding = encoding;

@ -41,7 +41,6 @@ namespace CSMDoc
std::vector<std::string> mBlacklistedScripts;
boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
bool mFsStrict;
Files::PathContainer mDataPaths;
@ -72,8 +71,6 @@ namespace CSMDoc
void setResourceDir (const boost::filesystem::path& parResDir);
void setFallbackMap (const std::map<std::string, std::string>& fallbackMap);
void setEncoding (ToUTF8::FromType encoding);
void setBlacklistedScripts (const std::vector<std::string>& scriptIds);

@ -38,11 +38,11 @@ bool CSMFilter::TextNode::test (const CSMWorld::IdTableBase& table, int row,
{
int value = data.toInt();
std::vector<std::string> enums =
std::vector<std::pair<int,std::string>> enums =
CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId));
if (value>=0 && value<static_cast<int> (enums.size()))
string = QString::fromUtf8 (enums[value].c_str());
string = QString::fromUtf8 (enums[value].second.c_str());
}
else if (data.type()==QVariant::Bool)
{

@ -75,11 +75,11 @@ void CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model
if (data==mValue)
{
std::vector<std::string> states =
std::vector<std::pair<int,std::string>> states =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
const std::string hint = "r: " + std::to_string(model->getColumnId(index.column()));
messages.add (id, states.at(data), hint);
messages.add (id, states.at(data).second, hint);
}
}

@ -5,8 +5,16 @@
#include <ostream>
#include <sstream>
#include <components/esm/loadland.hpp>
#include <components/misc/constants.hpp>
namespace
{
const int cellSize {ESM::Land::REAL_SIZE};
const int landSize {ESM::Land::LAND_SIZE};
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
}
CSMWorld::CellCoordinates::CellCoordinates() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {}
@ -32,11 +40,13 @@ CSMWorld::CellCoordinates CSMWorld::CellCoordinates::move (int x, int y) const
std::string CSMWorld::CellCoordinates::getId (const std::string& worldspace) const
{
// we ignore the worldspace for now, since there is only one (will change in 1.1)
std::ostringstream stream;
stream << "#" << mX << " " << mY;
return generateId(mX, mY);
}
return stream.str();
std::string CSMWorld::CellCoordinates::generateId (int x, int y)
{
std::string cellId = "#" + std::to_string(x) + " " + std::to_string(y);
return cellId;
}
bool CSMWorld::CellCoordinates::isExteriorCell (const std::string& id)
@ -66,6 +76,50 @@ std::pair<int, int> CSMWorld::CellCoordinates::coordinatesToCellIndex (float x,
return std::make_pair (std::floor (x / Constants::CellSizeInUnits), std::floor (y / Constants::CellSizeInUnits));
}
std::pair<int, int> CSMWorld::CellCoordinates::toTextureCoords(osg::Vec3d worldPos)
{
const auto xd = static_cast<float>(worldPos.x() * landTextureSize / cellSize - 0.25f);
const auto yd = static_cast<float>(worldPos.y() * landTextureSize / cellSize + 0.25f);
const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd));
return std::make_pair(x, y);
}
std::pair<int, int> CSMWorld::CellCoordinates::toVertexCoords(osg::Vec3d worldPos)
{
const auto xd = static_cast<float>(worldPos.x() * (landSize - 1) / cellSize + 0.5f);
const auto yd = static_cast<float>(worldPos.y() * (landSize - 1) / cellSize + 0.5f);
const auto x = static_cast<int>(std::floor(xd));
const auto y = static_cast<int>(std::floor(yd));
return std::make_pair(x, y);
}
float CSMWorld::CellCoordinates::textureSelectionToWorldCoords(int pos)
{
return cellSize * static_cast<float>(pos) / landTextureSize;
}
float CSMWorld::CellCoordinates::vertexSelectionToWorldCoords(int pos)
{
return cellSize * static_cast<float>(pos) / (landSize - 1);
}
int CSMWorld::CellCoordinates::vertexSelectionToInCellCoords(int pos)
{
return static_cast<int>(pos - std::floor(static_cast<float>(pos) / (landSize - 1)) * (landSize - 1));
}
std::string CSMWorld::CellCoordinates::vertexGlobalToCellId(std::pair<int, int> vertexGlobal)
{
int x = std::floor(static_cast<float>(vertexGlobal.first) / (landSize - 1));
int y = std::floor(static_cast<float>(vertexGlobal.second) / (landSize - 1));
return generateId(x, y);
}
bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{
return left.getX()==right.getX() && left.getY()==right.getY();

@ -7,6 +7,8 @@
#include <QMetaType>
#include <osg/Vec3d>
namespace CSMWorld
{
class CellCoordinates
@ -29,6 +31,9 @@ namespace CSMWorld
CellCoordinates move (int x, int y) const;
///< Return a copy of *this, moved by the given offset.
///Generate cell id string from x and y coordinates
static std::string generateId (int x, int y);
std::string getId (const std::string& worldspace) const;
///< Return the ID for the cell at these coordinates.
@ -42,6 +47,24 @@ namespace CSMWorld
/// \return cell coordinates such that given world coordinates are in it.
static std::pair<int, int> coordinatesToCellIndex (float x, float y);
///Converts worldspace coordinates to global texture selection, taking in account the texture offset.
static std::pair<int, int> toTextureCoords(osg::Vec3d worldPos);
///Converts worldspace coordinates to global vertex selection.
static std::pair<int, int> toVertexCoords(osg::Vec3d worldPos);
///Converts global texture coordinate to worldspace coordinate that is at the upper left corner of the selected texture.
static float textureSelectionToWorldCoords(int);
///Converts global vertex coordinate to worldspace coordinate
static float vertexSelectionToWorldCoords(int);
///Converts local cell's heightmap coordinates from the global vertex coordinate
static int vertexSelectionToInCellCoords(int);
///Converts global vertex coordinates to cell id
static std::string vertexGlobalToCellId(std::pair<int, int>);
};
bool operator== (const CellCoordinates& left, const CellCoordinates& right);

@ -86,6 +86,8 @@ namespace CSMWorld
Display_Enchantment,
//CONCRETE TYPES ENDS HERE
Display_SignedInteger8,
Display_SignedInteger16,
Display_UnsignedInteger8,
Display_UnsignedInteger16,
Display_Integer,

@ -1376,7 +1376,7 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
const ESM::Position& position = record.get().*mPosition;
return position.rot[mIndex];
return osg::RadiansToDegrees(position.rot[mIndex]);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
@ -1385,7 +1385,7 @@ namespace CSMWorld
ESM::Position& position = record2.*mPosition;
position.rot[mIndex] = data.toFloat();
position.rot[mIndex] = osg::DegreesToRadians(data.toFloat());
record.setModified (record2);
}

@ -1,5 +1,6 @@
#include "columns.hpp"
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp>
#include "universalid.hpp"
@ -573,11 +574,6 @@ namespace
"Book", "Scroll", 0
};
static const char *sBloodType[] =
{
"Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0
};
static const char *sEmitterType[] =
{
"<None>", "Flickering", "Flickering (Slow)", "Pulsing", "Pulsing (Slow)", 0
@ -613,7 +609,6 @@ namespace
case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
case CSMWorld::Columns::ColumnId_BookType: return sBookType;
case CSMWorld::Columns::ColumnId_BloodType: return sBloodType;
case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;
default: return 0;
@ -626,19 +621,28 @@ bool CSMWorld::Columns::hasEnums (ColumnId column)
return getEnumNames (column)!=0 || column==ColumnId_RecordType;
}
std::vector<std::string> CSMWorld::Columns::getEnums (ColumnId column)
std::vector<std::pair<int,std::string>>CSMWorld::Columns::getEnums (ColumnId column)
{
std::vector<std::string> enums;
std::vector<std::pair<int,std::string>> enums;
if (const char **table = getEnumNames (column))
for (int i=0; table[i]; ++i)
enums.push_back (table[i]);
enums.emplace_back(i, table[i]);
else if (column==ColumnId_BloodType)
{
for (int i=0; i<8; i++)
{
const std::string& bloodName = Fallback::Map::getString("Blood_Texture_Name_" + std::to_string(i));
if (!bloodName.empty())
enums.emplace_back(i, bloodName);
}
}
else if (column==ColumnId_RecordType)
{
enums.push_back (""); // none
enums.emplace_back(UniversalId::Type_None, ""); // none
for (int i=UniversalId::Type_None+1; i<UniversalId::NumberOfTypes; ++i)
enums.push_back (UniversalId (static_cast<UniversalId::Type> (i)).getTypeName());
enums.emplace_back (i, UniversalId (static_cast<UniversalId::Type> (i)).getTypeName());
}
return enums;

@ -390,7 +390,7 @@ namespace CSMWorld
bool hasEnums (ColumnId column);
std::vector<std::string> getEnums (ColumnId column);
std::vector<std::pair<int,std::string>> getEnums (ColumnId column);
///< Returns an empty vector, if \a column isn't an enum type column.
}
}

@ -11,6 +11,8 @@
#include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.hpp>
@ -64,9 +66,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
}
CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
const std::vector<std::string>& archives, const boost::filesystem::path& resDir)
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
mFallbackMap(fallback), mReader (0), mDialogue (0), mReaderIndex(1),
mReader (0), mDialogue (0), mReaderIndex(1),
mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
{
mVFS.reset(new VFS::Manager(mFsStrict));
@ -75,6 +77,14 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
mResourcesManager.setVFS(mVFS.get());
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
Shader::ShaderManager::DefineMap defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
Shader::ShaderManager::DefineMap shadowDefines = SceneUtil::ShadowManager::getShadowsDisabledDefines();
defines["forcePPL"] = "0";
defines["clamp"] = "1";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0;
@ -1360,8 +1370,3 @@ const VFS::Manager* CSMWorld::Data::getVFS() const
{
return mVFS.get();
}
const Fallback::Map* CSMWorld::Data::getFallbackMap() const
{
return mFallbackMap;
}

@ -112,7 +112,6 @@ namespace CSMWorld
IdCollection<ESM::Filter> mFilters;
Collection<MetaData> mMetaData;
std::unique_ptr<ActorAdapter> mActorAdapter;
const Fallback::Map* mFallbackMap;
std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
ESM::ESMReader *mReader;
@ -151,15 +150,12 @@ namespace CSMWorld
public:
Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
const std::vector<std::string>& archives, const Fallback::Map* fallback,
const boost::filesystem::path& resDir);
const std::vector<std::string>& archives, const boost::filesystem::path& resDir);
virtual ~Data();
const VFS::Manager* getVFS() const;
const Fallback::Map* getFallbackMap() const;
std::shared_ptr<Resource::ResourceSystem> getResourceSystem();
std::shared_ptr<const Resource::ResourceSystem> getResourceSystem() const;

@ -6,13 +6,13 @@
namespace
{
std::string getEnumValue(const std::vector<std::string> &values, int index)
std::string getEnumValue(const std::vector<std::pair<int,std::string>> &values, int index)
{
if (index < 0 || index >= static_cast<int>(values.size()))
{
return "";
}
return values[index];
return values[index].second;
}
}

@ -22,7 +22,7 @@ namespace CSMWorld
// Cache of enum values for enum columns (e.g. Modified, Record Type).
// Used to speed up comparisons during the sort by such columns.
typedef std::map<Columns::ColumnId, std::vector<std::string> > EnumColumnCache;
typedef std::map<Columns::ColumnId, std::vector<std::pair<int,std::string>> > EnumColumnCache;
mutable EnumColumnCache mEnumColumnCache;
protected:

@ -491,17 +491,7 @@ QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, con
return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column == mColumns.mBloodType)
{
int mask = ESM::Creature::Skeleton | ESM::Creature::Metal;
if ((record.get().mFlags & mask) == ESM::Creature::Skeleton)
return 1;
if ((record.get().mFlags & mask) == ESM::Creature::Metal)
return 2;
return 0;
}
return record.get().mBloodType;
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
mColumns.mFlags.find (column);
@ -527,16 +517,7 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa
else if (column==mColumns.mOriginal)
creature.mOriginal = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType)
{
int mask = ~(ESM::Creature::Skeleton | ESM::Creature::Metal);
if (value.toInt() == 1)
creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Skeleton;
else if (value.toInt() == 2)
creature.mFlags = (creature.mFlags & mask) | ESM::Creature::Metal;
else
creature.mFlags = creature.mFlags & mask;
}
creature.mBloodType = value.toInt();
else
{
std::map<const RefIdColumn *, unsigned int>::const_iterator iter =
@ -797,17 +778,7 @@ QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const Re
return QVariant::fromValue(ColumnBase::TableEdit_Full);
if (column == mColumns.mBloodType)
{
int mask = ESM::NPC::Skeleton | ESM::NPC::Metal;
if ((record.get().mFlags & mask) == ESM::NPC::Skeleton)
return 1;
if ((record.get().mFlags & mask) == ESM::NPC::Metal)
return 2;
return 0;
}
return record.get().mBloodType;
if (column == mColumns.mGender)
{
@ -846,16 +817,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d
else if (column==mColumns.mHead)
npc.mHead = value.toString().toUtf8().constData();
else if (column == mColumns.mBloodType)
{
int mask = ~(ESM::NPC::Skeleton | ESM::NPC::Metal);
if (value.toInt() == 1)
npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Skeleton;
else if (value.toInt() == 2)
npc.mFlags = (npc.mFlags & mask) | ESM::NPC::Metal;
else
npc.mFlags = npc.mFlags & mask;
}
npc.mBloodType = value.toInt();
else if (column == mColumns.mGender)
{
// Implemented this way to allow additional gender types in the future.

@ -512,7 +512,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Attribute, CSMWorld::ColumnBase::Display_Attribute, false, false));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8));
// Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcSkills,
@ -524,7 +524,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Skill, CSMWorld::ColumnBase::Display_SkillId, false, false));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_UChar, CSMWorld::ColumnBase::Display_UnsignedInteger8));
// Nested list
mColumns.push_back(RefIdColumn (Columns::ColumnId_NpcMisc,
@ -534,21 +534,21 @@ CSMWorld::RefIdCollection::RefIdCollection()
miscMap.insert(std::make_pair(UniversalId::Type_Npc, new NpcMiscRefIdAdapter()));
mNestedAdapters.push_back (std::make_pair(&mColumns.back(), miscMap));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_Level, CSMWorld::ColumnBase::Display_SignedInteger16));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_NpcFactionID, CSMWorld::ColumnBase::Display_SignedInteger8));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_Health, CSMWorld::ColumnBase::Display_UnsignedInteger16));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_Mana, CSMWorld::ColumnBase::Display_UnsignedInteger16));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_Fatigue, CSMWorld::ColumnBase::Display_UnsignedInteger16));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_NpcDisposition, CSMWorld::ColumnBase::Display_UnsignedInteger8));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_NpcReputation, CSMWorld::ColumnBase::Display_UnsignedInteger8));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_Integer));
new RefIdColumn (Columns::ColumnId_NpcRank, CSMWorld::ColumnBase::Display_UnsignedInteger8));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_Gold, CSMWorld::ColumnBase::Display_Integer));
mColumns.back().addColumn(

@ -164,7 +164,7 @@ namespace CSVRender
mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin));
// Add water texture
std::string textureName = mData.getFallbackMap()->getFallbackString("Water_SurfaceTexture");
std::string textureName = Fallback::Map::getString("Water_SurfaceTexture");
textureName = "textures/water/" + textureName + "00.dds";
Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager();

@ -29,7 +29,6 @@
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp>
#include "actor.hpp"
#include "mask.hpp"
@ -140,17 +139,8 @@ void CSVRender::Object::update()
if (light)
{
const Fallback::Map* fallback = mData.getFallbackMap();
static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin");
static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic");
static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue");
static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear");
static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue");
bool isExterior = false; // FIXME
SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior, outQuadInLin, useQuadratic,
quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior);
}
}

@ -40,13 +40,13 @@ CSVTools::SearchBox::SearchBox (QWidget *parent)
mLayout = new QGridLayout (this);
// search panel
std::vector<std::string> states =
std::vector<std::pair<int,std::string>> states =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
states.resize (states.size()-1); // ignore erased state
for (std::vector<std::string>::const_iterator iter (states.begin()); iter!=states.end();
for (std::vector<std::pair<int,std::string>>::const_iterator iter (states.begin()); iter!=states.end();
++iter)
mRecordState.addItem (QString::fromUtf8 (iter->c_str()));
mRecordState.addItem (QString::fromUtf8 (iter->second.c_str()));
mMode.addItem (tr("Text"));
mMode.addItem (tr("Text (RegEx)"));

@ -76,9 +76,9 @@ void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QMo
else if (CSMWorld::Columns::hasEnums (columnId))
{
int data = v.toInt();
std::vector<std::string> enumNames (CSMWorld::Columns::getEnums (columnId));
std::vector<std::pair<int,std::string>> enumNames (CSMWorld::Columns::getEnums (columnId));
label->setText(QString::fromUtf8(enumNames.at(data).c_str()));
label->setText(QString::fromUtf8(enumNames.at(data).second.c_str()));
}
else
{

@ -155,7 +155,7 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool all
add (i, names[i]);
}
CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::string>& names,
CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::pair<int,std::string>>& names,
bool allowNone)
{
if (allowNone)
@ -164,7 +164,7 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::strin
int size = static_cast<int> (names.size());
for (int i=0; i<size; ++i)
add (i, names[i].c_str());
add (names[i].first, names[i].second.c_str());
}
CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (

@ -65,7 +65,7 @@ namespace CSVWorld
///< \param names Array of char pointer with a 0-pointer as end mark
/// \param allowNone Use value of -1 for "none selected" (empty string)
EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false);
EnumDelegateFactory (const std::vector<std::pair<int,std::string>>& names, bool allowNone = false);
/// \param allowNone Use value of -1 for "none selected" (empty string)
virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const;

@ -22,7 +22,7 @@ CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
{
std::vector<std::string> enums =
std::vector<std::pair<int,std::string>> enums =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
static const char *sIcons[] =
@ -31,5 +31,8 @@ CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
};
for (int i=0; sIcons[i]; ++i)
add (i, enums.at (i).c_str(), sIcons[i]);
{
auto& enumPair = enums.at(i);
add (enumPair.first, enumPair.second.c_str(), sIcons[i]);
}
}

@ -212,6 +212,19 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
return sb;
}
case CSMWorld::ColumnBase::Display_SignedInteger8:
{
DialogueSpinBox *sb = new DialogueSpinBox(parent);
sb->setRange(std::numeric_limits<signed char>::min(), std::numeric_limits<signed char>::max());
return sb;
}
case CSMWorld::ColumnBase::Display_SignedInteger16:
{
DialogueSpinBox *sb = new DialogueSpinBox(parent);
sb->setRange(std::numeric_limits<short>::min(), std::numeric_limits<short>::max());
return sb;
}
case CSMWorld::ColumnBase::Display_UnsignedInteger8:
{
DialogueSpinBox *sb = new DialogueSpinBox(parent);

@ -73,11 +73,11 @@ CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (
void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)
{
std::vector<std::string> enums =
std::vector<std::pair<int,std::string>> enums =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType);
if (static_cast<size_t>(type) >= enums.size())
throw std::logic_error ("Unsupported variable type");
mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str())));
mValues.emplace_back(type, QString::fromUtf8 (enums[type].second.c_str()));
}

@ -8,8 +8,7 @@ set(GAME
)
if (ANDROID)
set(GAME ${GAME} android_commandLine.cpp)
set(GAME ${GAME} android_main.c)
set(GAME ${GAME} android_main.cpp)
endif()
set(GAME_HEADER
@ -32,7 +31,7 @@ add_openmw_dir (mwinput
add_openmw_dir (mwgui
layout textinput widgets race class birth review windowmanagerimp console dialogue
windowbase statswindow messagebox journalwindow charactercreation
mapwindow windowpinnablebase tooltips scrollwindow bookwindow
mapwindow windowpinnablebase tooltips scrollwindow bookwindow resourceskin
formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog

@ -1,27 +0,0 @@
#include "android_commandLine.h"
#include "string.h"
const char **argvData;
int argcData;
extern "C" void releaseArgv();
void releaseArgv() {
delete[] argvData;
}
JNIEXPORT void JNICALL Java_ui_activity_GameActivity_commandLine(JNIEnv *env,
jobject obj, jint argc, jobjectArray stringArray) {
jboolean iscopy;
argcData = (int) argc;
argvData = new const char *[argcData + 1];
argvData[0] = "openmw";
for (int i = 1; i < argcData + 1; i++) {
jstring string = (jstring) (env)->GetObjectArrayElement(stringArray,
i - 1);
argvData[i] = (env)->GetStringUTFChars(string, &iscopy);
(env)->DeleteLocalRef(string);
}
(env)->DeleteLocalRef(stringArray);
}

@ -1,15 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_ui_activity_GameActivity_commandLine
#define _Included_ui_activity_GameActivity_commandLine
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_ui_activity_GameActivity_commandLine(JNIEnv *env, jobject obj,jint argcData, jobjectArray stringArray);
#ifdef __cplusplus
}
#endif
#endif

@ -18,35 +18,35 @@ extern const char **argvData;
void releaseArgv();
int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {
extern "C" int Java_org_libsdl_app_SDLActivity_getMouseX(JNIEnv *env, jclass cls, jobject obj) {
int ret = 0;
SDL_GetMouseState(&ret, NULL);
SDL_GetMouseState(&ret, nullptr);
return ret;
}
int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {
extern "C" int Java_org_libsdl_app_SDLActivity_getMouseY(JNIEnv *env, jclass cls, jobject obj) {
int ret = 0;
SDL_GetMouseState(NULL, &ret);
SDL_GetMouseState(nullptr, &ret);
return ret;
}
int Java_org_libsdl_app_SDLActivity_isMouseShown(JNIEnv *env, jclass cls, jobject obj) {
extern "C" int Java_org_libsdl_app_SDLActivity_isMouseShown(JNIEnv *env, jclass cls, jobject obj) {
return SDL_ShowCursor(SDL_QUERY);
}
extern SDL_Window *Android_Window;
int SDL_SendMouseMotion(SDL_Window * window, int mouseID, int relative, int x, int y);
void Java_org_libsdl_app_SDLActivity_sendRelativeMouseMotion(JNIEnv *env, jclass cls, int x, int y) {
extern "C" int SDL_SendMouseMotion(SDL_Window * window, int mouseID, int relative, int x, int y);
extern "C" void Java_org_libsdl_app_SDLActivity_sendRelativeMouseMotion(JNIEnv *env, jclass cls, int x, int y) {
SDL_SendMouseMotion(Android_Window, 0, 1, x, y);
}
int SDL_SendMouseButton(SDL_Window * window, int mouseID, Uint8 state, Uint8 button);
void Java_org_libsdl_app_SDLActivity_sendMouseButton(JNIEnv *env, jclass cls, int state, int button) {
extern "C" int SDL_SendMouseButton(SDL_Window * window, int mouseID, Uint8 state, Uint8 button);
extern "C" void Java_org_libsdl_app_SDLActivity_sendMouseButton(JNIEnv *env, jclass cls, int state, int button) {
SDL_SendMouseButton(Android_Window, 0, state, button);
}
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
extern "C" int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
// On Android, we use a virtual controller with guid="Virtual"

@ -549,11 +549,11 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
camera->setGraphicsContext(graphicsWindow);
camera->setViewport(0, 0, width, height);
camera->setViewport(0, 0, traits->width, traits->height);
mViewer->realize();
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height);
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
}
void OMW::Engine::setWindowIcon()
@ -655,24 +655,24 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
rootNode->addChild(guiRoot);
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mFallbackMap, mUseSound));
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
if (!mSkipMenu)
{
std::string logo = mFallbackMap["Movies_Company_Logo"];
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
if (!logo.empty())
window->playVideo(logo, true);
}
// Create the world
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
mFileCollections, mContentFiles, mEncoder, mFallbackMap,
mActivationDistanceOverride, mCellName, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
@ -806,6 +806,11 @@ void OMW::Engine::go()
mViewer = new osgViewer::Viewer;
mViewer->setReleaseContextAtEndOfFrameHint(false);
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
// Do not try to outsmart the OS thread scheduler (see bug #4785).
mViewer->setUseConfigureAffinity(false);
#endif
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General"));
@ -864,7 +869,7 @@ void OMW::Engine::go()
// start in main menu
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
mEnvironment.getSoundManager()->playTitleMusic();
std::string logo = mFallbackMap["Movies_Morrowind_Logo"];
const std::string& logo = Fallback::Map::getString("Movies_Morrowind_Logo");
if (!logo.empty())
mEnvironment.getWindowManager()->playVideo(logo, true);
}
@ -949,11 +954,6 @@ void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
mEncoding = encoding;
}
void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap)
{
mFallbackMap = fallbackMap;
}
void OMW::Engine::setScriptConsoleMode (bool enabled)
{
mScriptConsoleMode = enabled;

@ -91,7 +91,6 @@ namespace OMW
bool mCompileAllDialogue;
int mWarningsMode;
std::string mFocusName;
std::map<std::string,std::string> mFallbackMap;
bool mScriptConsoleMode;
std::string mStartupScript;
int mActivationDistanceOverride;
@ -182,8 +181,6 @@ namespace OMW
/// Font encoding
void setEncoding(const ToUTF8::FromType& encoding);
void setFallbackValues(std::map<std::string,std::string> map);
/// Enable console-only script functionality
void setScriptConsoleMode (bool enabled);

@ -1,6 +1,7 @@
#include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/escape.hpp>
#include <components/fallback/fallback.hpp>
#include <components/fallback/validate.hpp>
#include <components/debug/debugging.hpp>
#include <components/misc/rng.hpp>
@ -314,8 +315,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setSaveGameFile (variables["load-savegame"].as<Files::EscapeHashString>().toStdString());
// other settings
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
engine.enableFontExport(variables["export-fonts"].as<bool>());
engine.setRandomSeed(variables["random-seed"].as<unsigned int>());

@ -73,11 +73,6 @@ namespace MWWorld
typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > PtrMovementList;
}
namespace Fallback
{
class Map;
}
namespace MWBase
{
/// \brief Interface for the World (implemented in MWWorld)
@ -131,8 +126,6 @@ namespace MWBase
virtual void adjustSky() = 0;
virtual const Fallback::Map *getFallback () const = 0;
virtual MWWorld::Player& getPlayer() = 0;
virtual MWWorld::Ptr getPlayerPtr() = 0;
virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0;
@ -631,8 +624,9 @@ namespace MWBase
{
Rest_Allowed = 0,
Rest_OnlyWaiting = 1,
Rest_PlayerIsUnderwater = 2,
Rest_EnemiesAreNearby = 3
Rest_PlayerIsInAir = 2,
Rest_PlayerIsUnderwater = 3,
Rest_EnemiesAreNearby = 4
};
/// check if the player is allowed to rest

@ -466,7 +466,8 @@ namespace MWClass
if (!successful)
{
// Missed
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
@ -856,6 +857,8 @@ namespace MWClass
if(name == "left")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
if(world->isFlying(ptr))
return -1;
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return ESM::SoundGenerator::SwimLeft;
@ -866,6 +869,8 @@ namespace MWClass
if(name == "right")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
if(world->isFlying(ptr))
return -1;
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
return ESM::SoundGenerator::SwimRight;
@ -911,13 +916,7 @@ namespace MWClass
int Creature::getBloodTexture(const MWWorld::ConstPtr &ptr) const
{
int flags = ptr.get<ESM::Creature>()->mBase->mFlags;
if (flags & ESM::Creature::Skeleton)
return 1;
if (flags & ESM::Creature::Metal)
return 2;
return 0;
return ptr.get<ESM::Creature>()->mBase->mBloodType;
}
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -800,7 +800,8 @@ namespace MWClass
if (!successful)
{
// Missed
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
@ -1399,6 +1400,8 @@ namespace MWClass
if(name == "left" || name == "right")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
if(world->isFlying(ptr))
return std::string();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isSwimming(ptr))
return (name == "left") ? "Swim Left" : "Swim Right";
@ -1468,13 +1471,7 @@ namespace MWClass
int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const
{
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
return ptr.get<ESM::NPC>()->mBase->mBloodType;
}
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -62,8 +62,7 @@ namespace MWDialogue
DialogueManager::DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage) :
mTranslationDataStorage(translationDataStorage)
, mCompilerContext (MWScript::CompilerContext::Type_Dialogue)
, mErrorStream(std::cout.rdbuf())
, mErrorHandler(mErrorStream)
, mErrorHandler()
, mTalkedTo(false)
, mTemporaryDispositionChange(0.f)
, mPermanentDispositionChange(0.f)
@ -241,8 +240,7 @@ namespace MWDialogue
if (!success)
{
Log(Debug::Warning)
<< "Warning: compiling failed (dialogue script)\n" << cmd << "\n\n";
Log(Debug::Error) << "Error: compiling failed (dialogue script): \n" << cmd << "\n";
}
return success;

@ -33,7 +33,6 @@ namespace MWDialogue
Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext;
std::ostream mErrorStream;
Compiler::StreamErrorHandler mErrorHandler;
MWWorld::Ptr mActor;

@ -28,8 +28,7 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::
MWScript::CompilerContext compilerContext(MWScript::CompilerContext::Type_Dialogue);
compilerContext.setExtensions(extensions);
std::ostream errorStream(std::cout.rdbuf());
Compiler::StreamErrorHandler errorHandler(errorStream);
Compiler::StreamErrorHandler errorHandler;
errorHandler.setWarningsMode (warningsMode);
const MWWorld::Store<ESM::Dialogue>& dialogues = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>();
@ -84,8 +83,7 @@ void test(const MWWorld::Ptr& actor, int &compiled, int &total, const Compiler::
if (!success)
{
Log(Debug::Warning)
<< "compiling failed (dialogue script)\n" << info->mResultScript << "\n\n";
Log(Debug::Error) << "Error: compiling failed (dialogue script): \n" << info->mResultScript << "\n";
}
}
}

@ -20,7 +20,7 @@ namespace MWGui
{
BookWindow::BookWindow ()
: WindowBase("openmw_book.layout")
: BookWindowBase("openmw_book.layout")
, mCurrentPage(0)
, mTakeButtonShow(true)
, mTakeButtonAllowed(true)
@ -43,10 +43,10 @@ namespace MWGui
getWidget(mLeftPage, "LeftPage");
getWidget(mRightPage, "RightPage");
adjustButton(mCloseButton);
adjustButton(mTakeButton);
adjustButton(mNextPageButton);
adjustButton(mPrevPageButton);
adjustButton("CloseButton");
adjustButton("TakeButton");
adjustButton("PrevPageBTN");
float scale = adjustButton("NextPageBTN");
mLeftPage->setNeedMouseFocus(true);
mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel);
@ -62,7 +62,7 @@ namespace MWGui
{
// english button has a 7 pixel wide strip of garbage on its right edge
mNextPageButton->setSize(64-7, mNextPageButton->getSize().height);
mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,mNextPageButton->getSize().height));
mNextPageButton->setImageCoord(MyGUI::IntCoord(0,0,(64-7)*scale,mNextPageButton->getSize().height*scale));
}
center();
@ -187,15 +187,6 @@ namespace MWGui
}
}
void BookWindow::adjustButton (Gui::ImageButton* button)
{
MyGUI::IntSize diff = button->getSize() - button->getRequestedSize();
button->setSize(button->getRequestedSize());
if (button->getAlign().isRight())
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
}
void BookWindow::nextPage()
{
if ((mCurrentPage+1)*2 < mPages.size())

@ -9,7 +9,7 @@
namespace MWGui
{
class BookWindow : public WindowBase
class BookWindow : public BookWindowBase
{
public:
BookWindow();
@ -34,7 +34,6 @@ namespace MWGui
void updatePages();
void clearPages();
void adjustButton(Gui::ImageButton* button);
private:
typedef std::pair<int, int> Page;

@ -33,14 +33,15 @@ namespace
};
const ESM::Class::Specialization mSpecializations[3]={ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth}; // The specialization for each answer
Step sGenerateClassSteps(int number) {
Step sGenerateClassSteps(int number)
{
number++;
const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback();
Step step = {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_Question"),
{fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"),
fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"),
fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")},
"vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav"
Step step = {
Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_Question"),
{Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"),
Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"),
Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")},
"vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav"
};
return step;
}

@ -254,16 +254,6 @@ namespace MWGui
if (plainText.size() && brAtEnd)
plainText.erase(plainText.end()-1);
#if (MYGUI_VERSION < MYGUI_DEFINE_VERSION(3, 2, 2))
// splitting won't be fully functional until 3.2.2 (see TextElement::pageSplit())
// hack: prevent newlines at the end of the book possibly creating unnecessary pages
if (event == BookTextParser::Event_EOF)
{
while (plainText.size() && plainText[plainText.size()-1] == '\n')
plainText.erase(plainText.end()-1);
}
#endif
if (!plainText.empty() || brBeforeLastTag || isPrevImg)
{
TextElement elem(paper, pag, mBlockStyle,
@ -443,8 +433,6 @@ namespace MWGui
int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;
// first empty lines that would go to the next page should be ignored
// unfortunately, getLineInfo method won't be available until 3.2.2
#if (MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3, 2, 2))
mPaginator.setIgnoreLeadingEmptyLines(true);
const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo();
@ -458,7 +446,6 @@ namespace MWGui
break;
}
}
#endif
return ret;
}

@ -2,6 +2,7 @@
#include <MyGUI_FactoryManager.h>
#include <MyGUI_ImageBox.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_TextBox.h>
// correctIconPath
@ -30,6 +31,7 @@ namespace
namespace MWGui
{
std::map<std::string, float> ItemWidget::mScales;
ItemWidget::ItemWidget()
: mItem(nullptr)
@ -146,10 +148,29 @@ namespace MWGui
if (backgroundTex != "")
backgroundTex += ".dds";
float scale = 1.f;
if (!backgroundTex.empty())
{
auto found = mScales.find(backgroundTex);
if (found == mScales.end())
{
// By default, background icons are supposed to use the 42x42 part of 64x64 image.
// If the image has a different size, we should use a different chunk size
// Cache result to do not retrieve background texture every frame.
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(backgroundTex);
if (texture)
scale = texture->getHeight() / 64.f;
mScales[backgroundTex] = scale;
}
else
scale = found->second;
}
if (state == Barter && !isMagic)
setFrame(backgroundTex, MyGUI::IntCoord(2,2,42,42));
setFrame(backgroundTex, MyGUI::IntCoord(2*scale,2*scale,44*scale,44*scale));
else
setFrame(backgroundTex, MyGUI::IntCoord(0,0,42,42));
setFrame(backgroundTex, MyGUI::IntCoord(0,0,44*scale,44*scale));
setIcon(ptr);
}

@ -50,6 +50,8 @@ namespace MWGui
std::string mCurrentIcon;
std::string mCurrentFrame;
static std::map<std::string, float> mScales;
};
class SpellWidget : public ItemWidget

@ -153,7 +153,7 @@ namespace
}
adjustButton(PrevPageBTN);
adjustButton(NextPageBTN);
float nextButtonScale = adjustButton(NextPageBTN);
adjustButton(CloseBTN);
adjustButton(CancelBTN);
adjustButton(JournalBTN);
@ -168,7 +168,7 @@ namespace
{
// english button has a 7 pixel wide strip of garbage on its right edge
nextButton->setSize(64-7, nextButton->getSize().height);
nextButton->setImageCoord(MyGUI::IntCoord(0,0,64-7,nextButton->getSize().height));
nextButton->setImageCoord(MyGUI::IntCoord(0,0,(64-7)*nextButtonScale,nextButton->getSize().height*nextButtonScale));
}
if (!questList)
@ -226,17 +226,6 @@ namespace
mTopicsMode = false;
}
void adjustButton (char const * name)
{
Gui::ImageButton* button = getWidget<Gui::ImageButton>(name);
MyGUI::IntSize diff = button->getSize() - button->getRequestedSize();
button->setSize(button->getRequestedSize());
if (button->getAlign().isRight())
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
}
void onOpen()
{
if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())
@ -665,7 +654,7 @@ MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model
}
MWGui::JournalWindow::JournalWindow()
:WindowBase("openmw_journal.layout")
: BookWindowBase("openmw_journal.layout")
{
}

@ -13,7 +13,7 @@ namespace MWGui
{
struct JournalViewModel;
struct JournalWindow : public WindowBase
struct JournalWindow : public BookWindowBase
{
JournalWindow();

@ -140,10 +140,10 @@ namespace MWGui
mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + MyGUI::utility::toString(level));
std::string levelupdescription;
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Level"+MyGUI::utility::toString(level));
levelupdescription = Fallback::Map::getString("Level_Up_Level"+MyGUI::utility::toString(level));
if (levelupdescription == "")
levelupdescription=world->getFallback()->getFallbackString("Level_Up_Default");
levelupdescription = Fallback::Map::getString("Level_Up_Default");
mLevelDescription->setCaption (levelupdescription);

@ -302,15 +302,18 @@ namespace MWGui
Gui::ImageButton* button = mButtons[buttonId];
button->setVisible(true);
// By default, assume that all menu buttons textures should have 64 height.
// If they have a different resolution, scale them.
MyGUI::IntSize requested = button->getRequestedSize();
float scale = requested.height / 64.f;
button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height));
// Trim off some of the excessive padding
// TODO: perhaps do this within ImageButton?
int height = requested.height-16;
button->setImageTile(MyGUI::IntSize(requested.width, height));
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height);
curH += height;
int height = requested.height;
button->setImageTile(MyGUI::IntSize(requested.width, requested.height-16*scale));
button->setCoord((maxwidth-requested.width/scale) / 2, curH, requested.width/scale, height/scale-16);
curH += height/scale-16;
}
if (state == MWBase::StateManager::State_NoGame)

@ -0,0 +1,83 @@
#include "resourceskin.hpp"
#include <MyGUI_RenderManager.h>
#include <components/misc/stringops.hpp>
namespace MWGui
{
void resizeSkin(MyGUI::xml::ElementPtr _node)
{
_node->setAttribute("type", "ResourceSkin");
const std::string size = _node->findAttribute("size");
if (!size.empty())
return;
const std::string textureName = _node->findAttribute("texture");
if (textureName.empty())
return;
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(textureName);
if (!texture)
return;
MyGUI::IntCoord coord(0, 0, texture->getWidth(), texture->getHeight());
MyGUI::xml::ElementEnumerator basis = _node->getElementEnumerator();
const std::string textureSize = std::to_string(coord.width) + " " + std::to_string(coord.height);
_node->addAttribute("size", textureSize);
while (basis.next())
{
if (basis->getName() != "BasisSkin")
continue;
const std::string basisSkinType = basis->findAttribute("type");
if (Misc::StringUtils::ciEqual(basisSkinType, "SimpleText"))
continue;
const std::string offset = basis->findAttribute("offset");
if (!offset.empty())
continue;
basis->addAttribute("offset", coord);
MyGUI::xml::ElementEnumerator state = basis->getElementEnumerator();
while (state.next())
{
if (state->getName() == "State")
{
const std::string stateOffset = state->findAttribute("offset");
if (!stateOffset.empty())
continue;
state->addAttribute("offset", coord);
if (Misc::StringUtils::ciEqual(basisSkinType, "TileRect"))
{
MyGUI::xml::ElementEnumerator property = state->getElementEnumerator();
bool hasTileSize = false;
while (property.next("Property"))
{
const std::string key = property->findAttribute("key");
if (key != "TileSize")
continue;
hasTileSize = true;
}
if (!hasTileSize)
{
MyGUI::xml::ElementPtr tileSizeProperty = state->createChild("Property");
tileSizeProperty->addAttribute("key", "TileSize");
tileSizeProperty->addAttribute("value", textureSize);
}
}
}
}
}
}
void AutoSizedResourceSkin::deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version)
{
resizeSkin(_node);
Base::deserialization(_node, _version);
}
}

@ -0,0 +1,18 @@
#ifndef MWGUI_RESOURCESKIN_H
#define MWGUI_RESOURCESKIN_H
#include <MyGUI_ResourceSkin.h>
namespace MWGui
{
class AutoSizedResourceSkin : public MyGUI::ResourceSkin
{
MYGUI_RTTI_DERIVED( AutoSizedResourceSkin )
public:
virtual void deserialization(MyGUI::xml::ElementPtr _node, MyGUI::Version _version);
};
}
#endif

@ -16,23 +16,11 @@
#include "formatting.hpp"
namespace
{
void adjustButton (Gui::ImageButton* button)
{
MyGUI::IntSize diff = button->getSize() - button->getRequestedSize();
button->setSize(button->getRequestedSize());
if (button->getAlign().isRight())
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
}
}
namespace MWGui
{
ScrollWindow::ScrollWindow ()
: WindowBase("openmw_scroll.layout")
: BookWindowBase("openmw_scroll.layout")
, mTakeButtonShow(true)
, mTakeButtonAllowed(true)
{
@ -44,8 +32,8 @@ namespace MWGui
getWidget(mTakeButton, "TakeButton");
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked);
adjustButton(mCloseButton);
adjustButton(mTakeButton);
adjustButton("CloseButton");
adjustButton("TakeButton");
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);
mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);

@ -12,7 +12,7 @@ namespace Gui
namespace MWGui
{
class ScrollWindow : public WindowBase
class ScrollWindow : public BookWindowBase
{
public:
ScrollWindow ();

@ -71,11 +71,8 @@ namespace MWGui
std::set<MWWorld::Ptr> followers;
MWWorld::ActionTeleport::getFollowersToTeleport(player, followers);
// Apply followers cost, in vanilla one follower travels for free
if (Settings::Manager::getBool("charge for every follower travelling", "Game"))
price *= 1 + static_cast<int>(followers.size());
else
price *= std::max(1, static_cast<int>(followers.size()));
// Apply followers cost, unlike vanilla the first follower doesn't travel for free
price *= 1 + static_cast<int>(followers.size());
int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2;

@ -91,10 +91,17 @@ namespace MWGui
mTimeAdvancer.eventInterrupted += MyGUI::newDelegate(this, &WaitDialog::onWaitingInterrupted);
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished);
}
void WaitDialog::onReferenceUnavailable ()
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Rest);
resetReference();
}
void WaitDialog::setPtr(const MWWorld::Ptr &ptr)
{
setCanRest(!ptr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed);
mPtr = ptr;
setCanRest(!mPtr.isEmpty() || MWBase::Environment::get().getWorld ()->canRest () == MWBase::World::Rest_Allowed);
if (mUntilHealedButton->getVisible())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton);
@ -111,6 +118,7 @@ namespace MWGui
{
mSleeping = false;
mTimeAdvancer.stop();
resetReference();
}
void WaitDialog::onOpen()
@ -140,7 +148,13 @@ namespace MWGui
}
else if (canRest == MWBase::World::Rest_PlayerIsUnderwater)
{
// resting underwater or mid-air not allowed
// resting underwater not allowed
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}");
MWBase::Environment::get().getWindowManager()->popGuiMode ();
}
else if (mPtr.isEmpty() && canRest == MWBase::World::Rest_PlayerIsInAir)
{
// Resting in air is not allowed either, unless you're using a bed
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage1}");
MWBase::Environment::get().getWindowManager()->popGuiMode ();
}
@ -337,6 +351,8 @@ namespace MWGui
void WaitDialog::onFrame(float dt)
{
checkReferenceAvailable();
mTimeAdvancer.onFrame(dt);
if (mFadeTimeRemaining <= 0)

@ -4,6 +4,7 @@
#include "timeadvancer.hpp"
#include "windowbase.hpp"
#include "referenceinterface.hpp"
namespace MWGui
{
@ -22,7 +23,7 @@ namespace MWGui
MyGUI::TextBox* mProgressText;
};
class WaitDialog : public WindowBase
class WaitDialog : public WindowBase, public ReferenceInterface
{
public:
WaitDialog();
@ -63,6 +64,8 @@ namespace MWGui
WaitDialogProgressBar mProgressBar;
virtual void onReferenceUnavailable();
void onUntilHealedButtonClicked(MyGUI::Widget* sender);
void onWaitButtonClicked(MyGUI::Widget* sender);
void onCancelButtonClicked(MyGUI::Widget* sender);

@ -535,9 +535,7 @@ namespace MWGui
, mRepeatStepTime(0.1f)
, mIsIncreasing(true)
{
#if MYGUI_VERSION >= MYGUI_DEFINE_VERSION(3,2,2)
ScrollBar::setRepeatEnabled(false);
#endif
}
MWScrollBar::~MWScrollBar()

@ -6,6 +6,8 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp"
#include <components/widgets/imagebutton.hpp>
#include "draganddrop.hpp"
using namespace MWGui;
@ -120,3 +122,30 @@ void NoDrop::setAlpha(float alpha)
if (mWidget)
mWidget->setAlpha(alpha);
}
BookWindowBase::BookWindowBase(const std::string& parLayout)
: WindowBase(parLayout)
{
}
float BookWindowBase::adjustButton (char const * name)
{
Gui::ImageButton* button;
WindowBase::getWidget (button, name);
MyGUI::IntSize requested = button->getRequestedSize();
float scale = requested.height / button->getSize().height;
MyGUI::IntSize newSize = requested;
newSize.width /= scale;
newSize.height /= scale;
button->setSize(newSize);
if (button->getAlign().isRight())
{
MyGUI::IntSize diff = (button->getSize() - requested);
diff.width /= scale;
diff.height /= scale;
button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0));
}
return scale;
}

@ -82,6 +82,15 @@ namespace MWGui
DragAndDrop* mDrag;
bool mTransparent;
};
class BookWindowBase : public WindowBase
{
public:
BookWindowBase(const std::string& parLayout);
protected:
float adjustButton (char const * name);
};
}
#endif

@ -13,7 +13,6 @@
#include <MyGUI_InputManager.h>
#include <MyGUI_Gui.h>
#include <MyGUI_ClipboardManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_WidgetManager.h>
#include <SDL_keyboard.h>
@ -124,6 +123,7 @@
#include "jailscreen.hpp"
#include "itemchargeview.hpp"
#include "keyboardnavigation.hpp"
#include "resourceskin.hpp"
namespace
{
@ -141,7 +141,7 @@ namespace MWGui
WindowManager::WindowManager(
osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, const std::string& resourcePath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::map<std::string, std::string>& fallbackMap, const std::string& versionDescription, const std::string& userDataPath)
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& userDataPath)
: mStore(nullptr)
, mResourceSystem(resourceSystem)
, mWorkQueue(workQueue)
@ -201,7 +201,6 @@ namespace MWGui
, mForceHidden(GW_None)
, mAllowed(GW_ALL)
, mRestAllowed(true)
, mFallbackMap(fallbackMap)
, mShowOwned(0)
, mEncoding(encoding)
, mFontHeight(16)
@ -253,6 +252,7 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>("Resource", "AutoSizedResourceSkin");
MyGUI::ResourceManager::getInstance().load("core.xml");
loadUserFonts();
@ -303,14 +303,13 @@ namespace MWGui
void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)
{
const std::string templateName = "Journalbook ";
MyGUI::xml::ElementEnumerator font = _node->getElementEnumerator();
MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator();
bool createCopy = false;
while (font.next("Resource"))
while (resourceNode.next("Resource"))
{
std::string type, name;
font->findAttribute("type", type);
font->findAttribute("name", name);
resourceNode->findAttribute("type", type);
resourceNode->findAttribute("name", name);
if (name.empty())
continue;
@ -328,18 +327,19 @@ namespace MWGui
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
resolution *= uiScale;
MyGUI::xml::ElementPtr resolutionNode = font->createChild("Property");
MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
resolutionNode->addAttribute("key", "Resolution");
resolutionNode->addAttribute("value", std::to_string(resolution));
MyGUI::xml::ElementPtr sizeNode = font->createChild("Property");
MyGUI::xml::ElementPtr sizeNode = resourceNode->createChild("Property");
sizeNode->addAttribute("key", "Size");
sizeNode->addAttribute("value", std::to_string(mFontHeight));
}
else if (Misc::StringUtils::ciEqual(type, "ResourceSkin"))
else if (Misc::StringUtils::ciEqual(type, "ResourceSkin") ||
Misc::StringUtils::ciEqual(type, "AutoSizedResourceSkin"))
{
// We should adjust line height for MyGUI widgets depending on font size
MyGUI::xml::ElementPtr heightNode = font->createChild("Property");
MyGUI::xml::ElementPtr heightNode = resourceNode->createChild("Property");
heightNode->addAttribute("key", "HeightLine");
heightNode->addAttribute("value", std::to_string(mFontHeight+2));
}
@ -1255,7 +1255,7 @@ namespace MWGui
{
_result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength));
}
else if (Gui::replaceTag(tag, _result, mFallbackMap))
else if (Gui::replaceTag(tag, _result))
{
return;
}
@ -1575,6 +1575,32 @@ namespace MWGui
if (getMode() != GM_Inventory)
return;
std::string settingName;
switch (wnd)
{
case GW_Inventory:
settingName = "inventory";
break;
case GW_Map:
settingName = "map";
break;
case GW_Magic:
settingName = "spells";
break;
case GW_Stats:
settingName = "stats";
break;
default:
break;
}
if (!settingName.empty())
{
settingName += " hidden";
bool hidden = Settings::Manager::getBool(settingName, "Windows");
Settings::Manager::setBool(settingName, "Windows", !hidden);
}
mShown = (GuiWindow)(mShown ^ wnd);
updateVisible();
}
@ -2058,12 +2084,20 @@ namespace MWGui
void WindowManager::updatePinnedWindows()
{
mInventoryWindow->setPinned(Settings::Manager::getBool("inventory pin", "Windows"));
if (Settings::Manager::getBool("inventory hidden", "Windows"))
mShown = (GuiWindow)(mShown ^ GW_Inventory);
mMap->setPinned(Settings::Manager::getBool("map pin", "Windows"));
if (Settings::Manager::getBool("map hidden", "Windows"))
mShown = (GuiWindow)(mShown ^ GW_Map);
mSpellWindow->setPinned(Settings::Manager::getBool("spells pin", "Windows"));
if (Settings::Manager::getBool("spells hidden", "Windows"))
mShown = (GuiWindow)(mShown ^ GW_Magic);
mStatsWindow->setPinned(Settings::Manager::getBool("stats pin", "Windows"));
if (Settings::Manager::getBool("stats hidden", "Windows"))
mShown = (GuiWindow)(mShown ^ GW_Stats);
}
void WindowManager::pinWindow(GuiWindow window)
@ -2246,6 +2280,9 @@ namespace MWGui
void WindowManager::createCursors()
{
// FIXME: currently we do not scale cursor since it is not a MyGUI widget.
// In theory, we can do it manually (rescale the cursor image via osg::Imag::scaleImage() and scale the hotspot position).
// Unfortunately, this apploach can lead to driver crashes on some setups (e.g. on laptops with nvidia-prime on Linux).
MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
while (enumerator.next())
{

@ -132,7 +132,7 @@ namespace MWGui
WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::map<std::string,std::string>& fallbackMap, const std::string& versionDescription, const std::string& localPath);
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, const std::string& localPath);
virtual ~WindowManager();
/// Set the ESMStore to use for retrieving of GUI-related strings.
@ -611,8 +611,6 @@ namespace MWGui
void updateMap();
std::map<std::string, std::string> mFallbackMap;
int mShowOwned;
ToUTF8::FromType mEncoding;

@ -791,7 +791,7 @@ namespace MWInput
mMouseLookEnabled = !guiMode;
if (guiMode)
MWBase::Environment::get().getWindowManager()->showCrosshair(false);
MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (mJoystickLastUsed && !mGamepadGuiCursorEnabled));
MWBase::Environment::get().getWindowManager()->setCursorVisible(guiMode && (!mJoystickLastUsed || mGamepadGuiCursorEnabled));
// if not in gui mode, the camera decides whether to show crosshair or not.
}
@ -1403,7 +1403,7 @@ namespace MWInput
void InputManager::quickKey (int index)
{
if (!mControlSwitch["playercontrols"])
if (!mControlSwitch["playercontrols"] || !mControlSwitch["playerfighting"] || !mControlSwitch["playermagic"])
return;
if (!checkAllowedToUseItems())
return;

@ -1725,7 +1725,10 @@ namespace MWMechanics
if(iter->first.getClass().isNpc())
{
updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer);
// We can not update drowning state for actors outside of AI distance - they can not resurface to breathe
if (inProcessingRange)
updateDrowning(iter->first, duration, ctrl->isKnockedOut(), isPlayer);
calculateNpcStatModifiers(iter->first, duration);
if (timerUpdateEquippedLight == 0)

@ -403,12 +403,9 @@ namespace MWMechanics
ESM::Position actorPos = actor.getRefData().getPosition();
ESM::Position enemyPos = enemy.getRefData().getPosition();
const CreatureStats& enemyStats = enemy.getClass().getCreatureStats(enemy);
if (enemyStats.getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
|| enemyStats.getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 0)
if (isTargetMagicallyHidden(enemy) && !MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor))
{
if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(enemy, actor))
return false;
return false;
}
if (actor.getClass().isPureWaterCreature(actor))

@ -261,7 +261,7 @@ const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const
CacheMap::iterator found = cache.find(id);
if (found == cache.end())
{
cache.insert(std::make_pair(id, std::unique_ptr<MWMechanics::PathgridGraph>(new MWMechanics::PathgridGraph(cell))));
cache.insert(std::make_pair(id, std::make_unique<MWMechanics::PathgridGraph>(MWMechanics::PathgridGraph(cell))));
}
return *cache[id].get();
}
@ -335,13 +335,6 @@ bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const
|| mPathFinder.getPathCell() != currentCell;
}
bool MWMechanics::AiPackage::isTargetMagicallyHidden(const MWWorld::Ptr& target)
{
const MagicEffects& magicEffects(target.getClass().getCreatureStats(target).getMagicEffects());
return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|| (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75);
}
bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
{
const ESM::Cell* playerCell(getPlayer().getCell()->getCell());

@ -102,8 +102,6 @@ namespace MWMechanics
/// Reset pathfinding state
void reset();
bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
/// Return if actor's rotation speed is sufficient to rotate to the destination pathpoint on the run. Otherwise actor should rotate while standing.
static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest);

@ -23,6 +23,7 @@
#include "movement.hpp"
#include "creaturestats.hpp"
#include "combat.hpp"
namespace MWMechanics
{

@ -3,7 +3,6 @@
#include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
@ -19,9 +18,8 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
{
// Maximum travel distance for vanilla compatibility.
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
// The specific range below is configurable, but its limit is currently 7168 units. Anything greater will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
float aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange();
return (pos1 - pos2).length2() <= aiDistance*aiDistance;
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
return (pos1 - pos2).length2() <= 7168*7168;
}
}

@ -366,11 +366,6 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
if (!force && jump == mJumpState && idle == CharState_None)
return;
if (jump == JumpState_InAir)
{
idle = CharState_None;
}
std::string jumpAnimName;
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
if (jump != JumpState_None)
@ -1244,7 +1239,7 @@ bool CharacterController::updateCreatureState()
*/
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid);
cast.playSpellCastingEffects(spellid, false);
/*
Start of tes3mp addition
@ -1575,9 +1570,31 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
stats.getSpells().setSelectedSpell(selectedSpell);
}
std::string spellid = stats.getSpells().getSelectedSpell();
bool isMagicItem = false;
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr);
if(!spellid.empty() && canCast)
if (spellid.empty())
{
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
const MWWorld::Ptr& enchantItem = *inv.getSelectedEnchantItem();
spellid = enchantItem.getClass().getEnchantment(enchantItem);
isMagicItem = true;
}
}
}
static const bool useCastingAnimations = Settings::Manager::getBool("use magic item animations", "Game");
if (isMagicItem && !useCastingAnimations)
{
// Enchanted items by default do not use casting animations
MWBase::Environment::get().getWorld()->castSpell(mPtr);
resetIdle = false;
}
else if(!spellid.empty() && canCast)
{
/*
Start of tes3mp addition
@ -1599,18 +1616,26 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
*/
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid);
cast.playSpellCastingEffects(spellid, isMagicItem);
std::vector<ESM::ENAMstruct> effects;
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back();
const ESM::MagicEffect *effect;
if (isMagicItem)
{
const ESM::Enchantment *enchantment = store.get<ESM::Enchantment>().find(spellid);
effects = enchantment->mEffects.mList;
}
else
{
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
effects = spell->mEffects.mList;
}
effect = store.get<ESM::MagicEffect>().find(lastEffect.mEffectID); // use last effect of list for color of VFX_Hands
const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(effects.back().mEffectID); // use last effect of list for color of VFX_Hands
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Hands");
for (size_t iter = 0; iter < spell->mEffects.mList.size(); ++iter) // play hands vfx for each effect
for (size_t iter = 0; iter < effects.size(); ++iter) // play hands vfx for each effect
{
if (mAnimation->getNode("Bip01 L Hand"))
mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle);
@ -1619,7 +1644,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 R Hand", effect->mParticle);
}
const ESM::ENAMstruct &firstEffect = spell->mEffects.mList.at(0); // first effect used for casting animation
const ESM::ENAMstruct &firstEffect = effects.at(0); // first effect used for casting animation
std::string startKey;
std::string stopKey;
@ -1653,17 +1678,6 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{
resetIdle = false;
}
if (mPtr.getClass().hasInventoryStore(mPtr))
{
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
if (inv.getSelectedEnchantItem() != inv.end())
{
// Enchanted items cast immediately (no animation)
MWBase::Environment::get().getWorld()->castSpell(mPtr);
}
}
}
else if(mWeaponType == WeapType_PickProbe)
{
@ -1756,7 +1770,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
idle != CharState_IdleSneak && idle != CharState_IdleSwim &&
mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
{
idle = CharState_None;
mAnimation->disable(mCurrentIdle);
}
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
@ -2242,6 +2256,9 @@ void CharacterController::update(float duration, bool animationOnly)
jumpstate = JumpState_Landing;
vec.z() = 0.0f;
// We should reset idle animation during landing
mAnimation->disable(mCurrentIdle);
float height = cls.getCreatureStats(mPtr).land(isPlayer);
float healthLost = getFallDamage(mPtr, height);
@ -2474,6 +2491,20 @@ void CharacterController::update(float duration, bool animationOnly)
else
moved = osg::Vec3f(0.f, 0.f, 0.f);
float scale = mPtr.getCellRef().getScale();
moved.x() *= scale;
moved.y() *= scale;
static const bool normalizeSpeed = Settings::Manager::getBool("normalise race speed", "Game");
if (mPtr.getClass().isNpc() && !normalizeSpeed)
{
const ESM::NPC* npc = mPtr.get<ESM::NPC>()->mBase;
const ESM::Race* race = world->getStore().get<ESM::Race>().find(npc->mRace);
float weight = npc->isMale() ? race->mData.mWeight.mMale : race->mData.mWeight.mFemale;
moved.x() *= weight;
moved.y() *= weight;
}
// Ensure we're moving in generally the right direction...
if(speed > 0.f)
{

@ -600,4 +600,11 @@ namespace MWMechanics
return (iFightDistanceBase - fFightDistanceMultiplier * d);
}
bool isTargetMagicallyHidden(const MWWorld::Ptr& target)
{
const MagicEffects& magicEffects = target.getClass().getCreatureStats(target).getMagicEffects();
return (magicEffects.get(ESM::MagicEffect::Invisibility).getMagnitude() > 0)
|| (magicEffects.get(ESM::MagicEffect::Chameleon).getMagnitude() > 75);
}
}

@ -53,6 +53,8 @@ void getHandToHandDamage (const MWWorld::Ptr& attacker, const MWWorld::Ptr& vict
void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float attackStrength);
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
}
#endif

@ -57,6 +57,7 @@ namespace MWMechanics
{
// Contracted disease!
actor.getClass().getCreatureStats(actor).getSpells().add(it->first);
MWBase::Environment::get().getWorld()->applyLoopingParticles(actor);
std::string msg = "sMagicContractDisease";
msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->mValue.getString();

@ -1559,22 +1559,24 @@ namespace MWMechanics
bool MechanicsManager::actorAttacked(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker)
{
if (target == getPlayer() || !attacker.getClass().isActor())
const MWWorld::Ptr& player = getPlayer();
if (target == player || !attacker.getClass().isActor())
return false;
std::set<MWWorld::Ptr> followersAttacker;
getActorsSidingWith(attacker, followersAttacker);
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
if (followersAttacker.find(target) != followersAttacker.end())
if (attacker == player)
{
statsTarget.friendlyHit();
if (statsTarget.getFriendlyHits() < 4)
std::set<MWWorld::Ptr> followersAttacker;
getActorsSidingWith(attacker, followersAttacker);
if (followersAttacker.find(target) != followersAttacker.end())
{
MWBase::Environment::get().getDialogueManager()->say(target, "hit");
return false;
statsTarget.friendlyHit();
if (statsTarget.getFriendlyHits() < 4)
{
MWBase::Environment::get().getDialogueManager()->say(target, "hit");
return false;
}
}
}
@ -1589,10 +1591,10 @@ namespace MWMechanics
Make it possible to start combat with DedicatedPlayers and DedicatedActors by
adding additional conditions for them
*/
if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target)
|| attacker == getPlayer() || mwmp::PlayerList::isDedicatedPlayer(attacker)
|| mwmp::Main::get().getCellController()->isDedicatedActor(attacker))
&& !seq.isInCombat(attacker))
if (!attacker.isEmpty()
&& (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == player
|| mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker))
&& !seq.isInCombat(attacker))
/*
End of tes3mp change (major)
*/
@ -1605,7 +1607,7 @@ namespace MWMechanics
// he will attack the player only if we will force him (e.g. via StartCombat console command)
bool peaceful = false;
std::string script = target.getClass().getScript(target);
if (!script.empty() && target.getRefData().getLocals().hasVar(script, "onpchitme") && attacker == getPlayer())
if (!script.empty() && target.getRefData().getLocals().hasVar(script, "onpchitme") && attacker == player)
{
int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified());
peaceful = (fight == 0);

@ -327,8 +327,7 @@ namespace MWMechanics
}
catch (const DetourNavigator::NavigatorException& exception)
{
DetourNavigator::log("PathFinder::buildPathByNavigator navigator exception: ", exception.what());
Log(Debug::Verbose) << "Build path by navigator exception: \"" << exception.what()
Log(Debug::Debug) << "Build path by navigator exception: \"" << exception.what()
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
<< ") from " << startPoint << " to " << endPoint << " with flags ("
<< DetourNavigator::WriteFlags {flags} << ")";

@ -3,6 +3,7 @@
#include <limits>
#include <iomanip>
#include <components/misc/constants.hpp>
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
@ -551,8 +552,7 @@ namespace MWMechanics
if (magnitudeMult > 0 && !absorbed)
{
float random = Misc::Rng::rollClosedProbability();
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
float magnitude = effectIt->mMagnMin + Misc::Rng::rollDice(effectIt->mMagnMax - effectIt->mMagnMin + 1);
magnitude *= magnitudeMult;
if (!target.getClass().isActor())
@ -977,16 +977,15 @@ namespace MWMechanics
if (mCaster == getPlayer())
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1);
}
if (enchantment->mData.mType == ESM::Enchantment::CastOnce && !godmode)
else if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
{
item.getContainerStore()->remove(item, 1, mCaster);
if (!godmode)
item.getContainerStore()->remove(item, 1, mCaster);
}
else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes)
else if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
if (mCaster == getPlayer())
{
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3);
}
}
inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self);
@ -1102,7 +1101,7 @@ namespace MWMechanics
// A non-actor doesn't play its spell cast effects from a character controller, so play them here
if (!mCaster.getClass().isActor())
playSpellCastingEffects(mId);
playSpellCastingEffects(mId, false);
inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self);
@ -1177,15 +1176,27 @@ namespace MWMechanics
return true;
}
void CastSpell::playSpellCastingEffects(const std::string &spellid)
void CastSpell::playSpellCastingEffects(const std::string &spellid, bool enchantment)
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
if (enchantment)
{
const ESM::Enchantment *spell = store.get<ESM::Enchantment>().find(spellid);
playSpellCastingEffects(spell->mEffects.mList);
std::vector<std::string> addedEffects;
}
else
{
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
playSpellCastingEffects(spell->mEffects.mList);
}
}
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->mEffects.mList.begin();
iter != spell->mEffects.mList.end(); ++iter)
void CastSpell::playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects)
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
std::vector<std::string> addedEffects;
for (std::vector<ESM::ENAMstruct>::const_iterator iter = effects.begin(); iter != effects.end(); ++iter)
{
const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(iter->mEffectID);
@ -1205,41 +1216,18 @@ namespace MWMechanics
std::string texture = effect->mParticle;
float scale = 1.0f;
osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3());
if (animation && mCaster.getClass().isNpc())
{
// For NPC we should take race height as scaling factor
const ESM::NPC *npc = mCaster.get<ESM::NPC>()->mBase;
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
const ESM::Race *race =
esmStore.get<ESM::Race>().find(npc->mRace);
scale = npc->isMale() ? race->mData.mHeight.mMale : race->mData.mHeight.mFemale;
}
else
{
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(mCaster);
// TODO: take a size of particle or NPC with height and weight = 1.0 as scale = 1.0
float scaleX = halfExtents.x() * 2 / 60.f;
float scaleY = halfExtents.y() * 2 / 60.f;
float scaleZ = halfExtents.z() * 2 / 120.f;
scale = std::max({ scaleX, scaleY, scaleZ });
}
// If the caster has no animation, add the effect directly to the effectManager
if (animation)
{
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale);
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
}
else
{
// We should set scale for effect manager manually
// If the caster has no animation, add the effect directly to the effectManager
// We should scale it manually
osg::Vec3f bounds (MWBase::Environment::get().getWorld()->getHalfExtents(mCaster) * 2.f / Constants::UnitsPerFoot);
float scale = std::max({ bounds.x()/3.f, bounds.y()/3.f, bounds.z()/6.f });
float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);
}

@ -83,6 +83,9 @@ namespace MWMechanics
private:
MWWorld::Ptr mCaster; // May be empty
MWWorld::Ptr mTarget; // May be empty
void playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects);
public:
bool mStack;
std::string mId; // ID of spell, potion, item etc
@ -109,7 +112,7 @@ namespace MWMechanics
/// @note Auto detects if spell, ingredient or potion
bool cast (const std::string& id);
void playSpellCastingEffects(const std::string &spellid);
void playSpellCastingEffects(const std::string &spellid, bool enchantment);
bool spellIncreasesSkill();

@ -94,7 +94,10 @@ namespace MWMechanics
for (unsigned int i=0; i<spell->mEffects.mList.size();++i)
{
if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax)
random[i] = Misc::Rng::rollClosedProbability();
{
int delta = spell->mEffects.mList[i].mMagnMax - spell->mEffects.mList[i].mMagnMin;
random[i] = Misc::Rng::rollDice(delta + 1) / static_cast<float>(delta);
}
}
}

@ -698,7 +698,7 @@ namespace MWPhysics
{
// First of all, try to hit where you aim to
int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor;
RayResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor, targets, CollisionType_Actor, hitmask);
RayResult result = castRay(origin, origin + (orient * osg::Vec3f(0.0f, queryDistance, 0.0f)), actor, targets, hitmask, CollisionType_Actor);
if (result.mHit)
{

@ -16,8 +16,6 @@
#include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp>
#include <components/settings/settings.hpp>
@ -428,19 +426,10 @@ void ActorAnimation::addHiddenItemLight(const MWWorld::ConstPtr& item, const ESM
if (mItemLights.find(item) != mItemLights.end())
return;
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback();
static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin");
static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic");
static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue");
static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear");
static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue");
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
osg::Vec4f ambient(1,1,1,1);
osg::ref_ptr<SceneUtil::LightSource> lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, outQuadInLin,
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue, ambient);
osg::ref_ptr<SceneUtil::LightSource> lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient);
mInsert->addChild(lightSource);

@ -38,8 +38,6 @@
#include <components/settings/settings.hpp>
#include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
@ -1628,21 +1626,12 @@ namespace MWRender
void Animation::addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *esmLight)
{
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback();
static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin");
static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic");
static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue");
static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear");
static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue");
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior, outQuadInLin,
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
}
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture, float scale)
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
{
if (!mObjectRoot.get())
return;
@ -1674,7 +1663,12 @@ namespace MWRender
}
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform;
trans->setScale(osg::Vec3f(scale, scale, scale));
if (!mPtr.getClass().isNpc())
{
osg::Vec3f bounds (MWBase::Environment::get().getWorld()->getHalfExtents(mPtr) * 2.f / Constants::UnitsPerFoot);
float scale = std::max({ bounds.x()/3.f, bounds.y()/3.f, bounds.z()/6.f });
trans->setScale(osg::Vec3f(scale, scale, scale));
}
parentNode->addChild(trans);
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model, trans);
@ -1834,14 +1828,12 @@ namespace MWRender
}
else
{
// TODO: use global attenuation settings
// 1 pt of Light magnitude corresponds to 1 foot of radius
float radius = effect * std::ceil(Constants::UnitsPerFoot);
const float linearValue = 3.f; // Currently hardcoded: unmodified Morrowind attenuation settings
float linearAttenuation = linearValue / radius;
// Arbitrary multiplier used to make the obvious cut-off less obvious
float cutoffMult = 3;
if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation())
if (!mGlowLight || (radius * cutoffMult) != mGlowLight->getRadius())
{
if (mGlowLight)
{
@ -1853,7 +1845,9 @@ namespace MWRender
light->setDiffuse(osg::Vec4f(0,0,0,0));
light->setSpecular(osg::Vec4f(0,0,0,0));
light->setAmbient(osg::Vec4f(1.5f,1.5f,1.5f,1.f));
light->setLinearAttenuation(linearAttenuation);
bool isExterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::configureLight(light, radius, isExterior);
mGlowLight = new SceneUtil::LightSource;
mGlowLight->setNodeMask(Mask_Lighting);
@ -1861,8 +1855,7 @@ namespace MWRender
mGlowLight->setLight(light);
}
// Make the obvious cut-off a bit less obvious
mGlowLight->setRadius(radius * 3);
mGlowLight->setRadius(radius * cutoffMult);
}
}

@ -365,7 +365,7 @@ public:
* @param texture override the texture specified in the model's materials - if empty, do not override
* @note Will not add an effect twice.
*/
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "", float scale = 1.0f);
void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = "");
void removeEffect (int effectId);
void removeEffects ();
void getLoopingEffects (std::vector<int>& out) const;

@ -168,16 +168,15 @@ namespace MWRender
stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON);
osg::ref_ptr<osg::Light> light = new osg::Light;
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback();
float diffuseR = fallback->getFallbackFloat("Inventory_DirectionalDiffuseR");
float diffuseG = fallback->getFallbackFloat("Inventory_DirectionalDiffuseG");
float diffuseB = fallback->getFallbackFloat("Inventory_DirectionalDiffuseB");
float ambientR = fallback->getFallbackFloat("Inventory_DirectionalAmbientR");
float ambientG = fallback->getFallbackFloat("Inventory_DirectionalAmbientG");
float ambientB = fallback->getFallbackFloat("Inventory_DirectionalAmbientB");
float azimuth = osg::DegreesToRadians(180.f - fallback->getFallbackFloat("Inventory_DirectionalRotationX"));
float altitude = osg::DegreesToRadians(fallback->getFallbackFloat("Inventory_DirectionalRotationY"));
float positionX = std::cos(azimuth) * std::sin(altitude);
float diffuseR = Fallback::Map::getFloat("Inventory_DirectionalDiffuseR");
float diffuseG = Fallback::Map::getFloat("Inventory_DirectionalDiffuseG");
float diffuseB = Fallback::Map::getFloat("Inventory_DirectionalDiffuseB");
float ambientR = Fallback::Map::getFloat("Inventory_DirectionalAmbientR");
float ambientG = Fallback::Map::getFloat("Inventory_DirectionalAmbientG");
float ambientB = Fallback::Map::getFloat("Inventory_DirectionalAmbientB");
float azimuth = osg::DegreesToRadians(Fallback::Map::getFloat("Inventory_DirectionalRotationX"));
float altitude = osg::DegreesToRadians(Fallback::Map::getFloat("Inventory_DirectionalRotationY"));
float positionX = -std::cos(azimuth) * std::sin(altitude);
float positionY = std::sin(azimuth) * std::sin(altitude);
float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));

@ -493,7 +493,7 @@ void NpcAnimation::updateNpcBase()
if(!is1stPerson)
{
const std::string base = "meshes\\xbase_anim.nif";
if (smodel != base)
if (smodel != base && !isWerewolf)
addAnimSource(base, smodel);
if (smodel != defaultSkeleton && base != defaultSkeleton)
@ -507,7 +507,7 @@ void NpcAnimation::updateNpcBase()
else
{
const std::string base = "meshes\\xbase_anim.1st.nif";
if (smodel != base)
if (smodel != base && !isWerewolf)
addAnimSource(base, smodel);
addAnimSource(smodel, smodel);

@ -195,8 +195,7 @@ namespace MWRender
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const Fallback::Map* fallback, const std::string& resourcePath,
DetourNavigator::Navigator& navigator)
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
: mViewer(viewer)
, mRootNode(rootNode)
, mResourceSystem(resourceSystem)
@ -207,9 +206,9 @@ namespace MWRender
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mUnderwaterColor(fallback->getFallbackColour("Water_UnderwaterColor"))
, mUnderwaterWeight(fallback->getFallbackFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(fallback->getFallbackFloat("Water_UnderwaterIndoorFog"))
, mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor"))
, mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog"))
, mNightEyeFactor(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
@ -275,7 +274,7 @@ namespace MWRender
mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
@ -312,7 +311,6 @@ namespace MWRender
else
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug));
mTerrain->setDefaultViewer(mViewer->getCamera());
mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
mTerrain->setWorkQueue(mWorkQueue.get());

@ -86,8 +86,7 @@ namespace MWRender
public:
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const Fallback::Map* fallback, const std::string& resourcePath,
DetourNavigator::Navigator& navigator);
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
~RenderingManager();
MWRender::Objects& getObjects();

@ -26,13 +26,13 @@
namespace
{
void createWaterRippleStateSet(Resource::ResourceSystem* resourceSystem, const Fallback::Map* fallback, osg::Node* node)
void createWaterRippleStateSet(Resource::ResourceSystem* resourceSystem,osg::Node* node)
{
int rippleFrameCount = fallback->getFallbackInt("Water_RippleFrameCount");
int rippleFrameCount = Fallback::Map::getInt("Water_RippleFrameCount");
if (rippleFrameCount <= 0)
return;
std::string tex = fallback->getFallbackString("Water_RippleTexture");
const std::string& tex = Fallback::Map::getString("Water_RippleTexture");
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
for (int i=0; i<rippleFrameCount; ++i)
@ -81,7 +81,7 @@ namespace
namespace MWRender
{
RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem, const Fallback::Map* fallback)
RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem)
: mParent(parent)
{
mParticleSystem = new osgParticle::ParticleSystem;
@ -94,8 +94,8 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem*
particleTemplate.setSizeRange(osgParticle::rangef(15, 180));
particleTemplate.setColorRange(osgParticle::rangev4(osg::Vec4f(1,1,1,0.7), osg::Vec4f(1,1,1,0.7)));
particleTemplate.setAlphaRange(osgParticle::rangef(1.f, 0.f));
particleTemplate.setAngularVelocity(osg::Vec3f(0,0,fallback->getFallbackFloat("Water_RippleRotSpeed")));
particleTemplate.setLifeTime(fallback->getFallbackFloat("Water_RippleLifetime"));
particleTemplate.setAngularVelocity(osg::Vec3f(0,0,Fallback::Map::getFloat("Water_RippleRotSpeed")));
particleTemplate.setLifeTime(Fallback::Map::getFloat("Water_RippleLifetime"));
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater);
updater->addParticleSystem(mParticleSystem);
@ -105,7 +105,7 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem*
mParticleNode->addChild(mParticleSystem);
mParticleNode->setNodeMask(Mask_Effect);
createWaterRippleStateSet(resourceSystem, fallback, mParticleNode);
createWaterRippleStateSet(resourceSystem, mParticleNode);
mParent->addChild(mParticleNode);
}

@ -40,7 +40,7 @@ namespace MWRender
class RippleSimulation
{
public:
RippleSimulation(osg::Group* parent, Resource::ResourceSystem* resourceSystem, const Fallback::Map* fallback);
RippleSimulation(osg::Group* parent, Resource::ResourceSystem* resourceSystem);
~RippleSimulation();
/// @param dt Time since the last frame

@ -266,16 +266,16 @@ public:
META_Node(MWRender, CameraRelativeTransform)
const osg::Vec3f& getLastEyePoint() const
const osg::Vec3f& getLastViewPoint() const
{
return mEyePoint;
return mViewPoint;
}
virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const
{
if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
{
mEyePoint = static_cast<osgUtil::CullVisitor*>(nv)->getEyePoint();
mViewPoint = static_cast<osgUtil::CullVisitor*>(nv)->getViewPoint();
}
if (_referenceFrame==RELATIVE_RF)
@ -337,8 +337,8 @@ public:
}
};
private:
// eyePoint for the current frame
mutable osg::Vec3f mEyePoint;
// viewPoint for the current frame
mutable osg::Vec3f mViewPoint;
};
class ModVertexAlphaVisitor : public osg::NodeVisitor
@ -391,7 +391,7 @@ private:
/// @brief Hides the node subgraph if the eye point is below water.
/// @note Must be added as cull callback.
/// @note Meant to be used on a node that is child of a CameraRelativeTransform.
/// The current eye point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space.
/// The current view point must be retrieved by the CameraRelativeTransform since we can't get it anymore once we are in camera-relative space.
class UnderwaterSwitchCallback : public osg::NodeCallback
{
public:
@ -404,8 +404,8 @@ public:
bool isUnderwater()
{
osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint();
return mEnabled && eyePoint.z() < mWaterLevel;
osg::Vec3f viewPoint = mCameraRelativeTransform->getLastViewPoint();
return mEnabled && viewPoint.z() < mWaterLevel;
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
@ -828,10 +828,9 @@ private:
, mTimeOfDayFade(1.f)
, mGlareView(1.f)
{
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback();
mColor = fallback->getFallbackColour("Weather_Sun_Glare_Fader_Color");
mSunGlareFaderMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Max");
mSunGlareFaderAngleMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Angle_Max");
mColor = Fallback::Map::getColour("Weather_Sun_Glare_Fader_Color");
mSunGlareFaderMax = Fallback::Map::getFloat("Weather_Sun_Glare_Fader_Max");
mSunGlareFaderAngleMax = Fallback::Map::getFloat("Weather_Sun_Glare_Fader_Angle_Max");
// Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
// then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
@ -1179,9 +1178,8 @@ void SkyManager::create()
mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager()));
const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback();
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mCloudNode = new osg::PositionAttitudeTransform;
mEarlyRenderBinRoot->addChild(mCloudNode);
@ -1205,7 +1203,7 @@ void SkyManager::create()
mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF);
mMoonScriptColor = fallback->getFallbackColour("Moons_Script_Color");
mMoonScriptColor = Fallback::Map::getColour("Moons_Script_Color");
mCreated = true;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save