forked from mirror/openmw-tes3mp
Merge pull request #258 from OpenMW/master while resolving conflicts
# Conflicts: # .travis.yml # README.md
This commit is contained in:
commit
721b218cc2
68 changed files with 1375 additions and 2429 deletions
|
@ -12,8 +12,10 @@ branches:
|
|||
- /openmw-.*$/
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
|
||||
- macos_qt_formula=qt@5.7
|
||||
- macos_qt_formula=qt
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
|
|
|
@ -50,6 +50,7 @@ Programmers
|
|||
Edmondo Tommasina (edmondo)
|
||||
Eduard Cot (trombonecot)
|
||||
Eli2
|
||||
elsid
|
||||
Emanuel Guével (potatoesmaster)
|
||||
eroen
|
||||
escondida
|
||||
|
@ -145,6 +146,7 @@ Programmers
|
|||
t6
|
||||
terrorfisch
|
||||
Thomas Luppi (Digmaster)
|
||||
Will Herrmann (Thunderforge)
|
||||
Tom Mason (wheybags)
|
||||
Torben Leif Carrington (TorbenC)
|
||||
viadanna
|
||||
|
|
79
CHANGELOG.md
79
CHANGELOG.md
|
@ -1,3 +1,82 @@
|
|||
0.42.0
|
||||
------
|
||||
|
||||
Bug #1956: Duplicate objects after loading the game, when a mod was edited
|
||||
Bug #2100: Falling leaves in Vurt's Leafy West Gash II not rendered correctly
|
||||
Bug #2116: Cant fit through some doorways pressed against staircases
|
||||
Bug #2289: Some modal dialogs are not centered on the screen when the window resizes
|
||||
Bug #2409: Softlock when pressing weapon/magic switch keys during chargen, afterwards switches weapons even though a text field is selected
|
||||
Bug #2483: Previous/Next Weapon hotkeys triggered while typing the name of game save
|
||||
Bug #2629: centeroncell, coc causes death / fall damage time to time when teleporting from high
|
||||
Bug #2645: Cycling weapons is possible while console/pause menu is open
|
||||
Bug #2678: Combat with water creatures do not end upon exiting water
|
||||
Bug #2759: Light Problems in Therana's Chamber in Tel Branora
|
||||
Bug #2771: unhandled sdl event of type 0x302
|
||||
Bug #2777: (constant/on cast) disintegrate armor/weapon on self is seemingly not working
|
||||
Bug #2838: Editor: '.' in a record name should be allowed
|
||||
Bug #2909: NPCs appear floating when standing on a slope
|
||||
Bug #3093: Controller movement cannot be used while mouse is moving
|
||||
Bug #3134: Crash possible when using console with open container
|
||||
Bug #3254: AI enemies hit between them.
|
||||
Bug #3344: Editor: Verification results sorting by Type is not alphabetical.
|
||||
Bug #3345: Editor: Cloned and added pathgrids are lost after reopen of saved omwgame file
|
||||
Bug #3355: [MGSO] Physics maxing out in south cornerclub Balmora
|
||||
Bug #3484: Editor: camera position is not set when changing cell via drag&drop
|
||||
Bug #3508: Slowfall kills Jump momentum
|
||||
Bug #3580: Crash: Error ElementBufferObject::remove BufferData<0> out of range
|
||||
Bug #3581: NPCs wander too much
|
||||
Bug #3601: Menu Titles not centered vertically
|
||||
Bug #3607: [Mac OS] Beginning of NPC speech cut off (same issue as closed bug #3453)
|
||||
Bug #3613: Can not map "next weapon" or "next spell" to controller
|
||||
Bug #3617: Enchanted arrows don't explode when hitting the ground
|
||||
Bug #3645: Unable to use steps in Vivec, Palace of Vivec
|
||||
Bug #3650: Tamriel Rebuilt 16.09.1 – Hist Cuirass GND nif is rendered inside a Pink Box
|
||||
Bug #3652: Item icon shadows get stuck in the alchemy GUI
|
||||
Bug #3653: Incorrect swish sounds
|
||||
Bug #3666: NPC collision should not be disabled until death animation has finished
|
||||
Bug #3669: Editor: Text field was missing from book object editing dialogue
|
||||
Bug #3670: Unhandled SDL event of type 0x304
|
||||
Bug #3671: Incorrect local variable value after picking up bittercup
|
||||
Bug #3686: Travelling followers doesn't increase travel fee
|
||||
Bug #3689: Problematic greetings from Antares Big Mod that override the appropriate ones.
|
||||
Bug #3690: Certain summoned creatures do not engage in combat with underwater creatures
|
||||
Bug #3691: Enemies do not initiate combat with player followers on sight
|
||||
Bug #3695: [Regression] Dispel does not always dispel spell effects in 0.41
|
||||
Bug #3699: Crash on MWWorld::ProjectileManager::moveMagicBolts
|
||||
Bug #3700: Climbing on rocks and mountains
|
||||
Bug #3704: Creatures don't auto-equip their shields on creation
|
||||
Bug #3705: AI combat engagement logic differs from vanilla
|
||||
Bug #3707: Animation playing does some very odd things if pc comes in contact with the animated mesh
|
||||
Bug #3712: [Mod] Freeze upon entering Adanumuran with mod Adanumuran Reclaimed
|
||||
Bug #3713: [Regression] Cancelling dialogue or using travel with creatures throws a (possibly game-breaking) exception
|
||||
Bug #3719: Dropped identification papers can't be picked up again
|
||||
Bug #3722: Command spell doesn't bring enemies out of combat
|
||||
Bug #3727: Using "Activate" mid-script-execution invalidates interpreter context
|
||||
Bug #3746: Editor: Book records show attribute IDs instead of skill IDs for teached skills entry.
|
||||
Bug #3755: Followers stop following after loading from savegame
|
||||
Bug #3772: ModStat lowers attribute to 100 if it was greater
|
||||
Bug #3781: Guns in Clean Hunter Rifles mod use crossbow sounds
|
||||
Bug #3797: NPC and creature names don't show up in combat when RMB windows are displayed
|
||||
Bug #3800: Wrong tooltip maximum width
|
||||
Bug #3801: Drowning widget is bugged
|
||||
Bug #3802: BarterOffer shouldn't limit pcMercantile
|
||||
Bug #3813: Some fatal error
|
||||
Bug #3816: Expression parser thinks the -> token is unexpected when a given explicit refID clashes with a journal ID
|
||||
Bug #3822: Custom added creatures are not animated
|
||||
Feature #451: Water sounds
|
||||
Feature #2691: Light particles sometimes not shown in inventory character preview
|
||||
Feature #3523: Light source on magic projectiles
|
||||
Feature #3644: Nif NiSphericalCollider Unknown Record Type
|
||||
Feature #3675: ess-Importer: convert mark location
|
||||
Feature #3693: ess-Importer: convert last known exterior cell
|
||||
Feature #3748: Editor: Replace "Scroll" check box in Book records with "Book Type" combo box.
|
||||
Feature #3751: Editor: Replace "Xyz Blood" check boxes in NPC and Creature records with "Blood Type" combo box
|
||||
Feature #3752: Editor: Replace emitter check boxes in Light records with "Emitter Type" combo box
|
||||
Feature #3756: Editor: Replace "Female" check box in NPC records with "Gender" combo box
|
||||
Feature #3757: Editor: Replace "Female" check box in BodyPart records with "Gender" combo box
|
||||
Task #3092: const version of ContainerStoreIterator
|
||||
Task #3795: /deps folder not in .gitignore
|
||||
|
||||
0.41.0
|
||||
------
|
||||
|
||||
|
|
|
@ -6,4 +6,4 @@ DATE=`date +'%d%m%Y'`
|
|||
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
|
||||
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
|
||||
|
||||
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "ftp://s3.mydevil.net:21/nightly/${TARGET_FILENAME}"
|
||||
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
|
||||
|
|
|
@ -25,7 +25,7 @@ endif()
|
|||
message(STATUS "Configuring OpenMW...")
|
||||
|
||||
set(OPENMW_VERSION_MAJOR 0)
|
||||
set(OPENMW_VERSION_MINOR 41)
|
||||
set(OPENMW_VERSION_MINOR 42)
|
||||
set(OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set(OPENMW_VERSION_COMMITHASH "")
|
||||
|
@ -436,7 +436,7 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
# Install icon and desktop file
|
||||
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")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/appdata" COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw")
|
||||
IF(BUILD_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")
|
||||
|
@ -768,17 +768,19 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
# Apple bundling
|
||||
if (APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||
if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
|
||||
get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY)
|
||||
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
|
||||
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
|
||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${APP_BUNDLE_DIR}/Contents/Resources/qt.conf" COPYONLY)
|
||||
|
||||
if (BUILD_OPENCS)
|
||||
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
|
||||
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
|
||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf" COPYONLY)
|
||||
endif ()
|
||||
|
||||
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)
|
||||
|
@ -837,8 +839,8 @@ if (APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
|||
install_plugins_for_bundle("${APP_BUNDLE_NAME}" PLUGINS)
|
||||
install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
|
||||
|
||||
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||
|
||||
install(CODE "
|
||||
function(gp_item_default_embedded_path_override item default_embedded_path_var)
|
||||
|
@ -848,12 +850,11 @@ if (APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
cmake_policy(SET CMP0009 OLD)
|
||||
fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\")
|
||||
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
|
||||
" COMPONENT Runtime)
|
||||
include(CPack)
|
||||
endif (APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||
endif ()
|
||||
|
||||
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'
|
||||
# output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen"
|
||||
|
|
|
@ -33,3 +33,38 @@ Furthermore, we advise to:
|
|||
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
|
||||
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
|
||||
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
|
||||
|
||||
Guidelines for original engine "fixes"
|
||||
=================================
|
||||
|
||||
From time to time you may be tempted to "fix" what you think was a "bug" in the original game engine.
|
||||
|
||||
Unfortunately, the definition of what is a "bug" is not so clear. Consider that your "bug" is actually a feature unless proven otherwise:
|
||||
|
||||
* We have no way of knowing what the original developers really intended (short of asking them, good luck with that).
|
||||
* What may seem like an illogical mechanic can actually be part of an attempt to balance the game.
|
||||
* Many people will actually <i>like</i> these "bugs" because that is what they remember the game for.
|
||||
* Exploits may be part of the fun of an open-world game - they reward knowledge with power. There are too many of them to plug them all, anyway.
|
||||
|
||||
OpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.
|
||||
|
||||
That said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:
|
||||
|
||||
* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells)
|
||||
* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder)
|
||||
* Bugs that were fixed in an official patch for Morrowind
|
||||
|
||||
Feature additions policy
|
||||
=====================
|
||||
|
||||
We get it, you have waited so long for feature XYZ to be available in Morrowind and now that OpenMW is here you can not wait to implement your ingenious idea and share it with the world.
|
||||
|
||||
Unfortunately, since maintaining features comes at a cost and our resources are limited, we have to be a little selective in what features we allow into the main repository. Generally:
|
||||
|
||||
- Features should be as generic and non-redundant as possible.
|
||||
- Any feature that is also possible with modding should be done as a mod instead.
|
||||
- In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.
|
||||
- Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.
|
||||
- If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.
|
||||
|
||||
If you are in doubt of your feature being within our scope, it is probably best to start a forum discussion first. See the [settings documentation](https://openmw.readthedocs.io/en/stable/reference/modding/settings/index.html) and [Features list](https://wiki.openmw.org/index.php?title=Features) for some examples of features that were deemed acceptable.
|
||||
|
|
|
@ -81,10 +81,10 @@ add_openmw_dir (mwclass
|
|||
|
||||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
||||
character actors objects aistate coordinateconverter trading aiface
|
||||
character actors objects aistate coordinateconverter trading aiface weaponpriority spellpriority
|
||||
)
|
||||
|
||||
add_openmw_dir (mwstate
|
||||
|
|
|
@ -334,7 +334,7 @@ namespace MWGui
|
|||
{
|
||||
ensureSelectedItemUnequipped(count);
|
||||
const ItemStack& item = mTradeModel->getItem(mSelectedItem);
|
||||
std::string sound = item.mBase.getClass().getDownSoundId(item.mBase);
|
||||
std::string sound = item.mBase.getClass().getUpSoundId(item.mBase);
|
||||
MWBase::Environment::get().getWindowManager()->playSound(sound);
|
||||
|
||||
if (item.mType == ItemStack::Type_Barter)
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace
|
|||
Book mTopicIndexBook;
|
||||
bool mQuestMode;
|
||||
bool mOptionsMode;
|
||||
bool mTopicsMode;
|
||||
bool mAllQuests;
|
||||
|
||||
template <typename T>
|
||||
|
@ -196,6 +197,7 @@ namespace
|
|||
mQuestMode = false;
|
||||
mAllQuests = false;
|
||||
mOptionsMode = false;
|
||||
mTopicsMode = false;
|
||||
}
|
||||
|
||||
void adjustButton (char const * name)
|
||||
|
@ -259,6 +261,7 @@ namespace
|
|||
void setBookMode ()
|
||||
{
|
||||
mOptionsMode = false;
|
||||
mTopicsMode = false;
|
||||
setVisible (OptionsBTN, true);
|
||||
setVisible (OptionsOverlay, false);
|
||||
|
||||
|
@ -269,6 +272,7 @@ namespace
|
|||
void setOptionsMode ()
|
||||
{
|
||||
mOptionsMode = true;
|
||||
mTopicsMode = false;
|
||||
|
||||
setVisible (OptionsBTN, false);
|
||||
setVisible (OptionsOverlay, true);
|
||||
|
@ -292,6 +296,8 @@ namespace
|
|||
// If in quest mode, ensure the quest list is updated
|
||||
if (mQuestMode)
|
||||
notifyQuests(getWidget<MyGUI::Widget>(QuestsList));
|
||||
else
|
||||
notifyTopics(getWidget<MyGUI::Widget>(TopicsList));
|
||||
}
|
||||
|
||||
void pushBook (Book book, unsigned int page)
|
||||
|
@ -370,6 +376,9 @@ namespace
|
|||
setVisible (JournalBTN, true);
|
||||
|
||||
mOptionsMode = false;
|
||||
mTopicsMode = false;
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
void notifyTopicSelected (const std::string& topic, int id)
|
||||
|
@ -399,6 +408,8 @@ namespace
|
|||
setVisible (JournalBTN, true);
|
||||
|
||||
mOptionsMode = false;
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
void notifyOptions(MyGUI::Widget* _sender)
|
||||
|
@ -416,6 +427,8 @@ namespace
|
|||
{
|
||||
assert (mStates.size () > 1);
|
||||
popBook ();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
void notifyIndexLinkClicked (MWGui::TypesetBook::InteractiveId character)
|
||||
|
@ -424,6 +437,8 @@ namespace
|
|||
setVisible (RightTopicIndex, false);
|
||||
setVisible (TopicsList, true);
|
||||
|
||||
mTopicsMode = true;
|
||||
|
||||
Gui::MWList* list = getWidget<Gui::MWList>(TopicsList);
|
||||
list->clear();
|
||||
|
||||
|
@ -432,17 +447,22 @@ namespace
|
|||
mModel->visitTopicNamesStartingWith((char) character, add);
|
||||
|
||||
list->adjustSize();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
void notifyTopics(MyGUI::Widget* _sender)
|
||||
{
|
||||
mQuestMode = false;
|
||||
mTopicsMode = false;
|
||||
setVisible (LeftTopicIndex, true);
|
||||
setVisible (RightTopicIndex, true);
|
||||
setVisible (TopicsList, false);
|
||||
setVisible (QuestsList, false);
|
||||
setVisible (ShowAllBTN, false);
|
||||
setVisible (ShowActiveBTN, false);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
struct AddNamesToList
|
||||
|
@ -494,6 +514,8 @@ namespace
|
|||
SetNamesInactive setInactive(list);
|
||||
mModel->visitQuestNames(!mAllQuests, setInactive);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
void notifyShowAll(MyGUI::Widget* _sender)
|
||||
|
@ -510,7 +532,16 @@ namespace
|
|||
|
||||
void notifyCancel(MyGUI::Widget* _sender)
|
||||
{
|
||||
setBookMode ();
|
||||
if (mTopicsMode)
|
||||
{
|
||||
notifyTopics(_sender);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBookMode();
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void notifyClose(MyGUI::Widget* _sender)
|
||||
|
@ -539,6 +570,8 @@ namespace
|
|||
|
||||
if (page+2 < book->pageCount())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
|
||||
page += 2;
|
||||
updateShowingPages ();
|
||||
}
|
||||
|
@ -555,6 +588,8 @@ namespace
|
|||
|
||||
if(page >= 2)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->playSound("book page");
|
||||
|
||||
page -= 2;
|
||||
updateShowingPages ();
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ namespace MWGui
|
|||
|
||||
SpellBuyingWindow::SpellBuyingWindow() :
|
||||
WindowBase("openmw_spell_buying_window.layout")
|
||||
, mLastPos(0)
|
||||
, mCurrentY(0)
|
||||
{
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
|
@ -48,13 +47,20 @@ namespace MWGui
|
|||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_SpellBuying);
|
||||
}
|
||||
|
||||
void SpellBuyingWindow::addSpell(const std::string& spellId)
|
||||
bool SpellBuyingWindow::sortSpells (const ESM::Spell* left, const ESM::Spell* right)
|
||||
{
|
||||
std::string leftName = Misc::StringUtils::lowerCase(left->mName);
|
||||
std::string rightName = Misc::StringUtils::lowerCase(right->mName);
|
||||
|
||||
return leftName.compare(rightName) < 0;
|
||||
}
|
||||
|
||||
void SpellBuyingWindow::addSpell(const ESM::Spell& spell)
|
||||
{
|
||||
const MWWorld::ESMStore &store =
|
||||
MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||
int price = static_cast<int>(spell->mData.mCost*store.get<ESM::GameSetting>().find("fSpellValueMult")->getFloat());
|
||||
int price = static_cast<int>(spell.mData.mCost*store.get<ESM::GameSetting>().find("fSpellValueMult")->getFloat());
|
||||
price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr,price,true);
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
@ -75,13 +81,13 @@ namespace MWGui
|
|||
mCurrentY += sLineHeight;
|
||||
|
||||
toAdd->setUserData(price);
|
||||
toAdd->setCaptionWithReplacing(spell->mName+" - "+MyGUI::utility::toString(price)+"#{sgp}");
|
||||
toAdd->setCaptionWithReplacing(spell.mName+" - "+MyGUI::utility::toString(price)+"#{sgp}");
|
||||
toAdd->setSize(mSpellsView->getWidth(),sLineHeight);
|
||||
toAdd->eventMouseWheel += MyGUI::newDelegate(this, &SpellBuyingWindow::onMouseWheel);
|
||||
toAdd->setUserString("ToolTipType", "Spell");
|
||||
toAdd->setUserString("Spell", spellId);
|
||||
toAdd->setUserString("Spell", spell.mId);
|
||||
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellBuyingWindow::onSpellButtonClick);
|
||||
mSpellsWidgetMap.insert(std::make_pair (toAdd, spellId));
|
||||
mSpellsWidgetMap.insert(std::make_pair (toAdd, spell.mId));
|
||||
}
|
||||
|
||||
void SpellBuyingWindow::clearSpells()
|
||||
|
@ -93,7 +99,7 @@ namespace MWGui
|
|||
mSpellsWidgetMap.clear();
|
||||
}
|
||||
|
||||
void SpellBuyingWindow::startSpellBuying(const MWWorld::Ptr& actor)
|
||||
void SpellBuyingWindow::startSpellBuying(const MWWorld::Ptr& actor, int startOffset)
|
||||
{
|
||||
center();
|
||||
mPtr = actor;
|
||||
|
@ -101,6 +107,8 @@ namespace MWGui
|
|||
|
||||
MWMechanics::Spells& merchantSpells = actor.getClass().getCreatureStats (actor).getSpells();
|
||||
|
||||
std::vector<const ESM::Spell*> spellsToSort;
|
||||
|
||||
for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter)
|
||||
{
|
||||
const ESM::Spell* spell = iter->first;
|
||||
|
@ -120,15 +128,25 @@ namespace MWGui
|
|||
if (playerHasSpell(iter->first->mId))
|
||||
continue;
|
||||
|
||||
addSpell (iter->first->mId);
|
||||
spellsToSort.push_back(iter->first);
|
||||
}
|
||||
|
||||
std::stable_sort(spellsToSort.begin(), spellsToSort.end(), sortSpells);
|
||||
|
||||
for (std::vector<const ESM::Spell*>::iterator it = spellsToSort.begin() ; it != spellsToSort.end(); ++it)
|
||||
{
|
||||
addSpell(**it);
|
||||
}
|
||||
|
||||
spellsToSort.clear();
|
||||
|
||||
updateLabels();
|
||||
|
||||
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
|
||||
mSpellsView->setVisibleVScroll(false);
|
||||
mSpellsView->setCanvasSize (MyGUI::IntSize(mSpellsView->getWidth(), std::max(mSpellsView->getHeight(), mCurrentY)));
|
||||
mSpellsView->setVisibleVScroll(true);
|
||||
mSpellsView->setViewOffset(MyGUI::IntPoint(0, startOffset));
|
||||
}
|
||||
|
||||
bool SpellBuyingWindow::playerHasSpell(const std::string &id)
|
||||
|
@ -165,7 +183,7 @@ namespace MWGui
|
|||
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
|
||||
npcStats.setGoldPool(npcStats.getGoldPool() + price);
|
||||
|
||||
startSpellBuying(mPtr);
|
||||
startSpellBuying(mPtr, mSpellsView->getViewOffset().top);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "windowbase.hpp"
|
||||
#include "referenceinterface.hpp"
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
class Gui;
|
||||
|
@ -23,7 +25,7 @@ namespace MWGui
|
|||
public:
|
||||
SpellBuyingWindow();
|
||||
|
||||
void startSpellBuying(const MWWorld::Ptr& actor);
|
||||
void startSpellBuying(const MWWorld::Ptr& actor, int startOffset);
|
||||
|
||||
virtual void exit();
|
||||
|
||||
|
@ -38,7 +40,7 @@ namespace MWGui
|
|||
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
||||
void onSpellButtonClick(MyGUI::Widget* _sender);
|
||||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
void addSpell(const std::string& spellID);
|
||||
void addSpell(const ESM::Spell& spell);
|
||||
void clearSpells();
|
||||
int mLastPos,mCurrentY;
|
||||
|
||||
|
@ -49,6 +51,9 @@ namespace MWGui
|
|||
virtual void onReferenceUnavailable();
|
||||
|
||||
bool playerHasSpell (const std::string& id);
|
||||
|
||||
private:
|
||||
static bool sortSpells (const ESM::Spell* left, const ESM::Spell* right);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -473,22 +473,11 @@ namespace MWGui
|
|||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
|
||||
{
|
||||
float x = 0.5f * (it->mMagnMin + it->mMagnMax);
|
||||
const ESM::ENAMstruct& effect = *it;
|
||||
|
||||
const ESM::MagicEffect* effect =
|
||||
store.get<ESM::MagicEffect>().find(it->mEffectID);
|
||||
y += std::max(1.f, MWMechanics::calcEffectCost(effect));
|
||||
|
||||
x *= 0.1f * effect->mData.mBaseCost;
|
||||
x *= 1 + it->mDuration;
|
||||
x += 0.05f * std::max(1, it->mArea) * effect->mData.mBaseCost;
|
||||
|
||||
float fEffectCostMult =
|
||||
store.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
|
||||
y += x * fEffectCostMult;
|
||||
y = std::max(1.f,y);
|
||||
|
||||
if (it->mRange == ESM::RT_Target)
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
y *= 1.5;
|
||||
}
|
||||
|
||||
|
@ -508,8 +497,10 @@ namespace MWGui
|
|||
|
||||
mPriceLabel->setCaption(MyGUI::utility::toString(int(price)));
|
||||
|
||||
float chance = MWMechanics::getSpellSuccessChance(&mSpell, MWMechanics::getPlayer());
|
||||
mSuccessChance->setCaption(MyGUI::utility::toString(int(chance)));
|
||||
float chance = MWMechanics::calcSpellBaseSuccessChance(&mSpell, MWMechanics::getPlayer(), NULL);
|
||||
|
||||
int intChance = std::min(100, int(chance));
|
||||
mSuccessChance->setCaption(MyGUI::utility::toString(intChance));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -122,8 +122,6 @@ namespace MWGui
|
|||
mCurrentBalance = 0;
|
||||
mCurrentMerchantOffer = 0;
|
||||
|
||||
restock();
|
||||
|
||||
std::vector<MWWorld::Ptr> itemSources;
|
||||
MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
|
||||
|
||||
|
@ -209,7 +207,7 @@ namespace MWGui
|
|||
void TradeWindow::sellItem(MyGUI::Widget* sender, int count)
|
||||
{
|
||||
const ItemStack& item = mTradeModel->getItem(mItemToSell);
|
||||
std::string sound = item.mBase.getClass().getDownSoundId(item.mBase);
|
||||
std::string sound = item.mBase.getClass().getUpSoundId(item.mBase);
|
||||
MWBase::Environment::get().getWindowManager()->playSound(sound);
|
||||
|
||||
TradeItemModel* playerTradeModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getTradeModel();
|
||||
|
@ -357,6 +355,8 @@ namespace MWGui
|
|||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Item Gold Up");
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Barter);
|
||||
|
||||
restock();
|
||||
}
|
||||
|
||||
void TradeWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
|
|
|
@ -2072,7 +2072,7 @@ namespace MWGui
|
|||
void WindowManager::startSpellBuying(const MWWorld::Ptr &actor)
|
||||
{
|
||||
pushGuiMode(GM_SpellBuying);
|
||||
mSpellBuyingWindow->startSpellBuying(actor);
|
||||
mSpellBuyingWindow->startSpellBuying(actor, 0);
|
||||
}
|
||||
|
||||
void WindowManager::startTrade(const MWWorld::Ptr &actor)
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
|
||||
#include "../mwmechanics/aibreathe.hpp"
|
||||
|
||||
#include "spellcasting.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
@ -863,6 +865,15 @@ namespace MWMechanics
|
|||
if (stats.getTimeToStartDrowning() == -1.f)
|
||||
stats.setTimeToStartDrowning(fHoldBreathTime);
|
||||
|
||||
if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2)
|
||||
{
|
||||
if(ptr != MWMechanics::getPlayer() ) {
|
||||
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once
|
||||
seq.stack(MWMechanics::AiBreathe(), ptr);
|
||||
}
|
||||
}
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3())));
|
||||
if((world->isSubmerged(ptr) || knockedOutUnderwater)
|
||||
|
@ -1625,6 +1636,11 @@ namespace MWMechanics
|
|||
calculateCreatureStatModifiers (iter->first, duration);
|
||||
if (iter->first.getClass().isNpc())
|
||||
calculateNpcStatModifiers(iter->first, duration);
|
||||
|
||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
|
||||
if (animation)
|
||||
animation->updateEffects(duration);
|
||||
|
||||
}
|
||||
|
||||
fastForwardAi();
|
||||
|
|
54
apps/openmw/mwmechanics/aibreathe.cpp
Normal file
54
apps/openmw/mwmechanics/aibreathe.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "aibreathe.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "npcstats.hpp"
|
||||
|
||||
#include "movement.hpp"
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiBreathe::AiBreathe()
|
||||
: AiPackage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
{
|
||||
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->getFloat();
|
||||
|
||||
const MWWorld::Class& actorClass = actor.getClass();
|
||||
if (actorClass.isNpc())
|
||||
{
|
||||
if (actorClass.getNpcStats(actor).getTimeToStartDrowning() < fHoldBreathTime / 2)
|
||||
{
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
smoothTurn(actor, -180, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const
|
||||
{
|
||||
return new AiBreathe(*this);
|
||||
}
|
||||
|
||||
int MWMechanics::AiBreathe::getTypeId() const
|
||||
{
|
||||
return TypeIdBreathe;
|
||||
}
|
||||
|
||||
unsigned int MWMechanics::AiBreathe::getPriority() const
|
||||
{
|
||||
return 2;
|
||||
}
|
28
apps/openmw/mwmechanics/aibreathe.hpp
Normal file
28
apps/openmw/mwmechanics/aibreathe.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef GAME_MWMECHANICS_AIBREATHE_H
|
||||
#define GAME_MWMECHANICS_AIBREATHE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief AiPackage to have an actor resurface to breathe
|
||||
// The AI will go up if lesser than half breath left
|
||||
class AiBreathe : public AiPackage
|
||||
{
|
||||
public:
|
||||
AiBreathe();
|
||||
|
||||
virtual AiBreathe *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration);
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
virtual unsigned int getPriority() const;
|
||||
|
||||
virtual bool canCancel() const { return false; }
|
||||
virtual bool shouldCancelPreviousAi() const { return false; }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
@ -603,6 +603,10 @@ namespace MWMechanics
|
|||
// opponent's weapon range, or not backing up if opponent is also using a ranged weapon
|
||||
if (isDistantCombat && distToTarget < rangeAttack / 4)
|
||||
{
|
||||
// actor should not back up into water
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))
|
||||
return;
|
||||
|
||||
mMovement.mPosition[1] = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,607 +16,25 @@
|
|||
#include "npcstats.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
#include "combat.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// RangeTypes using bitflags to allow multiple range types, as can be the case with spells having multiple effects.
|
||||
enum RangeTypes
|
||||
{
|
||||
Self = 0x1,
|
||||
Touch = 0x10,
|
||||
Target = 0x100
|
||||
};
|
||||
|
||||
int getRangeTypes (const ESM::EffectList& effects)
|
||||
{
|
||||
int types = 0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||
{
|
||||
if (it->mRange == ESM::RT_Self)
|
||||
types |= Self;
|
||||
else if (it->mRange == ESM::RT_Touch)
|
||||
types |= Touch;
|
||||
else if (it->mRange == ESM::RT_Target)
|
||||
types |= Target;
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
float suggestCombatRange(int rangeTypes)
|
||||
{
|
||||
static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
||||
static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat();
|
||||
|
||||
// This distance is a possible distance of melee attack
|
||||
static float distance = fCombatDistance * std::max(2.f, fHandToHandReach);
|
||||
|
||||
if (rangeTypes & Touch)
|
||||
{
|
||||
return fCombatDistance;
|
||||
}
|
||||
|
||||
return distance * 4;
|
||||
}
|
||||
|
||||
int numEffectsToDispel (const MWWorld::Ptr& actor, int effectFilter=-1, bool negative = true)
|
||||
{
|
||||
int toCure=0;
|
||||
const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)
|
||||
{
|
||||
const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;
|
||||
for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();
|
||||
effectIt != params.mEffects.end(); ++effectIt)
|
||||
{
|
||||
int effectId = effectIt->mEffectId;
|
||||
if (effectFilter != -1 && effectId != effectFilter)
|
||||
continue;
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);
|
||||
|
||||
if (effectIt->mDuration <= 3) // Don't attempt to dispel if effect runs out shortly anyway
|
||||
continue;
|
||||
|
||||
if (negative && magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
++toCure;
|
||||
|
||||
if (!negative && !(magicEffect->mData.mFlags & ESM::MagicEffect::Harmful))
|
||||
++toCure;
|
||||
}
|
||||
}
|
||||
return toCure;
|
||||
}
|
||||
|
||||
}
|
||||
#include "weaponpriority.hpp"
|
||||
#include "spellpriority.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
float ratePotion (const MWWorld::Ptr &item, const MWWorld::Ptr& actor)
|
||||
float suggestCombatRange(int rangeTypes)
|
||||
{
|
||||
if (item.getTypeName() != typeid(ESM::Potion).name())
|
||||
return 0.f;
|
||||
static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCombatDistance")->getFloat();
|
||||
static float fHandToHandReach = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHandToHandReach")->getFloat();
|
||||
|
||||
const ESM::Potion* potion = item.get<ESM::Potion>()->mBase;
|
||||
return rateEffects(potion->mEffects, actor, MWWorld::Ptr());
|
||||
}
|
||||
// This distance is a possible distance of melee attack
|
||||
static float distance = fCombatDistance * std::max(2.f, fHandToHandReach);
|
||||
|
||||
float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type,
|
||||
float arrowRating, float boltRating)
|
||||
{
|
||||
if (item.getTypeName() != typeid(ESM::Weapon).name())
|
||||
return 0.f;
|
||||
|
||||
const ESM::Weapon* weapon = item.get<ESM::Weapon>()->mBase;
|
||||
|
||||
if (type != -1 && weapon->mData.mType != type)
|
||||
return 0.f;
|
||||
|
||||
float rating=0.f;
|
||||
float bonus=0.f;
|
||||
|
||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
|
||||
bonus+=1.5f;
|
||||
|
||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||
if (rangeTypes & RangeTypes::Touch)
|
||||
{
|
||||
rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
rating += weapon->mData.mSlash[i];
|
||||
rating += weapon->mData.mThrust[i];
|
||||
rating += weapon->mData.mChop[i];
|
||||
}
|
||||
rating /= 6.f;
|
||||
return fCombatDistance;
|
||||
}
|
||||
|
||||
if (item.getClass().hasItemHealth(item))
|
||||
{
|
||||
if (item.getClass().getItemHealth(item) == 0)
|
||||
return 0.f;
|
||||
rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item));
|
||||
}
|
||||
|
||||
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
||||
{
|
||||
if (arrowRating <= 0.f)
|
||||
rating = 0.f;
|
||||
else
|
||||
rating += arrowRating;
|
||||
}
|
||||
else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
if (boltRating <= 0.f)
|
||||
rating = 0.f;
|
||||
else
|
||||
rating += boltRating;
|
||||
}
|
||||
|
||||
if (!weapon->mEnchant.empty())
|
||||
{
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes
|
||||
&& (item.getCellRef().getEnchantmentCharge() == -1
|
||||
|| item.getCellRef().getEnchantmentCharge() >= enchantment->mData.mCost))
|
||||
rating += rateEffects(enchantment->mEffects, actor, enemy);
|
||||
}
|
||||
|
||||
int skill = item.getClass().getEquipmentSkill(item);
|
||||
if (skill != -1)
|
||||
rating *= actor.getClass().getSkill(actor, skill) / 100.f;
|
||||
|
||||
return rating + bonus;
|
||||
}
|
||||
|
||||
float rateSpell(const ESM::Spell *spell, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
float successChance = MWMechanics::getSpellSuccessChance(spell, actor);
|
||||
if (successChance == 0.f)
|
||||
return 0.f;
|
||||
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 0.f;
|
||||
|
||||
// Don't make use of racial bonus spells, like MW. Can be made optional later
|
||||
if (actor.getClass().isNpc())
|
||||
{
|
||||
std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);
|
||||
if (race->mPowers.exists(spell->mId))
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
if (spell->mData.mCost > stats.getMagicka().getCurrent())
|
||||
return 0.f;
|
||||
|
||||
// Spells don't stack, so early out if the spell is still active on the target
|
||||
int types = getRangeTypes(spell->mEffects);
|
||||
if ((types & Self) && stats.getActiveSpells().isSpellActive(spell->mId))
|
||||
return 0.f;
|
||||
if ( ((types & Touch) || (types & Target)) && enemy.getClass().getCreatureStats(enemy).getActiveSpells().isSpellActive(spell->mId))
|
||||
return 0.f;
|
||||
|
||||
return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);
|
||||
}
|
||||
|
||||
float rateMagicItem(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
if (ptr.getClass().getEnchantment(ptr).empty())
|
||||
return 0.f;
|
||||
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));
|
||||
|
||||
if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
|
||||
{
|
||||
return rateEffects(enchantment->mEffects, actor, enemy);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (!ptr.getClass().canBeEquipped(ptr, actor))
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)
|
||||
{
|
||||
// NOTE: enemy may be empty
|
||||
|
||||
float rating = 1;
|
||||
switch (effect.mEffectID)
|
||||
{
|
||||
case ESM::MagicEffect::Soultrap:
|
||||
case ESM::MagicEffect::AlmsiviIntervention:
|
||||
case ESM::MagicEffect::DivineIntervention:
|
||||
case ESM::MagicEffect::CalmHumanoid:
|
||||
case ESM::MagicEffect::CalmCreature:
|
||||
case ESM::MagicEffect::FrenzyHumanoid:
|
||||
case ESM::MagicEffect::FrenzyCreature:
|
||||
case ESM::MagicEffect::DemoralizeHumanoid:
|
||||
case ESM::MagicEffect::DemoralizeCreature:
|
||||
case ESM::MagicEffect::RallyHumanoid:
|
||||
case ESM::MagicEffect::RallyCreature:
|
||||
case ESM::MagicEffect::Charm:
|
||||
case ESM::MagicEffect::DetectAnimal:
|
||||
case ESM::MagicEffect::DetectEnchantment:
|
||||
case ESM::MagicEffect::DetectKey:
|
||||
case ESM::MagicEffect::Telekinesis:
|
||||
case ESM::MagicEffect::Mark:
|
||||
case ESM::MagicEffect::Recall:
|
||||
case ESM::MagicEffect::Jump:
|
||||
case ESM::MagicEffect::WaterBreathing:
|
||||
case ESM::MagicEffect::SwiftSwim:
|
||||
case ESM::MagicEffect::WaterWalking:
|
||||
case ESM::MagicEffect::SlowFall:
|
||||
case ESM::MagicEffect::Light:
|
||||
case ESM::MagicEffect::Lock:
|
||||
case ESM::MagicEffect::Open:
|
||||
case ESM::MagicEffect::TurnUndead:
|
||||
case ESM::MagicEffect::WeaknessToCommonDisease:
|
||||
case ESM::MagicEffect::WeaknessToBlightDisease:
|
||||
case ESM::MagicEffect::WeaknessToCorprusDisease:
|
||||
case ESM::MagicEffect::CureCommonDisease:
|
||||
case ESM::MagicEffect::CureBlightDisease:
|
||||
case ESM::MagicEffect::CureCorprusDisease:
|
||||
case ESM::MagicEffect::ResistBlightDisease:
|
||||
case ESM::MagicEffect::ResistCommonDisease:
|
||||
case ESM::MagicEffect::ResistCorprusDisease:
|
||||
case ESM::MagicEffect::Invisibility:
|
||||
case ESM::MagicEffect::Chameleon:
|
||||
case ESM::MagicEffect::NightEye:
|
||||
case ESM::MagicEffect::Vampirism:
|
||||
case ESM::MagicEffect::StuntedMagicka:
|
||||
case ESM::MagicEffect::ExtraSpell:
|
||||
case ESM::MagicEffect::RemoveCurse:
|
||||
case ESM::MagicEffect::CommandCreature:
|
||||
case ESM::MagicEffect::CommandHumanoid:
|
||||
return 0.f;
|
||||
|
||||
case ESM::MagicEffect::Sound:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// there is no need to cast sound if enemy is not able to cast spells
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
|
||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::RestoreAttribute:
|
||||
return 0.f; // TODO: implement based on attribute damage
|
||||
case ESM::MagicEffect::RestoreSkill:
|
||||
return 0.f; // TODO: implement based on skill damage
|
||||
|
||||
case ESM::MagicEffect::ResistFire:
|
||||
case ESM::MagicEffect::ResistFrost:
|
||||
case ESM::MagicEffect::ResistMagicka:
|
||||
case ESM::MagicEffect::ResistNormalWeapons:
|
||||
case ESM::MagicEffect::ResistParalysis:
|
||||
case ESM::MagicEffect::ResistPoison:
|
||||
case ESM::MagicEffect::ResistShock:
|
||||
case ESM::MagicEffect::SpellAbsorption:
|
||||
case ESM::MagicEffect::Reflect:
|
||||
return 0.f; // probably useless since we don't know in advance what the enemy will cast
|
||||
|
||||
// don't cast these for now as they would make the NPC cast the same effect over and over again, especially when they have potions
|
||||
case ESM::MagicEffect::FortifyAttribute:
|
||||
case ESM::MagicEffect::FortifyHealth:
|
||||
case ESM::MagicEffect::FortifyMagicka:
|
||||
case ESM::MagicEffect::FortifyFatigue:
|
||||
case ESM::MagicEffect::FortifySkill:
|
||||
case ESM::MagicEffect::FortifyMaximumMagicka:
|
||||
case ESM::MagicEffect::FortifyAttack:
|
||||
return 0.f;
|
||||
|
||||
case ESM::MagicEffect::Burden:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
// burden makes sense only to overburden an enemy
|
||||
float burden = enemy.getClass().getEncumbrance(enemy) - enemy.getClass().getCapacity(enemy);
|
||||
if (burden > 0)
|
||||
return 0.f;
|
||||
|
||||
if ((effect.mMagnMin + effect.mMagnMax)/2.f > -burden)
|
||||
rating *= 3;
|
||||
else
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::Feather:
|
||||
{
|
||||
// Ignore actors without inventory
|
||||
if (!actor.getClass().hasInventoryStore(actor))
|
||||
return 0.f;
|
||||
|
||||
// feather makes sense only for overburden actors
|
||||
float burden = actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor);
|
||||
if (burden <= 0)
|
||||
return 0.f;
|
||||
|
||||
if ((effect.mMagnMin + effect.mMagnMax)/2.f >= burden)
|
||||
rating *= 3;
|
||||
else
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::Levitate:
|
||||
return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway
|
||||
case ESM::MagicEffect::BoundBoots:
|
||||
case ESM::MagicEffect::BoundHelm:
|
||||
if (actor.getClass().isNpc())
|
||||
{
|
||||
// Beast races can't wear helmets or boots
|
||||
std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);
|
||||
if (race->mData.mFlags & ESM::Race::Beast)
|
||||
return 0.f;
|
||||
}
|
||||
// Intended fall-through
|
||||
// Creatures can not wear armor
|
||||
case ESM::MagicEffect::BoundCuirass:
|
||||
case ESM::MagicEffect::BoundGloves:
|
||||
if (!actor.getClass().isNpc())
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::RestoreHealth:
|
||||
case ESM::MagicEffect::RestoreMagicka:
|
||||
case ESM::MagicEffect::RestoreFatigue:
|
||||
if (effect.mRange == ESM::RT_Self)
|
||||
{
|
||||
int priority = 1;
|
||||
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
||||
priority = 10;
|
||||
const DynamicStat<float>& current = actor.getClass().getCreatureStats(actor).
|
||||
getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
||||
float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration;
|
||||
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
||||
if (current.getModified() - current.getCurrent() > toHeal
|
||||
|| current.getCurrent() < current.getModified()*0.5)
|
||||
{
|
||||
return 10000.f * priority
|
||||
- (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion
|
||||
}
|
||||
else
|
||||
return -10000.f * priority; // Save for later
|
||||
}
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::Dispel:
|
||||
{
|
||||
int numPositive = 0;
|
||||
int numNegative = 0;
|
||||
int diff = 0;
|
||||
|
||||
if (effect.mRange == ESM::RT_Self)
|
||||
{
|
||||
numPositive = numEffectsToDispel(actor, -1, false);
|
||||
numNegative = numEffectsToDispel(actor);
|
||||
|
||||
diff = numNegative - numPositive;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
numPositive = numEffectsToDispel(enemy, -1, false);
|
||||
numNegative = numEffectsToDispel(enemy);
|
||||
|
||||
diff = numPositive - numNegative;
|
||||
|
||||
// if rating < 0 here, the spell will be considered as negative later
|
||||
rating *= -1;
|
||||
}
|
||||
|
||||
if (diff <= 0)
|
||||
return 0.f;
|
||||
|
||||
rating *= (diff) / 5.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Prefer Cure effects over Dispel, because Dispel also removes positive effects
|
||||
case ESM::MagicEffect::CureParalyzation:
|
||||
return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Paralyze);
|
||||
|
||||
case ESM::MagicEffect::CurePoison:
|
||||
return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Poison);
|
||||
case ESM::MagicEffect::DisintegrateArmor:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);
|
||||
|
||||
// According to UESP
|
||||
static const int armorSlots[] = {
|
||||
MWWorld::InventoryStore::Slot_CarriedLeft,
|
||||
MWWorld::InventoryStore::Slot_Cuirass,
|
||||
MWWorld::InventoryStore::Slot_LeftPauldron,
|
||||
MWWorld::InventoryStore::Slot_RightPauldron,
|
||||
MWWorld::InventoryStore::Slot_LeftGauntlet,
|
||||
MWWorld::InventoryStore::Slot_RightGauntlet,
|
||||
MWWorld::InventoryStore::Slot_Helmet,
|
||||
MWWorld::InventoryStore::Slot_Greaves,
|
||||
MWWorld::InventoryStore::Slot_Boots
|
||||
};
|
||||
|
||||
bool enemyHasArmor = false;
|
||||
|
||||
// Ignore enemy without armor
|
||||
for (unsigned int i=0; i<sizeof(armorSlots)/sizeof(int); ++i)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator item = inv.getSlot(armorSlots[i]);
|
||||
|
||||
if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor))
|
||||
{
|
||||
enemyHasArmor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enemyHasArmor)
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::DisintegrateWeapon:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);
|
||||
MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
||||
// Ignore enemy without weapons
|
||||
if (item == inv.end() || (item.getType() != MWWorld::ContainerStore::Type_Weapon))
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::DamageAttribute:
|
||||
case ESM::MagicEffect::DrainAttribute:
|
||||
if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getAttribute(effect.mAttribute).getModified() <= 0)
|
||||
return 0.f;
|
||||
{
|
||||
if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length)
|
||||
{
|
||||
const float attributePriorities[ESM::Attribute::Length] = {
|
||||
1.0f, // Strength
|
||||
0.5f, // Intelligence
|
||||
0.6f, // Willpower
|
||||
0.7f, // Agility
|
||||
0.5f, // Speed
|
||||
0.8f, // Endurance
|
||||
0.7f, // Personality
|
||||
0.3f // Luck
|
||||
};
|
||||
rating *= attributePriorities[effect.mAttribute];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::DamageSkill:
|
||||
case ESM::MagicEffect::DrainSkill:
|
||||
if (enemy.isEmpty() || !enemy.getClass().isNpc())
|
||||
return 0.f;
|
||||
if (enemy.getClass().getNpcStats(enemy).getSkill(effect.mSkill).getModified() <= 0)
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
|
||||
// Underwater casting not possible
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
{
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f))
|
||||
return 0.f;
|
||||
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
rating *= magicEffect->mData.mBaseCost;
|
||||
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
rating *= -1.f;
|
||||
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Check resistance for harmful effects
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
float resistance = MWMechanics::getEffectResistanceAttribute(effect.mEffectID, &stats.getMagicEffects());
|
||||
|
||||
rating *= (1.f - std::min(resistance, 100.f) / 100.f);
|
||||
}
|
||||
|
||||
// for harmful no-magnitude effects (e.g. silence) check if enemy is already has them
|
||||
// for non-harmful no-magnitude effects (e.g. bound items) check if actor is already has them
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
|
||||
{
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rating *= (effect.mMagnMin + effect.mMagnMax)/2.f;
|
||||
}
|
||||
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
|
||||
rating *= effect.mDuration;
|
||||
|
||||
// Currently treating all "on target" or "on touch" effects to target the enemy actor.
|
||||
// Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors.
|
||||
if (effect.mRange != ESM::RT_Self)
|
||||
rating *= -1.f;
|
||||
|
||||
return rating;
|
||||
}
|
||||
|
||||
float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
// NOTE: enemy may be empty
|
||||
float rating = 0.f;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
||||
{
|
||||
rating += rateEffect(*it, actor, enemy);
|
||||
}
|
||||
return rating;
|
||||
return distance * 4;
|
||||
}
|
||||
|
||||
void ActionSpell::prepare(const MWWorld::Ptr &actor)
|
||||
|
@ -638,7 +56,7 @@ namespace MWMechanics
|
|||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);
|
||||
int types = getRangeTypes(spell->mEffects);
|
||||
|
||||
isRanged = (types & Target) | (types & Self);
|
||||
isRanged = (types & RangeTypes::Target) | (types & RangeTypes::Self);
|
||||
return suggestCombatRange(types);
|
||||
}
|
||||
|
||||
|
@ -832,6 +250,79 @@ namespace MWMechanics
|
|||
return bestAction;
|
||||
}
|
||||
|
||||
float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)
|
||||
{
|
||||
Spells& spells = actor.getClass().getCreatureStats(actor).getSpells();
|
||||
|
||||
float bestActionRating = 0.f;
|
||||
// Default to hand-to-hand combat
|
||||
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||
{
|
||||
return bestActionRating;
|
||||
}
|
||||
|
||||
if (actor.getClass().hasInventoryStore(actor))
|
||||
{
|
||||
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateMagicItem(*it, actor, enemy);
|
||||
if (rating > bestActionRating)
|
||||
{
|
||||
bestActionRating = rating;
|
||||
}
|
||||
}
|
||||
|
||||
float bestArrowRating = 0;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Arrow);
|
||||
if (rating > bestArrowRating)
|
||||
{
|
||||
bestArrowRating = rating;
|
||||
}
|
||||
}
|
||||
|
||||
float bestBoltRating = 0;
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
float rating = rateWeapon(*it, actor, enemy, ESM::Weapon::Bolt);
|
||||
if (rating > bestBoltRating)
|
||||
{
|
||||
bestBoltRating = rating;
|
||||
}
|
||||
}
|
||||
|
||||
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
std::vector<int> equipmentSlots = it->getClass().getEquipmentSlots(*it).first;
|
||||
if (std::find(equipmentSlots.begin(), equipmentSlots.end(), (int)MWWorld::InventoryStore::Slot_CarriedRight)
|
||||
== equipmentSlots.end())
|
||||
continue;
|
||||
|
||||
float rating = rateWeapon(*it, actor, enemy, -1, bestArrowRating, bestBoltRating);
|
||||
if (rating > bestActionRating)
|
||||
{
|
||||
bestActionRating = rating;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = it->first;
|
||||
|
||||
float rating = rateSpell(spell, actor, enemy);
|
||||
if (rating > bestActionRating)
|
||||
{
|
||||
bestActionRating = rating;
|
||||
}
|
||||
}
|
||||
|
||||
return bestActionRating;
|
||||
}
|
||||
|
||||
|
||||
float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, bool minusZDist)
|
||||
{
|
||||
|
@ -1016,67 +507,6 @@ namespace MWMechanics
|
|||
return true;
|
||||
}
|
||||
|
||||
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat();
|
||||
static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat();
|
||||
|
||||
float mult = fAIMagicSpellMult;
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
|
||||
spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
|
||||
{
|
||||
if (effectIt->mRange == ESM::RT_Target)
|
||||
{
|
||||
if (!MWBase::Environment::get().getWorld()->isSwimming(enemy))
|
||||
mult = fAIRangeMagicSpellMult;
|
||||
else
|
||||
mult = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return MWMechanics::getSpellSuccessChance(spell, actor) * mult;
|
||||
}
|
||||
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat();
|
||||
static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat();
|
||||
static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat();
|
||||
|
||||
if (weapon.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;
|
||||
float chopMult = fAIMeleeWeaponMult;
|
||||
float bonusDamage = 0.f;
|
||||
|
||||
const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;
|
||||
|
||||
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||
{
|
||||
if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))
|
||||
{
|
||||
bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1];
|
||||
chopMult = fAIRangeMeleeWeaponMult;
|
||||
}
|
||||
else
|
||||
chopMult = 0.f;
|
||||
}
|
||||
|
||||
float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult;
|
||||
float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult;
|
||||
float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult;
|
||||
|
||||
return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult
|
||||
+ std::max(std::max(chopRating, slashRating), thrustRating);
|
||||
}
|
||||
|
||||
float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
@ -1122,5 +552,4 @@ namespace MWMechanics
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
|
@ -88,26 +87,13 @@ namespace MWMechanics
|
|||
virtual const ESM::Weapon* getWeapon() const;
|
||||
};
|
||||
|
||||
float rateSpell (const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float rateMagicItem (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float ratePotion (const MWWorld::Ptr& item, const MWWorld::Ptr &actor);
|
||||
/// @param type Skip all weapons that are not of this type (i.e. return rating 0)
|
||||
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
|
||||
int type=-1, float arrowRating=0.f, float boltRating=0.f);
|
||||
|
||||
/// @note target may be empty
|
||||
float rateEffect (const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
/// @note target may be empty
|
||||
float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
|
||||
std::shared_ptr<Action> prepareNextAction (const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float getBestActionRating(const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy);
|
||||
|
||||
float getDistanceMinusHalfExtents(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, bool minusZDist=false);
|
||||
float getMaxAttackDistance(const MWWorld::Ptr& actor);
|
||||
bool canFight(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
|
||||
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float vanillaRateFlee(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
bool makeFleeDecision(const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, float antiFleeRating);
|
||||
}
|
||||
|
|
|
@ -176,7 +176,9 @@ void MWMechanics::AiPackage::evadeObstacles(const MWWorld::Ptr& actor, float dur
|
|||
if (!mObstacleCheck.check(actor, duration)) return;
|
||||
|
||||
// first check if obstacle is a door
|
||||
MWWorld::Ptr door = getNearbyDoor(actor); // NOTE: checks interior cells only
|
||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
|
||||
|
||||
MWWorld::Ptr door = getNearbyDoor(actor, distance);
|
||||
if (door != MWWorld::Ptr())
|
||||
{
|
||||
// note: AiWander currently does not open doors
|
||||
|
|
|
@ -41,12 +41,13 @@ namespace MWMechanics
|
|||
TypeIdFollow = 3,
|
||||
TypeIdActivate = 4,
|
||||
|
||||
// These 4 are not really handled as Ai Packages in the MW engine
|
||||
// These 5 are not really handled as Ai Packages in the MW engine
|
||||
// For compatibility do *not* return these in the getCurrentAiPackage script function..
|
||||
TypeIdCombat = 5,
|
||||
TypeIdPursue = 6,
|
||||
TypeIdAvoidDoor = 7,
|
||||
TypeIdFace = 8
|
||||
TypeIdFace = 8,
|
||||
TypeIdBreathe = 9
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "aifollow.hpp"
|
||||
#include "aiactivate.hpp"
|
||||
#include "aicombat.hpp"
|
||||
#include "aicombataction.hpp"
|
||||
#include "aipursue.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
|
@ -182,7 +183,8 @@ bool isActualAiPackage(int packageTypeId)
|
|||
return (packageTypeId != AiPackage::TypeIdCombat
|
||||
&& packageTypeId != AiPackage::TypeIdPursue
|
||||
&& packageTypeId != AiPackage::TypeIdAvoidDoor
|
||||
&& packageTypeId != AiPackage::TypeIdFace);
|
||||
&& packageTypeId != AiPackage::TypeIdFace
|
||||
&& packageTypeId != AiPackage::TypeIdBreathe);
|
||||
}
|
||||
|
||||
void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
|
@ -208,6 +210,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
float nearestDist = std::numeric_limits<float>::max();
|
||||
osg::Vec3f vActorPos = actor.getRefData().getPosition().asVec3();
|
||||
|
||||
float bestRating = 0.f;
|
||||
|
||||
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();)
|
||||
{
|
||||
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
|
||||
|
@ -222,6 +226,8 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
}
|
||||
else
|
||||
{
|
||||
float rating = MWMechanics::getBestActionRating(actor, target);
|
||||
|
||||
const ESM::Position &targetPos = target.getRefData().getPosition();
|
||||
|
||||
float distTo = (targetPos.asVec3() - vActorPos).length();
|
||||
|
@ -230,10 +236,12 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
if (it == mPackages.begin())
|
||||
distTo = std::max(0.f, distTo - 50.f);
|
||||
|
||||
if (distTo < nearestDist)
|
||||
// if a target has higher priority than current target or has same priority but closer
|
||||
if (rating > bestRating || ((distTo < nearestDist) && rating == bestRating))
|
||||
{
|
||||
nearestDist = distTo;
|
||||
itActualCombat = it;
|
||||
bestRating = rating;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
|
|
@ -449,11 +449,14 @@ namespace MWMechanics
|
|||
{
|
||||
// Check if an idle actor is too close to a door - if so start walking
|
||||
storage.mDoorCheckDuration += duration;
|
||||
|
||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
|
||||
|
||||
if (storage.mDoorCheckDuration >= DOOR_CHECK_INTERVAL)
|
||||
{
|
||||
storage.mDoorCheckDuration = 0; // restart timer
|
||||
if (mDistance && // actor is not intended to be stationary
|
||||
proximityToDoor(actor, MIN_DIST_TO_DOOR_SQUARED*1.6f*1.6f)) // NOTE: checks interior cells only
|
||||
proximityToDoor(actor, distance*1.6f))
|
||||
{
|
||||
storage.setState(Wander_MoveNow);
|
||||
storage.mTrimCurrentNode = false; // just in case
|
||||
|
@ -486,7 +489,7 @@ namespace MWMechanics
|
|||
float duration, AiWanderStorage& storage, ESM::Position& pos)
|
||||
{
|
||||
// Is there no destination or are we there yet?
|
||||
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, mPathFinder.getPath().back(), duration, DESTINATION_TOLERANCE))
|
||||
if ((!mPathFinder.isPathConstructed()) || pathTo(actor, ESM::Pathgrid::Point(mPathFinder.getPath().back()), duration, DESTINATION_TOLERANCE))
|
||||
{
|
||||
stopWalking(actor, storage);
|
||||
storage.setState(Wander_ChooseAction);
|
||||
|
@ -527,10 +530,12 @@ namespace MWMechanics
|
|||
|
||||
void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration, ESM::Position& pos)
|
||||
{
|
||||
static float distance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
|
||||
|
||||
if (mObstacleCheck.isEvading())
|
||||
{
|
||||
// first check if we're walking into a door
|
||||
if (proximityToDoor(actor)) // NOTE: checks interior cells only
|
||||
if (proximityToDoor(actor, distance))
|
||||
{
|
||||
// remove allowed points then select another random destination
|
||||
storage.mTrimCurrentNode = true;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "autocalcspell.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
@ -255,27 +256,39 @@ namespace MWMechanics
|
|||
|
||||
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
|
||||
{
|
||||
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
||||
float minChance = std::numeric_limits<float>::max();
|
||||
|
||||
const ESM::EffectList& effects = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||
{
|
||||
const ESM::ENAMstruct& effect = *it;
|
||||
float x = static_cast<float>(effect.mDuration);
|
||||
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
|
||||
x = std::max(1.f, x);
|
||||
|
||||
x *= 0.1f * magicEffect->mData.mBaseCost;
|
||||
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
|
||||
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;
|
||||
int minMagn = 1;
|
||||
int maxMagn = 1;
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
|
||||
{
|
||||
minMagn = effect.mMagnMin;
|
||||
maxMagn = effect.mMagnMax;
|
||||
}
|
||||
|
||||
int duration = 0;
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
|
||||
duration = effect.mDuration;
|
||||
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
|
||||
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
|
||||
x *= 0.1 * magicEffect->mData.mBaseCost;
|
||||
x *= 1 + duration;
|
||||
x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;
|
||||
x *= fEffectCostMult;
|
||||
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
x *= 1.5f;
|
||||
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
x *= fEffectCostMult;
|
||||
|
||||
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
|
||||
if (s - x < minChance)
|
||||
{
|
||||
|
|
|
@ -2164,13 +2164,13 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
|
|||
}
|
||||
else
|
||||
{
|
||||
// If the given animation is a looped animation, is already playing
|
||||
// and has not yet reached its Loop Stop key, make it the only animation
|
||||
// in the queue, and retain the loop count from the animation that was
|
||||
// already playing. This emulates observed behavior from the original
|
||||
// engine and allows banners to animate correctly.
|
||||
// If this animation is a looped animation (has a "loop start" key) that is already playing
|
||||
// and has not yet reached the end of the loop, allow it to continue animating with its existing loop count
|
||||
// and remove any other animations that were queued.
|
||||
// This emulates observed behavior from the original allows the script "OutsideBanner" to animate banners correctly.
|
||||
if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname &&
|
||||
mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop start") >= 0)
|
||||
mAnimation->getTextKeyTime(mAnimQueue.front().mGroup + ": loop start") >= 0 &&
|
||||
mAnimation->isPlaying(groupname))
|
||||
{
|
||||
float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop");
|
||||
|
||||
|
@ -2179,8 +2179,7 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
|
|||
|
||||
if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop))
|
||||
{
|
||||
mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, true);
|
||||
mAnimQueue.resize(1);
|
||||
mAnimQueue.resize(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1547,7 +1547,7 @@ namespace MWMechanics
|
|||
{
|
||||
int disposition = 50;
|
||||
if (ptr.getClass().isNpc())
|
||||
disposition = getDerivedDisposition(ptr, false);
|
||||
disposition = getDerivedDisposition(ptr, true);
|
||||
|
||||
int fight = std::max(0, ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified()
|
||||
+ static_cast<int>(getFightDistanceBias(ptr, target) + getFightDispositionBias(static_cast<float>(disposition))));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "obstacle.hpp"
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -23,50 +24,48 @@ namespace MWMechanics
|
|||
{ -1.0f, -1.0f } // move to side and backwards
|
||||
};
|
||||
|
||||
// Proximity check function for interior doors. Given that most interior cells
|
||||
// do not have many doors performance shouldn't be too much of an issue.
|
||||
//
|
||||
// Limitation: there can be false detections, and does not test whether the
|
||||
// actor is facing the door.
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr)
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor, float minDist)
|
||||
{
|
||||
if(getNearbyDoor(actor, minSqr)!=MWWorld::Ptr())
|
||||
if(getNearbyDoor(actor, minDist)!=MWWorld::Ptr())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr)
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist)
|
||||
{
|
||||
MWWorld::CellStore *cell = actor.getCell();
|
||||
|
||||
if(cell->getCell()->isExterior())
|
||||
return MWWorld::Ptr(); // check interior cells only
|
||||
|
||||
// Check all the doors in this cell
|
||||
const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors();
|
||||
const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList;
|
||||
MWWorld::CellRefList<ESM::Door>::List::const_iterator it = refList.begin();
|
||||
osg::Vec3f pos(actor.getRefData().getPosition().asVec3());
|
||||
pos.z() = 0;
|
||||
|
||||
/// TODO: How to check whether the actor is facing a door? Below code is for
|
||||
/// the player, perhaps it can be adapted.
|
||||
//MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||
//if(!ptr.isEmpty())
|
||||
//std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl;
|
||||
osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0));
|
||||
|
||||
/// TODO: The in-game observation of rot[2] value seems to be the
|
||||
/// opposite of the code in World::activateDoor() ::confused::
|
||||
for (; it != refList.end(); ++it)
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||
if((pos - ref.mData.getPosition().asVec3()).length2() < minSqr
|
||||
&& ref.mData.getPosition().rot[2] == ref.mRef.getPosition().rot[2])
|
||||
{
|
||||
// FIXME cast
|
||||
return MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); // found, stop searching
|
||||
}
|
||||
|
||||
osg::Vec3f doorPos(ref.mData.getPosition().asVec3());
|
||||
doorPos.z() = 0;
|
||||
|
||||
float angle = std::acos(actorDir * (doorPos - pos) / (actorDir.length() * (doorPos - pos).length()));
|
||||
|
||||
// Allow 60 degrees angle between actor and door
|
||||
if (angle < -osg::PI / 3 || angle > osg::PI / 3)
|
||||
continue;
|
||||
|
||||
// Door is not close enough
|
||||
if ((pos - doorPos).length2() > minDist*minDist)
|
||||
continue;
|
||||
|
||||
// FIXME cast
|
||||
return MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); // found, stop searching
|
||||
}
|
||||
|
||||
return MWWorld::Ptr(); // none found
|
||||
}
|
||||
|
||||
|
|
|
@ -10,19 +10,14 @@ namespace MWMechanics
|
|||
{
|
||||
struct Movement;
|
||||
|
||||
/// NOTE: determined empirically based on in-game behaviour
|
||||
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
|
||||
|
||||
static const int NUM_EVADE_DIRECTIONS = 4;
|
||||
|
||||
/// tests actor's proximity to a closed door by default
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor,
|
||||
float minSqr = MIN_DIST_TO_DOOR_SQUARED);
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor, float minDist);
|
||||
|
||||
/// Returns door pointer within range. No guarantee is given as to which one
|
||||
/** \return Pointer to the door, or NULL if none exists **/
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor,
|
||||
float minSqr = MIN_DIST_TO_DOOR_SQUARED);
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist);
|
||||
|
||||
class ObstacleCheck
|
||||
{
|
||||
|
|
|
@ -60,8 +60,36 @@ namespace MWMechanics
|
|||
return schoolSkillMap[school];
|
||||
}
|
||||
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
|
||||
float calcEffectCost(const ESM::ENAMstruct& effect)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
|
||||
int minMagn = 1;
|
||||
int maxMagn = 1;
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
|
||||
{
|
||||
minMagn = effect.mMagnMin;
|
||||
maxMagn = effect.mMagnMax;
|
||||
}
|
||||
|
||||
int duration = 0;
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
|
||||
duration = effect.mDuration;
|
||||
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
|
||||
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
|
||||
x *= 0.1 * magicEffect->mData.mBaseCost;
|
||||
x *= 1 + duration;
|
||||
x += 0.05 * std::max(1, effect.mArea) * magicEffect->mData.mBaseCost;
|
||||
|
||||
return x * fEffectCostMult;
|
||||
}
|
||||
|
||||
float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)
|
||||
{
|
||||
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
|
||||
float y = std::numeric_limits<float>::max();
|
||||
float lowestSkill = 0;
|
||||
|
||||
|
@ -70,8 +98,10 @@ namespace MWMechanics
|
|||
float x = static_cast<float>(it->mDuration);
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
|
||||
it->mEffectID);
|
||||
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
|
||||
x = std::max(1.f, x);
|
||||
|
||||
x *= 0.1f * magicEffect->mData.mBaseCost;
|
||||
x *= 0.5f * (it->mMagnMin + it->mMagnMax);
|
||||
x *= it->mArea * 0.05f * magicEffect->mData.mBaseCost;
|
||||
|
@ -91,6 +121,18 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
|
||||
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);
|
||||
|
||||
return castChance;
|
||||
}
|
||||
|
||||
float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
|
||||
{
|
||||
bool godmode = actor == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
@ -114,10 +156,8 @@ namespace MWMechanics
|
|||
|
||||
float castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();
|
||||
|
||||
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
|
||||
float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2f * actorWillpower + 0.1f * actorLuck) * stats.getFatigueTerm();
|
||||
float castChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool) + castBonus;
|
||||
castChance *= stats.getFatigueTerm();
|
||||
|
||||
if (!cap)
|
||||
return std::max(0.f, castChance);
|
||||
|
@ -1165,6 +1205,9 @@ namespace MWMechanics
|
|||
receivedMagicDamage = true;
|
||||
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::DamageHealth, -magnitude);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::DamageMagicka:
|
||||
case ESM::MagicEffect::DamageFatigue:
|
||||
if (!godmode)
|
||||
|
@ -1181,6 +1224,9 @@ namespace MWMechanics
|
|||
receivedMagicDamage = true;
|
||||
adjustDynamicStat(creatureStats, effectKey.mId-ESM::MagicEffect::AbsorbHealth, -magnitude);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::AbsorbMagicka:
|
||||
case ESM::MagicEffect::AbsorbFatigue:
|
||||
if (!godmode)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef MWMECHANICS_SPELLSUCCESS_H
|
||||
#define MWMECHANICS_SPELLSUCCESS_H
|
||||
|
||||
#include <components/esm/effectlist.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
@ -21,6 +22,8 @@ namespace MWMechanics
|
|||
|
||||
ESM::Skill::SkillEnum spellSchoolToSkill(int school);
|
||||
|
||||
float calcEffectCost(const ESM::ENAMstruct& effect);
|
||||
|
||||
bool isSummoningEffect(int effectId);
|
||||
|
||||
/**
|
||||
|
@ -62,6 +65,7 @@ namespace MWMechanics
|
|||
bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, const MWWorld::Ptr& caster, bool castByPlayer);
|
||||
|
||||
int getEffectiveEnchantmentCastCost (float castCost, const MWWorld::Ptr& actor);
|
||||
float calcSpellBaseSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool);
|
||||
|
||||
/// Apply a magic effect that is applied in tick intervals until its remaining time ends or it is removed
|
||||
/// @return Was the effect a tickable effect with a magnitude?
|
||||
|
|
545
apps/openmw/mwmechanics/spellpriority.cpp
Normal file
545
apps/openmw/mwmechanics/spellpriority.cpp
Normal file
|
@ -0,0 +1,545 @@
|
|||
#include "spellpriority.hpp"
|
||||
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "npcstats.hpp"
|
||||
#include "spellcasting.hpp"
|
||||
#include "combat.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
int numEffectsToDispel (const MWWorld::Ptr& actor, int effectFilter=-1, bool negative = true)
|
||||
{
|
||||
int toCure=0;
|
||||
const MWMechanics::ActiveSpells& activeSpells = actor.getClass().getCreatureStats(actor).getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TIterator it = activeSpells.begin(); it != activeSpells.end(); ++it)
|
||||
{
|
||||
const MWMechanics::ActiveSpells::ActiveSpellParams& params = it->second;
|
||||
for (std::vector<MWMechanics::ActiveSpells::ActiveEffect>::const_iterator effectIt = params.mEffects.begin();
|
||||
effectIt != params.mEffects.end(); ++effectIt)
|
||||
{
|
||||
int effectId = effectIt->mEffectId;
|
||||
if (effectFilter != -1 && effectId != effectFilter)
|
||||
continue;
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectId);
|
||||
|
||||
if (effectIt->mDuration <= 3) // Don't attempt to dispel if effect runs out shortly anyway
|
||||
continue;
|
||||
|
||||
if (negative && magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
++toCure;
|
||||
|
||||
if (!negative && !(magicEffect->mData.mFlags & ESM::MagicEffect::Harmful))
|
||||
++toCure;
|
||||
}
|
||||
}
|
||||
return toCure;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
int getRangeTypes (const ESM::EffectList& effects)
|
||||
{
|
||||
int types = 0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||
{
|
||||
if (it->mRange == ESM::RT_Self)
|
||||
types |= RangeTypes::Self;
|
||||
else if (it->mRange == ESM::RT_Touch)
|
||||
types |= RangeTypes::Touch;
|
||||
else if (it->mRange == ESM::RT_Target)
|
||||
types |= RangeTypes::Target;
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
float ratePotion (const MWWorld::Ptr &item, const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (item.getTypeName() != typeid(ESM::Potion).name())
|
||||
return 0.f;
|
||||
|
||||
const ESM::Potion* potion = item.get<ESM::Potion>()->mBase;
|
||||
return rateEffects(potion->mEffects, actor, MWWorld::Ptr());
|
||||
}
|
||||
|
||||
float rateSpell(const ESM::Spell *spell, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
float successChance = MWMechanics::getSpellSuccessChance(spell, actor);
|
||||
if (successChance == 0.f)
|
||||
return 0.f;
|
||||
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 0.f;
|
||||
|
||||
// Don't make use of racial bonus spells, like MW. Can be made optional later
|
||||
if (actor.getClass().isNpc())
|
||||
{
|
||||
std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);
|
||||
if (race->mPowers.exists(spell->mId))
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
if (spell->mData.mCost > stats.getMagicka().getCurrent())
|
||||
return 0.f;
|
||||
|
||||
// Spells don't stack, so early out if the spell is still active on the target
|
||||
int types = getRangeTypes(spell->mEffects);
|
||||
if ((types & Self) && stats.getActiveSpells().isSpellActive(spell->mId))
|
||||
return 0.f;
|
||||
if ( ((types & Touch) || (types & Target)) && enemy.getClass().getCreatureStats(enemy).getActiveSpells().isSpellActive(spell->mId))
|
||||
return 0.f;
|
||||
|
||||
return rateEffects(spell->mEffects, actor, enemy) * (successChance / 100.f);
|
||||
}
|
||||
|
||||
float rateMagicItem(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
if (ptr.getClass().getEnchantment(ptr).empty())
|
||||
return 0.f;
|
||||
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(ptr.getClass().getEnchantment(ptr));
|
||||
|
||||
if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
|
||||
{
|
||||
return rateEffects(enchantment->mEffects, actor, enemy);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (!ptr.getClass().canBeEquipped(ptr, actor))
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float rateEffect(const ESM::ENAMstruct &effect, const MWWorld::Ptr &actor, const MWWorld::Ptr &enemy)
|
||||
{
|
||||
// NOTE: enemy may be empty
|
||||
|
||||
float rating = 1;
|
||||
switch (effect.mEffectID)
|
||||
{
|
||||
case ESM::MagicEffect::Soultrap:
|
||||
case ESM::MagicEffect::AlmsiviIntervention:
|
||||
case ESM::MagicEffect::DivineIntervention:
|
||||
case ESM::MagicEffect::CalmHumanoid:
|
||||
case ESM::MagicEffect::CalmCreature:
|
||||
case ESM::MagicEffect::FrenzyHumanoid:
|
||||
case ESM::MagicEffect::FrenzyCreature:
|
||||
case ESM::MagicEffect::DemoralizeHumanoid:
|
||||
case ESM::MagicEffect::DemoralizeCreature:
|
||||
case ESM::MagicEffect::RallyHumanoid:
|
||||
case ESM::MagicEffect::RallyCreature:
|
||||
case ESM::MagicEffect::Charm:
|
||||
case ESM::MagicEffect::DetectAnimal:
|
||||
case ESM::MagicEffect::DetectEnchantment:
|
||||
case ESM::MagicEffect::DetectKey:
|
||||
case ESM::MagicEffect::Telekinesis:
|
||||
case ESM::MagicEffect::Mark:
|
||||
case ESM::MagicEffect::Recall:
|
||||
case ESM::MagicEffect::Jump:
|
||||
case ESM::MagicEffect::WaterBreathing:
|
||||
case ESM::MagicEffect::SwiftSwim:
|
||||
case ESM::MagicEffect::WaterWalking:
|
||||
case ESM::MagicEffect::SlowFall:
|
||||
case ESM::MagicEffect::Light:
|
||||
case ESM::MagicEffect::Lock:
|
||||
case ESM::MagicEffect::Open:
|
||||
case ESM::MagicEffect::TurnUndead:
|
||||
case ESM::MagicEffect::WeaknessToCommonDisease:
|
||||
case ESM::MagicEffect::WeaknessToBlightDisease:
|
||||
case ESM::MagicEffect::WeaknessToCorprusDisease:
|
||||
case ESM::MagicEffect::CureCommonDisease:
|
||||
case ESM::MagicEffect::CureBlightDisease:
|
||||
case ESM::MagicEffect::CureCorprusDisease:
|
||||
case ESM::MagicEffect::ResistBlightDisease:
|
||||
case ESM::MagicEffect::ResistCommonDisease:
|
||||
case ESM::MagicEffect::ResistCorprusDisease:
|
||||
case ESM::MagicEffect::Invisibility:
|
||||
case ESM::MagicEffect::Chameleon:
|
||||
case ESM::MagicEffect::NightEye:
|
||||
case ESM::MagicEffect::Vampirism:
|
||||
case ESM::MagicEffect::StuntedMagicka:
|
||||
case ESM::MagicEffect::ExtraSpell:
|
||||
case ESM::MagicEffect::RemoveCurse:
|
||||
case ESM::MagicEffect::CommandCreature:
|
||||
case ESM::MagicEffect::CommandHumanoid:
|
||||
return 0.f;
|
||||
|
||||
case ESM::MagicEffect::Sound:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// there is no need to cast sound if enemy is not able to cast spells
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
|
||||
if (stats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::RestoreAttribute:
|
||||
return 0.f; // TODO: implement based on attribute damage
|
||||
case ESM::MagicEffect::RestoreSkill:
|
||||
return 0.f; // TODO: implement based on skill damage
|
||||
|
||||
case ESM::MagicEffect::ResistFire:
|
||||
case ESM::MagicEffect::ResistFrost:
|
||||
case ESM::MagicEffect::ResistMagicka:
|
||||
case ESM::MagicEffect::ResistNormalWeapons:
|
||||
case ESM::MagicEffect::ResistParalysis:
|
||||
case ESM::MagicEffect::ResistPoison:
|
||||
case ESM::MagicEffect::ResistShock:
|
||||
case ESM::MagicEffect::SpellAbsorption:
|
||||
case ESM::MagicEffect::Reflect:
|
||||
return 0.f; // probably useless since we don't know in advance what the enemy will cast
|
||||
|
||||
// don't cast these for now as they would make the NPC cast the same effect over and over again, especially when they have potions
|
||||
case ESM::MagicEffect::FortifyAttribute:
|
||||
case ESM::MagicEffect::FortifyHealth:
|
||||
case ESM::MagicEffect::FortifyMagicka:
|
||||
case ESM::MagicEffect::FortifyFatigue:
|
||||
case ESM::MagicEffect::FortifySkill:
|
||||
case ESM::MagicEffect::FortifyMaximumMagicka:
|
||||
case ESM::MagicEffect::FortifyAttack:
|
||||
return 0.f;
|
||||
|
||||
case ESM::MagicEffect::Burden:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
// burden makes sense only to overburden an enemy
|
||||
float burden = enemy.getClass().getEncumbrance(enemy) - enemy.getClass().getCapacity(enemy);
|
||||
if (burden > 0)
|
||||
return 0.f;
|
||||
|
||||
if ((effect.mMagnMin + effect.mMagnMax)/2.f > -burden)
|
||||
rating *= 3;
|
||||
else
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::Feather:
|
||||
{
|
||||
// Ignore actors without inventory
|
||||
if (!actor.getClass().hasInventoryStore(actor))
|
||||
return 0.f;
|
||||
|
||||
// feather makes sense only for overburden actors
|
||||
float burden = actor.getClass().getEncumbrance(actor) - actor.getClass().getCapacity(actor);
|
||||
if (burden <= 0)
|
||||
return 0.f;
|
||||
|
||||
if ((effect.mMagnMin + effect.mMagnMax)/2.f >= burden)
|
||||
rating *= 3;
|
||||
else
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::Levitate:
|
||||
return 0.f; // AI isn't designed to take advantage of this, and could be perceived as unfair anyway
|
||||
case ESM::MagicEffect::BoundBoots:
|
||||
case ESM::MagicEffect::BoundHelm:
|
||||
if (actor.getClass().isNpc())
|
||||
{
|
||||
// Beast races can't wear helmets or boots
|
||||
std::string raceid = actor.get<ESM::NPC>()->mBase->mRace;
|
||||
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(raceid);
|
||||
if (race->mData.mFlags & ESM::Race::Beast)
|
||||
return 0.f;
|
||||
}
|
||||
// Intended fall-through
|
||||
// Creatures can not wear armor
|
||||
case ESM::MagicEffect::BoundCuirass:
|
||||
case ESM::MagicEffect::BoundGloves:
|
||||
if (!actor.getClass().isNpc())
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::RestoreHealth:
|
||||
case ESM::MagicEffect::RestoreMagicka:
|
||||
case ESM::MagicEffect::RestoreFatigue:
|
||||
if (effect.mRange == ESM::RT_Self)
|
||||
{
|
||||
int priority = 1;
|
||||
if (effect.mEffectID == ESM::MagicEffect::RestoreHealth)
|
||||
priority = 10;
|
||||
const DynamicStat<float>& current = actor.getClass().getCreatureStats(actor).
|
||||
getDynamic(effect.mEffectID - ESM::MagicEffect::RestoreHealth);
|
||||
float toHeal = (effect.mMagnMin + effect.mMagnMax)/2.f * effect.mDuration;
|
||||
// Effect doesn't heal more than we need, *or* we are below 1/2 health
|
||||
if (current.getModified() - current.getCurrent() > toHeal
|
||||
|| current.getCurrent() < current.getModified()*0.5)
|
||||
{
|
||||
return 10000.f * priority
|
||||
- (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion
|
||||
}
|
||||
else
|
||||
return -10000.f * priority; // Save for later
|
||||
}
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::Dispel:
|
||||
{
|
||||
int numPositive = 0;
|
||||
int numNegative = 0;
|
||||
int diff = 0;
|
||||
|
||||
if (effect.mRange == ESM::RT_Self)
|
||||
{
|
||||
numPositive = numEffectsToDispel(actor, -1, false);
|
||||
numNegative = numEffectsToDispel(actor);
|
||||
|
||||
diff = numNegative - numPositive;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
numPositive = numEffectsToDispel(enemy, -1, false);
|
||||
numNegative = numEffectsToDispel(enemy);
|
||||
|
||||
diff = numPositive - numNegative;
|
||||
|
||||
// if rating < 0 here, the spell will be considered as negative later
|
||||
rating *= -1;
|
||||
}
|
||||
|
||||
if (diff <= 0)
|
||||
return 0.f;
|
||||
|
||||
rating *= (diff) / 5.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Prefer Cure effects over Dispel, because Dispel also removes positive effects
|
||||
case ESM::MagicEffect::CureParalyzation:
|
||||
return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Paralyze);
|
||||
|
||||
case ESM::MagicEffect::CurePoison:
|
||||
return 1001.f * numEffectsToDispel(actor, ESM::MagicEffect::Poison);
|
||||
case ESM::MagicEffect::DisintegrateArmor:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);
|
||||
|
||||
// According to UESP
|
||||
static const int armorSlots[] = {
|
||||
MWWorld::InventoryStore::Slot_CarriedLeft,
|
||||
MWWorld::InventoryStore::Slot_Cuirass,
|
||||
MWWorld::InventoryStore::Slot_LeftPauldron,
|
||||
MWWorld::InventoryStore::Slot_RightPauldron,
|
||||
MWWorld::InventoryStore::Slot_LeftGauntlet,
|
||||
MWWorld::InventoryStore::Slot_RightGauntlet,
|
||||
MWWorld::InventoryStore::Slot_Helmet,
|
||||
MWWorld::InventoryStore::Slot_Greaves,
|
||||
MWWorld::InventoryStore::Slot_Boots
|
||||
};
|
||||
|
||||
bool enemyHasArmor = false;
|
||||
|
||||
// Ignore enemy without armor
|
||||
for (unsigned int i=0; i<sizeof(armorSlots)/sizeof(int); ++i)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator item = inv.getSlot(armorSlots[i]);
|
||||
|
||||
if (item != inv.end() && (item.getType() == MWWorld::ContainerStore::Type_Armor))
|
||||
{
|
||||
enemyHasArmor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!enemyHasArmor)
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::DisintegrateWeapon:
|
||||
{
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Ignore enemy without inventory
|
||||
if (!enemy.getClass().hasInventoryStore(enemy))
|
||||
return 0.f;
|
||||
|
||||
MWWorld::InventoryStore& inv = enemy.getClass().getInventoryStore(enemy);
|
||||
MWWorld::ContainerStoreIterator item = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
||||
// Ignore enemy without weapons
|
||||
if (item == inv.end() || (item.getType() != MWWorld::ContainerStore::Type_Weapon))
|
||||
return 0.f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::MagicEffect::DamageAttribute:
|
||||
case ESM::MagicEffect::DrainAttribute:
|
||||
if (!enemy.isEmpty() && enemy.getClass().getCreatureStats(enemy).getAttribute(effect.mAttribute).getModified() <= 0)
|
||||
return 0.f;
|
||||
{
|
||||
if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length)
|
||||
{
|
||||
const float attributePriorities[ESM::Attribute::Length] = {
|
||||
1.0f, // Strength
|
||||
0.5f, // Intelligence
|
||||
0.6f, // Willpower
|
||||
0.7f, // Agility
|
||||
0.5f, // Speed
|
||||
0.8f, // Endurance
|
||||
0.7f, // Personality
|
||||
0.3f // Luck
|
||||
};
|
||||
rating *= attributePriorities[effect.mAttribute];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ESM::MagicEffect::DamageSkill:
|
||||
case ESM::MagicEffect::DrainSkill:
|
||||
if (enemy.isEmpty() || !enemy.getClass().isNpc())
|
||||
return 0.f;
|
||||
if (enemy.getClass().getNpcStats(enemy).getSkill(effect.mSkill).getModified() <= 0)
|
||||
return 0.f;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
|
||||
// Underwater casting not possible
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
{
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f))
|
||||
return 0.f;
|
||||
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
rating *= -1.f;
|
||||
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
// Check resistance for harmful effects
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
float resistance = MWMechanics::getEffectResistanceAttribute(effect.mEffectID, &stats.getMagicEffects());
|
||||
|
||||
rating *= (1.f - std::min(resistance, 100.f) / 100.f);
|
||||
}
|
||||
|
||||
// for harmful no-magnitude effects (e.g. silence) check if enemy is already has them
|
||||
// for non-harmful no-magnitude effects (e.g. bound items) check if actor is already has them
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
|
||||
{
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
|
||||
{
|
||||
CreatureStats& stats = enemy.getClass().getCreatureStats(enemy);
|
||||
|
||||
if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
if (stats.getMagicEffects().get(effect.mEffectID).getMagnitude() > 0)
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
rating *= calcEffectCost(effect);
|
||||
|
||||
// Currently treating all "on target" or "on touch" effects to target the enemy actor.
|
||||
// Combat AI is egoistic, so doesn't consider applying positive effects to friendly actors.
|
||||
if (effect.mRange != ESM::RT_Self)
|
||||
rating *= -1.f;
|
||||
|
||||
return rating;
|
||||
}
|
||||
|
||||
float rateEffects(const ESM::EffectList &list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
// NOTE: enemy may be empty
|
||||
float rating = 0.f;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
||||
{
|
||||
rating += rateEffect(*it, actor, enemy);
|
||||
|
||||
if (it->mRange == ESM::RT_Target)
|
||||
rating *= 1.5f;
|
||||
}
|
||||
return rating;
|
||||
}
|
||||
|
||||
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
static const float fAIMagicSpellMult = gmst.find("fAIMagicSpellMult")->getFloat();
|
||||
static const float fAIRangeMagicSpellMult = gmst.find("fAIRangeMagicSpellMult")->getFloat();
|
||||
|
||||
float mult = fAIMagicSpellMult;
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt =
|
||||
spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
|
||||
{
|
||||
if (effectIt->mRange == ESM::RT_Target)
|
||||
{
|
||||
if (!MWBase::Environment::get().getWorld()->isSwimming(enemy))
|
||||
mult = fAIRangeMagicSpellMult;
|
||||
else
|
||||
mult = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return MWMechanics::getSpellSuccessChance(spell, actor) * mult;
|
||||
}
|
||||
}
|
32
apps/openmw/mwmechanics/spellpriority.hpp
Normal file
32
apps/openmw/mwmechanics/spellpriority.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef OPENMW_SPELL_PRIORITY_H
|
||||
#define OPENMW_SPELL_PRIORITY_H
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
// RangeTypes using bitflags to allow multiple range types, as can be the case with spells having multiple effects.
|
||||
enum RangeTypes
|
||||
{
|
||||
Self = 0x1,
|
||||
Touch = 0x10,
|
||||
Target = 0x100
|
||||
};
|
||||
|
||||
int getRangeTypes (const ESM::EffectList& effects);
|
||||
|
||||
float rateSpell (const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float rateMagicItem (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
float ratePotion (const MWWorld::Ptr& item, const MWWorld::Ptr &actor);
|
||||
|
||||
/// @note target may be empty
|
||||
float rateEffect (const ESM::ENAMstruct& effect, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
/// @note target may be empty
|
||||
float rateEffects (const ESM::EffectList& list, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
|
||||
float vanillaRateSpell(const ESM::Spell* spell, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
}
|
||||
|
||||
#endif
|
146
apps/openmw/mwmechanics/weaponpriority.cpp
Normal file
146
apps/openmw/mwmechanics/weaponpriority.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
#include "weaponpriority.hpp"
|
||||
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "npcstats.hpp"
|
||||
#include "combat.hpp"
|
||||
#include "aicombataction.hpp"
|
||||
#include "spellpriority.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
float rateWeapon (const MWWorld::Ptr &item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy, int type,
|
||||
float arrowRating, float boltRating)
|
||||
{
|
||||
if (item.getTypeName() != typeid(ESM::Weapon).name())
|
||||
return 0.f;
|
||||
|
||||
const ESM::Weapon* weapon = item.get<ESM::Weapon>()->mBase;
|
||||
|
||||
if (type != -1 && weapon->mData.mType != type)
|
||||
return 0.f;
|
||||
|
||||
float rating=0.f;
|
||||
float bonus=0.f;
|
||||
|
||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow && weapon->mData.mType <= ESM::Weapon::MarksmanThrown)
|
||||
{
|
||||
// Range weapon is useless under water
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.75f))
|
||||
return 0.f;
|
||||
|
||||
if (enemy.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(enemy), 0.75f))
|
||||
return 0.f;
|
||||
|
||||
bonus+=1.5f;
|
||||
}
|
||||
|
||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||
{
|
||||
rating = (weapon->mData.mChop[0] + weapon->mData.mChop[1]) / 2.f;
|
||||
|
||||
if (weapon->mData.mType >= ESM::Weapon::MarksmanThrown)
|
||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
rating += weapon->mData.mSlash[i];
|
||||
rating += weapon->mData.mThrust[i];
|
||||
rating += weapon->mData.mChop[i];
|
||||
}
|
||||
rating /= 6.f;
|
||||
|
||||
MWMechanics::resistNormalWeapon(enemy, actor, item, rating);
|
||||
}
|
||||
|
||||
if (item.getClass().hasItemHealth(item))
|
||||
{
|
||||
if (item.getClass().getItemHealth(item) == 0)
|
||||
return 0.f;
|
||||
rating *= item.getClass().getItemHealth(item) / float(item.getClass().getItemMaxHealth(item));
|
||||
}
|
||||
|
||||
if (weapon->mData.mType == ESM::Weapon::MarksmanBow)
|
||||
{
|
||||
if (arrowRating <= 0.f)
|
||||
rating = 0.f;
|
||||
else
|
||||
rating += arrowRating;
|
||||
}
|
||||
else if (weapon->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
if (boltRating <= 0.f)
|
||||
rating = 0.f;
|
||||
else
|
||||
rating += boltRating;
|
||||
}
|
||||
|
||||
if (!weapon->mEnchant.empty())
|
||||
{
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes
|
||||
&& (item.getCellRef().getEnchantmentCharge() == -1
|
||||
|| item.getCellRef().getEnchantmentCharge() >= enchantment->mData.mCost))
|
||||
rating += rateEffects(enchantment->mEffects, actor, enemy);
|
||||
}
|
||||
|
||||
int skill = item.getClass().getEquipmentSkill(item);
|
||||
if (skill != -1)
|
||||
rating *= actor.getClass().getSkill(actor, skill) / 100.f;
|
||||
|
||||
// There is no need to apply bonus if weapon rating == 0
|
||||
if (rating == 0.f)
|
||||
return 0.f;
|
||||
|
||||
return rating + bonus;
|
||||
}
|
||||
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
static const float fAIMeleeWeaponMult = gmst.find("fAIMeleeWeaponMult")->getFloat();
|
||||
static const float fAIMeleeArmorMult = gmst.find("fAIMeleeArmorMult")->getFloat();
|
||||
static const float fAIRangeMeleeWeaponMult = gmst.find("fAIRangeMeleeWeaponMult")->getFloat();
|
||||
|
||||
if (weapon.isEmpty())
|
||||
return 0.f;
|
||||
|
||||
float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;
|
||||
float chopMult = fAIMeleeWeaponMult;
|
||||
float bonusDamage = 0.f;
|
||||
|
||||
const ESM::Weapon* esmWeap = weapon.get<ESM::Weapon>()->mBase;
|
||||
|
||||
if (esmWeap->mData.mType >= ESM::Weapon::MarksmanBow)
|
||||
{
|
||||
if (!ammo.isEmpty() && !MWBase::Environment::get().getWorld()->isSwimming(enemy))
|
||||
{
|
||||
bonusDamage = ammo.get<ESM::Weapon>()->mBase->mData.mChop[1];
|
||||
chopMult = fAIRangeMeleeWeaponMult;
|
||||
}
|
||||
else
|
||||
chopMult = 0.f;
|
||||
}
|
||||
|
||||
float chopRating = (esmWeap->mData.mChop[1] + bonusDamage) * skillMult * chopMult;
|
||||
float slashRating = esmWeap->mData.mSlash[1] * skillMult * fAIMeleeWeaponMult;
|
||||
float thrustRating = esmWeap->mData.mThrust[1] * skillMult * fAIMeleeWeaponMult;
|
||||
|
||||
return actor.getClass().getArmorRating(actor) * fAIMeleeArmorMult
|
||||
+ std::max(std::max(chopRating, slashRating), thrustRating);
|
||||
}
|
||||
|
||||
}
|
14
apps/openmw/mwmechanics/weaponpriority.hpp
Normal file
14
apps/openmw/mwmechanics/weaponpriority.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef OPENMW_WEAPON_PRIORITY_H
|
||||
#define OPENMW_WEAPON_PRIORITY_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
float rateWeapon (const MWWorld::Ptr& item, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy,
|
||||
int type=-1, float arrowRating=0.f, float boltRating=0.f);
|
||||
|
||||
float vanillaRateWeaponAndAmmo(const MWWorld::Ptr& weapon, const MWWorld::Ptr& ammo, const MWWorld::Ptr& actor, const MWWorld::Ptr& enemy);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -38,6 +38,8 @@ Objects::~Objects()
|
|||
|
||||
void Objects::insertBegin(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
assert(mObjects.find(ptr) == mObjects.end());
|
||||
|
||||
osg::ref_ptr<osg::Group> cellnode;
|
||||
|
||||
CellMap::iterator found = mCellSceneNodes.find(ptr.getCell());
|
||||
|
@ -90,9 +92,8 @@ void Objects::insertCreature(const MWWorld::Ptr &ptr, const std::string &mesh, b
|
|||
else
|
||||
anim = new CreatureAnimation(ptr, mesh, mResourceSystem);
|
||||
|
||||
ptr.getClass().getContainerStore(ptr).setContListener(static_cast<ActorAnimation*>(anim.get()));
|
||||
|
||||
mObjects.insert(std::make_pair(ptr, anim));
|
||||
if (mObjects.insert(std::make_pair(ptr, anim)).second)
|
||||
ptr.getClass().getContainerStore(ptr).setContListener(static_cast<ActorAnimation*>(anim.get()));
|
||||
}
|
||||
|
||||
void Objects::insertNPC(const MWWorld::Ptr &ptr)
|
||||
|
@ -102,10 +103,11 @@ void Objects::insertNPC(const MWWorld::Ptr &ptr)
|
|||
|
||||
osg::ref_ptr<NpcAnimation> anim (new NpcAnimation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), mResourceSystem));
|
||||
|
||||
ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get(), ptr);
|
||||
ptr.getClass().getInventoryStore(ptr).setContListener(anim.get());
|
||||
|
||||
mObjects.insert(std::make_pair(ptr, anim));
|
||||
if (mObjects.insert(std::make_pair(ptr, anim)).second)
|
||||
{
|
||||
ptr.getClass().getInventoryStore(ptr).setInvListener(anim.get(), ptr);
|
||||
ptr.getClass().getInventoryStore(ptr).setContListener(anim.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool Objects::removeObject (const MWWorld::Ptr& ptr)
|
||||
|
|
|
@ -65,6 +65,12 @@ namespace
|
|||
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
|
||||
MWRender::RenderingManager& rendering)
|
||||
{
|
||||
if (ptr.getRefData().getBaseNode() || physics.getActor(ptr))
|
||||
{
|
||||
std::cerr << "Warning: Tried to add " << ptr.getCellRef().getRefId() << " to the scene twice" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
bool useAnim = ptr.getClass().useAnim();
|
||||
std::string model = ptr.getClass().getModel(ptr);
|
||||
if (useAnim)
|
||||
|
|
|
@ -856,7 +856,10 @@ namespace MWWorld
|
|||
mWeatherManager->advanceTime (hours, incremental);
|
||||
|
||||
if (!incremental)
|
||||
{
|
||||
mRendering->notifyWorldSpaceChanged();
|
||||
mProjectileManager->clear();
|
||||
}
|
||||
|
||||
hours += mGameHour->getFloat();
|
||||
|
||||
|
@ -1552,6 +1555,7 @@ namespace MWWorld
|
|||
{
|
||||
osg::Vec3f a(x1,y1,z1);
|
||||
osg::Vec3f b(x2,y2,z2);
|
||||
|
||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door);
|
||||
return result.mHit;
|
||||
}
|
||||
|
|
|
@ -49,17 +49,42 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> v
|
|||
|
||||
SDL_PumpEvents();
|
||||
|
||||
SDL_Event evt;
|
||||
SDL_Event event;
|
||||
|
||||
if (windowEventsOnly)
|
||||
{
|
||||
// During loading, just handle window events, and keep others for later
|
||||
while (SDL_PeepEvents(&evt, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT))
|
||||
handleWindowEvent(evt);
|
||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_WINDOWEVENT, SDL_WINDOWEVENT))
|
||||
handleWindowEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
while(SDL_PollEvent(&evt))
|
||||
// Merge redundant events to avoid unnecessary listener calls
|
||||
std::vector<SDL_Event> events;
|
||||
while(SDL_PollEvent(&event)) {
|
||||
if (events.empty() || events.back().type != event.type)
|
||||
{
|
||||
events.emplace_back(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
SDL_Event& previousEvent = events.back();
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_MOUSEMOTION:
|
||||
previousEvent.motion.x = event.motion.x;
|
||||
previousEvent.motion.y = event.motion.y;
|
||||
previousEvent.motion.xrel += event.motion.xrel;
|
||||
previousEvent.motion.yrel += event.motion.yrel;
|
||||
break;
|
||||
default:
|
||||
events.emplace_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const SDL_Event& evt : events)
|
||||
{
|
||||
switch(evt.type)
|
||||
{
|
||||
|
|
|
@ -422,4 +422,10 @@ namespace Gui
|
|||
align();
|
||||
}
|
||||
|
||||
Spacer::Spacer()
|
||||
{
|
||||
setUserString("HStretch", "true");
|
||||
setUserString("VStretch", "true");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,6 +81,15 @@ namespace Gui
|
|||
bool mAutoResize; // auto resize the box so that it exactly fits all elements
|
||||
};
|
||||
|
||||
class Spacer : public AutoSizedWidget, public MyGUI::Widget
|
||||
{
|
||||
MYGUI_RTTI_DERIVED( Spacer )
|
||||
public:
|
||||
Spacer();
|
||||
|
||||
virtual MyGUI::IntSize getRequestedSize() { return MyGUI::IntSize(0,0); }
|
||||
};
|
||||
|
||||
class HBox : public Box, public MyGUI::Widget
|
||||
{
|
||||
MYGUI_RTTI_DERIVED( HBox )
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Gui
|
|||
{
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::MWList>("Widget");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::HBox>("Widget");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::Spacer>("Widget");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::VBox>("Widget");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedTextBox>("Widget");
|
||||
MyGUI::FactoryManager::getInstance().registerFactory<Gui::AutoSizedEditBox>("Widget");
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
breathe
|
||||
parse_cmake
|
||||
sphinx
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -21,12 +20,6 @@ import sys
|
|||
project_root = os.path.abspath('../../')
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
|
||||
def insensitive_glob(pattern):
|
||||
def either(c):
|
||||
return '[%s%s]' % (c.lower(), c.upper()) if c.isalpha() else c
|
||||
return glob.glob(''.join(map(either, pattern)))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
|
@ -43,37 +36,6 @@ extensions = [
|
|||
'sphinx.ext.viewcode',
|
||||
]
|
||||
|
||||
try:
|
||||
import breathe
|
||||
extensions.append('breathe')
|
||||
except ImportError:
|
||||
print("WARNING: Unable to import breathe, code documentation won't be generated.")
|
||||
|
||||
# Where breathe can find the source files
|
||||
openmw_path = os.path.join(project_root, "apps", "openmw")
|
||||
openmw_sub_dirs = os.walk(openmw_path).next()[1]
|
||||
openmw_headers = insensitive_glob(os.path.join(openmw_path, "*.hpp"))
|
||||
for dir in openmw_sub_dirs:
|
||||
openmw_headers += insensitive_glob(os.path.join(openmw_path, dir, "*.hpp"))
|
||||
# massage the headers to get the relative path needed
|
||||
openmw_headers = [os.path.relpath(x, openmw_path) for x in openmw_headers]
|
||||
|
||||
opencs_path = os.path.join(project_root, "apps", "opencs")
|
||||
opencs_sub_dirs = os.walk(opencs_path).next()[1]
|
||||
opencs_headers = insensitive_glob(os.path.join(opencs_path, "*.hpp"))
|
||||
opencs_sub_sub_dirs = []
|
||||
for dir in opencs_sub_dirs:
|
||||
opencs_headers += insensitive_glob(os.path.join(opencs_path, dir, "*.hpp"))
|
||||
opencs_sub_sub_dirs += os.walk(os.path.join(opencs_path, dir)).next()[1]
|
||||
for sub_dir in opencs_sub_sub_dirs:
|
||||
opencs_headers += insensitive_glob(os.path.join(opencs_path, dir, sub_dir, "*.hpp"))
|
||||
opencs_headers = [os.path.relpath(x, opencs_path) for x in opencs_headers]
|
||||
|
||||
breathe_projects_source = {
|
||||
"openmw": (openmw_path, openmw_headers),
|
||||
"opencs": (opencs_path, opencs_headers),
|
||||
}
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
|
@ -88,7 +50,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'OpenMW'
|
||||
copyright = u'2016, OpenMW Team'
|
||||
copyright = u'2017, OpenMW Team'
|
||||
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
|
|
@ -5,16 +5,7 @@ Sections
|
|||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 3
|
||||
|
||||
manuals/index
|
||||
reference/index
|
||||
source/index
|
||||
|
||||
|
||||
Indices and Tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
Project Source Documentation
|
||||
============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
openmw/index
|
||||
opencs/index
|
|
@ -1,11 +0,0 @@
|
|||
OpenMW-CS Source Documentation
|
||||
##############################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
model/index
|
||||
view/index
|
||||
|
||||
.. autodoxygenfile:: editor.hpp
|
||||
:project: opencs
|
|
@ -1,358 +0,0 @@
|
|||
./opencs/model
|
||||
##############
|
||||
|
||||
doc
|
||||
---
|
||||
.. autodoxygenfile:: model/doc/blacklist.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/document.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/documentmanager.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/loader.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/messages.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/operationholder.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/operation.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/runner.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/saving.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/savingstages.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/savingstate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/stage.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/doc/state.hpp
|
||||
:project: opencs
|
||||
|
||||
filter
|
||||
------
|
||||
.. autodoxygenfile:: model/filter/andnode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/booleannode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/leafnode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/narynode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/node.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/notnode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/ornode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/parser.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/textnode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/unarynode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/filter/valuenode.hpp
|
||||
:project: opencs
|
||||
|
||||
prefs
|
||||
-----
|
||||
.. autodoxygenfile:: model/prefs/boolsetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/category.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/coloursetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/doublesetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/enumsetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/intsetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/modifiersetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/setting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/shortcuteventhandler.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/shortcut.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/shortcutmanager.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/shortcutsetting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/prefs/state.hpp
|
||||
:project: opencs
|
||||
|
||||
tools
|
||||
-----
|
||||
.. autodoxygenfile:: model/tools/birthsigncheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/bodypartcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/classcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/factioncheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/gmstcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/journalcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/magiceffectcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/mandatoryid.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/mergeoperation.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/mergestages.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/mergestate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/pathgridcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/racecheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/referenceablecheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/referencecheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/regioncheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/reportmodel.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/scriptcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/search.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/searchoperation.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/searchstage.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/skillcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/soundcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/soundgencheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/spellcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/startscriptcheck.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/tools.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/tools/topicinfocheck.hpp
|
||||
:project: opencs
|
||||
|
||||
world
|
||||
-----
|
||||
.. autodoxygenfile:: model/world/cellcoordinates.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/cell.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/cellselection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/collectionbase.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/collection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/columnbase.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/columnimp.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/columns.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/commanddispatcher.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/commandmacro.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/commands.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/data.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/defaultgmsts.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idcompletionmanager.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idtablebase.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idtable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idtableproxymodel.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/idtree.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/infocollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/info.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/infoselectwrapper.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/infotableproxymodel.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/land.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/landtexture.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/metadata.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedcoladapterimp.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedcolumnadapter.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedidcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedinfocollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedtableproxymodel.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/nestedtablewrapper.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/pathgrid.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/record.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/refcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/ref.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/refidadapter.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/refidadapterimp.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/refidcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/refiddata.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/regionmap.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/resources.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/resourcesmanager.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/resourcetable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/scope.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/scriptcontext.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/subcellcollection.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/tablemimedata.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: model/world/universalid.hpp
|
||||
:project: opencs
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
./opencs/view
|
||||
#############
|
||||
|
||||
doc
|
||||
---
|
||||
.. autodoxygenfile:: view/doc/adjusterwidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/filedialog.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/filewidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/globaldebugprofilemenu.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/loader.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/newgame.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/operation.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/operations.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/runlogsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/sizehint.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/startup.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/subviewfactory.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/subviewfactoryimp.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/subview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/view.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/doc/viewmanager.hpp
|
||||
:project: opencs
|
||||
|
||||
filter
|
||||
------
|
||||
.. autodoxygenfile:: view/filter/editwidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/filter/filterbox.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/filter/recordfilterbox.hpp
|
||||
:project: opencs
|
||||
|
||||
prefs
|
||||
-----
|
||||
.. autodoxygenfile:: view/prefs/dialogue.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/prefs/keybindingpage.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/prefs/pagebase.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/prefs/page.hpp
|
||||
:project: opencs
|
||||
|
||||
render
|
||||
------
|
||||
.. autodoxygenfile:: view/render/cameracontroller.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/cellarrow.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/cellborder.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/cell.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/cellmarker.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/cellwater.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/editmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/instancemode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/instancemovemode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/instanceselectionmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/lightingbright.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/lightingday.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/lighting.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/lightingnight.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/mask.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/object.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/orbitcameramode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/pagedworldspacewidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/pathgrid.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/pathgridmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/pathgridselectionmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/previewwidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/scenewidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/selectionmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/tagbase.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/terrainstorage.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/unpagedworldspacewidget.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/render/worldspacewidget.hpp
|
||||
:project: opencs
|
||||
|
||||
tools
|
||||
-----
|
||||
.. autodoxygenfile:: view/tools/merge.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/tools/reportsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/tools/reporttable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/tools/searchbox.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/tools/searchsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/tools/subviews.hpp
|
||||
:project: opencs
|
||||
|
||||
widget
|
||||
------
|
||||
.. autodoxygenfile:: view/widget/coloreditor.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/colorpickerpopup.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/completerpopup.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/droplineedit.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/modebutton.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/pushbutton.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetoolbar.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetool.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetoolmode.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetoolrun.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetooltoggle2.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/widget/scenetooltoggle.hpp
|
||||
:project: opencs
|
||||
|
||||
world
|
||||
-----
|
||||
.. autodoxygenfile:: view/world/cellcreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/colordelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/creator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/datadisplaydelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/dialoguecreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/dialoguespinbox.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/dialoguesubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/dragdroputils.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/dragrecordtable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/enumdelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/extendedcommandconfigurator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/genericcreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/globalcreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/idcompletiondelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/idtypedelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/idvalidator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/infocreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/nestedtable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/pathgridcreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/previewsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/recordbuttonbar.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/recordstatusdelegate.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/referenceablecreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/referencecreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/regionmap.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/regionmapsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/scenesubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/scriptedit.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/scripterrortable.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/scripthighlighter.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/scriptsubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/startscriptcreator.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/subviews.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/tablebottombox.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/tableeditidaction.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/table.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/tablesubview.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/util.hpp
|
||||
:project: opencs
|
||||
|
||||
.. autodoxygenfile:: view/world/vartypedelegate.hpp
|
||||
:project: opencs
|
|
@ -1,24 +0,0 @@
|
|||
OpenMW Source Documentation
|
||||
###########################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
mwbase
|
||||
mwclass
|
||||
mwdialogue
|
||||
mwgui
|
||||
mwinput
|
||||
mwmechanics
|
||||
mwphysics
|
||||
mwrender
|
||||
mwscript
|
||||
mwsound
|
||||
mwstate
|
||||
mwworld
|
||||
|
||||
.. autodoxygenfile:: engine.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: doc.hpp
|
||||
:project: openmw
|
|
@ -1,32 +0,0 @@
|
|||
./mwbase
|
||||
########
|
||||
|
||||
.. autodoxygenfile:: mwbase/dialoguemanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/environment.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/inputmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/journal.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/mechanicsmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/scriptmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/soundmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/statemanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/windowmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwbase/world.hpp
|
||||
:project: openmw
|
|
@ -1,68 +0,0 @@
|
|||
./mwclass
|
||||
#########
|
||||
|
||||
.. autodoxygenfile:: mwclass/activator.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/actor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/apparatus.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/armor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/bodypart.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/book.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/classes.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/clothing.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/container.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/creature.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/creaturelevlist.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/ingredient.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/itemlevlist.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/light.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/lockpick.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/misc.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/npc.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/potion.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/probe.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/repair.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/static.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwclass/weapon.hpp
|
||||
:project: openmw
|
|
@ -1,32 +0,0 @@
|
|||
./mwdialogue
|
||||
############
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/dialoguemanagerimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/filter.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/hypertextparser.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/journalentry.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/journalimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/keywordsearch.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/quest.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/scripttest.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/selectwrapper.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwdialogue/topic.hpp
|
||||
:project: openmw
|
|
@ -1,227 +0,0 @@
|
|||
./mwgui
|
||||
#######
|
||||
|
||||
.. autodoxygenfile:: mwgui/alchemywindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/backgroundimage.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/birth.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/bookpage.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/bookwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/charactercreation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/class.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/companionitemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/companionwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/confirmationdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/console.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/container.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/containeritemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/controllers.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/countdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/cursor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/debugwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/dialogue.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/draganddrop.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/enchantingdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/exposedwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/formatting.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/hud.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/inventoryitemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/inventorywindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/itemchargeview.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/itemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/itemselection.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/itemview.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/itemwidget.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/jailscreen.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/journalbooks.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/journalviewmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/journalwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/layout.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/levelupdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/loadingscreen.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/mainmenu.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/mapwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/merchantrepair.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/messagebox.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/mode.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/pickpocketitemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/quickkeysmenu.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/race.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/recharge.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/referenceinterface.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/repair.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/review.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/savegamedialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/screenfader.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/scrollwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/settingswindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/sortfilteritemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/soulgemdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellbuyingwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellcreationdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellicons.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellview.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/spellwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/statswindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/textinput.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/timeadvancer.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/tooltips.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/tradeitemmodel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/tradewindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/trainingwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/travelwindow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/videowidget.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/waitdialog.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/widgets.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/windowbase.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/windowmanagerimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwgui/windowpinnablebase.hpp
|
||||
:project: openmw
|
|
@ -1,5 +0,0 @@
|
|||
./mwinput
|
||||
#########
|
||||
|
||||
.. autodoxygenfile:: mwinput/inputmanagerimp.hpp
|
||||
:project: openmw
|
|
@ -1,137 +0,0 @@
|
|||
./mwmechanics
|
||||
#############
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/activespells.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/actor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/actors.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/actorutil.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aiactivate.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aiavoiddoor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aicombataction.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aicombat.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aiescort.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aiface.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aifollow.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aipackage.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aipursue.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aisequence.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aistate.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aitravel.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/aiwander.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/alchemy.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/autocalcspell.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/character.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/combat.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/coordinateconverter.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/creaturestats.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/difficultyscaling.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/disease.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/drawstate.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/enchanting.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/levelledlist.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/magiceffects.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/mechanicsmanagerimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/movement.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/npcstats.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/objects.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/obstacle.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/pathfinding.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/pathgrid.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/pickpocket.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/repair.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/security.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/spellcasting.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/spells.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/stat.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/steering.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/summoning.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwmechanics/trading.hpp
|
||||
:project: openmw
|
|
@ -1,15 +0,0 @@
|
|||
./mwphysics
|
||||
###########
|
||||
|
||||
.. autodoxygenfile:: mwphysics/actor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwphysics/collisiontype.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwphysics/convert.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwphysics/physicssystem.hpp
|
||||
:project: openmw
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
./mwrender
|
||||
##########
|
||||
|
||||
.. autodoxygenfile:: mwrender/actoranimation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/animation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/bulletdebugdraw.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/camera.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/cell.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/characterpreview.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/creatureanimation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/effectmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/globalmap.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/localmap.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/npcanimation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/objects.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/pathgrid.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/renderbin.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/renderinginterface.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/renderingmanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/rendermode.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/ripplesimulation.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/rotatecontroller.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/sky.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/terrainstorage.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/util.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/vismask.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/water.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwrender/weaponanimation.hpp
|
||||
:project: openmw
|
|
@ -1,65 +0,0 @@
|
|||
./mwscript
|
||||
##########
|
||||
|
||||
.. autodoxygenfile:: mwscript/aiextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/animationextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/cellextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/compilercontext.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/consoleextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/containerextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/controlextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/dialogueextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/extensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/globalscripts.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/guiextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/interpretercontext.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/locals.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/miscextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/ref.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/scriptmanagerimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/skyextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/soundextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/statsextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/transformationextensions.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwscript/userextensions.hpp
|
||||
:project: openmw
|
|
@ -1,29 +0,0 @@
|
|||
./mwsound
|
||||
#########
|
||||
|
||||
.. autodoxygenfile:: mwsound/ffmpeg_decoder.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/loudness.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/movieaudiofactory.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/openal_output.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/sound_buffer.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/sound_decoder.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/sound.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/soundmanagerimp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwsound/sound_output.hpp
|
||||
:project: openmw
|
|
@ -1,11 +0,0 @@
|
|||
./mwstate
|
||||
#########
|
||||
|
||||
.. autodoxygenfile:: mwstate/character.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwstate/charactermanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwstate/statemanagerimp.hpp
|
||||
:project: openmw
|
|
@ -1,132 +0,0 @@
|
|||
./mwworld
|
||||
#########
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionalchemy.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionapply.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actiondoor.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actioneat.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionequip.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/action.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionopen.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionread.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionrepair.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionsoulgem.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actiontake.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actiontalk.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actionteleport.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/actiontrap.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cellpreloader.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cellref.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cellreflist.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cells.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cellstore.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/cellvisitors.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/class.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/containerstore.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/contentloader.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/customdata.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/esmloader.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/esmstore.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/failedaction.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/globals.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/inventorystore.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/livecellref.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/localscripts.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/manualref.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/nullaction.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/player.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/projectilemanager.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/ptr.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/recordcmp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/refdata.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/scene.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/store.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/timestamp.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/weather.hpp
|
||||
:project: openmw
|
||||
|
||||
.. autodoxygenfile:: mwworld/worldimp.hpp
|
||||
:project: openmw
|
||||
|
2
files/mac/qt.conf
Normal file
2
files/mac/qt.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
[Paths]
|
||||
Plugins = PlugIns
|
|
@ -10,9 +10,7 @@
|
|||
</Widget>
|
||||
|
||||
<Widget type="HBox" position="0 235 580 24" align="Right Bottom">
|
||||
<Widget type="Widget">
|
||||
<UserString key="HStretch" value="true"/>
|
||||
</Widget>
|
||||
<Widget type="Spacer"/>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" name="DisposeCorpseButton" align="Right Bottom">
|
||||
<Property key="Caption" value="#{sDisposeofCorpse}"/>
|
||||
<Property key="Visible" value="false"/>
|
||||
|
|
|
@ -16,13 +16,15 @@
|
|||
<Property key="Page" value="1"/>
|
||||
<Property key="WheelPage" value="1"/>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="0 91 140 24" align="Center Bottom">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 53 24" align="Center Top" name="OkButton">
|
||||
<Widget type="HBox" skin="" position="0 91 592 24" align="Center Bottom HStretch">
|
||||
<Widget type="Spacer" />
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 53 24" align="Left Top" name="OkButton">
|
||||
<Property key="Caption" value="#{sOk}"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 86 24" align="Center Top" name="CancelButton">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 86 24" align="Right Top" name="CancelButton">
|
||||
<Property key="Caption" value="#{sCancel}"/>
|
||||
</Widget>
|
||||
<Widget type="Spacer" />
|
||||
</Widget>
|
||||
</Widget>
|
||||
<CodeGeneratorSettings/>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<MyGUI type="Layout">
|
||||
|
||||
<Widget type="Window" skin="" layer="JournalBooks" align="Left|Top" position="0 0 565 390" name="_Main">
|
||||
<Property key="NeedKey" value="false"/>
|
||||
<!-- pages -->
|
||||
<Widget type="ImageBox" skin="ImageBox" position="-70 0 705 390" align="Top|Right" name="JImage">
|
||||
<Property key="ImageTexture" value="textures\tx_menubook.dds"/>
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>635</width>
|
||||
<height>575</height>
|
||||
<height>565</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>635</width>
|
||||
<height>535</height>
|
||||
<height>565</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -57,6 +57,18 @@
|
|||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="pagesWidget"/>
|
||||
</item>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1">
|
||||
<item row="1" column="1">
|
||||
<spacer name="horizontalSpacer">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -115,7 +115,7 @@
|
|||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
|
||||
<item row="2" column="1">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -134,15 +134,18 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in a new issue