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

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

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

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

@ -6,4 +6,4 @@ export CC=clang
brew tap openmw/openmw
brew update
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)
# 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)
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
@ -12,7 +19,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 34)
set(OPENMW_VERSION_MINOR 35)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_VERSION_COMMITHASH "")
@ -374,10 +381,8 @@ configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
"${OpenMW_BINARY_DIR}/openmw.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-mimeinfo.xml
"${OpenMW_BINARY_DIR}/openmw-mimeinfo.xml")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.desktop
"${OpenMW_BINARY_DIR}/opencs.desktop")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
endif()
# 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)
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)
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)
IF(NOT WIN32 AND NOT APPLE)
# Linux building
# Paths
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(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")
@ -411,7 +422,7 @@ IF(NOT WIN32 AND NOT APPLE)
# Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL)
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}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-iniimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-essimporter" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESSIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-cs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
IF(BUILD_NIFTEST)
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 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-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}" 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_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
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_SOURCE_DIR}/files/opencs/opencs.png" DESTINATION "${ICONDIR}" 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/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
ENDIF(BUILD_OPENCS)
# 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}/transparency-overrides.cfg" DESTINATION "${SYSCONFDIR}" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ 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}/gamecontrollerdb.txt" 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}" 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}" COMPONENT "openmw")
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)
# 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")
ENDIF(NOT WIN32 AND NOT APPLE)
@ -481,16 +492,16 @@ if(WIN32)
DESTINATION ".")
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)
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)
IF(BUILD_ESSIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
ENDIF(BUILD_ESSIMPORTER)
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 ".")
ENDIF(BUILD_OPENCS)
IF(BUILD_WIZARD)
@ -511,10 +522,10 @@ if(WIN32)
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
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)
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)
IF(BUILD_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_HELP_LINK "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_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
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)'
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'
4315 # undocumented, 'this' pointer for member might not be aligned (OgreMemoryStlAllocator.h)
# caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
@ -674,6 +686,7 @@ if (WIN32)
# OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter)
4101 # Unreferenced local variable (-Wunused-variable)
4127 # Conditional expression is constant
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)
@ -695,16 +708,18 @@ if (WIN32)
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
set(SHINY_WARNINGS "${WARNINGS} /wd4245")
set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}")
# there's an unreferenced local variable in the ogre platform, suppress it
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${SHINY_OGRE_WARNINGS} ${MT_BUILD}")
set_target_properties(shiny.OgrePlatform 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
set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_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)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_LAUNCHER)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_BSATOOL)
@ -719,10 +734,13 @@ if (WIN32)
if (BUILD_OPENCS)
# QT triggers an informational warning that the object layout may differ when compiled with /vd2
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)
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_ESSIMPORTER)
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(MSVC)
@ -767,7 +785,7 @@ if (APPLE)
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(ABSOLUTE_PLUGINS "")

@ -1,14 +1,12 @@
OpenMW
======
[![Build Status](https://travis-ci.org/OpenMW/openmw.svg?branch=coverity_scan)](https://travis-ci.org/OpenMW/openmw)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
[![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)
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.
* Version: 0.34.0
* Version: 0.35.0
* License: GPL (see docs/license/GPL3.txt for more information)
* Website: http://www.openmw.org
* IRC: #openmw on irc.freenode.net
@ -41,26 +39,26 @@ Command line options
--version print version information and quit
--data arg (=data) set data directories (later directories
have higher priority)
--data-local arg set local data directory (highest
--data-local arg set local data directory (highest
priority)
--fallback-archive arg (=fallback-archive)
set fallback BSA archives (later
set fallback BSA archives (later
archives have higher priority)
--resources arg (=resources) set resources directory
--start arg set initial cell
--content arg content file(s): esm/esp, or
--content arg content file(s): esm/esp, or
omwgame/omwaddon
--no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding 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
--script-run arg select a file containing a list of
console commands that is executed on
--script-run arg select a file containing a list of
console commands that is executed on
startup
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
scripts
0 - ignore warning
1 - show warning but consider script as
@ -70,29 +68,32 @@ Command line options
of the blacklist is enabled)
--script-blacklist-use [=arg(=1)] (=1)
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
--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)
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
folding)
--encoding arg (=win1252) Character encoding used in OpenMW game
--encoding arg (=win1252) Character encoding used in OpenMW game
messages:
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
Albanian languages
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
and other languages
win1252 - Western European (Latin)
win1252 - Western European (Latin)
alphabet, used by default
--fallback arg fallback values
--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
--activate-dist arg (=-1) activation distance override

@ -59,6 +59,7 @@ struct Arguments
std::string outname;
std::vector<std::string> types;
std::string name;
ESMData data;
ESM::ESMReader reader;
@ -78,6 +79,8 @@ bool parseOptions (int argc, char** argv, Arguments &info)
("type,t", bpo::value< std::vector<std::string> >(),
"Show only records of this type (four character record code). May "
"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. "
"(skipped by default)"
"Only affects dump mode.")
@ -148,7 +151,9 @@ bool parseOptions (int argc, char** argv, Arguments &info)
}
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>();
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 << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << 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())
id = esm.getHNOString("INAM");
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
interested = false;
if(!quiet && interested)
std::cout << "\nRecord: " << n.toString()
<< " '" << id << "'\n";
@ -385,7 +395,7 @@ int load(Arguments& info)
record->load(esm);
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);
}

@ -2,6 +2,8 @@
#include "labels.hpp"
#include <iostream>
#include <sstream>
#include <boost/format.hpp>
void printAIPackage(ESM::AIPackage p)
@ -533,10 +535,10 @@ void Record<ESM::Class>::print()
std::cout << " Specialization: " << specializationLabel(mData.mData.mSpecialization)
<< " (" << mData.mData.mSpecialization << ")" << std::endl;
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;
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;
}
@ -752,7 +754,7 @@ void Record<ESM::DialInfo>::print()
if (mData.mCell != "")
std::cout << " Cell: " << mData.mCell << std::endl;
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)
std::cout << " Gender: " << mData.mData.mGender << std::endl;
if (mData.mSound != "")
@ -812,7 +814,6 @@ void Record<ESM::Land>::print()
{
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
std::cout << " HasData: " << mData.mHasData << std::endl;
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
// 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 << " Flags: " << creatureListFlags(mData.mFlags) << 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)
std::cout << " Creature: Level: " << iit->mLevel
<< " 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 << " Flags: " << itemListFlags(mData.mFlags) << 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)
std::cout << " Inventory: Level: " << iit->mLevel
<< " Item: " << iit->mId << std::endl;

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

@ -29,6 +29,12 @@ namespace ESSImport
}
cStats.mGoldPool = acdt.mGoldPool;
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)

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

@ -3,6 +3,7 @@
#include <stdexcept>
#include <OgreImage.h>
#include <OgreColourValue.h>
#include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
@ -69,7 +70,65 @@ namespace ESSImport
esm.getSubHeader();
data.resize(esm.getSubSize());
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)
@ -103,6 +162,10 @@ namespace ESSImport
// (probably offset of that specific fog texture?)
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();
if (esm.getSubSize() == 36)
@ -211,6 +274,8 @@ namespace ESSImport
const CellRef& cellref = *refIt;
ESM::CellRef out (cellref);
// TODO: use mContext->mCreatures/mNpcs
if (!isIndexedRefId(cellref.mIndexedRefId))
{
// non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it
@ -246,9 +311,12 @@ namespace ESSImport
objstate.blank();
objstate.mRef = out;
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
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);
convertNPCC(npccIt->second, objstate);
convertCellRef(cellref, objstate);
@ -280,9 +348,12 @@ namespace ESSImport
objstate.blank();
objstate.mRef = out;
objstate.mRef.mRefID = idLower;
convertACDT(cellref.mACDT, objstate.mCreatureStats);
// 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
if (cellref.mHasACDT)
convertACDT(cellref.mACDT, objstate.mCreatureStats);
if (cellref.mHasACSC)
convertACSC(cellref.mACSC, objstate.mCreatureStats);
convertCREC(crecIt->second, objstate);
convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_CREA);
@ -313,10 +384,6 @@ namespace ESSImport
it->save(esm);
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
#define OPENMW_ESSIMPORT_CONVERTER_H
#include <OgreImage.h>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
@ -13,6 +15,10 @@
#include <components/esm/dialoguestate.hpp>
#include <components/esm/custommarkerstate.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 "importcntc.hpp"
@ -29,6 +35,8 @@
#include "convertacdt.hpp"
#include "convertnpcc.hpp"
#include "convertscpt.hpp"
#include "convertplayer.hpp"
namespace ESSImport
{
@ -98,10 +106,10 @@ public:
npc.load(esm);
if (id != "player")
{
// TODO:
// this should handle changes to the NPC struct, but since there is no index here
// Handles 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
// "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
{
@ -133,6 +141,7 @@ public:
ESM::Creature creature;
std::string id = esm.getHNString("NAME");
creature.load(esm);
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature;
}
};
@ -206,7 +215,7 @@ public:
else
{
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
{
public:
ConvertPCDT() : mFirstPersonCam(true) {}
virtual void read(ESM::ESMReader &esm)
{
PCDT pcdt;
pcdt.load(esm);
mContext->mPlayer.mBirthsign = pcdt.mBirthsign;
mContext->mPlayer.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;
faction.mRank = it->mRank;
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));
}
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam);
}
virtual void write(ESM::ESMWriter &esm)
{
esm.startRecord(ESM::REC_CAM_);
esm.writeHNT("FIRS", mFirstPersonCam);
esm.endRecord(ESM::REC_CAM_);
}
private:
bool mFirstPersonCam;
};
class ConvertCNTC : public Converter
@ -308,6 +308,10 @@ class ConvertFMAP : public Converter
{
public:
virtual void read(ESM::ESMReader &esm);
virtual void write(ESM::ESMWriter &esm);
private:
Ogre::Image mGlobalMapImage;
};
class ConvertCell : public Converter
@ -384,29 +388,55 @@ public:
virtual void read(ESM::ESMReader &esm)
{
std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::toLower(itemid);
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
{
if (esm.retSubName().toString() == "FNAM")
{
std::string factionid = esm.getHString();
mFactionStolenItems.insert(std::make_pair(itemid, factionid));
mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true));
}
else
{
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:
std::multimap<std::string, std::string> mStolenItems;
std::multimap<std::string, std::string> mFactionStolenItems;
typedef std::pair<std::string, bool> Owner; // <owner id, bool isFaction>
std::map<std::string, std::set<Owner> > mStolenItems;
};
/// Seen responses for a dialogue topic?
/// 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.
/// - 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.
@ -428,7 +458,24 @@ public:
std::string id = esm.getHNString("NAME");
DIAL dial;
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
@ -455,11 +502,69 @@ public:
class ConvertGAME : public Converter
{
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)
{
GAME game;
game.load(esm);
mGame.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
@ -470,7 +575,21 @@ public:
{
SCPT script;
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);
// FIXME: not all actors have this, add flag
esm.getHNOT(mACDT, "ACDT");
mHasACDT = false;
if (esm.isNextSub("ACDT"))
{
mHasACDT = true;
esm.getHT(mACDT);
}
mHasACSC = false;
if (esm.isNextSub("ACSC"))
{
mHasACSC = true;
esm.getHT(mACSC);
}
ACSC acsc;
esm.getHNOT(acsc, "ACSC");
esm.getHNOT(acsc, "ACSL");
if (esm.isNextSub("ACSL"))
esm.skipHSubSize(112);
if (esm.isNextSub("CSTN"))
esm.skipHSub(); // "PlayerSaveGame", link to some object?
@ -60,10 +70,6 @@ namespace ESSImport
if (esm.isNextSub("PWPS"))
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"))
{
std::string id = esm.getHString();
@ -77,6 +83,20 @@ namespace ESSImport
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
if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int));

@ -9,7 +9,7 @@
namespace ESM
{
struct ESMReader;
class ESMReader;
}
namespace ESSImport
@ -17,7 +17,13 @@ namespace ESSImport
enum ACDTFlags
{
TalkedToPlayer = 0x4
TalkedToPlayer = 0x4,
Attacked = 0x100,
Unknown = 0x200
};
enum ACSCFlags
{
Dead = 0x2
};
/// Actor data, shared by (at least) REFR and CellRef
@ -28,24 +34,38 @@ namespace ESSImport
// Note, not stored at *all*:
// - Level changes are lost on reload, except for the player (there it's in the NPC record).
unsigned char mUnknown[12];
unsigned char mFlags; // ACDTFlags
unsigned char mUnknown1[3];
unsigned int mFlags;
float mBreathMeter; // Seconds left before drowning
unsigned char mUnknown2[20];
float mDynamic[3][2];
unsigned char mUnknown3[16];
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 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)
struct ActorData : public ESM::CellRef
{
bool mHasACDT;
ACDT mACDT;
int mSkills[27][2];
bool mHasACSC;
ACSC mACSC;
int mSkills[27][2]; // skills, base and modified
// creature combat stats, base and modified
// 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);
};
/// Unknown, shared by (at least) REFR and CellRef
struct ACSC
{
unsigned char unknown[112];
};
}
#endif

@ -22,7 +22,7 @@ namespace ESSImport
ActorData::load(esm);
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?
unsigned char lvcr;
esm.getHT(lvcr);
@ -32,7 +32,7 @@ namespace ESSImport
mEnabled = true;
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
// alarmvoi0000.ess
esm.getHNOT(mPos, "DATA", 24);

@ -14,10 +14,10 @@ namespace ESSImport
float scale;
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")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mAiPackages.add(esm);
mInventory.load(esm);
}

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

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

@ -1,4 +1,5 @@
#include "importer.hpp"
#include <boost/shared_ptr.hpp>
#include <OgreRoot.h>
@ -20,6 +21,8 @@
#include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp>
#include <components/to_utf8/to_utf8.hpp>
#include "importercontext.hpp"
#include "converter.hpp"
@ -43,9 +46,10 @@ namespace
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)
, mOutFile(outfile)
, mEncoding(encoding)
{
}
@ -166,14 +170,30 @@ namespace ESSImport
std::cout << "Data 1:" << std::endl;
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] << " ";
if (different)
std::cout << "\033[0m";
}
std::cout << std::endl;
std::cout << "Data 2:" << std::endl;
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] << " ";
if (different)
std::cout << "\033[0m";
}
std::cout << std::endl;
}
@ -187,10 +207,10 @@ namespace ESSImport
Ogre::LogManager logman;
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.open(mEssFile);
esm.setEncoder(&encoder);
Context context;
@ -341,8 +361,8 @@ namespace ESSImport
{
// exterior cell -> determine cell coordinates based on position
const int cellSize = 8192;
int cellX = std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize);
int cellY = std::floor(context.mPlayer.mObject.mPosition.pos[1]/cellSize);
int cellX = static_cast<int>(std::floor(context.mPlayer.mObject.mPosition.pos[0]/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.mY = cellY;
}

@ -9,7 +9,7 @@ namespace ESSImport
class Importer
{
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();
@ -18,6 +18,7 @@ namespace ESSImport
private:
std::string mEssFile;
std::string mOutFile;
std::string mEncoding;
};
}

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

@ -7,7 +7,23 @@ namespace ESSImport
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 mWeatherTransition; // 0-100 transition between weathers, top 3 bytes may be garbage
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;

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

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

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

@ -9,10 +9,9 @@ namespace ESSImport
{
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")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mAiPackages.add(esm);
mInventory.load(esm);
}

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

@ -38,6 +38,17 @@ struct PCDT
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(1)
struct FNAM
@ -49,11 +60,13 @@ struct PCDT
unsigned char mUnknown2[3];
ESM::NAME32 mFactionName;
};
struct PNAM
{
unsigned char mUnknown1[4];
unsigned char mLevelProgress;
unsigned char mUnknown2[111];
short mDrawState; // DrawState
short mCameraState; // CameraState
unsigned int mLevelProgress;
float mSkillProgress[27]; // skill progress, non-uniform scaled
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
unsigned char mUnknown3[88];
};

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

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

@ -14,7 +14,6 @@ namespace ESSImport
{
// A running global script
// TODO: test how targeted scripts are saved
struct SCPT
{
ESM::Script::SCHD mSCHD;
@ -22,7 +21,8 @@ namespace ESSImport
// values of local variables
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);
};

@ -5,6 +5,8 @@
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <components/files/configurationmanager.hpp>
#include "importer.hpp"
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")
("output,o", bpo::value<std::string>(), "output file (.omwsave)")
("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);
bpo::variables_map vm;
bpo::variables_map variables;
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
.options(desc)
.positional(p_desc)
.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;
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 outputFile = vm["output"].as<std::string>();
std::string essFile = variables["mwsave"].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();
else
{

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

@ -20,6 +20,9 @@
#include "utils/textinputdialog.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)
: mCfgMgr(cfg)
, mGameSettings(gameSettings)
@ -48,9 +51,9 @@ void Launcher::DataFilesPage::buildView()
ui.deleteProfileButton->setToolTip ("Delete an existing Content List");
//combo box
ui.profilesComboBox->addItem ("Default");
ui.profilesComboBox->addItem(mDefaultContentListName);
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
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
@ -69,19 +72,6 @@ void Launcher::DataFilesPage::buildView()
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();
QString currentProfile = mLauncherSettings.getCurrentContentListName();
@ -94,11 +84,27 @@ bool Launcher::DataFilesPage::loadSettings()
if (!currentProfile.isEmpty())
addProfile(currentProfile, true);
mSelector->setProfileContent(filesInProfile(currentProfile, pathIterator));
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 files = mLauncherSettings.getContentListFiles(profileName);
@ -175,7 +181,7 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString
ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
loadSettings();
populateFileViews(current);
checkForDefaultProfile();
}
@ -229,13 +235,6 @@ void Launcher::DataFilesPage::on_newProfileAction_triggered()
mLauncherSettings.setCurrentContentListName(profile);
addProfile(profile, true);
mSelector->clearCheckStates();
mSelector->setGameFile();
saveSettings();
emit signalProfileChanged (ui.profilesComboBox->findText(profile));
}
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
int next = ui.profilesComboBox->currentIndex()-1;
// changing the profile forces a reload of plugin file views.
ui.profilesComboBox->setCurrentIndex(next);
removeProfile(profile);
ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile));
saveSettings();
loadSettings();
checkForDefaultProfile();
}
@ -290,7 +287,7 @@ void Launcher::DataFilesPage::updateOkButton(const QString &text)
void Launcher::DataFilesPage::checkForDefaultProfile()
{
//don't allow deleting "Default" profile
bool success = (ui.profilesComboBox->currentText() != "Default");
bool success = (ui.profilesComboBox->currentText() != mDefaultContentListName);
ui.deleteProfileAction->setEnabled (success);
ui.profilesComboBox->setEditEnabled (success);

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

@ -10,7 +10,7 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include <SDL_video.h>
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
@ -67,7 +67,7 @@ bool Launcher::GraphicsPage::setupOgre()
}
catch(Ogre::Exception &ex)
{
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str());
QString ogreError = QString::fromUtf8(ex.getFullDescription().c_str());
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating Ogre::Root");
msgBox.setIcon(QMessageBox::Critical);
@ -135,7 +135,7 @@ bool Launcher::GraphicsPage::setupSDL()
msgBox.setWindowTitle(tr("Error receiving number of screens"));
msgBox.setIcon(QMessageBox::Critical);
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();
return false;
}
@ -237,7 +237,7 @@ QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre
opt_it != i->second.possibleValues.end(); ++opt_it, ++idx)
{
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.setIcon(QMessageBox::Critical);
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();
return result;
}
@ -279,7 +279,7 @@ QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
msgBox.setWindowTitle(tr("Error receiving resolutions"));
msgBox.setIcon(QMessageBox::Critical);
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();
return result;
}

@ -1,4 +1,5 @@
#include <iostream>
#include <csignal>
#include <QApplication>
#include <QTextCodec>
@ -23,9 +24,11 @@ int main(int argc, char *argv[])
SDL_SetMainReady();
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;
}
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);

@ -27,25 +27,6 @@ using namespace Process;
Launcher::MainDialog::MainDialog(QWidget *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);
mGameInvoker = new ProcessInvoker();
@ -80,6 +61,7 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!revision.isEmpty() && !tag.isEmpty())
{
if (revision == tag) {
@ -257,24 +239,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous;
int currentIndex = iconWidget->row(current);
// int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex);
// 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();
// }
mSettingsPage->resetProgressBar();
}
bool Launcher::MainDialog::setupLauncherSettings()

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

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

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

@ -8,7 +8,8 @@
#include <sstream>
#include <components/misc/stringops.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem;
@ -660,7 +661,7 @@ std::string MwIniImporter::numberToString(int n) {
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::string section("");
@ -719,7 +720,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam
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;
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 {
std::vector<std::string> contentFiles;
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
std::vector<std::pair<std::time_t, std::string> > contentFiles;
std::string baseGameFile("Game Files: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();
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);
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.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(*it);
// this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
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;
}
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 <exception>
#include <iosfwd>
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -17,17 +18,22 @@ class MwIniImporter {
MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose);
multistrmap loadIniFile(const std::string& filename) const;
static multistrmap loadCfgFile(const std::string& filename);
multistrmap loadIniFile(const boost::filesystem::path& filename) const;
static multistrmap loadCfgFile(const boost::filesystem::path& filename);
void merge(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;
static void writeToFile(std::ostream &out, const multistrmap &cfg);
private:
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
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;
strmap mMergeMap;
std::vector<std::string> mMergeFallback;

@ -59,7 +59,7 @@ int wmain(int argc, wchar_t *wargv[]) {
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;
desc.add_options()
("help,h", "produce help message")
@ -93,8 +93,8 @@ int wmain(int argc, wchar_t *wargv[]) {
bpo::notify(vm);
std::string iniFile = vm["ini"].as<std::string>();
std::string cfgFile = vm["cfg"].as<std::string>();
boost::filesystem::path iniFile(vm["ini"].as<std::string>());
boost::filesystem::path cfgFile(vm["cfg"].as<std::string>());
// if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>());
@ -123,7 +123,7 @@ int wmain(int argc, wchar_t *wargv[]) {
importer.mergeFallback(cfg, ini);
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
importer.importGameFiles(cfg, ini, iniFile);
}
if(!vm.count("no-archives")) {

@ -4,8 +4,6 @@ set (OPENCS_SRC main.cpp
opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc
document operation saving documentmanager loader runner
)
@ -41,7 +39,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools
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
)
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
source_group (openmw-cs FILES ${OPENCS_SRC} ${OPENCS_HDR})
if(WIN32)
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})
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()
set (OPENCS_MAC_ICON "")
endif(APPLE)
add_executable(opencs
add_executable(openmw-cs
MACOSX_BUNDLE
${OENGINE_BULLET}
${OPENCS_SRC}
@ -185,10 +183,10 @@ add_executable(opencs
)
if(APPLE)
set_target_properties(opencs PROPERTIES
set_target_properties(openmw-cs PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
OUTPUT_NAME "OpenCS"
MACOSX_BUNDLE_ICON_FILE "opencs.icns"
OUTPUT_NAME "OpenMW-CS"
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
@ -199,7 +197,7 @@ if(APPLE)
MACOSX_PACKAGE_LOCATION Resources)
endif(APPLE)
target_link_libraries(opencs
target_link_libraries(openmw-cs
${OGRE_LIBRARIES}
${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS}
@ -211,5 +209,5 @@ target_link_libraries(opencs
)
if(APPLE)
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif()

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

@ -78,7 +78,7 @@ int main(int argc, char *argv[])
application.setLibraryPaths(libraryPaths);
#endif
application.setWindowIcon (QIcon (":./opencs.png"));
application.setWindowIcon (QIcon (":./openmw-cs.png"));
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 "scriptcheck.hpp"
#include "bodypartcheck.hpp"
#include "referencecheck.hpp"
CSMDoc::Operation *CSMTools::Tools::get (int type)
{
@ -57,9 +58,6 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace");
mandatoryIds.push_back ("PCVampire");
mandatoryIds.push_back ("PCWerewolf");
mandatoryIds.push_back ("PCYear");
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
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 ReferenceCheckStage(mData.getReferences(), mData.getReferenceables(), mData.getCells(), mData.getFactions()));
mVerifier->appendStage (new ScriptCheckStage (mDocument));
mVerifier->appendStage(

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

@ -6,11 +6,12 @@
#include <components/esm/loadpgrd.hpp>
#include "idcollection.hpp"
#include "cell.hpp"
namespace CSMWorld
{
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Wrapper for Pathgrid record
///
/// \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
#define CSM_WOLRD_SUBCOLLECTION_H
#include "idcollection.hpp"
namespace ESM
{
class ESMReader;
}
namespace CSMWorld
{
struct Cell;
template<typename T, typename AT>
class IdCollection;
/// \brief Single type collection of top level records that are associated with cells
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class SubCellCollection : public IdCollection<ESXRecordT, IdAccessorT>

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

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

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

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

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

@ -109,11 +109,14 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{
if (!paused)
{
// local scripts
executeLocalScripts();
if (MWBase::Environment::get().getWorld()->getScriptsEnabled())
{
// local scripts
executeLocalScripts();
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
}
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
}
@ -170,7 +173,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0)
, mFpsLevel(0)
, mVerboseScripts (false)
, mSkipMenu (false)
, mUseSound (true)
@ -292,16 +294,10 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
else
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";
if (boost::filesystem::exists(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
NifOverrides::Overrides nifOverrides;
@ -349,6 +345,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
addResourcesDirectory(mResDir / "shadows");
addResourcesDirectory(mResDir / "materials");
OEngine::Render::WindowSettings windowSettings;
windowSettings.fullscreen = settings.getBool("fullscreen", "Video");
@ -397,7 +394,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setInputManager (input);
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);
mEnvironment.setWindowManager (window);
@ -598,11 +595,6 @@ void OMW::Engine::setSoundUsage(bool soundUsage)
mUseSound = soundUsage;
}
void OMW::Engine::showFPS(int level)
{
mFpsLevel = level;
}
void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
{
mEncoding = encoding;

@ -71,7 +71,6 @@ namespace OMW
OEngine::Render::OgreRenderer *mOgre;
std::string mCellName;
std::vector<std::string> mContentFiles;
int mFpsLevel;
bool mVerboseScripts;
bool mSkipMenu;
bool mUseSound;
@ -151,9 +150,6 @@ namespace OMW
*/
void addContentFile(const std::string& file);
/// Enable fps counter
void showFPS(int level);
/// Enable or disable verbose script output
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")
("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)
->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::notify(variables);
bool run = true;
if (variables.count ("help"))
{
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;
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;
rev = rev.substr(0, 10);
std::cout << " (revision " << rev << ")";
}
std::cout << std::endl;
if (!run)
if (variables.count ("version"))
return false;
cfgMgr.readConfiguration(variables, desc);
@ -396,12 +390,12 @@ int main(int argc, char**argv)
catch (std::exception &e)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
if (isatty(fileno(stdin)))
std::cerr << "\nERROR: " << e.what() << std::endl;
else
if (!isatty(fileno(stdin)))
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
std::cerr << "\nERROR: " << e.what() << std::endl;
ret = 1;
}

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

@ -2,8 +2,8 @@
#define GAME_MWBASE_INPUTMANAGER_H
#include <string>
#include <components/settings/settings.hpp>
#include <set>
#include <vector>
namespace MWBase
{
@ -29,7 +29,7 @@ namespace MWBase
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;

@ -129,16 +129,15 @@ namespace MWBase
/// @return false if the attack was considered a "friendly hit" and forgiven
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
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
virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0;
/// Attempt sleeping in a bed. If this is illegal, call commitCrime.
/// @return was it illegal, and someone saw you doing it?
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
{
PT_Admire,
@ -203,6 +202,15 @@ namespace MWBase
virtual void keepPlayerAlive() = 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
#include <string>
#include <set>
#include <boost/shared_ptr.hpp>
#include <components/settings/settings.hpp>
#include "../mwworld/ptr.hpp"
namespace Ogre
@ -74,7 +72,7 @@ namespace MWBase
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;
///< Stops music if it's playing

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

@ -3,24 +3,23 @@
#include <vector>
#include <map>
#include <set>
#include <components/settings/settings.hpp>
#include <components/esm/cellid.hpp>
#include "../mwworld/globals.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre
{
class Vector2;
class Vector3;
class Quaternion;
class Image;
}
namespace OEngine
namespace Loading
{
namespace Physic
{
class PhysicEngine;
}
class Listener;
}
namespace ESM
@ -33,7 +32,6 @@ namespace ESM
struct Potion;
struct Spell;
struct NPC;
struct CellId;
struct Armor;
struct Weapon;
struct Clothing;
@ -92,6 +90,7 @@ namespace MWBase
{
std::string name;
float x, y; // world position
ESM::CellId dest;
};
World() {}
@ -269,6 +268,8 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< 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
/// 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.
@ -388,7 +389,7 @@ namespace MWBase
virtual bool canPlaceObject (float cursorX, float cursorY) = 0;
///< @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 isSlowFalling(const MWWorld::Ptr &ptr) const = 0;
@ -453,6 +454,7 @@ namespace MWBase
/// \todo Probably shouldn't be here
virtual MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) = 0;
virtual void reattachPlayerCamera() = 0;
/// \todo this does not belong here
virtual void frameStarted (float dt, bool paused) = 0;
@ -489,6 +491,9 @@ namespace MWBase
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.
* @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 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;

@ -59,8 +59,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>();
// setting ownership not needed, since taking items from a container inherits the
// container's owner automatically
data->mContainerStore.fill(
ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore());
ref->mBase->mInventory, "");
// store
ptr.getRefData().setCustomData (data.release());
@ -82,7 +84,10 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
const ESM::InventoryList& list = ref->mBase->mInventory;
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

@ -139,8 +139,7 @@ namespace MWClass
// store
ptr.getRefData().setCustomData(data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1,
MWBase::Environment::get().getWorld()->getStore());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr));
if (ref->mBase->mFlags & ESM::Creature::Weapon)
getInventoryStore(ptr).autoEquip(ptr);
@ -228,18 +227,7 @@ namespace MWClass
weapon = *weaponslot;
}
// Reduce fatigue
// 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);
MWMechanics::applyFatigueLoss(ptr, weapon);
// TODO: where is the distance defined?
float dist = 200.f;
@ -897,7 +885,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
const ESM::InventoryList& list = ref->mBase->mInventory;
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

@ -52,7 +52,7 @@ namespace MWClass
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);

@ -20,7 +20,7 @@ namespace MWClass
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
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)

@ -93,9 +93,6 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Door> *ref =
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;
}

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

@ -296,25 +296,6 @@ namespace MWClass
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
int gold=0;
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;
}
if (data->mNpcStats.getFactionRanks().size())
if (!ref->mBase->mFaction.empty())
{
static const int iAutoRepFacMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("iAutoRepFacMod")->getInt();
static const int iAutoRepLevMod = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.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));
}
@ -403,8 +384,8 @@ namespace MWClass
}
// inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1,
MWBase::Environment::get().getWorld()->getStore());
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr));
data->mNpcStats.setGoldPool(gold);
@ -505,18 +486,7 @@ namespace MWClass
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr();
// Reduce fatigue
// 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);
MWMechanics::applyFatigueLoss(ptr, weapon);
const float fCombatDistance = store.find("fCombatDistance")->getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ?
@ -1358,7 +1328,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
const ESM::InventoryList& list = ref->mBase->mInventory;
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
@ -1371,4 +1341,16 @@ namespace MWClass
{
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 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");
}
// Shortblade and thrown weapons
// thrown weapons may not be entirely correct
if (type == 0 || type == 11)
// Shortblade
if (type == 0)
{
return std::string("Item Weapon Shortblade Up");
}
@ -200,8 +199,8 @@ namespace MWClass
{
return std::string("Item Weapon Spear Up");
}
// Blunts and Axes
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8)
// Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{
return std::string("Item Weapon Blunt Up");
}
@ -235,9 +234,8 @@ namespace MWClass
{
return std::string("Item Weapon Longblade Down");
}
// Shortblade and thrown weapons
// thrown weapons may not be entirely correct
if (type == 0 || type == 11)
// Shortblade
if (type == 0)
{
return std::string("Item Weapon Shortblade Down");
}
@ -246,8 +244,8 @@ namespace MWClass
{
return std::string("Item Weapon Spear Down");
}
// Blunts and Axes
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8)
// Blunts, Axes and Thrown weapons
if (type == 3 || type == 4 || type == 5 || type == 7 || type == 8 || type == 11)
{
return std::string("Item Weapon Blunt Down");
}

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

@ -4,7 +4,7 @@
#include "../mwbase/dialoguemanager.hpp"
#include <map>
#include <list>
#include <set>
#include <components/compiler/streamerrorhandler.hpp>
#include <components/translation/translation.hpp>
@ -23,13 +23,13 @@ namespace MWDialogue
class DialogueManager : public MWBase::DialogueManager
{
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> >
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
ModFactionReactionMap mChangedFactionReaction;
std::list<std::string> mActorKnownTopics;
std::set<std::string> mActorKnownTopics;
Translation::Storage& mTranslationDataStorage;
MWScript::CompilerContext mCompilerContext;

@ -67,14 +67,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
if (isCreature)
return false;
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats (mActor);
std::map<std::string, int>::const_iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mFaction));
if (iter==stats.getFactionRanks().end())
if (!Misc::StringUtils::ciEqual(mActor.getClass().getPrimaryFaction(mActor), info.mFaction))
return false;
// check rank
if (iter->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
else if (info.mData.mRank != -1)
@ -83,13 +80,8 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
return false;
// 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
if (stats.getFactionRanks().begin()->second < info.mData.mRank)
if (mActor.getClass().getPrimaryFactionRank(mActor) < info.mData.mRank)
return false;
}
@ -336,12 +328,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_RankRequirement:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
if (faction.empty())
return 0;
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
int rank = getFactionRank (player, faction);
if (rank>=9)
@ -376,15 +366,14 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
case SelectWrapper::Function_FactionRankDiff:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0;
const std::pair<std::string, int> faction =
*mActor.getClass().getNpcStats (mActor).getFactionRanks().begin();
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
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:
@ -396,11 +385,10 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
{
bool low = select.getFunction()==SelectWrapper::Function_RankLow;
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return 0;
std::string factionId = mActor.getClass().getPrimaryFaction(mActor);
std::string factionId =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (factionId.empty())
return 0;
int value = 0;
@ -454,7 +442,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
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:
@ -494,8 +482,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameFaction:
return mActor.getClass().getNpcStats (mActor).isSameFaction (
player.getClass().getNpcStats (player));
return player.getClass().getNpcStats (player).isInFaction(mActor.getClass().getPrimaryFaction(mActor));
case SelectWrapper::Function_PcCommonDisease:
@ -512,11 +499,10 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_PcExpelled:
{
if (mActor.getClass().getNpcStats (mActor).getFactionRanks().empty())
return false;
std::string faction = mActor.getClass().getPrimaryFaction(mActor);
std::string faction =
mActor.getClass().getNpcStats (mActor).getFactionRanks().begin()->first;
if (faction.empty())
return false;
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);
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())
return -1;

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

@ -259,7 +259,12 @@ namespace MWDialogue
record.load (reader);
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())
{
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)
{
int size = it->mEnd - it->mBeg;

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

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

@ -5,14 +5,14 @@
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)
{
MyGUI::Gui::getInstance().destroyWidget(mChild);
mChild = NULL;
}
if (correct)
if (!stretch)
{
setImageTexture("black.png");

@ -18,9 +18,9 @@ namespace MWGui
/**
* @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 setCoord (const MyGUI::IntCoord &_value);

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

@ -3,6 +3,22 @@
#include "../mwmechanics/npcstats.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
{
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)
{
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(item.mBase.getClass().getValue(item.mBase) * count);
}
if (hasProfit(mActor))
modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);
return InventoryItemModel::copyItem(item, count, setNewOwner);
}
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
{
if (mActor.getClass().isNpc())
{
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count);
}
if (hasProfit(mActor))
modifyProfit(mActor, -item.mBase.getClass().getValue(item.mBase) * 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 void removeItem (const ItemStack& item, size_t count);
bool hasProfit(const MWWorld::Ptr& actor);
};
}

@ -17,6 +17,21 @@
#include "draganddrop.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
{
@ -116,13 +131,12 @@ void CompanionWindow::updateEncumbranceBar()
float encumbrance = mPtr.getClass().getEncumbrance(mPtr);
mEncumbranceBar->setValue(encumbrance, capacity);
if (mPtr.getTypeName() != typeid(ESM::NPC).name())
mProfitLabel->setCaption("");
else
if (mModel && mModel->hasProfit(mPtr))
{
MWMechanics::NpcStats& stats = mPtr.getClass().getNpcStats(mPtr);
mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(stats.getProfit()));
mProfitLabel->setCaptionWithReplacing("#{sProfitValue} " + MyGUI::utility::toString(getProfit(mPtr)));
}
else
mProfitLabel->setCaption("");
}
void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
@ -132,7 +146,7 @@ void CompanionWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
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;
buttons.push_back("#{sCompanionWarningButtonOne}");
@ -148,9 +162,6 @@ void CompanionWindow::onMessageBoxButtonClicked(int button)
{
if (button == 0)
{
mPtr.getRefData().getLocals().setVarByInt(mPtr.getClass().getScript(mPtr),
"minimumprofit", mPtr.getClass().getNpcStats(mPtr).getProfit());
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Companion);
// Important for Calvus' contract script to work properly
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
// the end of the list
mCommandHistory.push_back(cm);
if (mCommandHistory.empty() || mCommandHistory.back() != cm)
mCommandHistory.push_back(cm);
mCurrent = mCommandHistory.end();
mEditString.clear();

@ -154,8 +154,6 @@ namespace MWGui
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));
}
@ -287,7 +285,7 @@ namespace MWGui
if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead())
return true;
else
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count);
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, mPtr, count);
}
return true;
}

@ -339,7 +339,8 @@ namespace MWGui
for (int i=0; i<2; ++i)
{
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();
if (msg.find("%s") != std::string::npos)

@ -10,6 +10,7 @@
#include <MyGUI_ScrollView.h>
#include <components/misc/resourcehelpers.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
@ -182,6 +183,10 @@ namespace MWGui
HUD::~HUD()
{
mMainWidget->eventMouseLostFocus.clear();
mMainWidget->eventMouseMove.clear();
mMainWidget->eventMouseButtonClick.clear();
delete mSpellIcons;
}
@ -666,4 +671,14 @@ namespace MWGui
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