Merge branch 'master' of https://github.com/OpenMW/openmw into joystick

Conflicts:
	CMakeLists.txt
celladd
scrawl 10 years ago
commit 2b7287cbd2

@ -31,6 +31,7 @@ before_script:
script: script:
- cd ./build - cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
after_script: after_script:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
notifications: notifications:

@ -33,12 +33,13 @@ Programmers
Douglas Diniz (Dgdiniz) Douglas Diniz (Dgdiniz)
Douglas Mencken (dougmencken) Douglas Mencken (dougmencken)
dreamer-dead dreamer-dead
dteviot David Teviotdale (dteviot)
Edmondo Tommasina (edmondo) Edmondo Tommasina (edmondo)
Eduard Cot (trombonecot) Eduard Cot (trombonecot)
Eli2 Eli2
Emanuel Guével (potatoesmaster) Emanuel Guével (potatoesmaster)
eroen eroen
Evgeniy Mineev (sandstranger)
Fil Krynicki (filkry) Fil Krynicki (filkry)
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
@ -75,20 +76,22 @@ Programmers
Michał Bień (Glorf) Michał Bień (Glorf)
Miroslav Puda (pakanek) Miroslav Puda (pakanek)
MiroslavR MiroslavR
naclander
Narmo Narmo
Nathan Jeffords (blunted2night) Nathan Jeffords (blunted2night)
NeveHanter
Nikolay Kasyanov (corristo) Nikolay Kasyanov (corristo)
nobrakal nobrakal
Nolan Poe (nopoe) Nolan Poe (nopoe)
Paul McElroy (Greendogo) Paul McElroy (Greendogo)
Pieter van der Kloet (pvdk) Pieter van der Kloet (pvdk)
Radu-Marius Popovici (rpopovici) Radu-Marius Popovici (rpopovici)
rdimesio
riothamus riothamus
Robert MacGregor (Ragora) Robert MacGregor (Ragora)
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (humbug) Roman Proskuryakov (humbug)
sandstranger
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
Sebastian Wick (swick) Sebastian Wick (swick)

@ -1,3 +1,127 @@
0.35.0
------
Bug #244: Clipping/static in relation to the ghostgate/fence sound.
Bug #531: Missing transparent menu items
Bug #811: Content Lists in openmw.cfg are overwritten
Bug #925: OpenCS doesn't launch because it thinks its already started
Bug #969: Water shader strange behaviour on AMD card
Bug #1049: Partially highlighted word in dialogue may cause incorrect line break
Bug #1069: omwlauncher.exe crashes due to file lock
Bug #1192: It is possible to jump on top of hostile creatures in combat
Bug #1342: Loud ambient sounds
Bug #1431: Creatures can climb the player
Bug #1605: Guard in CharGen doesn't turn around to face you when reaching stairs
Bug #1624: Moon edges don't transition properly
Bug #1634: Items dropped by PC have collision
Bug #1637: Weird NPC behaviour in Vivec, Hlaalu Ancestral Vaults?
Bug #1638: Cannot climb staircases
Bug #1648: Enchanted equipment badly handled at game reload
Bug #1663: Crash when casting spell at enemy near you
Bug #1683: Scale doesn't apply to animated collision nodes
Bug #1702: Active enchanted item forgotten
Bug #1730: Scripts names starting with digit(s) fail to compile
Bug #1743: Moons are transparent
Bug #1745: Shadows crash: Assertion `mEffects.empty()' failed.
Bug #1785: Can't equip two-handed weapon and shield
Bug #1809: Player falls too easily
Bug #1825: Sword of Perithia can´t run in OpenMW
Bug #1899: The launcher resets any alterations you´ve made in the mod list order,
Bug #1964: Idle voices/dialogs not triggered correctly
Bug #1980: Please, change default click behavior in OpenMW Launchers Data Files list
Bug #1984: Vampire corpses standing up when looting the first item
Bug #1985: Calm spell does nothing
Bug #1986: Spell name lights up on mouseover but spell cost does not
Bug #1989: Tooltip still shown when menu toggled off
Bug #2010: Raindrops Displayed While Underwater
Bug #2023: Walking into plants causes massive framedrop
Bug #2031: [MOD: Shrines - Restore Health and Cancel Options]: Restore health option doesn't work
Bug #2039: Lake Fjalding pillar of fire not rendered
Bug #2040: AI_follow should stop further from the target
Bug #2076: Slaughterfish AI
Bug #2077: Direction of long jump can be changed much more than it is possible in vanilla
Bug #2078: error during rendering: Object '' not found (const)
Bug #2105: Lockpicking causes screen sync glitch
Bug #2113: [MOD: Julan Ashlander Companion] Julan does not act correctly within the Ghostfence.
Bug #2123: Window glow mod: Collision issues
Bug #2133: Missing collision for bridges in Balmora when using Morrowind Rebirth 2.81
Bug #2135: Casting a summon spell while the summon is active does not reset the summon.
Bug #2144: Changing equipment will unequip drawn arrows/bolts
Bug #2169: Yellow on faces when using opengl renderer and mods from overhaul on windows
Bug #2175: Pathgrid mods do not overwrite the existing pathgrid
Bug #2176: Morrowind -Russian localization end add-on ChaosHeart. Error in framelistener;object ;frenzying toush; not found <const>
Bug #2181: Mod Morrowind crafting merchants die.
Bug #2182: mods changing skill progression double the bonus for class specialization
Bug #2183: Editor: Skills "use value" only allows integer between 0 and 99
Bug #2184: Animated Morrowind Expanded produces an error on Open MW Launch
Bug #2185: Conditional Operator formats
Bug #2193: Quest: Gateway Ghost
Bug #2194: Cannot summon multiples of the same creature
Bug #2195: Pathgrid in the (0,0) exterior cell not loaded
Bug #2200: Outdoor NPCs can stray away and keep walking into a wall
Bug #2201: Creatures do not receive fall damage
Bug #2202: The enchantment the item can hold is calculated incorrectly
Bug #2203: Having the mod Living Cities of Vvardenfall running causes the game world to fail to load after leaving the prison ship
Bug #2204: Abot's Water Life - Book rendered incorrectly
Bug #2205: sound_waterfall script no longer compiles
Bug #2206: Dialogue script fails to compile (extra .)
Bug #2207: Script using instead of - character does not compile
Bug #2208: Failing dialogue scripts in french Morrowind.esm
Bug #2214: LGNPC Vivec Redoran 1.62 and The King Rat (Size and inventory Issues)
Bug #2215: Beast races can use enchanted boots
Bug #2218: Incorrect names body parts in 3D models for open helmet with skinning
Bug #2219: Orcs in Ghorak Manor in Caldera don't attack if you pick their pockets.
Bug #2220: Chargen race preview head incorrect orientation
Bug #2223: Reseting rock falling animation
Bug #2224: Fortify Attribute effects do not stack when Spellmaking.
Bug #2226: OpenCS pseudo-crash
Bug #2230: segfaulting when entering Ald'ruhn with a specific mod: "fermeture la nuit" (closed by night)
Bug #2233: Area effect spells on touch do not have the area effect
Bug #2234: Dwarven Crossbow clips through the ground when dropped
Bug #2235: class SettingsBase<> reverses the order of entries with multiple keys.
Bug #2236: Weird two handed longsword + torch interaction
Bug #2237: Shooting arrows while sneaking do not agro
Bug #2238: Bipedal creatures not using weapons are not handled properly
Bug #2245: Incorrect topic highlighting in HT_SpyBaladas quest
Bug #2252: Tab completion incomplete for places using COC from the console.
Bug #2255: Camera reverts to first person on load
Bug #2259: enhancement: the save/load progress bar is not very progressive
Bug #2263: TogglePOV can not be bound to Alt key
Bug #2267: dialogue disabling via mod
Bug #2268: Highlighting Files with load order problems in Data Files tab of Launcher
Bug #2276: [Mod]ShotN issues with Karthwasten
Bug #2283: Count argument for PlaceAt functions not working
Bug #2284: Local map notes should be visible on door marker leading to the cell with the note
Bug #2293: There is a graphical glitch at the end of the spell's animation in 3rd Person (looking over the shoulder) view
Bug #2294: When using Skyrim UI Overhaul, the tops of pinnable menus are invisible
Bug #2302: Random leveled items repeat way too often in a single dungeon
Bug #2306: Enchanted arrows should not be retrievable from corpses
Bug #2308: No sound effect when drawing the next throwing knife
Bug #2309: Guards chase see the player character even if they're invisible
Bug #2319: Inverted controls and other issues after becoming a vampire
Bug #2324: Spells cast when crossing cell border are imprinted on the local map
Bug #2330: Actors with Drain Health effect retain health after dying
Bug #2331: tgm (god mode) won't allow the player to cast spells if the player doesn't have enough mana
Bug #2332: Error in framelistener: Need a skeleton to attach the arrow to
Feature #114: ess-Importer
Feature #504: Editor: Delete selected rows from result windows
Feature #1024: Addition of remaining equipping hotkeys
Feature #1067: Handle NIF interpolation type 4 (XYZ_ROTATION_KEY)
Feature #1125: AI fast-forward
Feature #1228: Drowning while knocked out
Feature #1325: Editor: Opening window and User Settings window cleanup
Feature #1537: Ability to change the grid size from 3x3 to 5x5 (or more with good pc)
Feature #1546: Leveled list script functions
Feature #1659: Test dialogue scripts in --script-all
Feature #1720: NPC lookAt controller
Feature #2178: Load initial particle system state from NIF files
Feature #2197: Editor: When clicking on a script error in the report window set cursor in script editor to the respective line/column
Feature #2261: Warn when loading save games with mod mismatch
Feature #2313: ess-Importer: convert global map exploration overlay
Feature #2318: Add commandline option to load a save game
Task #810: Rename "profile" to "content list"
Task #2196: Label local/global openmw.cfg files via comments
0.34.0 0.34.0
------ ------

@ -6,4 +6,4 @@ export CC=clang
brew tap openmw/openmw brew tap openmw/openmw
brew update brew update
brew unlink boost brew unlink boost
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg qt unshield brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg openmw/openmw/qt unshield

@ -1,5 +1,12 @@
project(OpenMW) project(OpenMW)
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
IF(NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
FORCE)
ENDIF()
if (APPLE) if (APPLE)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
@ -12,7 +19,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...") message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 34) set(OPENMW_VERSION_MINOR 35)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -374,10 +381,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
if (NOT WIN32 AND NOT APPLE) if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop") "${OpenMW_BINARY_DIR}/openmw.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-mimeinfo.xml configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
"${OpenMW_BINARY_DIR}/openmw-mimeinfo.xml") "${OpenMW_BINARY_DIR}/openmw-cs.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop
"${OpenMW_BINARY_DIR}/opencs.desktop")
endif() endif()
# Compiler settings # Compiler settings
@ -389,13 +394,19 @@ if (CMAKE_COMPILER_IS_GNUCC)
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
SET(CMAKE_CXX_FLAGS "-Wno-unused-but-set-parameter ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "-Wno-unused-but-set-parameter ${CMAKE_CXX_FLAGS}")
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6) endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
elseif (MSVC)
# Enable link-time code generation globally for all linking
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} /LTCG")
endif (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC)
IF(NOT WIN32 AND NOT APPLE) IF(NOT WIN32 AND NOT APPLE)
# Linux building # Linux building
# Paths # Paths
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Where to install libraries") SET(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "Where to install libraries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix") SET(GLOBAL_DATA_PATH "${DATAROOTDIR}/games/" CACHE PATH "Set data path prefix")
SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") SET(DATADIR "${GLOBAL_DATA_PATH}/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
@ -411,7 +422,7 @@ IF(NOT WIN32 AND NOT APPLE)
# Install binaries # Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL) IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
@ -420,13 +431,13 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL) ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER) ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER) IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESSIMPORTER) ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
IF(BUILD_NIFTEST) IF(BUILD_NIFTEST)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/niftest" DESTINATION "${BINDIR}" )
@ -443,25 +454,25 @@ IF(NOT WIN32 AND NOT APPLE)
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
# Install icon and desktop file # Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-mimeinfo.xml" DESTINATION "${DATAROOTDIR}/mime/packages" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
# Install global configuration files # Install global configuration files
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "opencs") INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
# Install resources # Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
ENDIF(NOT WIN32 AND NOT APPLE) ENDIF(NOT WIN32 AND NOT APPLE)
@ -481,16 +492,16 @@ if(WIN32)
DESTINATION ".") DESTINATION ".")
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".")
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_MWINIIMPORTER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".")
ENDIF(BUILD_MWINIIMPORTER) ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER) IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
ENDIF(BUILD_ESSIMPORTER) ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.ini" DESTINATION ".")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD) IF(BUILD_WIZARD)
@ -511,10 +522,10 @@ if(WIN32)
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher") SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set") SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD) IF(BUILD_WIZARD)
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard") SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-wizard;OpenMW Wizard")
@ -530,7 +541,7 @@ if(WIN32)
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}") SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico") SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
@ -667,6 +678,7 @@ if (WIN32)
4193 # #pragma warning(pop) : no matching '#pragma warning(push)' 4193 # #pragma warning(pop) : no matching '#pragma warning(push)'
4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY' 4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY'
4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY' 4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY'
4315 # undocumented, 'this' pointer for member might not be aligned (OgreMemoryStlAllocator.h)
# caused by boost # caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
@ -674,6 +686,7 @@ if (WIN32)
# OpenMW specific warnings # OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type 4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter) 4100 # Unreferenced formal parameter (-Wunused-parameter)
4101 # Unreferenced local variable (-Wunused-variable)
4127 # Conditional expression is constant 4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data 4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example) 4244 # Storing value of one type in variable of another (size_t in int, for example)
@ -695,16 +708,18 @@ if (WIN32)
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here # boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
set(SHINY_WARNINGS "${WARNINGS} /wd4245") set(SHINY_WARNINGS "${WARNINGS} /wd4245")
set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}") set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}")
# there's an unreferenced local variable in the ogre platform, suppress it set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${SHINY_OGRE_WARNINGS} ${MT_BUILD}")
set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable # oics uses tinyxml, which has an initialized but unused variable
set(OICS_WARNINGS "${WARNINGS} /wd4189") set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}") set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}")
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(ogre-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_MYGUI_PLUGIN)
set_target_properties(Plugin_MyGUI_OpenMW_Resources PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_MYGUI_PLUGIN)
if (BUILD_LAUNCHER) if (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_LAUNCHER) endif (BUILD_LAUNCHER)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL) if (BUILD_BSATOOL)
@ -719,10 +734,13 @@ if (WIN32)
if (BUILD_OPENCS) if (BUILD_OPENCS)
# QT triggers an informational warning that the object layout may differ when compiled with /vd2 # QT triggers an informational warning that the object layout may differ when compiled with /vd2
set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435") set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435")
set_target_properties(opencs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS}) set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
endif (BUILD_OPENCS) endif (BUILD_OPENCS)
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_ESSIMPORTER)
if (BUILD_MWINIIMPORTER) if (BUILD_MWINIIMPORTER)
set_target_properties(mwiniimport PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_MWINIIMPORTER) endif (BUILD_MWINIIMPORTER)
endif(MSVC) endif(MSVC)
@ -767,7 +785,7 @@ if (APPLE)
set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
set(OPENCS_BUNDLE_NAME "OpenCS.app") set(OPENCS_BUNDLE_NAME "OpenMW-CS.app")
set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
set(ABSOLUTE_PLUGINS "") set(ABSOLUTE_PLUGINS "")

@ -1,14 +1,12 @@
OpenMW OpenMW
====== ======
[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw) [![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg?style=plastic)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
OpenMW is an attempt at recreating the engine for the popular role-playing game OpenMW is an attempt at recreating the engine for the popular role-playing game
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
* Version: 0.34.0 * Version: 0.35.0
* License: GPL (see docs/license/GPL3.txt for more information) * License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org * Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net * IRC: #openmw on irc.freenode.net
@ -41,26 +39,26 @@ Command line options
--version print version information and quit --version print version information and quit
--data arg (=data) set data directories (later directories --data arg (=data) set data directories (later directories
have higher priority) have higher priority)
--data-local arg set local data directory (highest --data-local arg set local data directory (highest
priority) priority)
--fallback-archive arg (=fallback-archive) --fallback-archive arg (=fallback-archive)
set fallback BSA archives (later set fallback BSA archives (later
archives have higher priority) archives have higher priority)
--resources arg (=resources) set resources directory --resources arg (=resources) set resources directory
--start arg set initial cell --start arg set initial cell
--content arg content file(s): esm/esp, or --content arg content file(s): esm/esp, or
omwgame/omwaddon omwgame/omwaddon
--no-sound [=arg(=1)] (=0) disable all sounds --no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output --script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
scripts) at startup scripts) at startup
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup --script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
--script-console [=arg(=1)] (=0) enable console-only script --script-console [=arg(=1)] (=0) enable console-only script
functionality functionality
--script-run arg select a file containing a list of --script-run arg select a file containing a list of
console commands that is executed on console commands that is executed on
startup startup
--script-warn [=arg(=1)] (=1) handling of warnings when compiling --script-warn [=arg(=1)] (=1) handling of warnings when compiling
scripts scripts
0 - ignore warning 0 - ignore warning
1 - show warning but consider script as 1 - show warning but consider script as
@ -70,29 +68,32 @@ Command line options
of the blacklist is enabled) of the blacklist is enabled)
--script-blacklist-use [=arg(=1)] (=1) --script-blacklist-use [=arg(=1)] (=1)
enable script blacklisting enable script blacklisting
--load-savegame arg load a save game file on game startup --load-savegame arg load a save game file on game startup
(specify an absolute filename or a
filename relative to the current
working directory)
--skip-menu [=arg(=1)] (=0) skip main menu on game startup --skip-menu [=arg(=1)] (=0) skip main menu on game startup
--new-game [=arg(=1)] (=0) run new game sequence (ignored if --new-game [=arg(=1)] (=0) run new game sequence (ignored if
skip-menu=0) skip-menu=0)
--fs-strict [=arg(=1)] (=0) strict file system handling (no case --fs-strict [=arg(=1)] (=0) strict file system handling (no case
folding) folding)
--encoding arg (=win1252) Character encoding used in OpenMW game --encoding arg (=win1252) Character encoding used in OpenMW game
messages: messages:
win1250 - Central and Eastern European win1250 - Central and Eastern European
such as Polish, Czech, Slovak, such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian, Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and Serbian (Latin script), Romanian and
Albanian languages Albanian languages
win1251 - Cyrillic alphabet such as win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic Russian, Bulgarian, Serbian Cyrillic
and other languages and other languages
win1252 - Western European (Latin) win1252 - Western European (Latin)
alphabet, used by default alphabet, used by default
--fallback arg fallback values --fallback arg fallback values
--no-grab Don't grab mouse cursor --no-grab Don't grab mouse cursor
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG --export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
image and XML file in current directory image and XML file in current directory
--activate-dist arg (=-1) activation distance override --activate-dist arg (=-1) activation distance override

@ -59,6 +59,7 @@ struct Arguments
std::string outname; std::string outname;
std::vector<std::string> types; std::vector<std::string> types;
std::string name;
ESMData data; ESMData data;
ESM::ESMReader reader; ESM::ESMReader reader;
@ -78,6 +79,8 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("type,t", bpo::value< std::vector<std::string> >(), ("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May " "Show only records of this type (four character record code). May "
"be specified multiple times. Only affects dump mode.") "be specified multiple times. Only affects dump mode.")
("name,n", bpo::value<std::string>(),
"Show only the record with this name. Only affects dump mode.")
("plain,p", "Print contents of dialogs, books and scripts. " ("plain,p", "Print contents of dialogs, books and scripts. "
"(skipped by default)" "(skipped by default)"
"Only affects dump mode.") "Only affects dump mode.")
@ -148,7 +151,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
} }
if (variables.count("type") > 0) if (variables.count("type") > 0)
info.types = variables["type"].as< std::vector<std::string> >(); info.types = variables["type"].as< std::vector<std::string> >();
if (variables.count("name") > 0)
info.name = variables["name"].as<std::string>();
info.mode = variables["mode"].as<std::string>(); info.mode = variables["mode"].as<std::string>();
if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp")) if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
@ -265,6 +270,8 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl; std::cout << " Deleted: " << deleted << std::endl;
if (!ref.mKey.empty())
std::cout << " Key: '" << ref.mKey << "'" << std::endl;
} }
} }
@ -358,6 +365,9 @@ int load(Arguments& info)
if (id.empty()) if (id.empty())
id = esm.getHNOString("INAM"); id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
interested = false;
if(!quiet && interested) if(!quiet && interested)
std::cout << "\nRecord: " << n.toString() std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n"; << " '" << id << "'\n";
@ -385,7 +395,7 @@ int load(Arguments& info)
record->load(esm); record->load(esm);
if (!quiet && interested) record->print(); if (!quiet && interested) record->print();
if (record->getType().val == ESM::REC_CELL && loadCells) { if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
loadCell(record->cast<ESM::Cell>()->get(), esm, info); loadCell(record->cast<ESM::Cell>()->get(), esm, info);
} }

@ -2,6 +2,8 @@
#include "labels.hpp" #include "labels.hpp"
#include <iostream> #include <iostream>
#include <sstream>
#include <boost/format.hpp> #include <boost/format.hpp>
void printAIPackage(ESM::AIPackage p) void printAIPackage(ESM::AIPackage p)
@ -533,10 +535,10 @@ void Record<ESM::Class>::print()
std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization) std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization)
<< " (" << mData.mData.mSpecialization << ")" << std::endl; << " (" << mData.mData.mSpecialization << ")" << std::endl;
for (int i = 0; i != 5; i++) for (int i = 0; i != 5; i++)
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][0]) std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][0])
<< " (" << mData.mData.mSkills[i][0] << ")" << std::endl; << " (" << mData.mData.mSkills[i][0] << ")" << std::endl;
for (int i = 0; i != 5; i++) for (int i = 0; i != 5; i++)
std::cout << " Minor Skill: " << skillLabel(mData.mData.mSkills[i][1]) std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl; << " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
} }
@ -752,7 +754,7 @@ void Record<ESM::DialInfo>::print()
if (mData.mCell != "") if (mData.mCell != "")
std::cout << " Cell: " << mData.mCell << std::endl; std::cout << " Cell: " << mData.mCell << std::endl;
if (mData.mData.mDisposition > 0) if (mData.mData.mDisposition > 0)
std::cout << " Disposition: " << mData.mData.mDisposition << std::endl; std::cout << " Disposition/Journal index: " << mData.mData.mDisposition << std::endl;
if (mData.mData.mGender != ESM::DialInfo::NA) if (mData.mData.mGender != ESM::DialInfo::NA)
std::cout << " Gender: " << mData.mData.mGender << std::endl; std::cout << " Gender: " << mData.mData.mGender << std::endl;
if (mData.mSound != "") if (mData.mSound != "")
@ -812,7 +814,6 @@ void Record<ESM::Land>::print()
{ {
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl; std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " HasData: " << mData.mHasData << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl; std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// Seems like this should done with reference counting in the // Seems like this should done with reference counting in the
@ -836,7 +837,7 @@ void Record<ESM::CreatureLevList>::print()
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit; std::vector<ESM::LevelledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Creature: Level: " << iit->mLevel std::cout << " Creature: Level: " << iit->mLevel
<< " Creature: " << iit->mId << std::endl; << " Creature: " << iit->mId << std::endl;
@ -848,7 +849,7 @@ void Record<ESM::ItemLevList>::print()
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit; std::vector<ESM::LevelledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit) for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
std::cout << " Inventory: Level: " << iit->mLevel std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl; << " Item: " << iit->mId << std::endl;

@ -24,6 +24,8 @@ set(ESSIMPORTER_FILES
convertcrec.cpp convertcrec.cpp
convertcntc.cpp convertcntc.cpp
convertscri.cpp convertscri.cpp
convertscpt.cpp
convertplayer.cpp
) )
add_executable(openmw-essimporter add_executable(openmw-essimporter

@ -29,6 +29,12 @@ namespace ESSImport
} }
cStats.mGoldPool = acdt.mGoldPool; cStats.mGoldPool = acdt.mGoldPool;
cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer; cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer;
cStats.mAttacked = acdt.mFlags & Attacked;
}
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats)
{
cStats.mDead = acsc.mFlags & Dead;
} }
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)

@ -15,6 +15,7 @@ namespace ESSImport
void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats); void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats);
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats); void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
} }

@ -3,6 +3,7 @@
#include <stdexcept> #include <stdexcept>
#include <OgreImage.h> #include <OgreImage.h>
#include <OgreColourValue.h>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp> #include <components/esm/containerstate.hpp>
@ -69,7 +70,65 @@ namespace ESSImport
esm.getSubHeader(); esm.getSubHeader();
data.resize(esm.getSubSize()); data.resize(esm.getSubSize());
esm.getExact(&data[0], data.size()); esm.getExact(&data[0], data.size());
convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga");
Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(&data[0], data.size()));
mGlobalMapImage.loadRawData(stream, maph.size, maph.size, 1, Ogre::PF_BYTE_RGB);
// to match openmw size
mGlobalMapImage.resize(maph.size*2, maph.size*2, Ogre::Image::FILTER_BILINEAR);
}
void ConvertFMAP::write(ESM::ESMWriter &esm)
{
int numcells = mGlobalMapImage.getWidth() / 18; // NB truncating, doesn't divide perfectly
// with the 512x512 map the game has by default
int cellSize = mGlobalMapImage.getWidth()/numcells;
// Note the upper left corner of the (0,0) cell should be at (width/2, height/2)
mContext->mGlobalMapState.mBounds.mMinX = -numcells/2;
mContext->mGlobalMapState.mBounds.mMaxX = (numcells-1)/2;
mContext->mGlobalMapState.mBounds.mMinY = -(numcells-1)/2;
mContext->mGlobalMapState.mBounds.mMaxY = numcells/2;
Ogre::Image image2;
std::vector<Ogre::uint8> data;
int width = cellSize*numcells;
int height = cellSize*numcells;
data.resize(width*height*4, 0);
image2.loadDynamicImage(&data[0], width, height, Ogre::PF_BYTE_RGBA);
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it)
{
if (it->first > mContext->mGlobalMapState.mBounds.mMaxX
|| it->first < mContext->mGlobalMapState.mBounds.mMinX
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY
|| it->second < mContext->mGlobalMapState.mBounds.mMinY)
{
// out of bounds, I think this could happen, since the original engine had a fixed-size map
continue;
}
int imageLeftSrc = mGlobalMapImage.getWidth()/2;
int imageTopSrc = mGlobalMapImage.getHeight()/2;
imageLeftSrc += it->first * cellSize;
imageTopSrc -= it->second * cellSize;
int imageLeftDst = width/2;
int imageTopDst = height/2;
imageLeftDst += it->first * cellSize;
imageTopDst -= it->second * cellSize;
for (int x=0; x<cellSize; ++x)
for (int y=0; y<cellSize; ++y)
image2.setColourAt(mGlobalMapImage.getColourAt(imageLeftSrc+x, imageTopSrc+y, 0)
, imageLeftDst+x, imageTopDst+y, 0);
}
Ogre::DataStreamPtr encoded = image2.encode("png");
mContext->mGlobalMapState.mImageData.resize(encoded->size());
encoded->read(&mContext->mGlobalMapState.mImageData[0], encoded->size());
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
} }
void ConvertCell::read(ESM::ESMReader &esm) void ConvertCell::read(ESM::ESMReader &esm)
@ -103,6 +162,10 @@ namespace ESSImport
// (probably offset of that specific fog texture?) // (probably offset of that specific fog texture?)
while (esm.isNextSub("NAM8")) while (esm.isNextSub("NAM8"))
{ {
if (cell.isExterior()) // TODO: NAM8 occasionally exists for cells that haven't been explored.
// are there any flags marking explored cells?
mContext->mExploredCells.insert(std::make_pair(cell.mData.mX, cell.mData.mY));
esm.getSubHeader(); esm.getSubHeader();
if (esm.getSubSize() == 36) if (esm.getSubSize() == 36)
@ -211,6 +274,8 @@ namespace ESSImport
const CellRef& cellref = *refIt; const CellRef& cellref = *refIt;
ESM::CellRef out (cellref); ESM::CellRef out (cellref);
// TODO: use mContext->mCreatures/mNpcs
if (!isIndexedRefId(cellref.mIndexedRefId)) if (!isIndexedRefId(cellref.mIndexedRefId))
{ {
// non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it
@ -246,9 +311,12 @@ namespace ESSImport
objstate.blank(); objstate.blank();
objstate.mRef = out; objstate.mRef = out;
objstate.mRef.mRefID = idLower; objstate.mRef.mRefID = idLower;
// probably need more micromanagement here so we don't overwrite values // TODO: need more micromanagement here so we don't overwrite values
// from the ESM with default values // from the ESM with default values
convertACDT(cellref.mACDT, objstate.mCreatureStats); if (cellref.mHasACDT)
convertACDT(cellref.mACDT, objstate.mCreatureStats);
if (cellref.mHasACSC)
convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertNpcData(cellref, objstate.mNpcStats); convertNpcData(cellref, objstate.mNpcStats);
convertNPCC(npccIt->second, objstate); convertNPCC(npccIt->second, objstate);
convertCellRef(cellref, objstate); convertCellRef(cellref, objstate);
@ -280,9 +348,12 @@ namespace ESSImport
objstate.blank(); objstate.blank();
objstate.mRef = out; objstate.mRef = out;
objstate.mRef.mRefID = idLower; objstate.mRef.mRefID = idLower;
convertACDT(cellref.mACDT, objstate.mCreatureStats); // TODO: need more micromanagement here so we don't overwrite values
// probably need more micromanagement here so we don't overwrite values
// from the ESM with default values // from the ESM with default values
if (cellref.mHasACDT)
convertACDT(cellref.mACDT, objstate.mCreatureStats);
if (cellref.mHasACSC)
convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertCREC(crecIt->second, objstate); convertCREC(crecIt->second, objstate);
convertCellRef(cellref, objstate); convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_CREA); esm.writeHNT ("OBJE", ESM::REC_CREA);
@ -313,10 +384,6 @@ namespace ESSImport
it->save(esm); it->save(esm);
esm.endRecord(ESM::REC_MARK); esm.endRecord(ESM::REC_MARK);
} }
esm.startRecord(ESM::REC_GMAP);
mContext->mGlobalMapState.save(esm);
esm.endRecord(ESM::REC_GMAP);
} }
} }

@ -1,6 +1,8 @@
#ifndef OPENMW_ESSIMPORT_CONVERTER_H #ifndef OPENMW_ESSIMPORT_CONVERTER_H
#define OPENMW_ESSIMPORT_CONVERTER_H #define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
@ -13,6 +15,10 @@
#include <components/esm/dialoguestate.hpp> #include <components/esm/dialoguestate.hpp>
#include <components/esm/custommarkerstate.hpp> #include <components/esm/custommarkerstate.hpp>
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/weatherstate.hpp>
#include <components/esm/globalscript.hpp>
#include <components/esm/queststate.hpp>
#include <components/esm/stolenitems.hpp>
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp" #include "importcntc.hpp"
@ -29,6 +35,8 @@
#include "convertacdt.hpp" #include "convertacdt.hpp"
#include "convertnpcc.hpp" #include "convertnpcc.hpp"
#include "convertscpt.hpp"
#include "convertplayer.hpp"
namespace ESSImport namespace ESSImport
{ {
@ -98,10 +106,10 @@ public:
npc.load(esm); npc.load(esm);
if (id != "player") if (id != "player")
{ {
// TODO: // Handles changes to the NPC struct, but since there is no index here
// this should handle changes to the NPC struct, but since there is no index here
// it will apply to ALL instances of the class. seems to be the reason for the // it will apply to ALL instances of the class. seems to be the reason for the
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID. // "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
mContext->mNpcs[Misc::StringUtils::lowerCase(id)] = npc;
} }
else else
{ {
@ -133,6 +141,7 @@ public:
ESM::Creature creature; ESM::Creature creature;
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
creature.load(esm); creature.load(esm);
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature;
} }
}; };
@ -206,7 +215,7 @@ public:
else else
{ {
int index = npcc.mNPDT.mIndex; int index = npcc.mNPDT.mIndex;
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc));
} }
} }
}; };
@ -253,32 +262,23 @@ private:
class ConvertPCDT : public Converter class ConvertPCDT : public Converter
{ {
public: public:
ConvertPCDT() : mFirstPersonCam(true) {}
virtual void read(ESM::ESMReader &esm) virtual void read(ESM::ESMReader &esm)
{ {
PCDT pcdt; PCDT pcdt;
pcdt.load(esm); pcdt.load(esm);
mContext->mPlayer.mBirthsign = pcdt.mBirthsign; convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam);
mContext->mPlayer.mObject.mNpcStats.mBounty = pcdt.mBounty; }
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) virtual void write(ESM::ESMWriter &esm)
{ {
ESM::NpcStats::Faction faction; esm.startRecord(ESM::REC_CAM_);
faction.mExpelled = it->mFlags & 0x2; esm.writeHNT("FIRS", mFirstPersonCam);
faction.mRank = it->mRank; esm.endRecord(ESM::REC_CAM_);
faction.mReputation = it->mReputation;
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction;
}
for (int i=0; i<8; ++i)
mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
mContext->mPlayer.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it)
{
mContext->mDialogueState.mKnownTopics.push_back(Misc::StringUtils::lowerCase(*it));
}
} }
private:
bool mFirstPersonCam;
}; };
class ConvertCNTC : public Converter class ConvertCNTC : public Converter
@ -308,6 +308,10 @@ class ConvertFMAP : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm); virtual void read(ESM::ESMReader &esm);
virtual void write(ESM::ESMWriter &esm);
private:
Ogre::Image mGlobalMapImage;
}; };
class ConvertCell : public Converter class ConvertCell : public Converter
@ -384,29 +388,55 @@ public:
virtual void read(ESM::ESMReader &esm) virtual void read(ESM::ESMReader &esm)
{ {
std::string itemid = esm.getHNString("NAME"); std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::toLower(itemid);
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
{ {
if (esm.retSubName().toString() == "FNAM") if (esm.retSubName().toString() == "FNAM")
{ {
std::string factionid = esm.getHString(); std::string factionid = esm.getHString();
mFactionStolenItems.insert(std::make_pair(itemid, factionid)); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));
} }
else else
{ {
std::string ownerid = esm.getHString(); std::string ownerid = esm.getHString();
mStolenItems.insert(std::make_pair(itemid, ownerid)); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false));
} }
} }
} }
virtual void write(ESM::ESMWriter &esm)
{
ESM::StolenItems items;
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
{
std::map<std::pair<std::string, bool>, int> owners;
for (std::set<Owner>::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt)
{
owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second)
// Since OpenMW doesn't suffer from the owner contamination bug,
// it needs a count argument. But for legacy savegames, we don't know
// this count, so must assume all items of that ID are stolen,
// like vanilla MW did.
,std::numeric_limits<int>::max()));
}
items.mStolenItems.insert(std::make_pair(it->first, owners));
}
esm.startRecord(ESM::REC_STLN);
items.write(esm);
esm.endRecord(ESM::REC_STLN);
}
private: private:
std::multimap<std::string, std::string> mStolenItems; typedef std::pair<std::string, bool> Owner; // <owner id, bool isFaction>
std::multimap<std::string, std::string> mFactionStolenItems;
std::map<std::string, std::set<Owner> > mStolenItems;
}; };
/// Seen responses for a dialogue topic? /// Seen responses for a dialogue topic?
/// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs /// Each DIAL record is followed by a number of INFO records, I believe, just like in ESMs
/// Dialogue conversion problems (probably have to adjust OpenMW format) - /// Dialogue conversion problems:
/// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID. /// - Journal is stored in one continuous HTML markup rather than each entry separately with associated info ID.
/// - Seen dialogue responses only store the INFO id, rather than the fulltext. /// - Seen dialogue responses only store the INFO id, rather than the fulltext.
/// - Quest stages only store the INFO id, rather than the journal entry fulltext. /// - Quest stages only store the INFO id, rather than the journal entry fulltext.
@ -428,7 +458,24 @@ public:
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
DIAL dial; DIAL dial;
dial.load(esm); dial.load(esm);
if (dial.mIndex > 0)
mDials[id] = dial;
} }
virtual void write(ESM::ESMWriter &esm)
{
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
{
esm.startRecord(ESM::REC_QUES);
ESM::QuestState state;
state.mFinished = 0;
state.mState = it->second.mIndex;
state.mTopic = Misc::StringUtils::lowerCase(it->first);
state.save(esm);
esm.endRecord(ESM::REC_QUES);
}
}
private:
std::map<std::string, DIAL> mDials;
}; };
class ConvertQUES : public Converter class ConvertQUES : public Converter
@ -455,11 +502,69 @@ public:
class ConvertGAME : public Converter class ConvertGAME : public Converter
{ {
public: public:
ConvertGAME() : mHasGame(false) {}
std::string toString(int weatherId)
{
switch (weatherId)
{
case 0:
return "clear";
case 1:
return "cloudy";
case 2:
return "foggy";
case 3:
return "overcast";
case 4:
return "rain";
case 5:
return "thunderstorm";
case 6:
return "ashstorm";
case 7:
return "blight";
case 8:
return "snow";
case 9:
return "blizzard";
case -1:
return "";
default:
{
std::stringstream error;
error << "unknown weather id: " << weatherId;
throw std::runtime_error(error.str());
}
}
}
virtual void read(ESM::ESMReader &esm) virtual void read(ESM::ESMReader &esm)
{ {
GAME game; mGame.load(esm);
game.load(esm); mHasGame = true;
} }
virtual void write(ESM::ESMWriter &esm)
{
if (!mHasGame)
return;
esm.startRecord(ESM::REC_WTHR);
ESM::WeatherState weather;
weather.mCurrentWeather = toString(mGame.mGMDT.mCurrentWeather);
weather.mNextWeather = toString(mGame.mGMDT.mNextWeather);
weather.mRemainingTransitionTime = mGame.mGMDT.mWeatherTransition/100.f*(0.015*24*3600);
weather.mHour = mContext->mHour;
weather.mWindSpeed = 0.f;
weather.mTimePassed = 0.0;
weather.mFirstUpdate = false;
weather.save(esm);
esm.endRecord(ESM::REC_WTHR);
}
private:
bool mHasGame;
GAME mGame;
}; };
/// Running global script /// Running global script
@ -470,7 +575,21 @@ public:
{ {
SCPT script; SCPT script;
script.load(esm); script.load(esm);
ESM::GlobalScript out;
convertSCPT(script, out);
mScripts.push_back(out);
} }
virtual void write(ESM::ESMWriter &esm)
{
for (std::vector<ESM::GlobalScript>::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it)
{
esm.startRecord(ESM::REC_GSCR);
it->save(esm);
esm.endRecord(ESM::REC_GSCR);
}
}
private:
std::vector<ESM::GlobalScript> mScripts;
}; };
} }

@ -0,0 +1,38 @@
#include "convertplayer.hpp"
namespace ESSImport
{
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam)
{
out.mBirthsign = pcdt.mBirthsign;
out.mObject.mNpcStats.mBounty = pcdt.mBounty;
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it)
{
ESM::NpcStats::Faction faction;
faction.mExpelled = (it->mFlags & 0x2) != 0;
faction.mRank = it->mRank;
faction.mReputation = it->mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
}
for (int i=0; i<8; ++i)
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
for (int i=0; i<27; ++i)
out.mObject.mNpcStats.mSkills[i].mRegular.mProgress = pcdt.mPNAM.mSkillProgress[i];
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon)
out.mObject.mCreatureStats.mDrawState = 1;
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
out.mObject.mCreatureStats.mDrawState = 2;
firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it)
{
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
}
}
}

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTPLAYER_H
#define OPENMW_ESSIMPORT_CONVERTPLAYER_H
#include "importplayer.hpp"
#include <components/esm/player.hpp>
namespace ESSImport
{
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam);
}
#endif

@ -0,0 +1,17 @@
#include "convertscpt.hpp"
#include <components/misc/stringops.hpp>
#include "convertscri.hpp"
namespace ESSImport
{
void convertSCPT(const SCPT &scpt, ESM::GlobalScript &out)
{
out.mId = Misc::StringUtils::lowerCase(scpt.mSCHD.mName.toString());
out.mRunning = scpt.mRunning;
convertSCRI(scpt.mSCRI, out.mLocals);
}
}

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTSCPT_H
#define OPENMW_ESSIMPORT_CONVERTSCPT_H
#include <components/esm/globalscript.hpp>
#include "importscpt.hpp"
namespace ESSImport
{
void convertSCPT(const SCPT& scpt, ESM::GlobalScript& out);
}
#endif

@ -20,12 +20,22 @@ namespace ESSImport
ESM::CellRef::loadData(esm); ESM::CellRef::loadData(esm);
// FIXME: not all actors have this, add flag mHasACDT = false;
esm.getHNOT(mACDT, "ACDT"); if (esm.isNextSub("ACDT"))
{
mHasACDT = true;
esm.getHT(mACDT);
}
mHasACSC = false;
if (esm.isNextSub("ACSC"))
{
mHasACSC = true;
esm.getHT(mACSC);
}
ACSC acsc; if (esm.isNextSub("ACSL"))
esm.getHNOT(acsc, "ACSC"); esm.skipHSubSize(112);
esm.getHNOT(acsc, "ACSL");
if (esm.isNextSub("CSTN")) if (esm.isNextSub("CSTN"))
esm.skipHSub(); // "PlayerSaveGame", link to some object? esm.skipHSub(); // "PlayerSaveGame", link to some object?
@ -60,10 +70,6 @@ namespace ESSImport
if (esm.isNextSub("PWPS")) if (esm.isNextSub("PWPS"))
esm.skipHSub(); esm.skipHSub();
// unsure at which point between LSTN and CHRD
if (esm.isNextSub("APUD"))
esm.skipHSub(); // 40 bytes, starts with string "ancestor guardian". maybe spellcasting in progress?
if (esm.isNextSub("WNAM")) if (esm.isNextSub("WNAM"))
{ {
std::string id = esm.getHString(); std::string id = esm.getHString();
@ -77,6 +83,20 @@ namespace ESSImport
esm.skipHSub(); // 4 byte, 0 esm.skipHSub(); // 4 byte, 0
} }
while (esm.isNextSub("APUD"))
{
// used power
esm.getSubHeader();
std::string id = esm.getString(32);
(void)id;
// timestamp can't be used: this is the total hours passed, calculated by
// timestamp = 24 * (365 * year + cumulativeDays[month] + day)
// unfortunately cumulativeDays[month] is not clearly defined,
// in the (non-MCP) vanilla version the first month was missing, but MCP added it.
double timestamp;
esm.getT(timestamp);
}
// FIXME: not all actors have this, add flag // FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int)); esm.getHExact(mSkills, 27*2*sizeof(int));

@ -9,7 +9,7 @@
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport
@ -17,7 +17,13 @@ namespace ESSImport
enum ACDTFlags enum ACDTFlags
{ {
TalkedToPlayer = 0x4 TalkedToPlayer = 0x4,
Attacked = 0x100,
Unknown = 0x200
};
enum ACSCFlags
{
Dead = 0x2
}; };
/// Actor data, shared by (at least) REFR and CellRef /// Actor data, shared by (at least) REFR and CellRef
@ -28,24 +34,38 @@ namespace ESSImport
// Note, not stored at *all*: // Note, not stored at *all*:
// - Level changes are lost on reload, except for the player (there it's in the NPC record). // - Level changes are lost on reload, except for the player (there it's in the NPC record).
unsigned char mUnknown[12]; unsigned char mUnknown[12];
unsigned char mFlags; // ACDTFlags unsigned int mFlags;
unsigned char mUnknown1[3];
float mBreathMeter; // Seconds left before drowning float mBreathMeter; // Seconds left before drowning
unsigned char mUnknown2[20]; unsigned char mUnknown2[20];
float mDynamic[3][2]; float mDynamic[3][2];
unsigned char mUnknown3[16]; unsigned char mUnknown3[16];
float mAttributes[8][2]; float mAttributes[8][2];
unsigned char mUnknown4[112]; float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes
unsigned char mUnknown4[4];
unsigned int mGoldPool; unsigned int mGoldPool;
unsigned char mUnknown5[4]; unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe
// this one is for respawning?
unsigned char mUnknown5[3];
};
struct ACSC
{
unsigned char mUnknown1[17];
unsigned char mFlags; // ACSCFlags
unsigned char mUnknown2[22];
unsigned char mCorpseClearCountdown; // hours?
unsigned char mUnknown3[71];
}; };
#pragma pack(pop) #pragma pack(pop)
struct ActorData : public ESM::CellRef struct ActorData : public ESM::CellRef
{ {
bool mHasACDT;
ACDT mACDT; ACDT mACDT;
int mSkills[27][2]; bool mHasACSC;
ACSC mACSC;
int mSkills[27][2]; // skills, base and modified
// creature combat stats, base and modified // creature combat stats, base and modified
// I think these can be ignored in the conversion, because it is not possible // I think these can be ignored in the conversion, because it is not possible
@ -60,12 +80,6 @@ namespace ESSImport
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };
/// Unknown, shared by (at least) REFR and CellRef
struct ACSC
{
unsigned char unknown[112];
};
} }
#endif #endif

@ -22,7 +22,7 @@ namespace ESSImport
ActorData::load(esm); ActorData::load(esm);
if (esm.isNextSub("LVCR")) if (esm.isNextSub("LVCR"))
{ {
// occurs on leveled creature spawner references // occurs on levelled creature spawner references
// probably some identifier for the creature that has been spawned? // probably some identifier for the creature that has been spawned?
unsigned char lvcr; unsigned char lvcr;
esm.getHT(lvcr); esm.getHT(lvcr);
@ -32,7 +32,7 @@ namespace ESSImport
mEnabled = true; mEnabled = true;
esm.getHNOT(mEnabled, "ZNAM"); esm.getHNOT(mEnabled, "ZNAM");
// DATA should occur for all references, except leveled creature spawners // DATA should occur for all references, except levelled creature spawners
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird // I've seen DATA *twice* on a creature record, and with the exact same content too! weird
// alarmvoi0000.ess // alarmvoi0000.ess
esm.getHNOT(mPos, "DATA", 24); esm.getHNOT(mPos, "DATA", 24);

@ -14,10 +14,10 @@ namespace ESSImport
float scale; float scale;
esm.getHNOT(scale, "XSCL"); esm.getHNOT(scale, "XSCL");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

@ -2,6 +2,7 @@
#define OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H
#include "importinventory.hpp" #include "importinventory.hpp"
#include <components/esm/aipackage.hpp>
namespace ESM namespace ESM
{ {
@ -17,6 +18,7 @@ namespace ESSImport
int mIndex; int mIndex;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

@ -2,7 +2,7 @@
#define OPENMW_ESSIMPORT_IMPORTDIAL_H #define OPENMW_ESSIMPORT_IMPORTDIAL_H
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport

@ -1,4 +1,5 @@
#include "importer.hpp" #include "importer.hpp"
#include <boost/shared_ptr.hpp>
#include <OgreRoot.h> #include <OgreRoot.h>
@ -20,6 +21,8 @@
#include <components/esm/loadlevlist.hpp> #include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "importercontext.hpp" #include "importercontext.hpp"
#include "converter.hpp" #include "converter.hpp"
@ -43,9 +46,10 @@ namespace
namespace ESSImport namespace ESSImport
{ {
Importer::Importer(const std::string &essfile, const std::string &outfile) Importer::Importer(const std::string &essfile, const std::string &outfile, const std::string &encoding)
: mEssFile(essfile) : mEssFile(essfile)
, mOutFile(outfile) , mOutFile(outfile)
, mEncoding(encoding)
{ {
} }
@ -166,14 +170,30 @@ namespace ESSImport
std::cout << "Data 1:" << std::endl; std::cout << "Data 1:" << std::endl;
for (unsigned int k=0; k<sub.mData.size(); ++k) for (unsigned int k=0; k<sub.mData.size(); ++k)
{ {
bool different = false;
if (k >= sub2.mData.size() || sub2.mData[k] != sub.mData[k])
different = true;
if (different)
std::cout << "\033[033m";
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << " "; std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << " ";
if (different)
std::cout << "\033[0m";
} }
std::cout << std::endl; std::cout << std::endl;
std::cout << "Data 2:" << std::endl; std::cout << "Data 2:" << std::endl;
for (unsigned int k=0; k<sub2.mData.size(); ++k) for (unsigned int k=0; k<sub2.mData.size(); ++k)
{ {
bool different = false;
if (k >= sub.mData.size() || sub.mData[k] != sub2.mData[k])
different = true;
if (different)
std::cout << "\033[033m";
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << " "; std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << " ";
if (different)
std::cout << "\033[0m";
} }
std::cout << std::endl; std::cout << std::endl;
} }
@ -187,10 +207,10 @@ namespace ESSImport
Ogre::LogManager logman; Ogre::LogManager logman;
Ogre::Root root; Ogre::Root root;
// TODO: set up encoding on ESMReader based on openmw.cfg / --encoding switch ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding));
ESM::ESMReader esm; ESM::ESMReader esm;
esm.open(mEssFile); esm.open(mEssFile);
esm.setEncoder(&encoder);
Context context; Context context;
@ -341,8 +361,8 @@ namespace ESSImport
{ {
// exterior cell -> determine cell coordinates based on position // exterior cell -> determine cell coordinates based on position
const int cellSize = 8192; const int cellSize = 8192;
int cellX = std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize); int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize));
int cellY = std::floor(context.mPlayer.mObject.mPosition.pos[1]/cellSize); int cellY = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[1] / cellSize));
context.mPlayer.mCellId.mIndex.mX = cellX; context.mPlayer.mCellId.mIndex.mX = cellX;
context.mPlayer.mCellId.mIndex.mY = cellY; context.mPlayer.mCellId.mIndex.mY = cellY;
} }

@ -9,7 +9,7 @@ namespace ESSImport
class Importer class Importer
{ {
public: public:
Importer(const std::string& essfile, const std::string& outfile); Importer(const std::string& essfile, const std::string& outfile, const std::string& encoding);
void run(); void run();
@ -18,6 +18,7 @@ namespace ESSImport
private: private:
std::string mEssFile; std::string mEssFile;
std::string mOutFile; std::string mOutFile;
std::string mEncoding;
}; };
} }

@ -7,6 +7,8 @@
#include <components/esm/player.hpp> #include <components/esm/player.hpp>
#include <components/esm/dialoguestate.hpp> #include <components/esm/dialoguestate.hpp>
#include <components/esm/globalmap.hpp> #include <components/esm/globalmap.hpp>
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
#include "importnpcc.hpp" #include "importnpcc.hpp"
#include "importcrec.hpp" #include "importcrec.hpp"
@ -15,6 +17,7 @@
namespace ESSImport namespace ESSImport
{ {
@ -29,6 +32,9 @@ namespace ESSImport
ESM::DialogueState mDialogueState; ESM::DialogueState mDialogueState;
// cells which should show an explored overlay on the global map
std::set<std::pair<int, int> > mExploredCells;
ESM::GlobalMap mGlobalMapState; ESM::GlobalMap mGlobalMapState;
int mDay, mMonth, mYear; int mDay, mMonth, mYear;
@ -39,6 +45,9 @@ namespace ESSImport
std::map<std::pair<int, std::string>, NPCC> mNpcChanges; std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
std::map<std::pair<int, std::string>, CNTC> mContainerChanges; std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
std::map<std::string, ESM::Creature> mCreatures;
std::map<std::string, ESM::NPC> mNpcs;
Context() Context()
{ {
mPlayer.mAutoMove = 0; mPlayer.mAutoMove = 0;

@ -7,7 +7,23 @@ namespace ESSImport
void GAME::load(ESM::ESMReader &esm) void GAME::load(ESM::ESMReader &esm)
{ {
esm.getHNT(mGMDT, "GMDT"); esm.getSubNameIs("GMDT");
esm.getSubHeader();
if (esm.getSubSize() == 92)
{
esm.getExact(&mGMDT, 92);
mGMDT.mSecundaPhase = 0;
}
else if (esm.getSubSize() == 96)
{
esm.getT(mGMDT);
}
else
esm.fail("unexpected subrecord size for GAME.GMDT");
mGMDT.mWeatherTransition &= (0x000000ff);
mGMDT.mSecundaPhase &= (0x000000ff);
mGMDT.mMasserPhase &= (0x000000ff);
} }
} }

@ -20,7 +20,7 @@ namespace ESSImport
int mCurrentWeather, mNextWeather; int mCurrentWeather, mNextWeather;
int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage int mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition float mTimeOfNextTransition; // weather changes when gamehour == timeOfNextTransition
int masserPhase, secundaPhase; // top 3 bytes may be garbage int mMasserPhase, mSecundaPhase; // top 3 bytes may be garbage
}; };
GMDT mGMDT; GMDT mGMDT;

@ -5,7 +5,7 @@
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport

@ -5,7 +5,7 @@
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport

@ -6,7 +6,7 @@
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport

@ -9,10 +9,9 @@ namespace ESSImport
{ {
esm.getHNT(mNPDT, "NPDT"); esm.getHNT(mNPDT, "NPDT");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

@ -27,6 +27,7 @@ namespace ESSImport
} mNPDT; } mNPDT;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader &esm); void load(ESM::ESMReader &esm);
}; };

@ -38,6 +38,17 @@ struct PCDT
std::vector<std::string> mKnownDialogueTopics; std::vector<std::string> mKnownDialogueTopics;
enum DrawState_
{
DrawState_Weapon = 0x80,
DrawState_Spell = 0x100
};
enum CameraState
{
CameraState_FirstPerson = 0x8,
CameraState_ThirdPerson = 0xa
};
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
struct FNAM struct FNAM
@ -49,11 +60,13 @@ struct PCDT
unsigned char mUnknown2[3]; unsigned char mUnknown2[3];
ESM::NAME32 mFactionName; ESM::NAME32 mFactionName;
}; };
struct PNAM struct PNAM
{ {
unsigned char mUnknown1[4]; short mDrawState; // DrawState
unsigned char mLevelProgress; short mCameraState; // CameraState
unsigned char mUnknown2[111]; unsigned int mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
unsigned char mUnknown3[88]; unsigned char mUnknown3[88];
}; };

@ -6,7 +6,7 @@
namespace ESM namespace ESM
{ {
struct ESMReader; class ESMReader;
} }
namespace ESSImport namespace ESSImport

@ -13,8 +13,14 @@ namespace ESSImport
mSCRI.load(esm); mSCRI.load(esm);
mRNAM = -1; mRefNum = -1;
esm.getHNOT(mRNAM, "RNAM"); if (esm.isNextSub("RNAM"))
{
mRunning = true;
esm.getHT(mRefNum);
}
else
mRunning = false;
} }
} }

@ -14,7 +14,6 @@ namespace ESSImport
{ {
// A running global script // A running global script
// TODO: test how targeted scripts are saved
struct SCPT struct SCPT
{ {
ESM::Script::SCHD mSCHD; ESM::Script::SCHD mSCHD;
@ -22,7 +21,8 @@ namespace ESSImport
// values of local variables // values of local variables
SCRI mSCRI; SCRI mSCRI;
int mRNAM; // unknown, seems to be -1 for some scripts, some huge integer for others bool mRunning;
int mRefNum; // Targeted reference, -1: no reference
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

@ -5,6 +5,8 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <components/files/configurationmanager.hpp>
#include "importer.hpp" #include "importer.hpp"
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
@ -23,31 +25,36 @@ int main(int argc, char** argv)
("mwsave,m", bpo::value<std::string>(), "morrowind .ess save file") ("mwsave,m", bpo::value<std::string>(), "morrowind .ess save file")
("output,o", bpo::value<std::string>(), "output file (.omwsave)") ("output,o", bpo::value<std::string>(), "output file (.omwsave)")
("compare,c", "compare two .ess files") ("compare,c", "compare two .ess files")
("encoding", boost::program_options::value<std::string>()->default_value("win1252"), "encoding of the save file")
; ;
p_desc.add("mwsave", 1).add("output", 1); p_desc.add("mwsave", 1).add("output", 1);
bpo::variables_map vm; bpo::variables_map variables;
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv) bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
.options(desc) .options(desc)
.positional(p_desc) .positional(p_desc)
.run(); .run();
bpo::store(parsed, vm); bpo::store(parsed, variables);
if(vm.count("help") || !vm.count("mwsave") || !vm.count("output")) { if(variables.count("help") || !variables.count("mwsave") || !variables.count("output")) {
std::cout << desc; std::cout << desc;
return 0; return 0;
} }
bpo::notify(vm); bpo::notify(variables);
Files::ConfigurationManager cfgManager(true);
cfgManager.readConfiguration(variables, desc);
std::string essFile = vm["mwsave"].as<std::string>(); std::string essFile = variables["mwsave"].as<std::string>();
std::string outputFile = vm["output"].as<std::string>(); std::string outputFile = variables["output"].as<std::string>();
std::string encoding = variables["encoding"].as<std::string>();
ESSImport::Importer importer(essFile, outputFile); ESSImport::Importer importer(essFile, outputFile, encoding);
if (vm.count("compare")) if (variables.count("compare"))
importer.compare(); importer.compare();
else else
{ {

@ -78,7 +78,7 @@ if(NOT WIN32)
endif(NOT WIN32) endif(NOT WIN32)
# Main executable # Main executable
add_executable(omwlauncher add_executable(openmw-launcher
${GUI_TYPE} ${GUI_TYPE}
${LAUNCHER} ${LAUNCHER}
${LAUNCHER_HEADER} ${LAUNCHER_HEADER}
@ -87,7 +87,7 @@ add_executable(omwlauncher
${UI_HDRS} ${UI_HDRS}
) )
target_link_libraries(omwlauncher target_link_libraries(openmw-launcher
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${OGRE_LIBRARIES} ${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS} ${OGRE_STATIC_PLUGINS}
@ -99,6 +99,6 @@ target_link_libraries(omwlauncher
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(omwlauncher gcov) target_link_libraries(openmw-launcher gcov)
endif() endif()

@ -20,6 +20,9 @@
#include "utils/textinputdialog.hpp" #include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp" #include "utils/profilescombobox.hpp"
const char *Launcher::DataFilesPage::mDefaultContentListName = "Default";
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent) Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings, Config::LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg) : mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
@ -48,9 +51,9 @@ void Launcher::DataFilesPage::buildView()
ui.deleteProfileButton->setToolTip ("Delete an existing Content List"); ui.deleteProfileButton->setToolTip ("Delete an existing Content List");
//combo box //combo box
ui.profilesComboBox->addItem ("Default"); ui.profilesComboBox->addItem(mDefaultContentListName);
ui.profilesComboBox->setPlaceholderText (QString("Select a Content List...")); ui.profilesComboBox->setPlaceholderText (QString("Select a Content List..."));
ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String("Default"))); ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(QLatin1String(mDefaultContentListName)));
// Add the actions to the toolbuttons // Add the actions to the toolbuttons
ui.newProfileButton->setDefaultAction (ui.newProfileAction); ui.newProfileButton->setDefaultAction (ui.newProfileAction);
@ -69,19 +72,6 @@ void Launcher::DataFilesPage::buildView()
bool Launcher::DataFilesPage::loadSettings() bool Launcher::DataFilesPage::loadSettings()
{ {
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
paths.insert (0, mDataLocal);
PathIterator pathIterator (paths);
QStringList profiles = mLauncherSettings.getContentLists(); QStringList profiles = mLauncherSettings.getContentLists();
QString currentProfile = mLauncherSettings.getCurrentContentListName(); QString currentProfile = mLauncherSettings.getCurrentContentListName();
@ -94,11 +84,27 @@ bool Launcher::DataFilesPage::loadSettings()
if (!currentProfile.isEmpty()) if (!currentProfile.isEmpty())
addProfile(currentProfile, true); addProfile(currentProfile, true);
mSelector->setProfileContent(filesInProfile(currentProfile, pathIterator));
return true; return true;
} }
void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
{
QStringList paths = mGameSettings.getDataDirs();
foreach(const QString &path, paths)
mSelector->addFiles(path);
mDataLocal = mGameSettings.getDataLocal();
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
paths.insert(0, mDataLocal);
PathIterator pathIterator(paths);
mSelector->setProfileContent(filesInProfile(contentModelName, pathIterator));
}
QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator) QStringList Launcher::DataFilesPage::filesInProfile(const QString& profileName, PathIterator& pathIterator)
{ {
QStringList files = mLauncherSettings.getContentListFiles(profileName); QStringList files = mLauncherSettings.getContentListFiles(profileName);
@ -175,7 +181,7 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current)); ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
loadSettings(); populateFileViews(current);
checkForDefaultProfile(); checkForDefaultProfile();
} }
@ -229,13 +235,6 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered()
mLauncherSettings.setCurrentContentListName(profile); mLauncherSettings.setCurrentContentListName(profile);
addProfile(profile, true); addProfile(profile, true);
mSelector->clearCheckStates();
mSelector->setGameFile();
saveSettings();
emit signalProfileChanged (ui.profilesComboBox->findText(profile));
} }
void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent)
@ -262,15 +261,13 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
// this should work since the Default profile can't be deleted and is always index 0 // this should work since the Default profile can't be deleted and is always index 0
int next = ui.profilesComboBox->currentIndex()-1; int next = ui.profilesComboBox->currentIndex()-1;
// changing the profile forces a reload of plugin file views.
ui.profilesComboBox->setCurrentIndex(next); ui.profilesComboBox->setCurrentIndex(next);
removeProfile(profile); removeProfile(profile);
ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile));
saveSettings();
loadSettings();
checkForDefaultProfile(); checkForDefaultProfile();
} }
@ -290,7 +287,7 @@ void Launcher::DataFilesPage::updateOkButton(const QString &text)
void Launcher::DataFilesPage::checkForDefaultProfile() void Launcher::DataFilesPage::checkForDefaultProfile()
{ {
//don't allow deleting "Default" profile //don't allow deleting "Default" profile
bool success = (ui.profilesComboBox->currentText() != "Default"); bool success = (ui.profilesComboBox->currentText() != mDefaultContentListName);
ui.deleteProfileAction->setEnabled (success); ui.deleteProfileAction->setEnabled (success);
ui.profilesComboBox->setEditEnabled (success); ui.profilesComboBox->setEditEnabled (success);

@ -58,6 +58,10 @@ namespace Launcher
void on_newProfileAction_triggered(); void on_newProfileAction_triggered();
void on_deleteProfileAction_triggered(); void on_deleteProfileAction_triggered();
public:
/// Content List that is always present
const static char *mDefaultContentListName;
private: private:
TextInputDialog *mProfileDialog; TextInputDialog *mProfileDialog;
@ -82,6 +86,7 @@ namespace Launcher
bool showDeleteMessageBox (const QString &text); bool showDeleteMessageBox (const QString &text);
void addProfile (const QString &profile, bool setAsCurrent); void addProfile (const QString &profile, bool setAsCurrent);
void checkForDefaultProfile(); void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName);
class PathIterator class PathIterator
{ {

@ -10,7 +10,7 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED #endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h> #include <SDL_video.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreRenderSystem.h> #include <OgreRenderSystem.h>
@ -67,7 +67,7 @@ bool Launcher::GraphicsPage::setupOgre()
} }
catch(Ogre::Exception &ex) catch(Ogre::Exception &ex)
{ {
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str()); QString ogreError = QString::fromUtf8(ex.getFullDescription().c_str());
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle("Error creating Ogre::Root"); msgBox.setWindowTitle("Error creating Ogre::Root");
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
@ -135,7 +135,7 @@ bool Launcher::GraphicsPage::setupSDL()
msgBox.setWindowTitle(tr("Error receiving number of screens")); msgBox.setWindowTitle(tr("Error receiving number of screens"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>"); msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec(); msgBox.exec();
return false; return false;
} }
@ -237,7 +237,7 @@ QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre
opt_it != i->second.possibleValues.end(); ++opt_it, ++idx) opt_it != i->second.possibleValues.end(); ++opt_it, ++idx)
{ {
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) { if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified(); result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromUtf8((*opt_it).c_str()).simplified();
} }
} }
} }
@ -266,7 +266,7 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
msgBox.setWindowTitle(tr("Error receiving resolutions")); msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>"); msgBox.setText(tr("<br><b>SDL_GetNumDisplayModes failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec(); msgBox.exec();
return result; return result;
} }
@ -279,7 +279,7 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
msgBox.setWindowTitle(tr("Error receiving resolutions")); msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical); msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>SDL_GetDisplayMode failed:</b><br><br>") + QString::fromStdString(SDL_GetError()) + "<br>"); msgBox.setText(tr("<br><b>SDL_GetDisplayMode failed:</b><br><br>") + QString::fromUtf8(SDL_GetError()) + "<br>");
msgBox.exec(); msgBox.exec();
return result; return result;
} }

@ -1,4 +1,5 @@
#include <iostream> #include <iostream>
#include <csignal>
#include <QApplication> #include <QApplication>
#include <QTextCodec> #include <QTextCodec>
@ -23,9 +24,11 @@ int main(int argc, char *argv[])
SDL_SetMainReady(); SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0) if (SDL_Init(SDL_INIT_VIDEO) != 0)
{ {
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError()); qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError());
return 0; return 0;
} }
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
QApplication app(argc, argv); QApplication app(argc, argv);

@ -27,25 +27,6 @@ using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *parent) Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr), QMainWindow (parent) : mGameSettings(mCfgMgr), QMainWindow (parent)
{ {
// Install the stylesheet font
QFile file;
QFontDatabase fontDatabase;
const QStringList fonts = fontDatabase.families();
// Check if the font is installed
if (!fonts.contains("EB Garamond")) {
QString font = QString::fromUtf8(mCfgMgr.getGlobalDataPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf");
file.setFileName(font);
if (!file.exists()) {
font = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str()) + QString("resources/mygui/EBGaramond-Regular.ttf");
}
fontDatabase.addApplicationFont(font);
}
setupUi(this); setupUi(this);
mGameInvoker = new ProcessInvoker(); mGameInvoker = new ProcessInvoker();
@ -80,6 +61,7 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH); QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH); QString tag(OPENMW_VERSION_TAGHASH);
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!revision.isEmpty() && !tag.isEmpty()) if (!revision.isEmpty() && !tag.isEmpty())
{ {
if (revision == tag) { if (revision == tag) {
@ -257,24 +239,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous; current = previous;
int currentIndex = iconWidget->row(current); int currentIndex = iconWidget->row(current);
// int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex); pagesWidget->setCurrentIndex(currentIndex);
mSettingsPage->resetProgressBar();
// DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
// DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
// //special call to update/save data files page list view when it's displayed/hidden.
// if (previousPage)
// {
// if (previousPage->objectName() == "DataFilesPage")
// previousPage->saveSettings();
// }
// else if (currentPage)
// {
// if (currentPage->objectName() == "DataFilesPage")
// currentPage->loadSettings();
// }
} }
bool Launcher::MainDialog::setupLauncherSettings() bool Launcher::MainDialog::setupLauncherSettings()

@ -11,6 +11,7 @@
#include <components/config/launchersettings.hpp> #include <components/config/launchersettings.hpp>
#include "utils/textinputdialog.hpp" #include "utils/textinputdialog.hpp"
#include "datafilespage.hpp"
using namespace Process; using namespace Process;
@ -38,6 +39,7 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
mWizardInvoker = new ProcessInvoker(); mWizardInvoker = new ProcessInvoker();
mImporterInvoker = new ProcessInvoker(); mImporterInvoker = new ProcessInvoker();
resetProgressBar();
connect(mWizardInvoker->getProcess(), SIGNAL(started()), connect(mWizardInvoker->getProcess(), SIGNAL(started()),
this, SLOT(wizardStarted())); this, SLOT(wizardStarted()));
@ -93,7 +95,7 @@ Launcher::SettingsPage::~SettingsPage()
void Launcher::SettingsPage::on_wizardButton_clicked() void Launcher::SettingsPage::on_wizardButton_clicked()
{ {
saveSettings(); mMain->writeSettings();
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
return; return;
@ -101,7 +103,7 @@ void Launcher::SettingsPage::on_wizardButton_clicked()
void Launcher::SettingsPage::on_importerButton_clicked() void Launcher::SettingsPage::on_importerButton_clicked()
{ {
saveSettings(); mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail // Create the file if it doesn't already exist, else the importer will fail
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str())); QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
@ -140,8 +142,13 @@ void Launcher::SettingsPage::on_importerButton_clicked()
qDebug() << "arguments " << arguments; qDebug() << "arguments " << arguments;
if (!mImporterInvoker->startProcess(QLatin1String("mwiniimport"), arguments, false)) // start the progress bar as a "bouncing ball"
return; progressBar->setMaximum(0);
progressBar->setValue(0);
if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
{
resetProgressBar();
}
} }
void Launcher::SettingsPage::on_browseButton_clicked() void Launcher::SettingsPage::on_browseButton_clicked()
@ -196,27 +203,35 @@ void Launcher::SettingsPage::importerStarted()
void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{ {
if (exitCode != 0 || exitStatus == QProcess::CrashExit) if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return;
// Re-read the settings in their current state
mMain->reloadSettings();
// Import selected data files from openmw.cfg
if (addonsCheckBox->isChecked())
{ {
if (mProfileDialog->exec() == QDialog::Accepted) resetProgressBar();
{
const QString profile(mProfileDialog->lineEdit()->text()); QMessageBox msgBox;
const QStringList files(mGameSettings.getContentList()); msgBox.setWindowTitle(tr("Importer finished"));
mLauncherSettings.setCurrentContentListName(profile); msgBox.setStandardButtons(QMessageBox::Ok);
mLauncherSettings.setContentList(profile, files); msgBox.setIcon(QMessageBox::Warning);
} msgBox.setText(tr("Failed to import settings from INI file."));
msgBox.exec();
}
else
{
// indicate progress finished
progressBar->setMaximum(1);
progressBar->setValue(1);
// Importer may have changed settings, so refresh
mMain->reloadSettings();
} }
mMain->reloadSettings();
importerButton->setEnabled(true); importerButton->setEnabled(true);
} }
void Launcher::SettingsPage::resetProgressBar()
{
// set progress bar to 0 %
progressBar->reset();
}
void Launcher::SettingsPage::updateOkButton(const QString &text) void Launcher::SettingsPage::updateOkButton(const QString &text)
{ {
// We do this here because we need to access the profiles // We do this here because we need to access the profiles

@ -29,6 +29,9 @@ namespace Launcher
void saveSettings(); void saveSettings();
bool loadSettings(); bool loadSettings();
/// set progress bar on page to 0%
void resetProgressBar();
private slots: private slots:
@ -57,7 +60,6 @@ namespace Launcher
MainDialog *mMain; MainDialog *mMain;
TextInputDialog *mProfileDialog; TextInputDialog *mProfileDialog;
}; };
} }

@ -9,16 +9,16 @@ set(MWINIIMPORT_HEADER
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER}) source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
add_executable(mwiniimport add_executable(openmw-iniimporter
${MWINIIMPORT} ${MWINIIMPORT}
) )
target_link_libraries(mwiniimport target_link_libraries(openmw-iniimporter
${Boost_LIBRARIES} ${Boost_LIBRARIES}
components components
) )
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(mwiniimport gcov) target_link_libraries(openmw-iniimporter gcov)
endif() endif()

@ -8,7 +8,8 @@
#include <sstream> #include <sstream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <boost/filesystem/path.hpp> #include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem; namespace bfs = boost::filesystem;
@ -660,7 +661,7 @@ std::string MwIniImporter::numberToString(int n) {
return str.str(); return str.str();
} }
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filename) const { MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
std::cout << "load ini file: " << filename << std::endl; std::cout << "load ini file: " << filename << std::endl;
std::string section(""); std::string section("");
@ -719,7 +720,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam
return map; return map;
} }
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filename) { MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::path& filename) {
std::cout << "load cfg file: " << filename << std::endl; std::cout << "load cfg file: " << filename << std::endl;
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
@ -825,10 +826,14 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
} }
} }
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
std::vector<std::string> contentFiles; std::vector<std::pair<std::time_t, std::string> > contentFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string gameFile(""); std::string gameFile("");
std::time_t defaultTime = 0;
// assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini
const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files");
multistrmap::const_iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for(int i=0; it != ini.end(); i++) { for(int i=0; it != ini.end(); i++) {
@ -845,18 +850,20 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co
Misc::StringUtils::toLower(filetype); Misc::StringUtils::toLower(filetype);
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
contentFiles.push_back(*entry); boost::filesystem::path filepath(gameFilesDir);
filepath /= *entry;
contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry));
} }
} }
gameFile = "";
} }
cfg.erase("content"); cfg.erase("content");
cfg.insert( std::make_pair("content", std::vector<std::string>() ) ); cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { // this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
cfg["content"].push_back(*it); sort(contentFiles.begin(), contentFiles.end());
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(it->second);
} }
} }
@ -873,3 +880,27 @@ void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
{ {
mEncoding = encoding; mEncoding = encoding;
} }
std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime)
{
std::time_t writeTime(defaultTime);
if (boost::filesystem::exists(filename))
{
// FixMe: remove #if when Boost dependency for Linux builds updated
// This allows Linux to build until then
#if (BOOST_VERSION >= 104800)
// need to resolve any symlinks so that we get time of file, not symlink
boost::filesystem::path resolved = boost::filesystem::canonical(filename);
#else
boost::filesystem::path resolved = filename;
#endif
writeTime = boost::filesystem::last_write_time(resolved);
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
") " << asctime(localtime(&writeTime)) << std::endl;
}
else
{
std::cout << "content file: " << filename << " not found" << std::endl;
}
return writeTime;
}

@ -6,6 +6,7 @@
#include <vector> #include <vector>
#include <exception> #include <exception>
#include <iosfwd> #include <iosfwd>
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -17,17 +18,22 @@ class MwIniImporter {
MwIniImporter(); MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding); void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose); void setVerbose(bool verbose);
multistrmap loadIniFile(const std::string& filename) const; multistrmap loadIniFile(const boost::filesystem::path& filename) const;
static multistrmap loadCfgFile(const std::string& filename); static multistrmap loadCfgFile(const boost::filesystem::path& filename);
void merge(multistrmap &cfg, const multistrmap &ini) const; void merge(multistrmap &cfg, const multistrmap &ini) const;
void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; void mergeFallback(multistrmap &cfg, const multistrmap &ini) const;
void importGameFiles(multistrmap &cfg, const multistrmap &ini) const; void importGameFiles(multistrmap &cfg, const multistrmap &ini,
const boost::filesystem::path& iniFilename) const;
void importArchives(multistrmap &cfg, const multistrmap &ini) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const;
static void writeToFile(std::ostream &out, const multistrmap &cfg); static void writeToFile(std::ostream &out, const multistrmap &cfg);
private: private:
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
static std::string numberToString(int n); static std::string numberToString(int n);
/// \return file's "last modified time", used in original MW to determine plug-in load order
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
bool mVerbose; bool mVerbose;
strmap mMergeMap; strmap mMergeMap;
std::vector<std::string> mMergeFallback; std::vector<std::string> mMergeFallback;

@ -59,7 +59,7 @@ int wmain(int argc, wchar_t *wargv[]) {
try try
{ {
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options"); bpo::options_description desc("Syntax: openmw-iniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description p_desc; bpo::positional_options_description p_desc;
desc.add_options() desc.add_options()
("help,h", "produce help message") ("help,h", "produce help message")
@ -93,8 +93,8 @@ int wmain(int argc, wchar_t *wargv[]) {
bpo::notify(vm); bpo::notify(vm);
std::string iniFile = vm["ini"].as<std::string>(); boost::filesystem::path iniFile(vm["ini"].as<std::string>());
std::string cfgFile = vm["cfg"].as<std::string>(); boost::filesystem::path cfgFile(vm["cfg"].as<std::string>());
// if no output is given, write back to cfg file // if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>()); std::string outputFile(vm["output"].as<std::string>());
@ -123,7 +123,7 @@ int wmain(int argc, wchar_t *wargv[]) {
importer.mergeFallback(cfg, ini); importer.mergeFallback(cfg, ini);
if(vm.count("game-files")) { if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini); importer.importGameFiles(cfg, ini, iniFile);
} }
if(!vm.count("no-archives")) { if(!vm.count("no-archives")) {

@ -4,8 +4,6 @@ set (OPENCS_SRC main.cpp
opencs_units (. editor) opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc opencs_units (model/doc
document operation saving documentmanager loader runner document operation saving documentmanager loader runner
) )
@ -41,7 +39,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referenceablecheck scriptcheck bodypartcheck birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
) )
@ -146,7 +144,7 @@ set (OPENCS_UI
${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui ${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui
) )
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR}) source_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32) if(WIN32)
set(QT_USE_QTMAIN TRUE) set(QT_USE_QTMAIN TRUE)
@ -169,12 +167,12 @@ qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${BULLET_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${BULLET_INCLUDE_DIRS})
if(APPLE) if(APPLE)
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/opencs.icns) set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns)
else() else()
set (OPENCS_MAC_ICON "") set (OPENCS_MAC_ICON "")
endif(APPLE) endif(APPLE)
add_executable(opencs add_executable(openmw-cs
MACOSX_BUNDLE MACOSX_BUNDLE
${OENGINE_BULLET} ${OENGINE_BULLET}
${OPENCS_SRC} ${OPENCS_SRC}
@ -185,10 +183,10 @@ add_executable(opencs
) )
if(APPLE) if(APPLE)
set_target_properties(opencs PROPERTIES set_target_properties(openmw-cs PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenCS" OUTPUT_NAME "OpenMW-CS"
MACOSX_BUNDLE_ICON_FILE "opencs.icns" MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
@ -199,7 +197,7 @@ if(APPLE)
MACOSX_PACKAGE_LOCATION Resources) MACOSX_PACKAGE_LOCATION Resources)
endif(APPLE) endif(APPLE)
target_link_libraries(opencs target_link_libraries(openmw-cs
${OGRE_LIBRARIES} ${OGRE_LIBRARIES}
${OGRE_Overlay_LIBRARIES} ${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS} ${OGRE_STATIC_PLUGINS}
@ -211,5 +209,5 @@ target_link_libraries(opencs
) )
if(APPLE) if(APPLE)
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif() endif()

@ -96,7 +96,7 @@ void CS::Editor::setupDataFiles (const Files::PathContainer& dataDirs)
std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig() std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfig()
{ {
boost::program_options::variables_map variables; boost::program_options::variables_map variables;
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options"); boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
desc.add_options() desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) ("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing())
@ -249,7 +249,7 @@ bool CS::Editor::makeIPCServer()
try try
{ {
mPid = boost::filesystem::temp_directory_path(); mPid = boost::filesystem::temp_directory_path();
mPid /= "opencs.pid"; mPid /= "openmw-cs.pid";
bool pidExists = boost::filesystem::exists(mPid); bool pidExists = boost::filesystem::exists(mPid);
mPidFile.open(mPid); mPidFile.open(mPid);

@ -78,7 +78,7 @@ int main(int argc, char *argv[])
application.setLibraryPaths(libraryPaths); application.setLibraryPaths(libraryPaths);
#endif #endif
application.setWindowIcon (QIcon (":./opencs.png")); application.setWindowIcon (QIcon (":./openmw-cs.png"));
CS::Editor editor (ogreInit); CS::Editor editor (ogreInit);

@ -0,0 +1,110 @@
#include "referencecheck.hpp"
#include <boost/lexical_cast.hpp>
CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Faction>& factions)
:
mReferences(references),
mReferencables(referencables),
mDataSet(referencables.getDataSet()),
mCells(cells),
mFactions(factions)
{
}
void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage);
if (record.isDeleted())
return;
const CSMWorld::CellRef& cellRef = record.get();
const CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Reference, cellRef.mId);
// Check for empty reference id
if (cellRef.mRefID.empty()) {
messages.push_back(std::make_pair(id, " is an empty reference"));
} else {
// Check for non existing referenced object
if (mReferencables.searchId(cellRef.mRefID) == -1) {
messages.push_back(std::make_pair(id, " is referencing non existing object " + cellRef.mRefID));
} else {
// Check if reference charge is valid for it's proper referenced type
CSMWorld::RefIdData::LocalIndex localIndex = mDataSet.searchId(cellRef.mRefID);
bool isLight = localIndex.second == CSMWorld::UniversalId::Type_Light;
if ((isLight && cellRef.mChargeFloat < -1) || (!isLight && cellRef.mChargeInt < -1)) {
std::string str = " has invalid charge ";
if (localIndex.second == CSMWorld::UniversalId::Type_Light)
str += boost::lexical_cast<std::string>(cellRef.mChargeFloat);
else
str += boost::lexical_cast<std::string>(cellRef.mChargeInt);
messages.push_back(std::make_pair(id, id.getId() + str));
}
}
}
// Check if referenced object is in valid cell
if (mCells.searchId(cellRef.mCell) == -1)
messages.push_back(std::make_pair(id, " is referencing object from non existing cell " + cellRef.mCell));
// If object have owner, check if that owner reference is valid
if (!cellRef.mOwner.empty() && mReferencables.searchId(cellRef.mOwner) == -1)
messages.push_back(std::make_pair(id, " has non existing owner " + cellRef.mOwner));
// If object have creature soul trapped, check if that creature reference is valid
if (!cellRef.mSoul.empty())
if (mReferencables.searchId(cellRef.mSoul) == -1)
messages.push_back(std::make_pair(id, " has non existing trapped soul " + cellRef.mSoul));
bool hasFaction = !cellRef.mFaction.empty();
// If object have faction, check if that faction reference is valid
if (hasFaction)
if (mFactions.searchId(cellRef.mFaction) == -1)
messages.push_back(std::make_pair(id, " has non existing faction " + cellRef.mFaction));
// Check item's faction rank
if (hasFaction && cellRef.mFactionRank < -1)
messages.push_back(std::make_pair(id, " has faction set but has invalid faction rank " + cellRef.mFactionRank));
else if (!hasFaction && cellRef.mFactionRank != -2)
messages.push_back(std::make_pair(id, " has invalid faction rank " + cellRef.mFactionRank));
// If door have destination cell, check if that reference is valid
if (!cellRef.mDestCell.empty())
if (mCells.searchId(cellRef.mDestCell) == -1)
messages.push_back(std::make_pair(id, " has non existing destination cell " + cellRef.mDestCell));
// Check if scale isn't negative
if (cellRef.mScale < 0)
{
std::string str = " has negative scale ";
str += boost::lexical_cast<std::string>(cellRef.mScale);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if enchantement points aren't negative or are at full (-1)
if (cellRef.mEnchantmentCharge < 0 && cellRef.mEnchantmentCharge != -1)
{
std::string str = " has negative enchantment points ";
str += boost::lexical_cast<std::string>(cellRef.mEnchantmentCharge);
messages.push_back(std::make_pair(id, id.getId() + str));
}
// Check if gold value isn't negative
if (cellRef.mGoldValue < 0)
{
std::string str = " has negative gold value ";
str += cellRef.mGoldValue;
messages.push_back(std::make_pair(id, id.getId() + str));
}
}
int CSMTools::ReferenceCheckStage::setup()
{
return mReferences.getSize();
}

@ -0,0 +1,29 @@
#ifndef CSM_TOOLS_REFERENCECHECK_H
#define CSM_TOOLS_REFERENCECHECK_H
#include "../doc/state.hpp"
#include "../doc/document.hpp"
namespace CSMTools
{
class ReferenceCheckStage : public CSMDoc::Stage
{
public:
ReferenceCheckStage (const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Faction>& factions);
virtual void perform(int stage, CSMDoc::Messages& messages);
virtual int setup();
private:
const CSMWorld::RefCollection& mReferences;
const CSMWorld::RefIdCollection& mReferencables;
const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
};
}
#endif // CSM_TOOLS_REFERENCECHECK_H

@ -23,6 +23,7 @@
#include "referenceablecheck.hpp" #include "referenceablecheck.hpp"
#include "scriptcheck.hpp" #include "scriptcheck.hpp"
#include "bodypartcheck.hpp" #include "bodypartcheck.hpp"
#include "referencecheck.hpp"
CSMDoc::Operation *CSMTools::Tools::get (int type) CSMDoc::Operation *CSMTools::Tools::get (int type)
{ {
@ -57,9 +58,6 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mandatoryIds.push_back ("GameHour"); mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month"); mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace"); mandatoryIds.push_back ("PCRace");
mandatoryIds.push_back ("PCVampire");
mandatoryIds.push_back ("PCWerewolf");
mandatoryIds.push_back ("PCYear");
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
@ -82,6 +80,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
mVerifier->appendStage (new ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
mVerifier->appendStage (new ScriptCheckStage (mDocument)); mVerifier->appendStage (new ScriptCheckStage (mDocument));
mVerifier->appendStage( mVerifier->appendStage(

@ -1,4 +1,5 @@
#include "cell.hpp"
#include "idcollection.hpp"
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include <sstream> #include <sstream>

@ -6,11 +6,12 @@
#include <components/esm/loadpgrd.hpp> #include <components/esm/loadpgrd.hpp>
#include "idcollection.hpp"
#include "cell.hpp"
namespace CSMWorld namespace CSMWorld
{ {
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Wrapper for Pathgrid record /// \brief Wrapper for Pathgrid record
/// ///
/// \attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used. /// \attention The mData.mX and mData.mY fields of the ESM::Pathgrid struct are not used.

@ -1,10 +1,17 @@
#ifndef CSM_WOLRD_SUBCOLLECTION_H #ifndef CSM_WOLRD_SUBCOLLECTION_H
#define CSM_WOLRD_SUBCOLLECTION_H #define CSM_WOLRD_SUBCOLLECTION_H
#include "idcollection.hpp" namespace ESM
{
class ESMReader;
}
namespace CSMWorld namespace CSMWorld
{ {
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Single type collection of top level records that are associated with cells /// \brief Single type collection of top level records that are associated with cells
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class SubCellCollection : public IdCollection<ESXRecordT, IdAccessorT> class SubCellCollection : public IdCollection<ESXRecordT, IdAccessorT>

@ -5,6 +5,7 @@
#include <string> #include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@ -72,8 +73,11 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{ {
boost::filesystem::path path (name.toUtf8().data()); boost::filesystem::path path (name.toUtf8().data());
bool isLegacyPath = (path.extension() == ".esm" || std::string extension = path.extension().string();
path.extension() == ".esp"); boost::algorithm::to_lower(extension);
bool isLegacyPath = (extension == ".esm" ||
extension == ".esp");
bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); bool isFilePathChanged = (path.parent_path().string() != mLocalData.string());

@ -96,7 +96,7 @@ QWidget *CSVDoc::StartupDialogue::createTools()
CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
{ {
setWindowTitle ("Open CS"); setWindowTitle ("OpenMW-CS");
QVBoxLayout *layout = new QVBoxLayout (this); QVBoxLayout *layout = new QVBoxLayout (this);

@ -9,7 +9,9 @@
#include <OgreVector3.h> #include <OgreVector3.h>
#ifndef Q_MOC_RUN
#include <components/terrain/terraingrid.hpp> #include <components/terrain/terraingrid.hpp>
#endif
#include "object.hpp" #include "object.hpp"

@ -3,7 +3,9 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#ifndef Q_MOC_RUN
#include <components/nifogre/ogrenifloader.hpp> #include <components/nifogre/ogrenifloader.hpp>
#endif
class QModelIndex; class QModelIndex;

@ -41,7 +41,7 @@ add_openmw_dir (mwgui
itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel controllers savegamedialog
recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview recharge mode videowidget backgroundimage itemwidget screenfader debugwindow spellmodel spellview
draganddrop draganddrop timeadvancer jailscreen
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue

@ -109,11 +109,14 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{ {
if (!paused) if (!paused)
{ {
// local scripts if (MWBase::Environment::get().getWorld()->getScriptsEnabled())
executeLocalScripts(); {
// local scripts
executeLocalScripts();
// global scripts // global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run(); MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
}
MWBase::Environment::get().getWorld()->markCellAsUnchanged(); MWBase::Environment::get().getWorld()->markCellAsUnchanged();
} }
@ -170,7 +173,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0) : mOgre (0)
, mFpsLevel(0)
, mVerboseScripts (false) , mVerboseScripts (false)
, mSkipMenu (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
@ -292,16 +294,10 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
else else
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed.");
// load user settings if they exist, otherwise just load the default settings as user settings // load user settings if they exist
const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg"; const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg";
if (boost::filesystem::exists(settingspath)) if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath); settings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault))
settings.loadUser(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadUser(globaldefault);
mFpsLevel = settings.getInt("fps", "HUD");
// load nif overrides // load nif overrides
NifOverrides::Overrides nifOverrides; NifOverrides::Overrides nifOverrides;
@ -349,6 +345,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "water");
addResourcesDirectory(mResDir / "shadows"); addResourcesDirectory(mResDir / "shadows");
addResourcesDirectory(mResDir / "materials");
OEngine::Render::WindowSettings windowSettings; OEngine::Render::WindowSettings windowSettings;
windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); windowSettings.fullscreen = settings.getBool("fullscreen", "Video");
@ -397,7 +394,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setInputManager (input); mEnvironment.setInputManager (input);
MWGui::WindowManager* window = new MWGui::WindowManager( MWGui::WindowManager* window = new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mExtensions, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap); mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mFallbackMap);
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
@ -598,11 +595,6 @@ void OMW::Engine::setSoundUsage(bool soundUsage)
mUseSound = soundUsage; mUseSound = soundUsage;
} }
void OMW::Engine::showFPS(int level)
{
mFpsLevel = level;
}
void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
{ {
mEncoding = encoding; mEncoding = encoding;

@ -71,7 +71,6 @@ namespace OMW
OEngine::Render::OgreRenderer *mOgre; OEngine::Render::OgreRenderer *mOgre;
std::string mCellName; std::string mCellName;
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
int mFpsLevel;
bool mVerboseScripts; bool mVerboseScripts;
bool mSkipMenu; bool mSkipMenu;
bool mUseSound; bool mUseSound;
@ -151,9 +150,6 @@ namespace OMW
*/ */
void addContentFile(const std::string& file); void addContentFile(const std::string& file);
/// Enable fps counter
void showFPS(int level);
/// Enable or disable verbose script output /// Enable or disable verbose script output
void setScriptsVerbosity(bool scriptsVerbosity); void setScriptsVerbosity(bool scriptsVerbosity);

@ -153,7 +153,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
->default_value(true), "enable script blacklisting") ->default_value(true), "enable script blacklisting")
("load-savegame", bpo::value<std::string>()->default_value(""), ("load-savegame", bpo::value<std::string>()->default_value(""),
"load a save game file on game startup") "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)")
("skip-menu", bpo::value<bool>()->implicit_value(true) ("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "skip main menu on game startup") ->default_value(false), "skip main menu on game startup")
@ -190,29 +190,23 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
bpo::store(valid_opts, variables); bpo::store(valid_opts, variables);
bpo::notify(variables); bpo::notify(variables);
bool run = true;
if (variables.count ("help")) if (variables.count ("help"))
{ {
std::cout << desc << std::endl; std::cout << desc << std::endl;
run = false; return false;
} }
if (variables.count ("version")) std::cout << "OpenMW version " << OPENMW_VERSION;
std::string rev = OPENMW_VERSION_COMMITHASH;
std::string tag = OPENMW_VERSION_TAGHASH;
if (!rev.empty() && !tag.empty())
{ {
std::cout << "OpenMW version " << OPENMW_VERSION << std::endl; rev = rev.substr(0, 10);
std::cout << " (revision " << rev << ")";
std::string rev = OPENMW_VERSION_COMMITHASH;
std::string tag = OPENMW_VERSION_TAGHASH;
if (!rev.empty() && !tag.empty())
{
rev = rev.substr(0, 10);
std::cout << "Revision " << rev << std::endl;
}
run = false;
} }
std::cout << std::endl;
if (!run) if (variables.count ("version"))
return false; return false;
cfgMgr.readConfiguration(variables, desc); cfgMgr.readConfiguration(variables, desc);
@ -396,12 +390,12 @@ int main(int argc, char**argv)
catch (std::exception &e) catch (std::exception &e)
{ {
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
if (isatty(fileno(stdin))) if (!isatty(fileno(stdin)))
std::cerr << "\nERROR: " << e.what() << std::endl;
else
#endif #endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL); SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
std::cerr << "\nERROR: " << e.what() << std::endl;
ret = 1; ret = 1;
} }

@ -1,5 +1,5 @@
#ifndef GAME_BASE_INVIRONMENT_H #ifndef GAME_BASE_ENVIRONMENT_H
#define GAME_BASE_INVIRONMENT_H #define GAME_BASE_ENVIRONMENT_H
namespace MWBase namespace MWBase
{ {

@ -2,8 +2,8 @@
#define GAME_MWBASE_INPUTMANAGER_H #define GAME_MWBASE_INPUTMANAGER_H
#include <string> #include <string>
#include <set>
#include <components/settings/settings.hpp> #include <vector>
namespace MWBase namespace MWBase
{ {
@ -29,7 +29,7 @@ namespace MWBase
virtual void changeInputMode(bool guiMode) = 0; virtual void changeInputMode(bool guiMode) = 0;
virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;
virtual void setDragDrop(bool dragDrop) = 0; virtual void setDragDrop(bool dragDrop) = 0;

@ -129,16 +129,15 @@ namespace MWBase
/// @return false if the attack was considered a "friendly hit" and forgiven /// @return false if the attack was considered a "friendly hit" and forgiven
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
/// Utility to check if taking this item is illegal and calling commitCrime if so /// Utility to check if taking this item is illegal and calling commitCrime if so
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; /// @param container The container the item is in; may be empty for an item in the world
virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,
int count) = 0;
/// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;
/// Attempt sleeping in a bed. If this is illegal, call commitCrime. /// Attempt sleeping in a bed. If this is illegal, call commitCrime.
/// @return was it illegal, and someone saw you doing it? /// @return was it illegal, and someone saw you doing it?
virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0; virtual bool sleepInBed (const MWWorld::Ptr& ptr, const MWWorld::Ptr& bed) = 0;
/// @return is \a ptr allowed to take/use \a item or is it a crime?
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) = 0;
enum PersuasionType enum PersuasionType
{ {
PT_Admire, PT_Admire,
@ -203,6 +202,15 @@ namespace MWBase
virtual void keepPlayerAlive() = 0; virtual void keepPlayerAlive() = 0;
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;
/// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).
/// <Owner, item count>
virtual std::vector<std::pair<std::string, int> > getStolenItemOwners(const std::string& itemid) = 0;
/// Has the player stolen this item from the given owner?
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
}; };
} }

@ -2,11 +2,9 @@
#define GAME_MWBASE_SOUNDMANAGER_H #define GAME_MWBASE_SOUNDMANAGER_H
#include <string> #include <string>
#include <set>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <components/settings/settings.hpp>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace Ogre namespace Ogre
@ -74,7 +72,7 @@ namespace MWBase
virtual ~SoundManager() {} virtual ~SoundManager() {}
virtual void processChangedSettings(const Settings::CategorySettingVector& settings) = 0; virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& settings) = 0;
virtual void stopMusic() = 0; virtual void stopMusic() = 0;
///< Stops music if it's playing ///< Stops music if it's playing

@ -1,19 +1,23 @@
#ifndef GAME_MWBASE_WINDOWMANAGER_H #ifndef GAME_MWBASE_WINDOWMANAGER_H
#define GAME_MWBASE_WINDOWMANAGER_H #define GAME_MWBASE_WINDOWMANAGER_H
#include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <components/settings/settings.hpp> #include "../mwgui/mode.hpp"
#include <components/translation/translation.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include "../mwmechanics/stat.hpp" namespace Loading
{
class Listener;
}
#include "../mwgui/mode.hpp" namespace Translation
{
class Storage;
}
namespace MyGUI namespace MyGUI
{ {
@ -35,6 +39,15 @@ namespace ESM
struct Class; struct Class;
class ESMReader; class ESMReader;
class ESMWriter; class ESMWriter;
struct CellId;
}
namespace MWMechanics
{
class AttributeValue;
template<typename T>
class DynamicStat;
class SkillValue;
} }
namespace MWWorld namespace MWWorld
@ -58,6 +71,7 @@ namespace MWGui
class ContainerWindow; class ContainerWindow;
class DialogueWindow; class DialogueWindow;
class WindowModal; class WindowModal;
class JailScreen;
enum ShowInDialogueMode { enum ShowInDialogueMode {
ShowInDialogueMode_IfPossible, ShowInDialogueMode_IfPossible,
@ -109,6 +123,8 @@ namespace MWBase
virtual void removeGuiMode (MWGui::GuiMode mode) = 0; virtual void removeGuiMode (MWGui::GuiMode mode) = 0;
///< can be anywhere in the stack ///< can be anywhere in the stack
virtual void goToJail(int days) = 0;
virtual void updatePlayer() = 0; virtual void updatePlayer() = 0;
virtual MWGui::GuiMode getMode() const = 0; virtual MWGui::GuiMode getMode() const = 0;
@ -266,7 +282,7 @@ namespace MWBase
*/ */
virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0; virtual std::string getGameSettingString(const std::string &id, const std::string &default_) = 0;
virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0; virtual void processChangedSettings(const std::set< std::pair<std::string, std::string> >& changed) = 0;
virtual void windowResized(int x, int y) = 0; virtual void windowResized(int x, int y) = 0;

@ -3,24 +3,23 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <components/settings/settings.hpp> #include <components/esm/cellid.hpp>
#include "../mwworld/globals.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace Ogre namespace Ogre
{ {
class Vector2; class Vector2;
class Vector3; class Vector3;
class Quaternion;
class Image;
} }
namespace OEngine namespace Loading
{ {
namespace Physic class Listener;
{
class PhysicEngine;
}
} }
namespace ESM namespace ESM
@ -33,7 +32,6 @@ namespace ESM
struct Potion; struct Potion;
struct Spell; struct Spell;
struct NPC; struct NPC;
struct CellId;
struct Armor; struct Armor;
struct Weapon; struct Weapon;
struct Clothing; struct Clothing;
@ -92,6 +90,7 @@ namespace MWBase
{ {
std::string name; std::string name;
float x, y; // world position float x, y; // world position
ESM::CellId dest;
}; };
World() {} World() {}
@ -269,6 +268,8 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0; virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range ///< Return pointer to the object the player is looking at, if it is within activation range
virtual float getMaxActivationDistance() = 0;
/// Returns a pointer to the object the provided object would hit (if within the /// Returns a pointer to the object the provided object would hit (if within the
/// specified distance), and the point where the hit occurs. This will attempt to /// specified distance), and the point where the hit occurs. This will attempt to
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
@ -388,7 +389,7 @@ namespace MWBase
virtual bool canPlaceObject (float cursorX, float cursorY) = 0; virtual bool canPlaceObject (float cursorX, float cursorY) = 0;
///< @return true if it is possible to place on object at specified cursor location ///< @return true if it is possible to place on object at specified cursor location
virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; virtual void processChangedSettings (const std::set< std::pair<std::string, std::string> >& settings) = 0;
virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0;
virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0;
@ -453,6 +454,7 @@ namespace MWBase
/// \todo Probably shouldn't be here /// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0; virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
virtual void reattachPlayerCamera() = 0;
/// \todo this does not belong here /// \todo this does not belong here
virtual void frameStarted (float dt, bool paused) = 0; virtual void frameStarted (float dt, bool paused) = 0;
@ -489,6 +491,9 @@ namespace MWBase
virtual bool toggleGodMode() = 0; virtual bool toggleGodMode() = 0;
virtual bool toggleScripts() = 0;
virtual bool getScriptsEnabled() const = 0;
/** /**
* @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met. * @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.
* @param actor * @param actor
@ -548,7 +553,7 @@ namespace MWBase
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0; virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName) = 0; const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) = 0;
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;

@ -59,8 +59,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>(); ptr.get<ESM::Container>();
// setting ownership not needed, since taking items from a container inherits the
// container's owner automatically
data->mContainerStore.fill( data->mContainerStore.fill(
ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore()); ref->mBase->mInventory, "");
// store // store
ptr.getRefData().setCustomData (data.release()); ptr.getRefData().setCustomData (data.release());
@ -82,7 +84,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>(); MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank());
// setting ownership not needed, since taking items from a container inherits the
// container's owner automatically
store.restock(list, ptr, "");
} }
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const void Container::insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const

@ -139,8 +139,7 @@ namespace MWClass
// store // store
ptr.getRefData().setCustomData(data.release()); ptr.getRefData().setCustomData(data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1, getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr));
MWBase::Environment::get().getWorld()->getStore());
if (ref->mBase->mFlags & ESM::Creature::Weapon) if (ref->mBase->mFlags & ESM::Creature::Weapon)
getInventoryStore(ptr).autoEquip(ptr); getInventoryStore(ptr).autoEquip(ptr);
@ -228,18 +227,7 @@ namespace MWClass
weapon = *weaponslot; weapon = *weaponslot;
} }
// Reduce fatigue MWMechanics::applyFatigueLoss(ptr, weapon);
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
stats.setFatigue(fatigue);
// TODO: where is the distance defined? // TODO: where is the distance defined?
float dist = 200.f; float dist = 200.f;
@ -897,7 +885,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); store.restock(list, ptr, ptr.getCellRef().getRefId());
} }
int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const

@ -52,7 +52,7 @@ namespace MWClass
registerClass (typeid (ESM::CreatureLevList).name(), instance); registerClass (typeid (ESM::CreatureLevList).name(), instance);
} }
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const
{ {
ensureCustomData(ptr); ensureCustomData(ptr);

@ -20,7 +20,7 @@ namespace MWClass
static void registerSelf(); static void registerSelf();
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -93,9 +93,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Door> *ref = MWWorld::LiveCellRef<ESM::Door> *ref =
ptr.get<ESM::Door>(); ptr.get<ESM::Door>();
if (ptr.getCellRef().getTeleport() && !ptr.getCellRef().getDestCell().empty()) // TODO doors that lead to exteriors
return ptr.getCellRef().getDestCell();
return ref->mBase->mName; return ref->mBase->mName;
} }

@ -12,6 +12,7 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp" #include "../mwworld/actiontake.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp" #include "../mwworld/physicssystem.hpp"
#include "../mwworld/manualref.hpp" #include "../mwworld/manualref.hpp"
#include "../mwworld/nullaction.hpp" #include "../mwworld/nullaction.hpp"

@ -296,25 +296,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
// NPC stats
if (!ref->mBase->mFaction.empty())
{
std::string faction = ref->mBase->mFaction;
if (const ESM::Faction* fact = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().search(faction))
{
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt52.mRank);
}
else
{
data->mNpcStats.setFactionRank(fact->mId, (int)ref->mBase->mNpdt12.mRank);
}
}
else
std::cerr << "Warning: ignoring nonexistent faction '" << faction << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
}
// creature stats // creature stats
int gold=0; int gold=0;
if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) if(ref->mBase->mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
@ -371,13 +352,13 @@ namespace MWClass
std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl; std::cerr << "Warning: ignoring nonexistent race power '" << *iter << "' on NPC '" << ref->mBase->mId << "'" << std::endl;
} }
if (data->mNpcStats.getFactionRanks().size()) if (!ref->mBase->mFaction.empty())
{ {
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt(); .find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepLevMod")->getInt(); .find("iAutoRepLevMod")->getInt();
int rank = data->mNpcStats.getFactionRanks().begin()->second; int rank = ref->mBase->getFactionRank();
data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1)); data->mNpcStats.setReputation(iAutoRepFacMod * (rank+1) + iAutoRepLevMod * (data->mNpcStats.getLevel()-1));
} }
@ -403,8 +384,8 @@ namespace MWClass
} }
// inventory // inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1, // setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
MWBase::Environment::get().getWorld()->getStore()); data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr));
data->mNpcStats.setGoldPool(gold); data->mNpcStats.setGoldPool(gold);
@ -505,18 +486,7 @@ namespace MWClass
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name()) if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr(); weapon = MWWorld::Ptr();
// Reduce fatigue MWMechanics::applyFatigueLoss(ptr, weapon);
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
const float normalizedEncumbrance = getNormalizedEncumbrance(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * getNpcStats(ptr).getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
getCreatureStats(ptr).setFatigue(fatigue);
const float fCombatDistance = store.find("fCombatDistance")->getFloat(); const float fCombatDistance = store.find("fCombatDistance")->getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ? float dist = fCombatDistance * (!weapon.isEmpty() ?
@ -1358,7 +1328,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
const ESM::InventoryList& list = ref->mBase->mInventory; const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1); store.restock(list, ptr, ptr.getCellRef().getRefId());
} }
int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const
@ -1371,4 +1341,16 @@ namespace MWClass
{ {
return true; return true;
} }
std::string Npc::getPrimaryFaction (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->mFaction;
}
int Npc::getPrimaryFactionRank (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank();
}
} }

@ -189,6 +189,9 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr& ptr) const; virtual void restock (const MWWorld::Ptr& ptr) const;
virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const; virtual int getBaseFightRating (const MWWorld::Ptr& ptr) const;
virtual std::string getPrimaryFaction(const MWWorld::Ptr &ptr) const;
virtual int getPrimaryFactionRank(const MWWorld::Ptr &ptr) const;
}; };
} }

@ -189,9 +189,8 @@ namespace MWClass
{ {
return std::string("Item Weapon Longblade Up"); return std::string("Item Weapon Longblade Up");
} }
// Shortblade and thrown weapons // Shortblade
// thrown weapons may not be entirely correct if (type == 0)
if (type == 0 || type == 11)
{ {
return std::string("Item Weapon Shortblade Up"); return std::string("Item Weapon Shortblade Up");
} }
@ -200,8 +199,8 @@ namespace MWClass
{ {
return std::string("Item Weapon Spear Up"); return std::string("Item Weapon Spear Up");
} }
// Blunts and Axes // Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{ {
return std::string("Item Weapon Blunt Up"); return std::string("Item Weapon Blunt Up");
} }
@ -235,9 +234,8 @@ namespace MWClass
{ {
return std::string("Item Weapon Longblade Down"); return std::string("Item Weapon Longblade Down");
} }
// Shortblade and thrown weapons // Shortblade
// thrown weapons may not be entirely correct if (type == 0)
if (type == 0 || type == 11)
{ {
return std::string("Item Weapon Shortblade Down"); return std::string("Item Weapon Shortblade Down");
} }
@ -246,8 +244,8 @@ namespace MWClass
{ {
return std::string("Item Weapon Spear Down"); return std::string("Item Weapon Spear Down");
} }
// Blunts and Axes // Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8) if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{ {
return std::string("Item Weapon Blunt Down"); return std::string("Item Weapon Blunt Down");
} }

@ -5,6 +5,7 @@
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <list>
#include <components/esm/loaddial.hpp> #include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp> #include <components/esm/loadinfo.hpp>
@ -78,7 +79,7 @@ namespace MWDialogue
void DialogueManager::addTopic (const std::string& topic) void DialogueManager::addTopic (const std::string& topic)
{ {
mKnownTopics[Misc::StringUtils::lowerCase(topic)] = true; mKnownTopics.insert( Misc::StringUtils::lowerCase(topic) );
} }
void DialogueManager::parseText (const std::string& text) void DialogueManager::parseText (const std::string& text)
@ -102,8 +103,8 @@ namespace MWDialogue
if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation()) if (tok->isImplicitKeyword() && mTranslationDataStorage.hasTranslation())
continue; continue;
if (std::find(mActorKnownTopics.begin(), mActorKnownTopics.end(), topicId) != mActorKnownTopics.end()) if (mActorKnownTopics.count( topicId ))
mKnownTopics[topicId] = true; mKnownTopics.insert( topicId );
} }
updateTopics(); updateTopics();
@ -341,10 +342,10 @@ namespace MWDialogue
if (filter.responseAvailable (*iter)) if (filter.responseAvailable (*iter))
{ {
std::string lower = Misc::StringUtils::lowerCase(iter->mId); std::string lower = Misc::StringUtils::lowerCase(iter->mId);
mActorKnownTopics.push_back (lower); mActorKnownTopics.insert (lower);
//does the player know the topic? //does the player know the topic?
if (mKnownTopics.find (lower) != mKnownTopics.end()) if (mKnownTopics.count(lower))
{ {
keywordList.push_back (iter->mId); keywordList.push_back (iter->mId);
} }
@ -635,10 +636,11 @@ namespace MWDialogue
{ {
ESM::DialogueState state; ESM::DialogueState state;
for (std::map<std::string, bool>::const_iterator iter (mKnownTopics.begin()); for (std::set<std::string>::const_iterator iter (mKnownTopics.begin());
iter!=mKnownTopics.end(); ++iter) iter!=mKnownTopics.end(); ++iter)
if (iter->second) {
state.mKnownTopics.push_back (iter->first); state.mKnownTopics.push_back (*iter);
}
state.mChangedFactionReaction = mChangedFactionReaction; state.mChangedFactionReaction = mChangedFactionReaction;
@ -659,7 +661,7 @@ namespace MWDialogue
for (std::vector<std::string>::const_iterator iter (state.mKnownTopics.begin()); for (std::vector<std::string>::const_iterator iter (state.mKnownTopics.begin());
iter!=state.mKnownTopics.end(); ++iter) iter!=state.mKnownTopics.end(); ++iter)
if (store.get<ESM::Dialogue>().search (*iter)) if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (std::make_pair (*iter, true)); mKnownTopics.insert (*iter);
mChangedFactionReaction = state.mChangedFactionReaction; mChangedFactionReaction = state.mChangedFactionReaction;
} }

@ -4,7 +4,7 @@
#include "../mwbase/dialoguemanager.hpp" #include "../mwbase/dialoguemanager.hpp"
#include <map> #include <map>
#include <list> #include <set>
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/translation/translation.hpp> #include <components/translation/translation.hpp>
@ -23,13 +23,13 @@ namespace MWDialogue
class DialogueManager : public MWBase::DialogueManager class DialogueManager : public MWBase::DialogueManager
{ {
std::map<std::string, ESM::Dialogue> mDialogueMap; std::map<std::string, ESM::Dialogue> mDialogueMap;
std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows. std::set<std::string> mKnownTopics;// Those are the topics the player knows.
// Modified faction reactions. <Faction1, <Faction2, Difference> > // Modified faction reactions. <Faction1, <Faction2, Difference> >
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap; typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
ModFactionReactionMap mChangedFactionReaction; ModFactionReactionMap mChangedFactionReaction;
std::list<std::string> mActorKnownTopics; std::set<std::string> mActorKnownTopics;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext; MWScript::CompilerContext mCompilerContext;

@ -67,14 +67,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
if (isCreature) if (isCreature)
return false; return false;
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor); if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction))
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end())
return false; return false;
// check rank // check rank
if (iter->second < info.mData.mRank) if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false; return false;
} }
else if (info.mData.mRank != -1) else if (info.mData.mRank != -1)
@ -83,13 +80,8 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
return false; return false;
// Rank requirement, but no faction given. Use the actor's faction, if there is one. // Rank requirement, but no faction given. Use the actor's faction, if there is one.
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
if (!stats.getFactionRanks().size())
return false;
// check rank // check rank
if (stats.getFactionRanks().begin()->second < info.mData.mRank) if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false; return false;
} }
@ -336,12 +328,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_RankRequirement: case SelectWrapper::Function_RankRequirement:
{ {
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) std::string faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty())
return 0; return 0;
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
int rank = getFactionRank (player, faction); int rank = getFactionRank (player, faction);
if (rank>=9) if (rank>=9)
@ -376,15 +366,14 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_FactionRankDiff: case SelectWrapper::Function_FactionRankDiff:
{ {
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) std::string faction = mActor.getClass().getPrimaryFaction(mActor);
return 0;
const std::pair<std::string, int> faction =
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
int rank = getFactionRank (player, faction.first); if (faction.empty())
return 0;
return rank-faction.second; int rank = getFactionRank (player, faction);
int npcRank = mActor.getClass().getPrimaryFactionRank(mActor);
return rank-npcRank;
} }
case SelectWrapper::Function_WerewolfKills: case SelectWrapper::Function_WerewolfKills:
@ -396,11 +385,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{ {
bool low = select.getFunction()==SelectWrapper::Function_RankLow; bool low = select.getFunction()==SelectWrapper::Function_RankLow;
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) std::string factionId = mActor.getClass().getPrimaryFaction(mActor);
return 0;
std::string factionId = if (factionId.empty())
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first; return 0;
int value = 0; int value = 0;
@ -454,7 +442,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotFaction: case SelectWrapper::Function_NotFaction:
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mFaction, select.getName()); return !Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), select.getName());
case SelectWrapper::Function_NotClass: case SelectWrapper::Function_NotClass:
@ -494,8 +482,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameFaction: case SelectWrapper::Function_SameFaction:
return mActor.getClass().getNpcStats (mActor).isSameFaction ( return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor));
player.getClass().getNpcStats (player));
case SelectWrapper::Function_PcCommonDisease: case SelectWrapper::Function_PcCommonDisease:
@ -512,11 +499,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_PcExpelled: case SelectWrapper::Function_PcExpelled:
{ {
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty()) std::string faction = mActor.getClass().getPrimaryFaction(mActor);
return false;
std::string faction = if (faction.empty())
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first; return false;
return player.getClass().getNpcStats(player).getExpelled(faction); return player.getClass().getNpcStats(player).getExpelled(faction);
} }
@ -561,7 +547,7 @@ int MWDialogue::Filter::getFactionRank (const MWWorld::Ptr& actor, const std::st
{ {
MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor); MWMechanics::NpcStats& stats = actor.getClass().getNpcStats (actor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (factionId); std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase(factionId));
if (iter==stats.getFactionRanks().end()) if (iter==stats.getFactionRanks().end())
return -1; return -1;

@ -89,7 +89,7 @@ namespace MWDialogue
for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mData.mDisposition==index) /// \todo cleanup info structure if (iter->mData.mJournalIndex==index)
{ {
return iter->mId; return iter->mId;
} }

@ -259,7 +259,12 @@ namespace MWDialogue
record.load (reader); record.load (reader);
if (isThere (record.mTopic)) if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record)); {
std::pair<TQuestContainer::iterator, bool> result = mQuests.insert (std::make_pair (record.mTopic, record));
// reapply quest index, this is to handle users upgrading from only
// Morrowind.esm (no quest states) to Morrowind.esm + Tribunal.esm
result.first->second.setIndex(record.mState);
}
} }
} }
} }

@ -161,7 +161,7 @@ public:
while (matches.size()) while (matches.size())
{ {
int longestKeywordSize = 0; int longestKeywordSize = 0;
typename std::vector<Match>::iterator longestKeyword; typename std::vector<Match>::iterator longestKeyword = matches.begin();
for (typename std::vector<Match>::iterator it = matches.begin(); it != matches.end(); ++it) for (typename std::vector<Match>::iterator it = matches.begin(); it != matches.end(); ++it)
{ {
int size = it->mEnd - it->mBeg; int size = it->mEnd - it->mBeg;

@ -75,7 +75,7 @@ namespace MWDialogue
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == entry.mInfoId) if (iter->mId == entry.mInfoId)
{ {
index = iter->mData.mDisposition; /// \todo cleanup info structure index = iter->mData.mJournalIndex;
break; break;
} }

@ -1,6 +1,9 @@
#include "scripttest.hpp" #include "scripttest.hpp"
#include <iostream>
#include "../mwworld/manualref.hpp" #include "../mwworld/manualref.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"

@ -5,14 +5,14 @@
namespace MWGui namespace MWGui
{ {
void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool correct) void BackgroundImage::setBackgroundImage (const std::string& image, bool fixedRatio, bool stretch)
{ {
if (mChild) if (mChild)
{ {
MyGUI::Gui::getInstance().destroyWidget(mChild); MyGUI::Gui::getInstance().destroyWidget(mChild);
mChild = NULL; mChild = NULL;
} }
if (correct) if (!stretch)
{ {
setImageTexture("black.png"); setImageTexture("black.png");

@ -18,9 +18,9 @@ namespace MWGui
/** /**
* @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions * @param fixedRatio Use a fixed ratio of 4:3, regardless of the image dimensions
* @param correct Add black bars? * @param stretch Stretch to fill the whole screen, or add black bars?
*/ */
void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool correct=true); void setBackgroundImage (const std::string& image, bool fixedRatio=true, bool stretch=true);
virtual void setSize (const MyGUI::IntSize &_value); virtual void setSize (const MyGUI::IntSize &_value);
virtual void setCoord (const MyGUI::IntCoord &_value); virtual void setCoord (const MyGUI::IntCoord &_value);

@ -195,8 +195,20 @@ struct TypesetBookImpl : TypesetBook
struct TypesetBookImpl::Typesetter : BookTypesetter struct TypesetBookImpl::Typesetter : BookTypesetter
{ {
struct PartialText {
StyleImpl *mStyle;
Utf8Stream::Point mBegin;
Utf8Stream::Point mEnd;
int mWidth;
PartialText( StyleImpl *style, Utf8Stream::Point begin, Utf8Stream::Point end, int width) :
mStyle(style), mBegin(begin), mEnd(end), mWidth(width)
{}
};
typedef TypesetBookImpl Book; typedef TypesetBookImpl Book;
typedef boost::shared_ptr <Book> BookPtr; typedef boost::shared_ptr <Book> BookPtr;
typedef std::vector<PartialText>::const_iterator PartialTextConstIterator;
int mPageWidth; int mPageWidth;
int mPageHeight; int mPageHeight;
@ -207,6 +219,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
Run * mRun; Run * mRun;
std::vector <Alignment> mSectionAlignment; std::vector <Alignment> mSectionAlignment;
std::vector <PartialText> mPartialWhitespace;
std::vector <PartialText> mPartialWord;
Book::Content const * mCurrentContent; Book::Content const * mCurrentContent;
Alignment mCurrentAlignment; Alignment mCurrentAlignment;
@ -273,6 +287,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
intptr_t addContent (Utf8Span text, bool select) intptr_t addContent (Utf8Span text, bool select)
{ {
add_partial_text();
Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second)); Contents::iterator i = mBook->mContents.insert (mBook->mContents.end (), Content (text.first, text.second));
if (select) if (select)
@ -283,6 +299,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
void selectContent (intptr_t contentHandle) void selectContent (intptr_t contentHandle)
{ {
add_partial_text();
mCurrentContent = reinterpret_cast <Content const *> (contentHandle); mCurrentContent = reinterpret_cast <Content const *> (contentHandle);
} }
@ -302,12 +320,16 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
{ {
assert (margin == 0); //TODO: figure out proper behavior here... assert (margin == 0); //TODO: figure out proper behavior here...
add_partial_text();
mRun = NULL; mRun = NULL;
mLine = NULL; mLine = NULL;
} }
void sectionBreak (float margin) void sectionBreak (float margin)
{ {
add_partial_text();
if (mBook->mSections.size () > 0) if (mBook->mSections.size () > 0)
{ {
mRun = NULL; mRun = NULL;
@ -321,6 +343,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
void setSectionAlignment (Alignment sectionAlignment) void setSectionAlignment (Alignment sectionAlignment)
{ {
add_partial_text();
if (mSection != NULL) if (mSection != NULL)
mSectionAlignment.back () = sectionAlignment; mSectionAlignment.back () = sectionAlignment;
mCurrentAlignment = sectionAlignment; mCurrentAlignment = sectionAlignment;
@ -331,6 +355,8 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
int curPageStart = 0; int curPageStart = 0;
int curPageStop = 0; int curPageStop = 0;
add_partial_text();
std::vector <Alignment>::iterator sa = mSectionAlignment.begin (); std::vector <Alignment>::iterator sa = mSectionAlignment.begin ();
for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa) for (Sections::iterator i = mBook->mSections.begin (); i != mBook->mSections.end (); ++i, ++sa)
{ {
@ -415,23 +441,23 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end) void writeImpl (StyleImpl * style, Utf8Stream::Point _begin, Utf8Stream::Point _end)
{ {
int line_height = style->mFont->getDefaultHeight ();
Utf8Stream stream (_begin, _end); Utf8Stream stream (_begin, _end);
while (!stream.eof ()) while (!stream.eof ())
{ {
if (ucsLineBreak (stream.peek ())) if (ucsLineBreak (stream.peek ()))
{ {
add_partial_text();
stream.consume (); stream.consume ();
mLine = NULL, mRun = NULL; mLine = NULL, mRun = NULL;
continue; continue;
} }
if (ucsBreakingSpace (stream.peek ()) && !mPartialWord.empty())
add_partial_text();
int word_width = 0; int word_width = 0;
int word_height = 0;
int space_width = 0; int space_width = 0;
int character_count = 0;
Utf8Stream::Point lead = stream.current (); Utf8Stream::Point lead = stream.current ();
@ -450,8 +476,6 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ()); MyGUI::GlyphInfo* gi = style->mFont->getGlyphInfo (stream.peek ());
if (gi) if (gi)
word_width += gi->advance + gi->bearingX; word_width += gi->advance + gi->bearingX;
word_height = line_height;
++character_count;
stream.consume (); stream.consume ();
} }
@ -460,21 +484,57 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
if (lead == extent) if (lead == extent)
break; break;
int left = mLine ? mLine->mRect.right : 0; if ( lead != origin )
mPartialWhitespace.push_back (PartialText (style, lead, origin, space_width));
if ( origin != extent )
mPartialWord.push_back (PartialText (style, origin, extent, word_width));
}
}
if (left + space_width + word_width > mPageWidth) void add_partial_text ()
{ {
mLine = NULL, mRun = NULL; if (mPartialWhitespace.empty() && mPartialWord.empty())
return;
append_run (style, origin, extent, extent - origin, word_width, mBook->mRect.bottom + word_height); int space_width = 0;
} int word_width = 0;
else
for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i)
space_width += i->mWidth;
for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i)
word_width += i->mWidth;
int left = mLine ? mLine->mRect.right : 0;
if (left + space_width + word_width > mPageWidth)
{
mLine = NULL, mRun = NULL, left = 0;
}
else
{
for (PartialTextConstIterator i = mPartialWhitespace.begin (); i != mPartialWhitespace.end (); ++i)
{ {
int top = mLine ? mLine->mRect.top : mBook->mRect.bottom; int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;
int line_height = i->mStyle->mFont->getDefaultHeight ();
append_run ( i->mStyle, i->mBegin, i->mEnd, 0, left + i->mWidth, top + line_height);
append_run (style, lead, extent, extent - origin, left + space_width + word_width, top + word_height); left = mLine->mRect.right;
} }
} }
for (PartialTextConstIterator i = mPartialWord.begin (); i != mPartialWord.end (); ++i)
{
int top = mLine ? mLine->mRect.top : mBook->mRect.bottom;
int line_height = i->mStyle->mFont->getDefaultHeight ();
append_run (i->mStyle, i->mBegin, i->mEnd, i->mEnd - i->mBegin, left + i->mWidth, top + line_height);
left = mLine->mRect.right;
}
mPartialWhitespace.clear();
mPartialWord.clear();
} }
void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom) void append_run (StyleImpl * style, Utf8Stream::Point begin, Utf8Stream::Point end, int pc, int right, int bottom)

@ -3,6 +3,22 @@
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
namespace
{
void modifyProfit(const MWWorld::Ptr& actor, int diff)
{
std::string script = actor.getClass().getScript(actor);
if (!script.empty())
{
int profit = actor.getRefData().getLocals().getIntVar(script, "minimumprofit");
profit += diff;
actor.getRefData().getLocals().setVarByInt(script, "minimumprofit", profit);
}
}
}
namespace MWGui namespace MWGui
{ {
CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor) CompanionItemModel::CompanionItemModel(const MWWorld::Ptr &actor)
@ -12,23 +28,25 @@ namespace MWGui
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
{ {
if (mActor.getClass().isNpc()) if (hasProfit(mActor))
{ modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(item.mBase.getClass().getValue(item.mBase) * count);
}
return InventoryItemModel::copyItem(item, count, setNewOwner); return InventoryItemModel::copyItem(item, count, setNewOwner);
} }
void CompanionItemModel::removeItem (const ItemStack& item, size_t count) void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
{ {
if (mActor.getClass().isNpc()) if (hasProfit(mActor))
{ modifyProfit(mActor, -item.mBase.getClass().getValue(item.mBase) * count);
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count);
}
InventoryItemModel::removeItem(item, count); InventoryItemModel::removeItem(item, count);
} }
bool CompanionItemModel::hasProfit(const MWWorld::Ptr &actor)
{
std::string script = actor.getClass().getScript(actor);
if (script.empty())
return false;
return actor.getRefData().getLocals().hasVar(script, "minimumprofit");
}
} }

@ -15,6 +15,8 @@ namespace MWGui
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner); virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
bool hasProfit(const MWWorld::Ptr& actor);
}; };
} }

@ -17,6 +17,21 @@
#include "draganddrop.hpp" #include "draganddrop.hpp"
#include "countdialog.hpp" #include "countdialog.hpp"
namespace
{
int getProfit(const MWWorld::Ptr& actor)
{
std::string script = actor.getClass().getScript(actor);
if (!script.empty())
{
return actor.getRefData().getLocals().getIntVar(script, "minimumprofit");
}
return 0;
}
}
namespace MWGui namespace MWGui
{ {
@ -116,13 +131,12 @@ void CompanionWindow::updateEncumbranceBar()
float encumbrance = mPtr.getClass().getEncumbrance(mPtr); float encumbrance = mPtr.getClass().getEncumbrance(mPtr);
mEncumbranceBar->setValue(encumbrance, capacity); mEncumbranceBar->setValue(encumbrance, capacity);
if (mPtr.getTypeName() != typeid(ESM::NPC).name()) if (mModel && mModel->hasProfit(mPtr))
mProfitLabel->setCaption("");
else
{ {
MWMechanics::NpcStats& stats = mPtr.getClass().getNpcStats(mPtr); mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(getProfit(mPtr)));
mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(stats.getProfit()));
} }
else
mProfitLabel->setCaption("");
} }
void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender) void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
@ -132,7 +146,7 @@ void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
void CompanionWindow::exit() void CompanionWindow::exit()
{ {
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && mPtr.getClass().getNpcStats(mPtr).getProfit() < 0) if (mModel && mModel->hasProfit(mPtr) && getProfit(mPtr) < 0)
{ {
std::vector<std::string> buttons; std::vector<std::string> buttons;
buttons.push_back("#{sCompanionWarningButtonOne}"); buttons.push_back("#{sCompanionWarningButtonOne}");
@ -148,9 +162,6 @@ void CompanionWindow::onMessageBoxButtonClicked(int button)
{ {
if (button == 0) if (button == 0)
{ {
mPtr.getRefData().getLocals().setVarByInt(mPtr.getClass().getScript(mPtr),
"minimumprofit", mPtr.getClass().getNpcStats(mPtr).getProfit());
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
// Important for Calvus' contract script to work properly // Important for Calvus' contract script to work properly
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);

@ -290,7 +290,8 @@ namespace MWGui
// Add the command to the history, and set the current pointer to // Add the command to the history, and set the current pointer to
// the end of the list // the end of the list
mCommandHistory.push_back(cm); if (mCommandHistory.empty() || mCommandHistory.back() != cm)
mCommandHistory.push_back(cm);
mCurrent = mCommandHistory.end(); mCurrent = mCommandHistory.end();
mEditString.clear(); mEditString.clear();

@ -154,8 +154,6 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model.
setTitle(container.getClass().getName(container)); setTitle(container.getClass().getName(container));
} }
@ -287,7 +285,7 @@ namespace MWGui
if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead()) if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead())
return true; return true;
else else
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, mPtr, count);
} }
return true; return true;
} }

@ -339,7 +339,8 @@ namespace MWGui
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
{ {
MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem(); MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();
if (Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), mPtr.getCellRef().getRefId())) if (MWBase::Environment::get().getMechanicsManager()->isItemStolenFrom(item.getCellRef().getRefId(),
mPtr.getCellRef().getRefId()))
{ {
std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString(); std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString();
if (msg.find("%s") != std::string::npos) if (msg.find("%s") != std::string::npos)

@ -10,6 +10,7 @@
#include <MyGUI_ScrollView.h> #include <MyGUI_ScrollView.h>
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -182,6 +183,10 @@ namespace MWGui
HUD::~HUD() HUD::~HUD()
{ {
mMainWidget->eventMouseLostFocus.clear();
mMainWidget->eventMouseMove.clear();
mMainWidget->eventMouseButtonClick.clear();
delete mSpellIcons; delete mSpellIcons;
} }
@ -666,4 +671,14 @@ namespace MWGui
mEnemyHealthTimer = -1; mEnemyHealthTimer = -1;
} }
void HUD::customMarkerCreated(MyGUI::Widget *marker)
{
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);
}
void HUD::doorMarkerCreated(MyGUI::Widget *marker)
{
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);
}
} }

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

Loading…
Cancel
Save