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

@ -6,25 +6,32 @@
Bug #3006: 'else if' operator breaks script compilation Bug #3006: 'else if' operator breaks script compilation
Bug #3109: SetPos/Position handles actors differently Bug #3109: SetPos/Position handles actors differently
Bug #3282: Unintended behaviour when assigning F3 and Windows keys 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 #3733: Normal maps are inverted on mirrored UVs
Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable Bug #3765: DisableTeleporting makes Mark/Recall/Intervention effects undetectable
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation 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 #4329: Removed birthsign abilities are restored after reloading the save
Bug #4383: Bow model obscures crosshair when arrow is drawn Bug #4383: Bow model obscures crosshair when arrow is drawn
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons 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 #4411: Reloading a saved game while falling prevents damage in some cases
Bug #4540: Rain delay when exiting water Bug #4540: Rain delay when exiting water
Bug #4701: PrisonMarker record is not hardcoded like other markers 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 #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 #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 #4720: Inventory avatar has shield with two-handed weapon during [un]equipping animation
Bug #4723: ResetActors command works incorrectly 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 #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 #4746: Non-solid player can't run or sneak
Bug #4747: Bones are not read from X.NIF file for NPC animation 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 #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 #4768: Fallback numerical value recovery chokes on invalid arguments
Bug #4775: Slowfall effect resets player jumping flag Bug #4775: Slowfall effect resets player jumping flag
Bug #4778: Interiors of Illusion puzzle in Sotha Sil Expanded mod is broken 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 #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 #4803: Stray special characters before begin statement break script compilation
Bug #4804: Particle system with the "Has Sizes = false" causes an exception 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 #4810: Raki creature broken in OpenMW
Bug #4813: Creatures with known file but no "Sound Gen Creature" assigned use default sounds 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 #4815: "Finished" journal entry with lower index doesn't close journal, SetJournalIndex closes journal
Bug #4820: Spell absorption is broken Bug #4820: Spell absorption is broken
Bug #4823: Jail progress bar works incorrectly Bug #4823: Jail progress bar works incorrectly
Bug #4826: Uninitialized memory in unit test
Bug #4827: NiUVController is handled incorrectly Bug #4827: NiUVController is handled incorrectly
Bug #4828: Potion looping effects VFX are not shown for NPCs 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 #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 #4860: Actors outside of processing range visible for one frame after spawning
Bug #4867: Arbitrary text after local variable declarations breaks script compilation Bug #4867: Arbitrary text after local variable declarations breaks script compilation
Bug #4876: AI ratings handling inconsistencies Bug #4876: AI ratings handling inconsistencies
@ -49,15 +60,39 @@
Bug #4896: Title screen music doesn't loop Bug #4896: Title screen music doesn't loop
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5 Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used. 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 #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 #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 #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 #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls
Feature #3442: Default values for fallbacks from ini file Feature #3442: Default values for fallbacks from ini file
Feature #3610: Option to invert X axis Feature #3610: Option to invert X axis
Feature #3893: Implicit target for "set" function in console Feature #3893: Implicit target for "set" function in console
Feature #3980: In-game option to disable controller Feature #3980: In-game option to disable controller
Feature #4001: Toggle sneak controller shortcut
Feature #4209: Editor: Faction rank sub-table Feature #4209: Editor: Faction rank sub-table
Feature #4360: Improve default controller bindings
Feature #4673: Weapon sheathing Feature #4673: Weapon sheathing
Feature #4675: Support for NiRollController Feature #4675: Support for NiRollController
Feature #4730: Native animated containers support Feature #4730: Native animated containers support
@ -66,8 +101,14 @@
Feature #4859: Make water reflections more configurable Feature #4859: Make water reflections more configurable
Feature #4887: Add openmw command option to set initial random seed Feature #4887: Add openmw command option to set initial random seed
Feature #4890: Make Distant Terrain configurable 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 #4686: Upgrade media decoder to a more current FFmpeg API
Task #4695: Optimize Distant Terrain memory consumption Task #4695: Optimize Distant Terrain memory consumption
Task #4721: Add NMake support to the Windows prebuild script
0.45.0 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}") SHORT_COMMIT=$(git rev-parse --short "${TRAVIS_COMMIT}")
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg" 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" scp -P "$OSX_DEPLOY_PORT" -i "$SSH_KEY_PATH" ./*.dmg "$OSX_DEPLOY_HOST:$REMOTE_PATH/$TARGET_FILENAME"
else else
echo "An existing nightly build for commit ${SHORT_COMMIT} has been found, skipping upload." 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 set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
endif() endif()
find_package(MyGUI 3.2.1 REQUIRED) find_package(MyGUI 3.2.2 REQUIRED)
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
find_package(OpenAL REQUIRED) find_package(OpenAL REQUIRED)
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath) find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
@ -429,7 +429,7 @@ endif()
# CXX Compiler settings # CXX Compiler settings
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) 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 ) add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
if (APPLE) if (APPLE)

@ -704,29 +704,25 @@ std::string creatureFlags(int flags)
{ {
std::string properties; std::string properties;
if (flags == 0) properties += "[None] "; 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::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Bipedal) properties += "Bipedal "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon "; 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 "; if (flags & ESM::Creature::Essential) properties += "Essential ";
int unused = (0xFFFFFFFF ^ int unused = (0xFF ^
(ESM::Creature::None| (ESM::Creature::Base|
ESM::Creature::Walks| ESM::Creature::Walks|
ESM::Creature::Swims| ESM::Creature::Swims|
ESM::Creature::Flies| ESM::Creature::Flies|
ESM::Creature::Bipedal| ESM::Creature::Bipedal|
ESM::Creature::Respawn| ESM::Creature::Respawn|
ESM::Creature::Weapon| ESM::Creature::Weapon|
ESM::Creature::Skeleton|
ESM::Creature::Metal|
ESM::Creature::Essential)); ESM::Creature::Essential));
if (flags & unused) properties += "Invalid "; if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags); properties += str(boost::format("(0x%02X)") % flags);
return properties; return properties;
} }
@ -828,33 +824,21 @@ std::string npcFlags(int flags)
{ {
std::string properties; std::string properties;
if (flags == 0) properties += "[None] "; if (flags == 0) properties += "[None] ";
// Mythicmods and the ESM component differ. Mythicmods says if (flags & ESM::NPC::Base) properties += "Base ";
// 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::Autocalc) properties += "Autocalc "; if (flags & ESM::NPC::Autocalc) properties += "Autocalc ";
if (flags & ESM::NPC::Female) properties += "Female "; if (flags & ESM::NPC::Female) properties += "Female ";
if (flags & ESM::NPC::Respawn) properties += "Respawn "; if (flags & ESM::NPC::Respawn) properties += "Respawn ";
if (flags & ESM::NPC::Essential) properties += "Essential "; 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, // Whether corpses persist is a bit that is unaccounted for,
// however the only unknown bit occurs on ALL records, and // however relatively few NPCs have this bit set.
// relatively few NPCs have this bit set. int unused = (0xFF ^
int unused = (0xFFFFFFFF ^ (ESM::NPC::Base|
(0x00000008|
ESM::NPC::Autocalc| ESM::NPC::Autocalc|
ESM::NPC::Female| ESM::NPC::Female|
ESM::NPC::Respawn| ESM::NPC::Respawn|
ESM::NPC::Essential| ESM::NPC::Essential));
ESM::NPC::Skeleton|
ESM::NPC::Metal));
if (flags & unused) properties += "Invalid "; if (flags & unused) properties += "Invalid ";
properties += str(boost::format("(0x%08X)") % flags); properties += str(boost::format("(0x%02X)") % flags);
return properties; return properties;
} }

@ -618,7 +618,8 @@ void Record<ESM::Creature>::print()
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Model: " << mData.mModel << std::endl;
std::cout << " Script: " << mData.mScript << 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 << " Original: " << mData.mOriginal << std::endl;
std::cout << " Scale: " << mData.mScale << 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; std::cout << " Script: " << mData.mScript << std::endl;
if (!mData.mFaction.empty()) if (!mData.mFaction.empty())
std::cout << " Faction: " << mData.mFaction << std::endl; 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) if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{ {

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

@ -230,9 +230,19 @@ MwIniImporter::MwIniImporter()
"Blood:Texture 0", "Blood:Texture 0",
"Blood:Texture 1", "Blood:Texture 1",
"Blood:Texture 2", "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 0",
"Blood:Texture Name 1", "Blood:Texture Name 1",
"Blood:Texture Name 2", "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
"Movies:Company Logo", "Movies:Company Logo",
@ -624,17 +634,6 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish", "Moons:Masser Fade Out Finish",
"Moons:Script Color", "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) // werewolf (Bloodmoon)
"General:Werewolf FOV", "General:Werewolf FOV",

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

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

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

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

@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
const std::vector< boost::filesystem::path >& files, const std::vector< boost::filesystem::path >& files,
const boost::filesystem::path& savePath, bool new_) 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) 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); 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) void CSMDoc::DocumentManager::setEncoding (ToUTF8::FromType encoding)
{ {
mEncoding = encoding; mEncoding = encoding;

@ -41,7 +41,6 @@ namespace CSMDoc
std::vector<std::string> mBlacklistedScripts; std::vector<std::string> mBlacklistedScripts;
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
Fallback::Map mFallbackMap;
bool mFsStrict; bool mFsStrict;
Files::PathContainer mDataPaths; Files::PathContainer mDataPaths;
@ -72,8 +71,6 @@ namespace CSMDoc
void setResourceDir (const boost::filesystem::path& parResDir); void setResourceDir (const boost::filesystem::path& parResDir);
void setFallbackMap (const std::map<std::string, std::string>& fallbackMap);
void setEncoding (ToUTF8::FromType encoding); void setEncoding (ToUTF8::FromType encoding);
void setBlacklistedScripts (const std::vector<std::string>& scriptIds); 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(); 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)); CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId));
if (value>=0 && value<static_cast<int> (enums.size())) 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) else if (data.type()==QVariant::Bool)
{ {

@ -75,11 +75,11 @@ void CSMTools::Search::searchRecordStateCell (const CSMWorld::IdTableBase *model
if (data==mValue) if (data==mValue)
{ {
std::vector<std::string> states = std::vector<std::pair<int,std::string>> states =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
const std::string hint = "r: " + std::to_string(model->getColumnId(index.column())); 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 <ostream>
#include <sstream> #include <sstream>
#include <components/esm/loadland.hpp>
#include <components/misc/constants.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() : mX (0), mY (0) {}
CSMWorld::CellCoordinates::CellCoordinates (int x, int y) : mX (x), mY (y) {} 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 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) // we ignore the worldspace for now, since there is only one (will change in 1.1)
std::ostringstream stream; return generateId(mX, mY);
}
stream << "#" << 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) 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)); 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) bool CSMWorld::operator== (const CellCoordinates& left, const CellCoordinates& right)
{ {
return left.getX()==right.getX() && left.getY()==right.getY(); return left.getX()==right.getX() && left.getY()==right.getY();

@ -7,6 +7,8 @@
#include <QMetaType> #include <QMetaType>
#include <osg/Vec3d>
namespace CSMWorld namespace CSMWorld
{ {
class CellCoordinates class CellCoordinates
@ -29,6 +31,9 @@ namespace CSMWorld
CellCoordinates move (int x, int y) const; CellCoordinates move (int x, int y) const;
///< Return a copy of *this, moved by the given offset. ///< 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; std::string getId (const std::string& worldspace) const;
///< Return the ID for the cell at these coordinates. ///< 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. /// \return cell coordinates such that given world coordinates are in it.
static std::pair<int, int> coordinatesToCellIndex (float x, float y); 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); bool operator== (const CellCoordinates& left, const CellCoordinates& right);

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

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

@ -1,5 +1,6 @@
#include "columns.hpp" #include "columns.hpp"
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include "universalid.hpp" #include "universalid.hpp"
@ -573,11 +574,6 @@ namespace
"Book", "Scroll", 0 "Book", "Scroll", 0
}; };
static const char *sBloodType[] =
{
"Default (Red)", "Skeleton Blood (White)", "Metal Blood (Golden)", 0
};
static const char *sEmitterType[] = static const char *sEmitterType[] =
{ {
"<None>", "Flickering", "Flickering (Slow)", "Pulsing", "Pulsing (Slow)", 0 "<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_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings; case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
case CSMWorld::Columns::ColumnId_BookType: return sBookType; case CSMWorld::Columns::ColumnId_BookType: return sBookType;
case CSMWorld::Columns::ColumnId_BloodType: return sBloodType;
case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType; case CSMWorld::Columns::ColumnId_EmitterType: return sEmitterType;
default: return 0; default: return 0;
@ -626,19 +621,28 @@ bool CSMWorld::Columns::hasEnums (ColumnId column)
return getEnumNames (column)!=0 || column==ColumnId_RecordType; 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)) if (const char **table = getEnumNames (column))
for (int i=0; table[i]; ++i) 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) 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) 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; return enums;

@ -390,7 +390,7 @@ namespace CSMWorld
bool hasEnums (ColumnId column); 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. ///< Returns an empty vector, if \a column isn't an enum type column.
} }
} }

@ -11,6 +11,8 @@
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include <components/vfs/registerarchives.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, 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), : 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) mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
{ {
mVFS.reset(new VFS::Manager(mFsStrict)); 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()); mResourcesManager.setVFS(mVFS.get());
mResourceSystem.reset(new Resource::ResourceSystem(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()); mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
int index = 0; int index = 0;
@ -1360,8 +1370,3 @@ const VFS::Manager* CSMWorld::Data::getVFS() const
{ {
return mVFS.get(); return mVFS.get();
} }
const Fallback::Map* CSMWorld::Data::getFallbackMap() const
{
return mFallbackMap;
}

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

@ -6,13 +6,13 @@
namespace 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())) if (index < 0 || index >= static_cast<int>(values.size()))
{ {
return ""; 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). // Cache of enum values for enum columns (e.g. Modified, Record Type).
// Used to speed up comparisons during the sort by such columns. // 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; mutable EnumColumnCache mEnumColumnCache;
protected: protected:

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

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

@ -164,7 +164,7 @@ namespace CSVRender
mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin)); mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin));
// Add water texture // 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"; textureName = "textures/water/" + textureName + "00.dds";
Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager(); Resource::ImageManager* imageManager = mData.getResourceSystem()->getImageManager();

@ -29,7 +29,6 @@
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightutil.hpp> #include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/lightmanager.hpp> #include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp>
#include "actor.hpp" #include "actor.hpp"
#include "mask.hpp" #include "mask.hpp"
@ -140,17 +139,8 @@ void CSVRender::Object::update()
if (light) 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 bool isExterior = false; // FIXME
SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior, outQuadInLin, useQuadratic, SceneUtil::addLight(mBaseNode, light, Mask_ParticleSystem, Mask_Lighting, isExterior);
quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
} }
} }

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

@ -76,9 +76,9 @@ void CSVWorld::NotEditableSubDelegate::setEditorData (QWidget* editor, const QMo
else if (CSMWorld::Columns::hasEnums (columnId)) else if (CSMWorld::Columns::hasEnums (columnId))
{ {
int data = v.toInt(); 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 else
{ {

@ -155,7 +155,7 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool all
add (i, names[i]); 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) bool allowNone)
{ {
if (allowNone) if (allowNone)
@ -164,7 +164,7 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::strin
int size = static_cast<int> (names.size()); int size = static_cast<int> (names.size());
for (int i=0; i<size; ++i) 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 ( 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 names Array of char pointer with a 0-pointer as end mark
/// \param allowNone Use value of -1 for "none selected" (empty string) /// \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) /// \param allowNone Use value of -1 for "none selected" (empty string)
virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const;

@ -22,7 +22,7 @@ CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
{ {
std::vector<std::string> enums = std::vector<std::pair<int,std::string>> enums =
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification);
static const char *sIcons[] = static const char *sIcons[] =
@ -31,5 +31,8 @@ CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
}; };
for (int i=0; sIcons[i]; ++i) 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; 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: case CSMWorld::ColumnBase::Display_UnsignedInteger8:
{ {
DialogueSpinBox *sb = new DialogueSpinBox(parent); DialogueSpinBox *sb = new DialogueSpinBox(parent);

@ -73,11 +73,11 @@ CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (
void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) 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); CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType);
if (static_cast<size_t>(type) >= enums.size()) if (static_cast<size_t>(type) >= enums.size())
throw std::logic_error ("Unsupported variable type"); 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) if (ANDROID)
set(GAME ${GAME} android_commandLine.cpp) set(GAME ${GAME} android_main.cpp)
set(GAME ${GAME} android_main.c)
endif() endif()
set(GAME_HEADER set(GAME_HEADER
@ -32,7 +31,7 @@ add_openmw_dir (mwinput
add_openmw_dir (mwgui add_openmw_dir (mwgui
layout textinput widgets race class birth review windowmanagerimp console dialogue layout textinput widgets race class birth review windowmanagerimp console dialogue
windowbase statswindow messagebox journalwindow charactercreation windowbase statswindow messagebox journalwindow charactercreation
mapwindow windowpinnablebase tooltips scrollwindow bookwindow mapwindow windowpinnablebase tooltips scrollwindow bookwindow resourceskin
formatting inventorywindow container hud countdialog tradewindow settingswindow formatting inventorywindow container hud countdialog tradewindow settingswindow
confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu
itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog 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(); 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; int ret = 0;
SDL_GetMouseState(&ret, NULL); SDL_GetMouseState(&ret, nullptr);
return ret; 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; int ret = 0;
SDL_GetMouseState(NULL, &ret); SDL_GetMouseState(nullptr, &ret);
return 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); return SDL_ShowCursor(SDL_QUERY);
} }
extern SDL_Window *Android_Window; extern SDL_Window *Android_Window;
int SDL_SendMouseMotion(SDL_Window * window, int mouseID, int relative, int x, int y); extern "C" 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" void Java_org_libsdl_app_SDLActivity_sendRelativeMouseMotion(JNIEnv *env, jclass cls, int x, int y) {
SDL_SendMouseMotion(Android_Window, 0, 1, x, y); SDL_SendMouseMotion(Android_Window, 0, 1, x, y);
} }
int SDL_SendMouseButton(SDL_Window * window, int mouseID, Uint8 state, Uint8 button); extern "C" 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" void Java_org_libsdl_app_SDLActivity_sendMouseButton(JNIEnv *env, jclass cls, int state, int button) {
SDL_SendMouseButton(Android_Window, 0, state, 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); setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1);
// On Android, we use a virtual controller with guid="Virtual" // 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(); osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
camera->setGraphicsContext(graphicsWindow); camera->setGraphicsContext(graphicsWindow);
camera->setViewport(0, 0, width, height); camera->setViewport(0, 0, traits->width, traits->height);
mViewer->realize(); mViewer->realize();
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, width, height); mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
} }
void OMW::Engine::setWindowIcon() void OMW::Engine::setWindowIcon()
@ -655,24 +655,24 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
rootNode->addChild(guiRoot); rootNode->addChild(guiRoot);
MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(), MWGui::WindowManager* window = new MWGui::WindowManager(mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources, mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap, mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string()); Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
// Create sound system // Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mFallbackMap, mUseSound)); mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
if (!mSkipMenu) if (!mSkipMenu)
{ {
std::string logo = mFallbackMap["Movies_Company_Logo"]; const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
if (!logo.empty()) if (!logo.empty())
window->playVideo(logo, true); window->playVideo(logo, true);
} }
// Create the world // Create the world
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(), mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
mFileCollections, mContentFiles, mEncoder, mFallbackMap, mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
mActivationDistanceOverride, mCellName, mResDir.string(), mCfgMgr.getUserDataPath().string())); mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
mEnvironment.getWorld()->setupPlayer(); mEnvironment.getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer()); input->setPlayer(&mEnvironment.getWorld()->getPlayer());
@ -806,6 +806,11 @@ void OMW::Engine::go()
mViewer = new osgViewer::Viewer; mViewer = new osgViewer::Viewer;
mViewer->setReleaseContextAtEndOfFrameHint(false); 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(), mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General")); Settings::Manager::getString("screenshot format", "General"));
@ -864,7 +869,7 @@ void OMW::Engine::go()
// start in main menu // start in main menu
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
mEnvironment.getSoundManager()->playTitleMusic(); mEnvironment.getSoundManager()->playTitleMusic();
std::string logo = mFallbackMap["Movies_Morrowind_Logo"]; const std::string& logo = Fallback::Map::getString("Movies_Morrowind_Logo");
if (!logo.empty()) if (!logo.empty())
mEnvironment.getWindowManager()->playVideo(logo, true); mEnvironment.getWindowManager()->playVideo(logo, true);
} }
@ -949,11 +954,6 @@ void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
mEncoding = encoding; mEncoding = encoding;
} }
void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap)
{
mFallbackMap = fallbackMap;
}
void OMW::Engine::setScriptConsoleMode (bool enabled) void OMW::Engine::setScriptConsoleMode (bool enabled)
{ {
mScriptConsoleMode = enabled; mScriptConsoleMode = enabled;

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

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

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

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

@ -800,7 +800,8 @@ namespace MWClass
if (!successful) if (!successful)
{ {
// Missed // Missed
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f); if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
return; return;
} }
@ -1399,6 +1400,8 @@ namespace MWClass
if(name == "left" || name == "right") if(name == "left" || name == "right")
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
if(world->isFlying(ptr))
return std::string();
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3()); osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
if(world->isSwimming(ptr)) if(world->isSwimming(ptr))
return (name == "left") ? "Swim Left" : "Swim Right"; return (name == "left") ? "Swim Left" : "Swim Right";
@ -1468,13 +1471,7 @@ namespace MWClass
int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const int Npc::getBloodTexture(const MWWorld::ConstPtr &ptr) const
{ {
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); return ptr.get<ESM::NPC>()->mBase->mBloodType;
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
} }
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) 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) : DialogueManager::DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage) :
mTranslationDataStorage(translationDataStorage) mTranslationDataStorage(translationDataStorage)
, mCompilerContext (MWScript::CompilerContext::Type_Dialogue) , mCompilerContext (MWScript::CompilerContext::Type_Dialogue)
, mErrorStream(std::cout.rdbuf()) , mErrorHandler()
, mErrorHandler(mErrorStream)
, mTalkedTo(false) , mTalkedTo(false)
, mTemporaryDispositionChange(0.f) , mTemporaryDispositionChange(0.f)
, mPermanentDispositionChange(0.f) , mPermanentDispositionChange(0.f)
@ -241,8 +240,7 @@ namespace MWDialogue
if (!success) if (!success)
{ {
Log(Debug::Warning) Log(Debug::Error) << "Error: compiling failed (dialogue script): \n" << cmd << "\n";
<< "Warning: compiling failed (dialogue script)\n" << cmd << "\n\n";
} }
return success; return success;

@ -33,7 +33,6 @@ namespace MWDialogue
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext; MWScript::CompilerContext mCompilerContext;
std::ostream mErrorStream;
Compiler::StreamErrorHandler mErrorHandler; Compiler::StreamErrorHandler mErrorHandler;
MWWorld::Ptr mActor; 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); MWScript::CompilerContext compilerContext(MWScript::CompilerContext::Type_Dialogue);
compilerContext.setExtensions(extensions); compilerContext.setExtensions(extensions);
std::ostream errorStream(std::cout.rdbuf()); Compiler::StreamErrorHandler errorHandler;
Compiler::StreamErrorHandler errorHandler(errorStream);
errorHandler.setWarningsMode (warningsMode); errorHandler.setWarningsMode (warningsMode);
const MWWorld::Store<ESM::Dialogue>& dialogues = MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>(); 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) if (!success)
{ {
Log(Debug::Warning) Log(Debug::Error) << "Error: compiling failed (dialogue script): \n" << info->mResultScript << "\n";
<< "compiling failed (dialogue script)\n" << info->mResultScript << "\n\n";
} }
} }
} }

@ -20,7 +20,7 @@ namespace MWGui
{ {
BookWindow::BookWindow () BookWindow::BookWindow ()
: WindowBase("openmw_book.layout") : BookWindowBase("openmw_book.layout")
, mCurrentPage(0) , mCurrentPage(0)
, mTakeButtonShow(true) , mTakeButtonShow(true)
, mTakeButtonAllowed(true) , mTakeButtonAllowed(true)
@ -43,10 +43,10 @@ namespace MWGui
getWidget(mLeftPage, "LeftPage"); getWidget(mLeftPage, "LeftPage");
getWidget(mRightPage, "RightPage"); getWidget(mRightPage, "RightPage");
adjustButton(mCloseButton); adjustButton("CloseButton");
adjustButton(mTakeButton); adjustButton("TakeButton");
adjustButton(mNextPageButton); adjustButton("PrevPageBTN");
adjustButton(mPrevPageButton); float scale = adjustButton("NextPageBTN");
mLeftPage->setNeedMouseFocus(true); mLeftPage->setNeedMouseFocus(true);
mLeftPage->eventMouseWheel += MyGUI::newDelegate(this, &BookWindow::onMouseWheel); 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 // english button has a 7 pixel wide strip of garbage on its right edge
mNextPageButton->setSize(64-7, mNextPageButton->getSize().height); 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(); 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() void BookWindow::nextPage()
{ {
if ((mCurrentPage+1)*2 < mPages.size()) if ((mCurrentPage+1)*2 < mPages.size())

@ -9,7 +9,7 @@
namespace MWGui namespace MWGui
{ {
class BookWindow : public WindowBase class BookWindow : public BookWindowBase
{ {
public: public:
BookWindow(); BookWindow();
@ -34,7 +34,6 @@ namespace MWGui
void updatePages(); void updatePages();
void clearPages(); void clearPages();
void adjustButton(Gui::ImageButton* button);
private: private:
typedef std::pair<int, int> Page; 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 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++; number++;
const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback(); Step step = {
Step step = {fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_Question"), Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_Question"),
{fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"), {Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerOne"),
fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"), Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerTwo"),
fallback->getFallbackString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")}, Fallback::Map::getString("Question_"+MyGUI::utility::toString(number)+"_AnswerThree")},
"vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav" "vo\\misc\\chargen qa"+MyGUI::utility::toString(number)+".wav"
}; };
return step; return step;
} }

@ -254,16 +254,6 @@ namespace MWGui
if (plainText.size() && brAtEnd) if (plainText.size() && brAtEnd)
plainText.erase(plainText.end()-1); 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) if (!plainText.empty() || brBeforeLastTag || isPrevImg)
{ {
TextElement elem(paper, pag, mBlockStyle, TextElement elem(paper, pag, mBlockStyle,
@ -443,8 +433,6 @@ namespace MWGui
int ret = mPaginator.getCurrentTop() + lastLine * lineHeight; int ret = mPaginator.getCurrentTop() + lastLine * lineHeight;
// first empty lines that would go to the next page should be ignored // 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); mPaginator.setIgnoreLeadingEmptyLines(true);
const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo(); const MyGUI::VectorLineInfo & lines = mEditBox->getSubWidgetText()->castType<MyGUI::EditText>()->getLineInfo();
@ -458,7 +446,6 @@ namespace MWGui
break; break;
} }
} }
#endif
return ret; return ret;
} }

@ -2,6 +2,7 @@
#include <MyGUI_FactoryManager.h> #include <MyGUI_FactoryManager.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
// correctIconPath // correctIconPath
@ -30,6 +31,7 @@ namespace
namespace MWGui namespace MWGui
{ {
std::map<std::string, float> ItemWidget::mScales;
ItemWidget::ItemWidget() ItemWidget::ItemWidget()
: mItem(nullptr) : mItem(nullptr)
@ -146,10 +148,29 @@ namespace MWGui
if (backgroundTex != "") if (backgroundTex != "")
backgroundTex += ".dds"; 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) 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 else
setFrame(backgroundTex, MyGUI::IntCoord(0,0,42,42)); setFrame(backgroundTex, MyGUI::IntCoord(0,0,44*scale,44*scale));
setIcon(ptr); setIcon(ptr);
} }

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

@ -153,7 +153,7 @@ namespace
} }
adjustButton(PrevPageBTN); adjustButton(PrevPageBTN);
adjustButton(NextPageBTN); float nextButtonScale = adjustButton(NextPageBTN);
adjustButton(CloseBTN); adjustButton(CloseBTN);
adjustButton(CancelBTN); adjustButton(CancelBTN);
adjustButton(JournalBTN); adjustButton(JournalBTN);
@ -168,7 +168,7 @@ namespace
{ {
// english button has a 7 pixel wide strip of garbage on its right edge // english button has a 7 pixel wide strip of garbage on its right edge
nextButton->setSize(64-7, nextButton->getSize().height); 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) if (!questList)
@ -226,17 +226,6 @@ namespace
mTopicsMode = false; 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() void onOpen()
{ {
if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ()) if (!MWBase::Environment::get().getWindowManager ()->getJournalAllowed ())
@ -665,7 +654,7 @@ MWGui::JournalWindow * MWGui::JournalWindow::create (JournalViewModel::Ptr Model
} }
MWGui::JournalWindow::JournalWindow() MWGui::JournalWindow::JournalWindow()
:WindowBase("openmw_journal.layout") : BookWindowBase("openmw_journal.layout")
{ {
} }

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

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

@ -302,15 +302,18 @@ namespace MWGui
Gui::ImageButton* button = mButtons[buttonId]; Gui::ImageButton* button = mButtons[buttonId];
button->setVisible(true); 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(); MyGUI::IntSize requested = button->getRequestedSize();
float scale = requested.height / 64.f;
button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height)); button->setImageCoord(MyGUI::IntCoord(0, 0, requested.width, requested.height));
// Trim off some of the excessive padding // Trim off some of the excessive padding
// TODO: perhaps do this within ImageButton? // TODO: perhaps do this within ImageButton?
int height = requested.height-16; int height = requested.height;
button->setImageTile(MyGUI::IntSize(requested.width, height)); button->setImageTile(MyGUI::IntSize(requested.width, requested.height-16*scale));
button->setCoord((maxwidth-requested.width) / 2, curH, requested.width, height); button->setCoord((maxwidth-requested.width/scale) / 2, curH, requested.width/scale, height/scale-16);
curH += height; curH += height/scale-16;
} }
if (state == MWBase::StateManager::State_NoGame) 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" #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 namespace MWGui
{ {
ScrollWindow::ScrollWindow () ScrollWindow::ScrollWindow ()
: WindowBase("openmw_scroll.layout") : BookWindowBase("openmw_scroll.layout")
, mTakeButtonShow(true) , mTakeButtonShow(true)
, mTakeButtonAllowed(true) , mTakeButtonAllowed(true)
{ {
@ -44,8 +32,8 @@ namespace MWGui
getWidget(mTakeButton, "TakeButton"); getWidget(mTakeButton, "TakeButton");
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked); mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked);
adjustButton(mCloseButton); adjustButton("CloseButton");
adjustButton(mTakeButton); adjustButton("TakeButton");
mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mCloseButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);
mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed); mTakeButton->eventKeyButtonPressed += MyGUI::newDelegate(this, &ScrollWindow::onKeyButtonPressed);

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

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

@ -92,9 +92,16 @@ namespace MWGui
mTimeAdvancer.eventFinished += MyGUI::newDelegate(this, &WaitDialog::onWaitingFinished); 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) 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()) if (mUntilHealedButton->getVisible())
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mUntilHealedButton);
@ -111,6 +118,7 @@ namespace MWGui
{ {
mSleeping = false; mSleeping = false;
mTimeAdvancer.stop(); mTimeAdvancer.stop();
resetReference();
} }
void WaitDialog::onOpen() void WaitDialog::onOpen()
@ -140,7 +148,13 @@ namespace MWGui
} }
else if (canRest == MWBase::World::Rest_PlayerIsUnderwater) 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()->messageBox ("#{sNotifyMessage1}");
MWBase::Environment::get().getWindowManager()->popGuiMode (); MWBase::Environment::get().getWindowManager()->popGuiMode ();
} }
@ -337,6 +351,8 @@ namespace MWGui
void WaitDialog::onFrame(float dt) void WaitDialog::onFrame(float dt)
{ {
checkReferenceAvailable();
mTimeAdvancer.onFrame(dt); mTimeAdvancer.onFrame(dt);
if (mFadeTimeRemaining <= 0) if (mFadeTimeRemaining <= 0)

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

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

@ -6,6 +6,8 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include <components/widgets/imagebutton.hpp>
#include "draganddrop.hpp" #include "draganddrop.hpp"
using namespace MWGui; using namespace MWGui;
@ -120,3 +122,30 @@ void NoDrop::setAlpha(float alpha)
if (mWidget) if (mWidget)
mWidget->setAlpha(alpha); 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; DragAndDrop* mDrag;
bool mTransparent; bool mTransparent;
}; };
class BookWindowBase : public WindowBase
{
public:
BookWindowBase(const std::string& parLayout);
protected:
float adjustButton (char const * name);
};
} }
#endif #endif

@ -13,7 +13,6 @@
#include <MyGUI_InputManager.h> #include <MyGUI_InputManager.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_ClipboardManager.h> #include <MyGUI_ClipboardManager.h>
#include <MyGUI_RenderManager.h>
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include <SDL_keyboard.h> #include <SDL_keyboard.h>
@ -124,6 +123,7 @@
#include "jailscreen.hpp" #include "jailscreen.hpp"
#include "itemchargeview.hpp" #include "itemchargeview.hpp"
#include "keyboardnavigation.hpp" #include "keyboardnavigation.hpp"
#include "resourceskin.hpp"
namespace namespace
{ {
@ -141,7 +141,7 @@ namespace MWGui
WindowManager::WindowManager( WindowManager::WindowManager(
osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, 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, 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) : mStore(nullptr)
, mResourceSystem(resourceSystem) , mResourceSystem(resourceSystem)
, mWorkQueue(workQueue) , mWorkQueue(workQueue)
@ -201,7 +201,6 @@ namespace MWGui
, mForceHidden(GW_None) , mForceHidden(GW_None)
, mAllowed(GW_ALL) , mAllowed(GW_ALL)
, mRestAllowed(true) , mRestAllowed(true)
, mFallbackMap(fallbackMap)
, mShowOwned(0) , mShowOwned(0)
, mEncoding(encoding) , mEncoding(encoding)
, mFontHeight(16) , mFontHeight(16)
@ -253,6 +252,7 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerFollowMouse>("Controller");
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer"); MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>("Resource", "AutoSizedResourceSkin");
MyGUI::ResourceManager::getInstance().load("core.xml"); MyGUI::ResourceManager::getInstance().load("core.xml");
loadUserFonts(); loadUserFonts();
@ -303,14 +303,13 @@ namespace MWGui
void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version) void WindowManager::loadFontDelegate(MyGUI::xml::ElementPtr _node, const std::string& _file, MyGUI::Version _version)
{ {
const std::string templateName = "Journalbook "; MyGUI::xml::ElementEnumerator resourceNode = _node->getElementEnumerator();
MyGUI::xml::ElementEnumerator font = _node->getElementEnumerator();
bool createCopy = false; bool createCopy = false;
while (font.next("Resource")) while (resourceNode.next("Resource"))
{ {
std::string type, name; std::string type, name;
font->findAttribute("type", type); resourceNode->findAttribute("type", type);
font->findAttribute("name", name); resourceNode->findAttribute("name", name);
if (name.empty()) if (name.empty())
continue; continue;
@ -328,18 +327,19 @@ namespace MWGui
float uiScale = Settings::Manager::getFloat("scaling factor", "GUI"); float uiScale = Settings::Manager::getFloat("scaling factor", "GUI");
resolution *= uiScale; resolution *= uiScale;
MyGUI::xml::ElementPtr resolutionNode = font->createChild("Property"); MyGUI::xml::ElementPtr resolutionNode = resourceNode->createChild("Property");
resolutionNode->addAttribute("key", "Resolution"); resolutionNode->addAttribute("key", "Resolution");
resolutionNode->addAttribute("value", std::to_string(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("key", "Size");
sizeNode->addAttribute("value", std::to_string(mFontHeight)); 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 // 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("key", "HeightLine");
heightNode->addAttribute("value", std::to_string(mFontHeight+2)); heightNode->addAttribute("value", std::to_string(mFontHeight+2));
} }
@ -1255,7 +1255,7 @@ namespace MWGui
{ {
_result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength));
} }
else if (Gui::replaceTag(tag, _result, mFallbackMap)) else if (Gui::replaceTag(tag, _result))
{ {
return; return;
} }
@ -1575,6 +1575,32 @@ namespace MWGui
if (getMode() != GM_Inventory) if (getMode() != GM_Inventory)
return; 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); mShown = (GuiWindow)(mShown ^ wnd);
updateVisible(); updateVisible();
} }
@ -2058,12 +2084,20 @@ namespace MWGui
void WindowManager::updatePinnedWindows() void WindowManager::updatePinnedWindows()
{ {
mInventoryWindow->setPinned(Settings::Manager::getBool("inventory pin", "Windows")); 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")); 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")); 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")); 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) void WindowManager::pinWindow(GuiWindow window)
@ -2246,6 +2280,9 @@ namespace MWGui
void WindowManager::createCursors() 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(); MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator();
while (enumerator.next()) while (enumerator.next())
{ {

@ -132,7 +132,7 @@ namespace MWGui
WindowManager(osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, 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, 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(); virtual ~WindowManager();
/// Set the ESMStore to use for retrieving of GUI-related strings. /// Set the ESMStore to use for retrieving of GUI-related strings.
@ -611,8 +611,6 @@ namespace MWGui
void updateMap(); void updateMap();
std::map<std::string, std::string> mFallbackMap;
int mShowOwned; int mShowOwned;
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;

@ -791,7 +791,7 @@ namespace MWInput
mMouseLookEnabled = !guiMode; mMouseLookEnabled = !guiMode;
if (guiMode) if (guiMode)
MWBase::Environment::get().getWindowManager()->showCrosshair(false); 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. // 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) void InputManager::quickKey (int index)
{ {
if (!mControlSwitch["playercontrols"]) if (!mControlSwitch["playercontrols"] || !mControlSwitch["playerfighting"] || !mControlSwitch["playermagic"])
return; return;
if (!checkAllowedToUseItems()) if (!checkAllowedToUseItems())
return; return;

@ -1725,7 +1725,10 @@ namespace MWMechanics
if(iter->first.getClass().isNpc()) 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); calculateNpcStatModifiers(iter->first, duration);
if (timerUpdateEquippedLight == 0) if (timerUpdateEquippedLight == 0)

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

@ -261,7 +261,7 @@ const MWMechanics::PathgridGraph& MWMechanics::AiPackage::getPathGridGraph(const
CacheMap::iterator found = cache.find(id); CacheMap::iterator found = cache.find(id);
if (found == cache.end()) 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(); return *cache[id].get();
} }
@ -335,13 +335,6 @@ bool MWMechanics::AiPackage::doesPathNeedRecalc(const osg::Vec3f& newDest, const
|| mPathFinder.getPathCell() != currentCell; || 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) bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
{ {
const ESM::Cell* playerCell(getPlayer().getCell()->getCell()); const ESM::Cell* playerCell(getPlayer().getCell()->getCell());

@ -102,8 +102,6 @@ namespace MWMechanics
/// Reset pathfinding state /// Reset pathfinding state
void reset(); 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. /// 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); static bool isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest);

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

@ -3,7 +3,6 @@
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/class.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. // 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. // 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. // 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.
float aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange(); return (pos1 - pos2).length2() <= 7168*7168;
return (pos1 - pos2).length2() <= aiDistance*aiDistance;
} }
} }

@ -366,11 +366,6 @@ void CharacterController::refreshJumpAnims(const WeaponInfo* weap, JumpingState
if (!force && jump == mJumpState && idle == CharState_None) if (!force && jump == mJumpState && idle == CharState_None)
return; return;
if (jump == JumpState_InAir)
{
idle = CharState_None;
}
std::string jumpAnimName; std::string jumpAnimName;
MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All; MWRender::Animation::BlendMask jumpmask = MWRender::Animation::BlendMask_All;
if (jump != JumpState_None) if (jump != JumpState_None)
@ -1244,7 +1239,7 @@ bool CharacterController::updateCreatureState()
*/ */
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell);
cast.playSpellCastingEffects(spellid); cast.playSpellCastingEffects(spellid, false);
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -1575,9 +1570,31 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
stats.getSpells().setSelectedSpell(selectedSpell); stats.getSpells().setSelectedSpell(selectedSpell);
} }
std::string spellid = stats.getSpells().getSelectedSpell(); std::string spellid = stats.getSpells().getSelectedSpell();
bool isMagicItem = false;
bool canCast = mCastingManualSpell || MWBase::Environment::get().getWorld()->startSpellCast(mPtr); 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 Start of tes3mp addition
@ -1599,18 +1616,26 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
*/ */
MWMechanics::CastSpell cast(mPtr, nullptr, false, mCastingManualSpell); 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 MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid); if (isMagicItem)
const ESM::ENAMstruct &lastEffect = spell->mEffects.mList.back(); {
const ESM::MagicEffect *effect; 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"); 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")) if (mAnimation->getNode("Bip01 L Hand"))
mAnimation->addEffect("meshes\\" + castStatic->mModel, -1, false, "Bip01 L Hand", effect->mParticle); 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); 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 startKey;
std::string stopKey; std::string stopKey;
@ -1653,17 +1678,6 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
resetIdle = false; 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) else if(mWeaponType == WeapType_PickProbe)
{ {
@ -1756,7 +1770,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
idle != CharState_IdleSneak && idle != CharState_IdleSwim && idle != CharState_IdleSneak && idle != CharState_IdleSwim &&
mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim) mIdleState != CharState_IdleSneak && mIdleState != CharState_IdleSwim)
{ {
idle = CharState_None; mAnimation->disable(mCurrentIdle);
} }
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete); animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
@ -2242,6 +2256,9 @@ void CharacterController::update(float duration, bool animationOnly)
jumpstate = JumpState_Landing; jumpstate = JumpState_Landing;
vec.z() = 0.0f; vec.z() = 0.0f;
// We should reset idle animation during landing
mAnimation->disable(mCurrentIdle);
float height = cls.getCreatureStats(mPtr).land(isPlayer); float height = cls.getCreatureStats(mPtr).land(isPlayer);
float healthLost = getFallDamage(mPtr, height); float healthLost = getFallDamage(mPtr, height);
@ -2474,6 +2491,20 @@ void CharacterController::update(float duration, bool animationOnly)
else else
moved = osg::Vec3f(0.f, 0.f, 0.f); 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... // Ensure we're moving in generally the right direction...
if(speed > 0.f) if(speed > 0.f)
{ {

@ -600,4 +600,11 @@ namespace MWMechanics
return (iFightDistanceBase - fFightDistanceMultiplier * d); 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); void applyFatigueLoss(const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float attackStrength);
float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2); float getFightDistanceBias(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2);
bool isTargetMagicallyHidden(const MWWorld::Ptr& target);
} }
#endif #endif

@ -57,6 +57,7 @@ namespace MWMechanics
{ {
// Contracted disease! // Contracted disease!
actor.getClass().getCreatureStats(actor).getSpells().add(it->first); actor.getClass().getCreatureStats(actor).getSpells().add(it->first);
MWBase::Environment::get().getWorld()->applyLoopingParticles(actor);
std::string msg = "sMagicContractDisease"; std::string msg = "sMagicContractDisease";
msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(msg)->mValue.getString(); 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) 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; return false;
std::set<MWWorld::Ptr> followersAttacker;
getActorsSidingWith(attacker, followersAttacker);
MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target); MWMechanics::CreatureStats& statsTarget = target.getClass().getCreatureStats(target);
if (attacker == player)
if (followersAttacker.find(target) != followersAttacker.end())
{ {
statsTarget.friendlyHit(); std::set<MWWorld::Ptr> followersAttacker;
getActorsSidingWith(attacker, followersAttacker);
if (statsTarget.getFriendlyHits() < 4) if (followersAttacker.find(target) != followersAttacker.end())
{ {
MWBase::Environment::get().getDialogueManager()->say(target, "hit"); statsTarget.friendlyHit();
return false;
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 Make it possible to start combat with DedicatedPlayers and DedicatedActors by
adding additional conditions for them adding additional conditions for them
*/ */
if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) if (!attacker.isEmpty()
|| attacker == getPlayer() || mwmp::PlayerList::isDedicatedPlayer(attacker) && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == player
|| mwmp::Main::get().getCellController()->isDedicatedActor(attacker)) || mwmp::PlayerList::isDedicatedPlayer(attacker) || mwmp::Main::get().getCellController()->isDedicatedActor(attacker))
&& !seq.isInCombat(attacker)) && !seq.isInCombat(attacker))
/* /*
End of tes3mp change (major) 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) // he will attack the player only if we will force him (e.g. via StartCombat console command)
bool peaceful = false; bool peaceful = false;
std::string script = target.getClass().getScript(target); 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()); int fight = std::max(0, target.getClass().getCreatureStats(target).getAiSetting(CreatureStats::AI_Fight).getModified());
peaceful = (fight == 0); peaceful = (fight == 0);

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

@ -3,6 +3,7 @@
#include <limits> #include <limits>
#include <iomanip> #include <iomanip>
#include <components/misc/constants.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -551,8 +552,7 @@ namespace MWMechanics
if (magnitudeMult > 0 && !absorbed) if (magnitudeMult > 0 && !absorbed)
{ {
float random = Misc::Rng::rollClosedProbability(); float magnitude = effectIt->mMagnMin + Misc::Rng::rollDice(effectIt->mMagnMax - effectIt->mMagnMin + 1);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
magnitude *= magnitudeMult; magnitude *= magnitudeMult;
if (!target.getClass().isActor()) if (!target.getClass().isActor())
@ -977,16 +977,15 @@ namespace MWMechanics
if (mCaster == getPlayer()) if (mCaster == getPlayer())
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1); 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()) if (mCaster == getPlayer())
{
mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3); mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3);
}
} }
inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self); 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 // A non-actor doesn't play its spell cast effects from a character controller, so play them here
if (!mCaster.getClass().isActor()) if (!mCaster.getClass().isActor())
playSpellCastingEffects(mId); playSpellCastingEffects(mId, false);
inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self); inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self);
@ -1177,15 +1176,27 @@ namespace MWMechanics
return true; 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 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(); void CastSpell::playSpellCastingEffects(const std::vector<ESM::ENAMstruct>& effects)
iter != spell->mEffects.mList.end(); ++iter) {
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; const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(iter->mEffectID); effect = store.get<ESM::MagicEffect>().find(iter->mEffectID);
@ -1205,41 +1216,18 @@ namespace MWMechanics
std::string texture = effect->mParticle; std::string texture = effect->mParticle;
float scale = 1.0f;
osg::Vec3f pos (mCaster.getRefData().getPosition().asVec3()); 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) if (animation)
{ {
animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture, scale); animation->addEffect("meshes\\" + castStatic->mModel, effect->mIndex, false, "", texture);
} }
else 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; float meshScale = !mCaster.getClass().isActor() ? mCaster.getCellRef().getScale() : 1.0f;
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale); MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + castStatic->mModel, effect->mParticle, pos, scale * meshScale);
} }

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

@ -94,7 +94,10 @@ namespace MWMechanics
for (unsigned int i=0; i<spell->mEffects.mList.size();++i) for (unsigned int i=0; i<spell->mEffects.mList.size();++i)
{ {
if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax) 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 // First of all, try to hit where you aim to
int hitmask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor; 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) if (result.mHit)
{ {

@ -16,8 +16,6 @@
#include <components/sceneutil/lightutil.hpp> #include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/visitor.hpp> #include <components/sceneutil/visitor.hpp>
#include <components/fallback/fallback.hpp>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/settings/settings.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()) if (mItemLights.find(item) != mItemLights.end())
return; 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(); bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
osg::Vec4f ambient(1,1,1,1); osg::Vec4f ambient(1,1,1,1);
osg::ref_ptr<SceneUtil::LightSource> lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, outQuadInLin, osg::ref_ptr<SceneUtil::LightSource> lightSource = SceneUtil::createLightSource(esmLight, Mask_Lighting, exterior, ambient);
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue, ambient);
mInsert->addChild(lightSource); mInsert->addChild(lightSource);

@ -38,8 +38,6 @@
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
#include <components/fallback/fallback.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -1628,21 +1626,12 @@ namespace MWRender
void Animation::addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *esmLight) 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(); bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior, outQuadInLin, SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
useQuadratic, quadraticValue, quadraticRadiusMult, useLinear, linearRadiusMult, linearValue);
} }
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()) if (!mObjectRoot.get())
return; return;
@ -1674,7 +1663,12 @@ namespace MWRender
} }
osg::ref_ptr<osg::PositionAttitudeTransform> trans = new osg::PositionAttitudeTransform; 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); parentNode->addChild(trans);
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model, trans); osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model, trans);
@ -1834,14 +1828,12 @@ namespace MWRender
} }
else else
{ {
// TODO: use global attenuation settings
// 1 pt of Light magnitude corresponds to 1 foot of radius // 1 pt of Light magnitude corresponds to 1 foot of radius
float radius = effect * std::ceil(Constants::UnitsPerFoot); float radius = effect * std::ceil(Constants::UnitsPerFoot);
const float linearValue = 3.f; // Currently hardcoded: unmodified Morrowind attenuation settings // Arbitrary multiplier used to make the obvious cut-off less obvious
float linearAttenuation = linearValue / radius; float cutoffMult = 3;
if (!mGlowLight || linearAttenuation != mGlowLight->getLight(0)->getLinearAttenuation()) if (!mGlowLight || (radius * cutoffMult) != mGlowLight->getRadius())
{ {
if (mGlowLight) if (mGlowLight)
{ {
@ -1853,7 +1845,9 @@ namespace MWRender
light->setDiffuse(osg::Vec4f(0,0,0,0)); light->setDiffuse(osg::Vec4f(0,0,0,0));
light->setSpecular(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->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 = new SceneUtil::LightSource;
mGlowLight->setNodeMask(Mask_Lighting); mGlowLight->setNodeMask(Mask_Lighting);
@ -1861,8 +1855,7 @@ namespace MWRender
mGlowLight->setLight(light); mGlowLight->setLight(light);
} }
// Make the obvious cut-off a bit less obvious mGlowLight->setRadius(radius * cutoffMult);
mGlowLight->setRadius(radius * 3);
} }
} }

@ -365,7 +365,7 @@ public:
* @param texture override the texture specified in the model's materials - if empty, do not override * @param texture override the texture specified in the model's materials - if empty, do not override
* @note Will not add an effect twice. * @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 removeEffect (int effectId);
void removeEffects (); void removeEffects ();
void getLoopingEffects (std::vector<int>& out) const; void getLoopingEffects (std::vector<int>& out) const;

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

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

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

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

@ -26,13 +26,13 @@
namespace 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) if (rippleFrameCount <= 0)
return; 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; std::vector<osg::ref_ptr<osg::Texture2D> > textures;
for (int i=0; i<rippleFrameCount; ++i) for (int i=0; i<rippleFrameCount; ++i)
@ -81,7 +81,7 @@ namespace
namespace MWRender namespace MWRender
{ {
RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem, const Fallback::Map* fallback) RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem* resourceSystem)
: mParent(parent) : mParent(parent)
{ {
mParticleSystem = new osgParticle::ParticleSystem; mParticleSystem = new osgParticle::ParticleSystem;
@ -94,8 +94,8 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem*
particleTemplate.setSizeRange(osgParticle::rangef(15, 180)); 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.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.setAlphaRange(osgParticle::rangef(1.f, 0.f));
particleTemplate.setAngularVelocity(osg::Vec3f(0,0,fallback->getFallbackFloat("Water_RippleRotSpeed"))); particleTemplate.setAngularVelocity(osg::Vec3f(0,0,Fallback::Map::getFloat("Water_RippleRotSpeed")));
particleTemplate.setLifeTime(fallback->getFallbackFloat("Water_RippleLifetime")); particleTemplate.setLifeTime(Fallback::Map::getFloat("Water_RippleLifetime"));
osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater); osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater (new osgParticle::ParticleSystemUpdater);
updater->addParticleSystem(mParticleSystem); updater->addParticleSystem(mParticleSystem);
@ -105,7 +105,7 @@ RippleSimulation::RippleSimulation(osg::Group *parent, Resource::ResourceSystem*
mParticleNode->addChild(mParticleSystem); mParticleNode->addChild(mParticleSystem);
mParticleNode->setNodeMask(Mask_Effect); mParticleNode->setNodeMask(Mask_Effect);
createWaterRippleStateSet(resourceSystem, fallback, mParticleNode); createWaterRippleStateSet(resourceSystem, mParticleNode);
mParent->addChild(mParticleNode); mParent->addChild(mParticleNode);
} }

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

@ -266,16 +266,16 @@ public:
META_Node(MWRender, CameraRelativeTransform) 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 virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix, osg::NodeVisitor* nv) const
{ {
if (nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) 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) if (_referenceFrame==RELATIVE_RF)
@ -337,8 +337,8 @@ public:
} }
}; };
private: private:
// eyePoint for the current frame // viewPoint for the current frame
mutable osg::Vec3f mEyePoint; mutable osg::Vec3f mViewPoint;
}; };
class ModVertexAlphaVisitor : public osg::NodeVisitor class ModVertexAlphaVisitor : public osg::NodeVisitor
@ -391,7 +391,7 @@ private:
/// @brief Hides the node subgraph if the eye point is below water. /// @brief Hides the node subgraph if the eye point is below water.
/// @note Must be added as cull callback. /// @note Must be added as cull callback.
/// @note Meant to be used on a node that is child of a CameraRelativeTransform. /// @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 class UnderwaterSwitchCallback : public osg::NodeCallback
{ {
public: public:
@ -404,8 +404,8 @@ public:
bool isUnderwater() bool isUnderwater()
{ {
osg::Vec3f eyePoint = mCameraRelativeTransform->getLastEyePoint(); osg::Vec3f viewPoint = mCameraRelativeTransform->getLastViewPoint();
return mEnabled && eyePoint.z() < mWaterLevel; return mEnabled && viewPoint.z() < mWaterLevel;
} }
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
@ -828,10 +828,9 @@ private:
, mTimeOfDayFade(1.f) , mTimeOfDayFade(1.f)
, mGlareView(1.f) , mGlareView(1.f)
{ {
const Fallback::Map* fallback = MWBase::Environment::get().getWorld()->getFallback(); mColor = Fallback::Map::getColour("Weather_Sun_Glare_Fader_Color");
mColor = fallback->getFallbackColour("Weather_Sun_Glare_Fader_Color"); mSunGlareFaderMax = Fallback::Map::getFloat("Weather_Sun_Glare_Fader_Max");
mSunGlareFaderMax = fallback->getFallbackFloat("Weather_Sun_Glare_Fader_Max"); mSunGlareFaderAngleMax = Fallback::Map::getFloat("Weather_Sun_Glare_Fader_Angle_Max");
mSunGlareFaderAngleMax = fallback->getFallbackFloat("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, // 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, // 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())); mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager()));
const Fallback::Map* fallback=MWBase::Environment::get().getWorld()->getFallback(); mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser));
mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), fallback->getFallbackFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda));
mCloudNode = new osg::PositionAttitudeTransform; mCloudNode = new osg::PositionAttitudeTransform;
mEarlyRenderBinRoot->addChild(mCloudNode); mEarlyRenderBinRoot->addChild(mCloudNode);
@ -1205,7 +1203,7 @@ void SkyManager::create()
mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF);
mMoonScriptColor = fallback->getFallbackColour("Moons_Script_Color"); mMoonScriptColor = Fallback::Map::getColour("Moons_Script_Color");
mCreated = true; mCreated = true;
} }

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

Loading…
Cancel
Save