forked from teamnwah/openmw-tes3coop
Merge branch 'master' into 0.6.0
This commit is contained in:
commit
98a7769b91
214 changed files with 3351 additions and 2838 deletions
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*.cpp]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
|
||||
[*.hpp]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -26,6 +26,7 @@ Doxygen
|
|||
.settings
|
||||
.directory
|
||||
.idea
|
||||
files/windows/*.aps
|
||||
cmake-build-*
|
||||
## qt-creator
|
||||
CMakeLists.txt.user*
|
||||
|
|
|
@ -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
|
||||
|
@ -59,10 +60,11 @@ Programmers
|
|||
Gašper Sedej
|
||||
gugus/gus
|
||||
Hallfaer Tuilinn
|
||||
Haoda Wang (h313)
|
||||
hristoast
|
||||
Internecine
|
||||
Jacob Essex (Yacoby)
|
||||
Jannik Heller (scrawl)
|
||||
Jake Westrip (16bitint)
|
||||
Jason Hooks (jhooks)
|
||||
jeaye
|
||||
Jeffrey Haines (Jyby)
|
||||
|
@ -131,12 +133,14 @@ Programmers
|
|||
Roman Proskuryakov (kpp)
|
||||
Sandy Carter (bwrsandman)
|
||||
Scott Howard
|
||||
scrawl
|
||||
Sebastian Wick (swick)
|
||||
Sergey Shambir
|
||||
ShadowRadiance
|
||||
Siimacore
|
||||
sir_herrbatka
|
||||
smbas
|
||||
spycrab
|
||||
Stefan Galowicz (bogglez)
|
||||
Stanislav Bobrov (Jiub)
|
||||
stil-t
|
||||
|
@ -145,6 +149,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.
|
||||
|
|
|
@ -27,9 +27,11 @@ QueryHelper::QueryHelper(QAbstractItemModel *model)
|
|||
void QueryHelper::refresh()
|
||||
{
|
||||
if (!queryThread->isRunning())
|
||||
{
|
||||
_model->removeRows(0, _model->rowCount());
|
||||
queryThread->start();
|
||||
emit started();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryHelper::terminate()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <iostream>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
#include <qdebug.h>
|
||||
|
||||
using namespace RakNet;
|
||||
using namespace std;
|
||||
|
@ -42,48 +43,87 @@ QueryClient &QueryClient::Get()
|
|||
|
||||
map<SystemAddress, QueryData> QueryClient::Query()
|
||||
{
|
||||
status = -1;
|
||||
map<SystemAddress, QueryData> query;
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
return query;
|
||||
|
||||
BitStream bs;
|
||||
bs.Write((unsigned char) (ID_MASTER_QUERY));
|
||||
qDebug() << "Locking mutex in QueryClient::Query()";
|
||||
mxServers.lock();
|
||||
status = -1;
|
||||
int attempts = 3;
|
||||
do
|
||||
{
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
{
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
mxServers.unlock();
|
||||
return query;
|
||||
}
|
||||
|
||||
int code = peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
||||
|
||||
if (code == 0)
|
||||
{
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
mxServers.unlock();
|
||||
return query;
|
||||
}
|
||||
|
||||
pmq->SetServers(&query);
|
||||
status = GetAnswer();
|
||||
status = GetAnswer(ID_MASTER_QUERY);
|
||||
RakSleep(100);
|
||||
}
|
||||
while(status != ID_MASTER_QUERY && attempts-- > 0);
|
||||
if(status != ID_MASTER_QUERY)
|
||||
qDebug() << "Getting query was failed";
|
||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
||||
peer->CloseConnection(masterAddr, true);
|
||||
mxServers.unlock();
|
||||
qDebug() <<"Answer" << (status == ID_MASTER_QUERY ? "ok." : "wrong.");
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
pair<SystemAddress, QueryData> QueryClient::Update(RakNet::SystemAddress addr)
|
||||
{
|
||||
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
pair<SystemAddress, QueryData> server;
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
{
|
||||
status = -1;
|
||||
return server;
|
||||
}
|
||||
|
||||
BitStream bs;
|
||||
bs.Write((unsigned char) (ID_MASTER_UPDATE));
|
||||
bs.Write(addr);
|
||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
||||
|
||||
mxServers.lock();
|
||||
status = -1;
|
||||
int attempts = 3;
|
||||
pmu->SetServer(&server);
|
||||
status = GetAnswer();
|
||||
do
|
||||
{
|
||||
if (Connect() == IS_NOT_CONNECTED)
|
||||
{
|
||||
qDebug() << IS_NOT_CONNECTED;
|
||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
mxServers.unlock();
|
||||
return server;
|
||||
}
|
||||
|
||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
||||
status = GetAnswer(ID_MASTER_UPDATE);
|
||||
RakSleep(100);
|
||||
}
|
||||
while(status != ID_MASTER_UPDATE && attempts-- > 0);
|
||||
if(status != ID_MASTER_UPDATE)
|
||||
qDebug() << "Getting update was failed";
|
||||
peer->CloseConnection(masterAddr, true);
|
||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
mxServers.unlock();
|
||||
return server;
|
||||
}
|
||||
|
||||
MASTER_PACKETS QueryClient::GetAnswer()
|
||||
MASTER_PACKETS QueryClient::GetAnswer(MASTER_PACKETS waitingPacket)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
bool update = true;
|
||||
unsigned char pid = 0;
|
||||
int id;
|
||||
int id = -1;
|
||||
while (update)
|
||||
{
|
||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
||||
|
@ -94,25 +134,37 @@ MASTER_PACKETS QueryClient::GetAnswer()
|
|||
data.Read(pid);
|
||||
switch(pid)
|
||||
{
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
case ID_CONNECTION_LOST:
|
||||
qDebug() << "ID_CONNECTION_LOST";
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
qDebug() << "Disconnected";
|
||||
update = false;
|
||||
break;
|
||||
case ID_MASTER_QUERY:
|
||||
qDebug() << "ID_MASTER_QUERY";
|
||||
if (waitingPacket == ID_MASTER_QUERY)
|
||||
pmq->Read();
|
||||
else
|
||||
qDebug() << "Got wrong packet";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_MASTER_UPDATE:
|
||||
qDebug() << "ID_MASTER_UPDATE";
|
||||
if (waitingPacket == ID_MASTER_UPDATE)
|
||||
pmu->Read();
|
||||
else
|
||||
qDebug() << "Got wrong packet";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_MASTER_ANNOUNCE:
|
||||
qDebug() << "ID_MASTER_ANNOUNCE";
|
||||
update = false;
|
||||
id = pid;
|
||||
break;
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
qDebug() << "ID_CONNECTION_REQUEST_ACCEPTED";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -125,6 +177,7 @@ MASTER_PACKETS QueryClient::GetAnswer()
|
|||
|
||||
ConnectionState QueryClient::Connect()
|
||||
{
|
||||
|
||||
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
|
||||
strlen(TES3MP_MASTERSERVER_PASSW), 0, 0, 5, 500);
|
||||
|
||||
|
@ -134,17 +187,19 @@ ConnectionState QueryClient::Connect()
|
|||
switch (state)
|
||||
{
|
||||
case IS_CONNECTED:
|
||||
qDebug() << "Connected";
|
||||
return IS_CONNECTED;
|
||||
case IS_NOT_CONNECTED:
|
||||
case IS_DISCONNECTED:
|
||||
case IS_SILENTLY_DISCONNECTING:
|
||||
case IS_DISCONNECTING:
|
||||
{
|
||||
//LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Cannot connect to master server: %d", masterAddr.ToString());
|
||||
qDebug() << "Cannot connect to the master server. Code:"<< state;
|
||||
return IS_NOT_CONNECTED;
|
||||
}
|
||||
case IS_PENDING:
|
||||
case IS_CONNECTING:
|
||||
qDebug() << "Pending";
|
||||
break;
|
||||
}
|
||||
RakSleep(500);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <components/openmw-mp/Master/PacketMasterQuery.hpp>
|
||||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
||||
#include <apps/browser/ServerModel.hpp>
|
||||
#include <mutex>
|
||||
|
||||
class QueryClient
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ public:
|
|||
int Status();
|
||||
private:
|
||||
RakNet::ConnectionState Connect();
|
||||
MASTER_PACKETS GetAnswer();
|
||||
MASTER_PACKETS GetAnswer(MASTER_PACKETS packet);
|
||||
protected:
|
||||
QueryClient();
|
||||
~QueryClient();
|
||||
|
@ -37,6 +38,7 @@ private:
|
|||
mwmp::PacketMasterQuery *pmq;
|
||||
mwmp::PacketMasterUpdate *pmu;
|
||||
std::pair<RakNet::SystemAddress, ServerData> server;
|
||||
std::mutex mxServers;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -172,7 +172,10 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
|
|||
}
|
||||
}
|
||||
|
||||
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure;
|
||||
if (!setup() || !setupGameData()) {
|
||||
return FirstRunDialogResultFailure;
|
||||
}
|
||||
return FirstRunDialogResultContinue;
|
||||
}
|
||||
|
||||
void Launcher::MainDialog::setVersionLabel()
|
||||
|
@ -344,6 +347,11 @@ bool Launcher::MainDialog::setupGameSettings()
|
|||
file.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Launcher::MainDialog::setupGameData()
|
||||
{
|
||||
QStringList dataDirs;
|
||||
|
||||
// Check if the paths actually contain data files
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace Launcher
|
|||
bool setupLauncherSettings();
|
||||
bool setupGameSettings();
|
||||
bool setupGraphicsSettings();
|
||||
bool setupGameData();
|
||||
|
||||
void setVersionLabel();
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
using namespace RakNet;
|
||||
using namespace std;
|
||||
using namespace mwmp;
|
||||
using namespace chrono;
|
||||
|
||||
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
|
||||
{
|
||||
|
@ -55,10 +56,10 @@ void MasterServer::Thread()
|
|||
{
|
||||
Packet *packet = peer->Receive();
|
||||
|
||||
auto now = chrono::steady_clock::now();
|
||||
auto now = steady_clock::now();
|
||||
if (now - startTime >= 60s)
|
||||
{
|
||||
startTime = chrono::steady_clock::now();
|
||||
startTime = steady_clock::now();
|
||||
for (auto it = servers.begin(); it != servers.end();)
|
||||
{
|
||||
|
||||
|
@ -66,6 +67,17 @@ void MasterServer::Thread()
|
|||
servers.erase(it++);
|
||||
else ++it;
|
||||
}
|
||||
for(auto id = pendingACKs.begin(); id != pendingACKs.end();)
|
||||
{
|
||||
if(now - id->second >= 30s)
|
||||
{
|
||||
cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
|
||||
peer->CloseConnection(id->first, true);
|
||||
id = pendingACKs.erase(id);
|
||||
}
|
||||
else
|
||||
++id;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet == nullptr)
|
||||
|
@ -90,10 +102,10 @@ void MasterServer::Thread()
|
|||
{
|
||||
pmq.SetServers(reinterpret_cast<map<SystemAddress, QueryData> *>(&servers));
|
||||
pmq.Send(packet->systemAddress);
|
||||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
|
||||
cout << "Sent info about all " << servers.size() << " servers to "
|
||||
<< packet->systemAddress.ToString() << endl;
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
break;
|
||||
}
|
||||
case ID_MASTER_UPDATE:
|
||||
|
@ -107,10 +119,10 @@ void MasterServer::Thread()
|
|||
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
|
||||
pmu.SetServer(&pairPtr);
|
||||
pmu.Send(packet->systemAddress);
|
||||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
cout << "Sent info about " << addr.ToString() << " to " << packet->systemAddress.ToString()
|
||||
<< endl;
|
||||
}
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
break;
|
||||
}
|
||||
case ID_MASTER_ANNOUNCE:
|
||||
|
@ -126,6 +138,7 @@ void MasterServer::Thread()
|
|||
iter->second.lastUpdate = now;
|
||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP);
|
||||
pma.Send(packet->systemAddress);
|
||||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
};
|
||||
|
||||
if (iter != servers.end())
|
||||
|
@ -135,6 +148,7 @@ void MasterServer::Thread()
|
|||
servers.erase(iter);
|
||||
cout << "Deleted";
|
||||
pma.Send(packet->systemAddress);
|
||||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
}
|
||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
||||
{
|
||||
|
@ -159,14 +173,20 @@ void MasterServer::Thread()
|
|||
cout << "Unknown";
|
||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_DELETE);
|
||||
pma.Send(packet->systemAddress);
|
||||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
}
|
||||
cout << " server " << packet->systemAddress.ToString() << endl;
|
||||
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
break;
|
||||
}
|
||||
case ID_SND_RECEIPT_ACKED:
|
||||
uint32_t num;
|
||||
memcpy(&num, packet->data+1, 4);
|
||||
cout << "Packet with id " << num << " was delivered." << endl;
|
||||
pendingACKs.erase(packet->guid);
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
break;
|
||||
default:
|
||||
cout << "Wrong packet" << endl;
|
||||
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length " << packet->length << " from " << packet->systemAddress.ToString() << endl;
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ private:
|
|||
RakNet::SocketDescriptor sockdescr;
|
||||
ServerMap servers;
|
||||
bool run;
|
||||
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -164,11 +164,11 @@ void RestServer::start()
|
|||
httpServer.resource["/api/servers/info"]["GET"] = [this](auto response, auto /*request*/) {
|
||||
stringstream ss;
|
||||
ss << '{';
|
||||
ss << "servers: " << serverMap->size();
|
||||
ss << "\"servers\": " << serverMap->size();
|
||||
unsigned int players = 0;
|
||||
for (auto s : *serverMap)
|
||||
players += s.second.GetPlayers();
|
||||
ss << ", players: " << players;
|
||||
ss << ", \"players\": " << players;
|
||||
ss << "}";
|
||||
|
||||
ResponseStr(*response, ss.str(), "application/json");
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
||||
#include <components/fallback/validate.hpp>
|
||||
|
||||
#include <components/nifosg/nifloader.hpp>
|
||||
|
@ -33,11 +30,7 @@ CS::Editor::Editor ()
|
|||
|
||||
NifOsg::Loader::setShowMarkers(true);
|
||||
|
||||
mVFS.reset(new VFS::Manager(mFsStrict));
|
||||
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true);
|
||||
|
||||
mDocumentManager.setVFS(mVFS.get());
|
||||
mDocumentManager.setFileData(mFsStrict, config.first, config.second);
|
||||
|
||||
mNewGame.setLocalData (mLocal);
|
||||
mFileDialog.setLocalData (mLocal);
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef CS_EDITOR_H
|
||||
#define CS_EDITOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/interprocess/sync/file_lock.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
|
@ -30,11 +28,6 @@
|
|||
|
||||
#include "view/tools/merge.hpp"
|
||||
|
||||
namespace VFS
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
@ -46,9 +39,6 @@ namespace CS
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
// FIXME: should be moved to document, so we can have different resources for each opened project
|
||||
std::unique_ptr<VFS::Manager> mVFS;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
CSMPrefs::State mSettingsState;
|
||||
CSMDoc::DocumentManager mDocumentManager;
|
||||
|
|
|
@ -269,13 +269,14 @@ void CSMDoc::Document::createBase()
|
|||
}
|
||||
}
|
||||
|
||||
CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
|
||||
const std::vector< boost::filesystem::path >& files, bool new_,
|
||||
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
|
||||
const std::vector< boost::filesystem::path >& files,bool new_,
|
||||
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
|
||||
const Fallback::Map* fallback,
|
||||
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
|
||||
const std::vector<std::string>& blacklistedScripts)
|
||||
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback, resDir),
|
||||
ToUTF8::FromType encoding,
|
||||
const std::vector<std::string>& blacklistedScripts,
|
||||
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
|
||||
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, fallback, resDir),
|
||||
mTools (*this, encoding),
|
||||
mProjectPath ((configuration.getUserDataPath() / "projects") /
|
||||
(savePath.filename().string() + ".project")),
|
||||
|
@ -337,11 +338,6 @@ CSMDoc::Document::~Document()
|
|||
{
|
||||
}
|
||||
|
||||
const VFS::Manager *CSMDoc::Document::getVFS() const
|
||||
{
|
||||
return mVFS;
|
||||
}
|
||||
|
||||
QUndoStack& CSMDoc::Document::getUndoStack()
|
||||
{
|
||||
return mUndoStack;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "../world/data.hpp"
|
||||
|
@ -59,7 +60,6 @@ namespace CSMDoc
|
|||
|
||||
private:
|
||||
|
||||
const VFS::Manager* mVFS;
|
||||
boost::filesystem::path mSavePath;
|
||||
std::vector<boost::filesystem::path> mContentFiles;
|
||||
bool mNew;
|
||||
|
@ -102,17 +102,15 @@ namespace CSMDoc
|
|||
|
||||
public:
|
||||
|
||||
Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
|
||||
Document (const Files::ConfigurationManager& configuration,
|
||||
const std::vector< boost::filesystem::path >& files, bool new_,
|
||||
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
|
||||
const Fallback::Map* fallback,
|
||||
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
|
||||
const std::vector<std::string>& blacklistedScripts);
|
||||
const Fallback::Map* fallback, ToUTF8::FromType encoding,
|
||||
const std::vector<std::string>& blacklistedScripts,
|
||||
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
|
||||
|
||||
~Document();
|
||||
|
||||
const VFS::Manager* getVFS() const;
|
||||
|
||||
QUndoStack& getUndoStack();
|
||||
|
||||
int getState() const;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "document.hpp"
|
||||
|
||||
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
|
||||
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252), mVFS(NULL)
|
||||
: mConfiguration (configuration), mEncoding (ToUTF8::WINDOWS_1252)
|
||||
{
|
||||
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
|
||||
|
||||
|
@ -62,7 +62,7 @@ CSMDoc::Document *CSMDoc::DocumentManager::makeDocument (
|
|||
const std::vector< boost::filesystem::path >& files,
|
||||
const boost::filesystem::path& savePath, bool new_)
|
||||
{
|
||||
return new Document (mVFS, mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mResourcesManager, mBlacklistedScripts);
|
||||
return new Document (mConfiguration, files, new_, savePath, mResDir, &mFallbackMap, mEncoding, mBlacklistedScripts, mFsStrict, mDataPaths, mArchives);
|
||||
}
|
||||
|
||||
void CSMDoc::DocumentManager::insertDocument (CSMDoc::Document *document)
|
||||
|
@ -127,8 +127,9 @@ void CSMDoc::DocumentManager::documentNotLoaded (Document *document, const std::
|
|||
removeDocument (document);
|
||||
}
|
||||
|
||||
void CSMDoc::DocumentManager::setVFS(const VFS::Manager *vfs)
|
||||
void CSMDoc::DocumentManager::setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
|
||||
{
|
||||
mResourcesManager.setVFS(vfs);
|
||||
mVFS = vfs;
|
||||
mFsStrict = strict;
|
||||
mDataPaths = dataPaths;
|
||||
mArchives = archives;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
#include <components/fallback/fallback.hpp>
|
||||
|
||||
#include "../world/resourcesmanager.hpp"
|
||||
#include <components/files/multidircollection.hpp>
|
||||
|
||||
#include "loader.hpp"
|
||||
|
||||
|
@ -39,9 +38,14 @@ namespace CSMDoc
|
|||
QThread mLoaderThread;
|
||||
Loader mLoader;
|
||||
ToUTF8::FromType mEncoding;
|
||||
CSMWorld::ResourcesManager mResourcesManager;
|
||||
std::vector<std::string> mBlacklistedScripts;
|
||||
const VFS::Manager* mVFS;
|
||||
|
||||
boost::filesystem::path mResDir;
|
||||
Fallback::Map mFallbackMap;
|
||||
|
||||
bool mFsStrict;
|
||||
Files::PathContainer mDataPaths;
|
||||
std::vector<std::string> mArchives;
|
||||
|
||||
DocumentManager (const DocumentManager&);
|
||||
DocumentManager& operator= (const DocumentManager&);
|
||||
|
@ -74,15 +78,11 @@ namespace CSMDoc
|
|||
|
||||
void setBlacklistedScripts (const std::vector<std::string>& scriptIds);
|
||||
|
||||
void setVFS(const VFS::Manager* vfs);
|
||||
/// Sets the file data that gets passed to newly created documents.
|
||||
void setFileData(bool strict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives);
|
||||
|
||||
bool isEmpty();
|
||||
|
||||
private:
|
||||
|
||||
boost::filesystem::path mResDir;
|
||||
Fallback::Map mFallbackMap;
|
||||
|
||||
private slots:
|
||||
|
||||
void documentLoaded (Document *document);
|
||||
|
|
|
@ -259,6 +259,7 @@ void CSMPrefs::State::declare()
|
|||
declareShortcut ("document-character-topicinfos", "Open Topic Info List", QKeySequence());
|
||||
declareShortcut ("document-character-journalinfos", "Open Journal Info List", QKeySequence());
|
||||
declareShortcut ("document-character-bodyparts", "Open Body Part List", QKeySequence());
|
||||
declareShortcut ("document-assets-reload", "Reload Assets", QKeySequence(Qt::Key_F5));
|
||||
declareShortcut ("document-assets-sounds", "Open Sound Asset List", QKeySequence());
|
||||
declareShortcut ("document-assets-soundgens", "Open Sound Generator List", QKeySequence());
|
||||
declareShortcut ("document-assets-meshes", "Open Mesh Asset List", QKeySequence());
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/vfs/registerarchives.hpp>
|
||||
|
||||
#include "idtable.hpp"
|
||||
#include "idtree.hpp"
|
||||
|
@ -61,11 +63,18 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec
|
|||
return number;
|
||||
}
|
||||
|
||||
CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
|
||||
CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
|
||||
const std::vector<std::string>& archives, const Fallback::Map* fallback, const boost::filesystem::path& resDir)
|
||||
: mEncoder (encoding), mPathgrids (mCells), mRefs (mCells),
|
||||
mResourcesManager (resourcesManager), mFallbackMap(fallback),
|
||||
mReader (0), mDialogue (0), mReaderIndex(1), mResourceSystem(new Resource::ResourceSystem(resourcesManager.getVFS()))
|
||||
mFallbackMap(fallback), mReader (0), mDialogue (0), mReaderIndex(1),
|
||||
mFsStrict(fsStrict), mDataPaths(dataPaths), mArchives(archives)
|
||||
{
|
||||
mVFS.reset(new VFS::Manager(mFsStrict));
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
|
||||
|
||||
mResourcesManager.setVFS(mVFS.get());
|
||||
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
|
||||
|
||||
mResourceSystem->getSceneManager()->setShaderPath((resDir / "shaders").string());
|
||||
|
||||
int index = 0;
|
||||
|
@ -1215,6 +1224,43 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
|||
return ids;
|
||||
}
|
||||
|
||||
void CSMWorld::Data::assetsChanged()
|
||||
{
|
||||
mVFS.get()->reset();
|
||||
VFS::registerArchives(mVFS.get(), Files::Collections(mDataPaths, !mFsStrict), mArchives, true);
|
||||
|
||||
const UniversalId assetTableIds[] = {
|
||||
UniversalId::Type_Meshes,
|
||||
UniversalId::Type_Icons,
|
||||
UniversalId::Type_Musics,
|
||||
UniversalId::Type_SoundsRes,
|
||||
UniversalId::Type_Textures,
|
||||
UniversalId::Type_Videos
|
||||
};
|
||||
|
||||
size_t numAssetTables = sizeof(assetTableIds) / sizeof(UniversalId);
|
||||
|
||||
for (size_t i = 0; i < numAssetTables; ++i)
|
||||
{
|
||||
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
|
||||
table->beginReset();
|
||||
}
|
||||
|
||||
// Trigger recreation
|
||||
mResourcesManager.recreateResources();
|
||||
|
||||
for (size_t i = 0; i < numAssetTables; ++i)
|
||||
{
|
||||
ResourceTable* table = static_cast<ResourceTable*>(getTableModel(assetTableIds[i]));
|
||||
table->endReset();
|
||||
}
|
||||
|
||||
// Get rid of potentially old cached assets
|
||||
mResourceSystem->clearCache();
|
||||
|
||||
emit assetTablesChanged();
|
||||
}
|
||||
|
||||
void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
if (topLeft.column()<=0)
|
||||
|
@ -1228,7 +1274,7 @@ void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end)
|
|||
|
||||
const VFS::Manager* CSMWorld::Data::getVFS() const
|
||||
{
|
||||
return mResourcesManager.getVFS();
|
||||
return mVFS.get();
|
||||
}
|
||||
|
||||
const Fallback::Map* CSMWorld::Data::getFallbackMap() const
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include <components/files/multidircollection.hpp>
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "../doc/stage.hpp"
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include "infocollection.hpp"
|
||||
#include "nestedinfocollection.hpp"
|
||||
#include "pathgrid.hpp"
|
||||
#include "resourcesmanager.hpp"
|
||||
#include "metadata.hpp"
|
||||
#ifndef Q_MOC_RUN
|
||||
#include "subcellcollection.hpp"
|
||||
|
@ -108,7 +110,6 @@ namespace CSMWorld
|
|||
RefCollection mRefs;
|
||||
IdCollection<ESM::Filter> mFilters;
|
||||
Collection<MetaData> mMetaData;
|
||||
const ResourcesManager& mResourcesManager;
|
||||
const Fallback::Map* mFallbackMap;
|
||||
std::vector<QAbstractItemModel *> mModels;
|
||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||
|
@ -119,6 +120,11 @@ namespace CSMWorld
|
|||
std::map<std::string, std::map<ESM::RefNum, std::string> > mRefLoadCache;
|
||||
int mReaderIndex;
|
||||
|
||||
bool mFsStrict;
|
||||
Files::PathContainer mDataPaths;
|
||||
std::vector<std::string> mArchives;
|
||||
std::unique_ptr<VFS::Manager> mVFS;
|
||||
ResourcesManager mResourcesManager;
|
||||
std::shared_ptr<Resource::ResourceSystem> mResourceSystem;
|
||||
|
||||
std::vector<std::shared_ptr<ESM::ESMReader> > mReaders;
|
||||
|
@ -140,7 +146,9 @@ namespace CSMWorld
|
|||
|
||||
public:
|
||||
|
||||
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager, const Fallback::Map* fallback, const boost::filesystem::path& resDir);
|
||||
Data (ToUTF8::FromType encoding, bool fsStrict, const Files::PathContainer& dataPaths,
|
||||
const std::vector<std::string>& archives, const Fallback::Map* fallback,
|
||||
const boost::filesystem::path& resDir);
|
||||
|
||||
virtual ~Data();
|
||||
|
||||
|
@ -304,8 +312,12 @@ namespace CSMWorld
|
|||
|
||||
void idListChanged();
|
||||
|
||||
void assetTablesChanged();
|
||||
|
||||
private slots:
|
||||
|
||||
void assetsChanged();
|
||||
|
||||
void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void rowsChanged (const QModelIndex& parent, int start, int end);
|
||||
|
|
|
@ -12,6 +12,14 @@ CSMWorld::Resources::Resources (const VFS::Manager* vfs, const std::string& base
|
|||
const char * const *extensions)
|
||||
: mBaseDirectory (baseDirectory), mType (type)
|
||||
{
|
||||
recreate(vfs, extensions);
|
||||
}
|
||||
|
||||
void CSMWorld::Resources::recreate(const VFS::Manager* vfs, const char * const *extensions)
|
||||
{
|
||||
mFiles.clear();
|
||||
mIndex.clear();
|
||||
|
||||
int baseSize = mBaseDirectory.size();
|
||||
|
||||
const std::map<std::string, VFS::File*>& index = vfs->getIndex();
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace CSMWorld
|
|||
Resources (const VFS::Manager* vfs, const std::string& baseDirectory, UniversalId::Type type,
|
||||
const char * const *extensions = 0);
|
||||
|
||||
void recreate(const VFS::Manager* vfs, const char * const *extensions = 0);
|
||||
|
||||
int getSize() const;
|
||||
|
||||
std::string getId (int index) const;
|
||||
|
|
|
@ -14,21 +14,24 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources)
|
|||
resources));
|
||||
}
|
||||
|
||||
const char * const * CSMWorld::ResourcesManager::getMeshExtensions()
|
||||
{
|
||||
// maybe we could go over the osgDB::Registry to list all supported node formats
|
||||
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
|
||||
return sMeshTypes;
|
||||
}
|
||||
|
||||
void CSMWorld::ResourcesManager::setVFS(const VFS::Manager *vfs)
|
||||
{
|
||||
mVFS = vfs;
|
||||
mResources.clear();
|
||||
|
||||
// maybe we could go over the osgDB::Registry to list all supported node formats
|
||||
|
||||
static const char * const sMeshTypes[] = { "nif", "osg", "osgt", "osgb", "osgx", "osg2", 0 };
|
||||
|
||||
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, sMeshTypes));
|
||||
addResources (Resources (vfs, "meshes", UniversalId::Type_Mesh, getMeshExtensions()));
|
||||
addResources (Resources (vfs, "icons", UniversalId::Type_Icon));
|
||||
addResources (Resources (vfs, "music", UniversalId::Type_Music));
|
||||
addResources (Resources (vfs, "sound", UniversalId::Type_SoundRes));
|
||||
addResources (Resources (vfs, "textures", UniversalId::Type_Texture));
|
||||
addResources (Resources (vfs, "videos", UniversalId::Type_Video));
|
||||
addResources (Resources (vfs, "video", UniversalId::Type_Video));
|
||||
}
|
||||
|
||||
const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
|
||||
|
@ -36,6 +39,18 @@ const VFS::Manager* CSMWorld::ResourcesManager::getVFS() const
|
|||
return mVFS;
|
||||
}
|
||||
|
||||
void CSMWorld::ResourcesManager::recreateResources()
|
||||
{
|
||||
std::map<UniversalId::Type, Resources>::iterator it = mResources.begin();
|
||||
for ( ; it != mResources.end(); ++it)
|
||||
{
|
||||
if (it->first == UniversalId::Type_Mesh)
|
||||
it->second.recreate(mVFS, getMeshExtensions());
|
||||
else
|
||||
it->second.recreate(mVFS);
|
||||
}
|
||||
}
|
||||
|
||||
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
|
||||
{
|
||||
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace CSMWorld
|
|||
|
||||
void addResources (const Resources& resources);
|
||||
|
||||
const char * const * getMeshExtensions();
|
||||
|
||||
public:
|
||||
|
||||
ResourcesManager();
|
||||
|
@ -30,6 +32,8 @@ namespace CSMWorld
|
|||
|
||||
void setVFS(const VFS::Manager* vfs);
|
||||
|
||||
void recreateResources();
|
||||
|
||||
const Resources& get (UniversalId::Type type) const;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -154,3 +154,13 @@ int CSMWorld::ResourceTable::getColumnId (int column) const
|
|||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CSMWorld::ResourceTable::beginReset()
|
||||
{
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void CSMWorld::ResourceTable::endReset()
|
||||
{
|
||||
endResetModel();
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ namespace CSMWorld
|
|||
virtual bool isDeleted (const std::string& id) const;
|
||||
|
||||
virtual int getColumnId (int column) const;
|
||||
|
||||
/// Signal Qt that the data is about to change.
|
||||
void beginReset();
|
||||
/// Signal Qt that the data has been changed.
|
||||
void endReset();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -284,6 +284,13 @@ void CSVDoc::View::setupAssetsMenu()
|
|||
{
|
||||
QMenu *assets = menuBar()->addMenu (tr ("Assets"));
|
||||
|
||||
QAction *reload = new QAction (tr ("Reload"), this);
|
||||
connect (reload, SIGNAL (triggered()), &mDocument->getData(), SLOT (assetsChanged()));
|
||||
setupShortcut("document-assets-reload", reload);
|
||||
assets->addAction (reload);
|
||||
|
||||
assets->addSeparator();
|
||||
|
||||
QAction *sounds = new QAction (tr ("Sounds"), this);
|
||||
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
|
||||
setupShortcut("document-assets-sounds", sounds);
|
||||
|
|
|
@ -275,14 +275,34 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int
|
|||
|
||||
void CSVRender::Cell::pathgridModified()
|
||||
{
|
||||
if (mPathgrid)
|
||||
mPathgrid->recreateGeometry();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::pathgridRemoved()
|
||||
{
|
||||
if (mPathgrid)
|
||||
mPathgrid->removeGeometry();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::reloadAssets()
|
||||
{
|
||||
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
|
||||
iter != mObjects.end(); ++iter)
|
||||
{
|
||||
iter->second->reloadAssets();
|
||||
}
|
||||
|
||||
if (mTerrain)
|
||||
{
|
||||
mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY());
|
||||
mTerrain->loadCell(mCoordinates.getX(), mCoordinates.getY());
|
||||
}
|
||||
|
||||
if (mCellWater)
|
||||
mCellWater->reloadAssets();
|
||||
}
|
||||
|
||||
void CSVRender::Cell::setSelection (int elementMask, Selection mode)
|
||||
{
|
||||
if (elementMask & Mask_Reference)
|
||||
|
@ -302,7 +322,7 @@ void CSVRender::Cell::setSelection (int elementMask, Selection mode)
|
|||
iter->second->setSelected (selected);
|
||||
}
|
||||
}
|
||||
if (elementMask & Mask_Pathgrid)
|
||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||
{
|
||||
// Only one pathgrid may be selected, so some operations will only have an effect
|
||||
// if the pathgrid is already focused
|
||||
|
@ -402,7 +422,7 @@ std::vector<osg::ref_ptr<CSVRender::TagBase> > CSVRender::Cell::getSelection (un
|
|||
iter!=mObjects.end(); ++iter)
|
||||
if (iter->second->getSelected())
|
||||
result.push_back (iter->second->getTag());
|
||||
if (elementMask & Mask_Pathgrid)
|
||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||
if (mPathgrid->isSelected())
|
||||
result.push_back(mPathgrid->getTag());
|
||||
|
||||
|
@ -439,6 +459,6 @@ void CSVRender::Cell::reset (unsigned int elementMask)
|
|||
for (std::map<std::string, Object *>::const_iterator iter (mObjects.begin());
|
||||
iter!=mObjects.end(); ++iter)
|
||||
iter->second->reset();
|
||||
if (elementMask & Mask_Pathgrid)
|
||||
if (mPathgrid && elementMask & Mask_Pathgrid)
|
||||
mPathgrid->resetIndicators();
|
||||
}
|
||||
|
|
|
@ -118,6 +118,8 @@ namespace CSVRender
|
|||
|
||||
void pathgridRemoved();
|
||||
|
||||
void reloadAssets();
|
||||
|
||||
void setSelection (int elementMask, Selection mode);
|
||||
|
||||
// Select everything that references the same ID as at least one of the elements
|
||||
|
|
|
@ -92,6 +92,11 @@ namespace CSVRender
|
|||
}
|
||||
}
|
||||
|
||||
void CellWater::reloadAssets()
|
||||
{
|
||||
recreate();
|
||||
}
|
||||
|
||||
void CellWater::cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
|
||||
{
|
||||
const CSMWorld::Collection<CSMWorld::Cell>& cells = mData.getCells();
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace CSVRender
|
|||
|
||||
void updateCellData(const CSMWorld::Record<CSMWorld::Cell>& cellRecord);
|
||||
|
||||
void reloadAssets();
|
||||
|
||||
private slots:
|
||||
|
||||
void cellDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
|
|
@ -532,6 +532,12 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
|
|||
return false;
|
||||
}
|
||||
|
||||
void CSVRender::Object::reloadAssets()
|
||||
{
|
||||
update();
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
std::string CSVRender::Object::getReferenceId() const
|
||||
{
|
||||
return mReferenceId;
|
||||
|
|
|
@ -151,6 +151,9 @@ namespace CSVRender
|
|||
/// this object?
|
||||
bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
/// Reloads the underlying asset
|
||||
void reloadAssets();
|
||||
|
||||
/// Returns an empty string if this is a refereceable-type object.
|
||||
std::string getReferenceId() const;
|
||||
|
||||
|
|
|
@ -472,6 +472,9 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc
|
|||
connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
|
||||
this, SLOT (cellAdded (const QModelIndex&, int, int)));
|
||||
|
||||
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
|
||||
this, SLOT (assetTablesChanged ()));
|
||||
|
||||
// Shortcuts
|
||||
CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this);
|
||||
connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell()));
|
||||
|
@ -763,6 +766,15 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
|
|||
flagAsModified();
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::assetTablesChanged()
|
||||
{
|
||||
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter = mCells.begin();
|
||||
for ( ; iter != mCells.end(); ++iter)
|
||||
{
|
||||
iter->second->reloadAssets();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVRender::PagedWorldspaceWidget::loadCameraCell()
|
||||
{
|
||||
addCellToSceneFromCamera(0, 0);
|
||||
|
|
|
@ -155,6 +155,8 @@ namespace CSVRender
|
|||
|
||||
virtual void cellAdded (const QModelIndex& index, int start, int end);
|
||||
|
||||
void assetTablesChanged ();
|
||||
|
||||
void loadCameraCell();
|
||||
|
||||
void loadEastCell();
|
||||
|
|
|
@ -71,6 +71,8 @@ namespace CSVRender
|
|||
primarySelectPressed(hitResult);
|
||||
}
|
||||
else if (Cell* cell = getWorldspaceWidget().getCell (hitResult.worldPos))
|
||||
{
|
||||
if (cell->getPathgrid())
|
||||
{
|
||||
// Add node
|
||||
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
|
||||
|
@ -80,6 +82,7 @@ namespace CSVRender
|
|||
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit)
|
||||
{
|
||||
|
@ -205,7 +208,7 @@ namespace CSVRender
|
|||
WorldspaceHitResult hit = getWorldspaceWidget().mousePick (pos, getWorldspaceWidget().getInteractionMask());
|
||||
|
||||
Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);
|
||||
if (cell)
|
||||
if (cell && cell->getPathgrid())
|
||||
{
|
||||
PathgridTag* tag = 0;
|
||||
if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get())) && tag->getPathgrid()->getId() == mEdgeId)
|
||||
|
|
|
@ -17,6 +17,9 @@ CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
|
|||
connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
|
||||
this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
|
||||
|
||||
connect (&mData, SIGNAL (assetTablesChanged ()),
|
||||
this, SLOT (assetTablesChanged ()));
|
||||
|
||||
if (!referenceable)
|
||||
{
|
||||
QAbstractItemModel *references =
|
||||
|
@ -119,3 +122,8 @@ void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& par
|
|||
if (index.row()>=start && index.row()<=end)
|
||||
emit closeRequest();
|
||||
}
|
||||
|
||||
void CSVRender::PreviewWidget::assetTablesChanged ()
|
||||
{
|
||||
mObject.reloadAssets();
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ namespace CSVRender
|
|||
void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
|
||||
void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
|
||||
|
||||
void assetTablesChanged ();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -221,8 +221,8 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
|
|||
|
||||
SceneWidget::~SceneWidget()
|
||||
{
|
||||
// Since we're holding on to the scene templates past the existence of this graphics context, we'll need to manually release the created objects
|
||||
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
|
||||
// Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
|
||||
mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
|
||||
}
|
||||
|
||||
void SceneWidget::setLighting(Lighting *lighting)
|
||||
|
@ -393,6 +393,7 @@ void SceneWidget::selectNavigationMode (const std::string& mode)
|
|||
mCurrentCamControl->setCamera(NULL);
|
||||
mCurrentCamControl = mOrbitCamControl;
|
||||
mOrbitCamControl->setCamera(getCamera());
|
||||
mOrbitCamControl->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
|
|||
connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
|
||||
this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));
|
||||
|
||||
connect (&document.getData(), SIGNAL (assetTablesChanged ()),
|
||||
this, SLOT (assetTablesChanged ()));
|
||||
|
||||
update();
|
||||
|
||||
mCell.reset (new Cell (document.getData(), mRootNode, mCellId));
|
||||
|
@ -82,6 +85,12 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
|
|||
emit closeRequest();
|
||||
}
|
||||
|
||||
void CSVRender::UnpagedWorldspaceWidget::assetTablesChanged()
|
||||
{
|
||||
if (mCell)
|
||||
mCell->reloadAssets();
|
||||
}
|
||||
|
||||
bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld::UniversalId>& universalIdData, DropType type)
|
||||
{
|
||||
if (WorldspaceWidget::handleDrop (universalIdData, type))
|
||||
|
|
|
@ -108,6 +108,8 @@ namespace CSVRender
|
|||
|
||||
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
|
||||
|
||||
void assetTablesChanged ();
|
||||
|
||||
signals:
|
||||
|
||||
void cellChanged(const CSMWorld::UniversalId& id);
|
||||
|
|
|
@ -32,13 +32,19 @@ std::string CSVWorld::InfoCreator::getId() const
|
|||
|
||||
void CSVWorld::InfoCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
int index =
|
||||
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
|
||||
findColumnIndex (
|
||||
getCollectionId().getType()==CSMWorld::UniversalId::Type_TopicInfos ?
|
||||
CSMWorld::Columns::ColumnId_Topic : CSMWorld::Columns::ColumnId_Journal);
|
||||
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId()));
|
||||
|
||||
command.addValue (index, mTopic->text());
|
||||
if (getCollectionId() == CSMWorld::UniversalId::Type_TopicInfos)
|
||||
{
|
||||
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Topic), mTopic->text());
|
||||
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Rank), -1);
|
||||
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Gender), -1);
|
||||
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_PcRank), -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
command.addValue (table.findColumnIndex(CSMWorld::Columns::ColumnId_Journal), mTopic->text());
|
||||
}
|
||||
}
|
||||
|
||||
CSVWorld::InfoCreator::InfoCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
|
|
|
@ -45,6 +45,30 @@ double PositionFunctions::GetPosZ(unsigned short pid) noexcept
|
|||
return player->position.pos[2];
|
||||
}
|
||||
|
||||
double PositionFunctions::GetPreviousCellPosX(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0.0f);
|
||||
|
||||
return player->previousCellPosition.pos[0];
|
||||
}
|
||||
|
||||
double PositionFunctions::GetPreviousCellPosY(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0.0f);
|
||||
|
||||
return player->previousCellPosition.pos[1];
|
||||
}
|
||||
|
||||
double PositionFunctions::GetPreviousCellPosZ(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0.0f);
|
||||
|
||||
return player->previousCellPosition.pos[2];
|
||||
}
|
||||
|
||||
void PositionFunctions::GetRot(unsigned short pid, float *x, float *y, float *z) noexcept
|
||||
{
|
||||
*x = 0.00;
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
{"GetPosY", PositionFunctions::GetPosY},\
|
||||
{"GetPosZ", PositionFunctions::GetPosZ},\
|
||||
\
|
||||
{"GetPreviousCellPosX", PositionFunctions::GetPreviousCellPosX},\
|
||||
{"GetPreviousCellPosY", PositionFunctions::GetPreviousCellPosY},\
|
||||
{"GetPreviousCellPosZ", PositionFunctions::GetPreviousCellPosZ},\
|
||||
\
|
||||
{"GetRot", PositionFunctions::GetRot},\
|
||||
{"GetRotX", PositionFunctions::GetRotX},\
|
||||
{"GetRotZ", PositionFunctions::GetRotZ},\
|
||||
|
@ -59,6 +63,30 @@ public:
|
|||
*/
|
||||
static double GetPosZ(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X position of a player from before their latest cell change.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The X position.
|
||||
*/
|
||||
static double GetPreviousCellPosX(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Y position of a player from before their latest cell change.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The Y position.
|
||||
*/
|
||||
static double GetPreviousCellPosY(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Z position of a player from before their latest cell change.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The Z position.
|
||||
*/
|
||||
static double GetPreviousCellPosZ(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Assign the player's rotational coordinate values to the variables passed as
|
||||
* parameters.
|
||||
|
|
|
@ -51,6 +51,70 @@ void SpellFunctions::AddSpell(unsigned short pid, const char* spellId) noexcept
|
|||
player->spellbookChanges.spells.push_back(spell);
|
||||
}
|
||||
|
||||
void SpellFunctions::AddCustomSpell(unsigned short pid, const char* spellId, const char* spellName) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
ESM::Spell spell;
|
||||
spell.mName = spellName;
|
||||
spell.mId = spellId;
|
||||
|
||||
player->spellbookChanges.spells.push_back(spell);
|
||||
}
|
||||
|
||||
void SpellFunctions::AddCustomSpellData(unsigned short pid, const char* spellId, int type, int cost, int flags) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
int index = -1;
|
||||
for(int i = 0; i < player->spellbookChanges.spells.size(); i++)
|
||||
{
|
||||
if( strcmp(player->spellbookChanges.spells.at(i).mId.c_str(), spellId) == 0)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(index == -1)
|
||||
return;
|
||||
|
||||
player->spellbookChanges.spells.at(index).mData.mType = type;
|
||||
player->spellbookChanges.spells.at(index).mData.mCost = cost;
|
||||
player->spellbookChanges.spells.at(index).mData.mFlags = flags;
|
||||
}
|
||||
|
||||
void SpellFunctions::AddCustomSpellEffect(unsigned short pid, const char* spellId, short effectId, signed char mSkill, signed char mAttribute, int mRange, int mArea, int mDuration, int mMagnMin, int mMagnMax) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
int index = -1;
|
||||
for(int i = 0; i < player->spellbookChanges.spells.size(); i++)
|
||||
{
|
||||
if( strcmp(player->spellbookChanges.spells.at(i).mId.c_str(), spellId) == 0)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(index == -1)
|
||||
return;
|
||||
|
||||
ESM::ENAMstruct effect;
|
||||
effect.mEffectID = effectId;
|
||||
effect.mSkill = mSkill;
|
||||
effect.mAttribute = mAttribute;
|
||||
effect.mRange = mRange;
|
||||
effect.mArea = mArea;
|
||||
effect.mDuration = mDuration;
|
||||
effect.mMagnMin = mMagnMin;
|
||||
effect.mMagnMax = mMagnMax;
|
||||
|
||||
player->spellbookChanges.spells.at(index).mEffects.mList.push_back(effect);
|
||||
}
|
||||
|
||||
const char *SpellFunctions::GetSpellId(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
@ -62,6 +126,149 @@ const char *SpellFunctions::GetSpellId(unsigned short pid, unsigned int i) noexc
|
|||
return player->spellbookChanges.spells.at(i).mId.c_str();
|
||||
}
|
||||
|
||||
const char *SpellFunctions::GetSpellName(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return "invalid";
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mName.c_str();
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellType(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mData.mType;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellCost(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mData.mCost;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellFlags(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mData.mFlags;
|
||||
}
|
||||
|
||||
unsigned int SpellFunctions::GetSpellEffectCount(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.size();
|
||||
}
|
||||
|
||||
short SpellFunctions::GetSpellEffectId(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mEffectID;
|
||||
}
|
||||
|
||||
signed char SpellFunctions::GetSpellEffectSkill(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mSkill;
|
||||
}
|
||||
|
||||
signed char SpellFunctions::GetSpellEffectAttribute(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mAttribute;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellEffectRange(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mRange;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellEffectArea(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mArea;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellEffectDuration(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mDuration;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellEffectMagnMin(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mMagnMin;
|
||||
}
|
||||
|
||||
int SpellFunctions::GetSpellEffectMagnMax(unsigned short pid, unsigned int i, unsigned int j) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (i >= player->spellbookChanges.count)
|
||||
return 0;
|
||||
|
||||
return player->spellbookChanges.spells.at(i).mEffects.mList.at(j).mMagnMax;
|
||||
}
|
||||
|
||||
void SpellFunctions::SendSpellbookChanges(unsigned short pid, bool toOthers) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
|
|
@ -9,8 +9,24 @@
|
|||
\
|
||||
{"SetSpellbookChangesAction", SpellFunctions::SetSpellbookChangesAction},\
|
||||
{"AddSpell", SpellFunctions::AddSpell},\
|
||||
{"AddCustomSpell", SpellFunctions::AddCustomSpell},\
|
||||
{"AddCustomSpellData", SpellFunctions::AddCustomSpellData},\
|
||||
{"AddCustomSpellEffect", SpellFunctions::AddCustomSpellEffect},\
|
||||
\
|
||||
{"GetSpellId", SpellFunctions::GetSpellId},\
|
||||
{"GetSpellName", SpellFunctions::GetSpellName},\
|
||||
{"GetSpellType", SpellFunctions::GetSpellType},\
|
||||
{"GetSpellCost", SpellFunctions::GetSpellCost},\
|
||||
{"GetSpellFlags", SpellFunctions::GetSpellFlags},\
|
||||
{"GetSpellEffectCount", SpellFunctions::GetSpellEffectCount},\
|
||||
{"GetSpellEffectId", SpellFunctions::GetSpellEffectId},\
|
||||
{"GetSpellEffectSkill", SpellFunctions::GetSpellEffectSkill},\
|
||||
{"GetSpellEffectAttribute", SpellFunctions::GetSpellEffectAttribute},\
|
||||
{"GetSpellEffectRange", SpellFunctions::GetSpellEffectRange},\
|
||||
{"GetSpellEffectArea", SpellFunctions::GetSpellEffectArea},\
|
||||
{"GetSpellEffectDuration", SpellFunctions::GetSpellEffectDuration},\
|
||||
{"GetSpellEffectMagnMin", SpellFunctions::GetSpellEffectMagnMin},\
|
||||
{"GetSpellEffectMagnMax", SpellFunctions::GetSpellEffectMagnMax},\
|
||||
\
|
||||
{"SendSpellbookChanges", SpellFunctions::SendSpellbookChanges}
|
||||
|
||||
|
@ -57,11 +73,50 @@ public:
|
|||
* \brief Add a new spell to the spellbook changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param topicId The spellId of the spell.
|
||||
* \param spellId The spellId of the spell.
|
||||
* \return void
|
||||
*/
|
||||
static void AddSpell(unsigned short pid, const char* spellId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add a new custom spell to the spellbook changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param spellId The spellId of the spell.
|
||||
* \param spellName The name of the spell.
|
||||
* \return void
|
||||
*/
|
||||
static void AddCustomSpell(unsigned short pid, const char* spellId, const char* spellName) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add custom spell data to the spellbook changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param spellId The spellId of the spell.
|
||||
* \param type The type of the spell.
|
||||
* \param cost The cost of the spell.
|
||||
* \param flags The flags of the spell.
|
||||
* \return void
|
||||
*/
|
||||
static void AddCustomSpellData(unsigned short pid, const char* spellId, int type, int cost, int flags) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add custom spell effect to the spellbook changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param spellId The spellId of the spell.
|
||||
* \param effectId The effectId of the spell effect.
|
||||
* \param mSkill The skill affected by the spell effect.
|
||||
* \param mAttribute The attribute affected by the spell effect.
|
||||
* \param mRange The range of the spell effect.
|
||||
* \param mArea The area of the spell effect.
|
||||
* \param mDuration The duration of the spell effect.
|
||||
* \param mMagnMin The minimum magnitude of the spell effect.
|
||||
* \param mMagnMax The maximum magnitude of the spell effect.
|
||||
* \return void
|
||||
*/
|
||||
static void AddCustomSpellEffect(unsigned short pid, const char* spellId, short effectId, signed char mSkill, signed char mAttribute, int mRange, int mArea, int mDuration, int mMagnMin, int mMagnMax) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the spellId at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
|
@ -71,6 +126,131 @@ public:
|
|||
*/
|
||||
static const char *GetSpellId(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the name of the spell at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \return The spell name.
|
||||
*/
|
||||
static const char *GetSpellName(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the type of the spell at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \return The spell type.
|
||||
*/
|
||||
static int GetSpellType(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the cost of the spell at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \return The spell cost.
|
||||
*/
|
||||
static int GetSpellCost(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the flags of the spell at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \return The spell flags.
|
||||
*/
|
||||
static int GetSpellFlags(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the number of effects on the spell at a certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \return The spell effect count.
|
||||
*/
|
||||
static unsigned int GetSpellEffectCount(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the effectId of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The effectId.
|
||||
*/
|
||||
static short GetSpellEffectId(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the affected skill of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The affected skill.
|
||||
*/
|
||||
static signed char GetSpellEffectSkill(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the affected attribute of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The affected attribute.
|
||||
*/
|
||||
static signed char GetSpellEffectAttribute(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the range of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The range.
|
||||
*/
|
||||
static int GetSpellEffectRange(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the area of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The area.
|
||||
*/
|
||||
static int GetSpellEffectArea(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the duration of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The duration.
|
||||
*/
|
||||
static int GetSpellEffectDuration(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the minimum magnitude of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The minimum magnitude.
|
||||
*/
|
||||
static int GetSpellEffectMagnMin(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the maximum magnitude of the effect at a certain index in the spell at another certain index in a player's latest spellbook changes.
|
||||
*
|
||||
* \param pid The player ID whose spellbook changes should be used.
|
||||
* \param i The index of the spell.
|
||||
* \param j The index of the effect.
|
||||
* \return The maximum magnitude.
|
||||
*/
|
||||
static int GetSpellEffectMagnMax(unsigned short pid, unsigned int i, unsigned int j) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerSpellbook packet with a player's recorded spellbook changes.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -113,7 +113,6 @@ void OMW::Engine::frame(float frametime)
|
|||
try
|
||||
{
|
||||
mStartTick = mViewer->getStartTick();
|
||||
mEnvironment.setFrameDuration (frametime);
|
||||
|
||||
// update input
|
||||
mEnvironment.getInputManager()->update(frametime, false);
|
||||
|
@ -378,8 +377,7 @@ void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)
|
|||
mResDir = parResDir;
|
||||
}
|
||||
|
||||
// Set start cell name (only interiors for now)
|
||||
|
||||
// Set start cell name
|
||||
void OMW::Engine::setCell (const std::string& cellName)
|
||||
{
|
||||
mCellName = cellName;
|
||||
|
@ -763,6 +761,8 @@ void OMW::Engine::go()
|
|||
Settings::Manager::getString("screenshot format", "General")));
|
||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||
|
||||
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||
|
||||
// Create encoder
|
||||
ToUTF8::Utf8Encoder encoder (mEncoding);
|
||||
mEncoder = &encoder;
|
||||
|
@ -816,7 +816,6 @@ void OMW::Engine::go()
|
|||
// Start the main rendering loop
|
||||
osg::Timer frameTimer;
|
||||
double simulationTime = 0.0;
|
||||
float framerateLimit = Settings::Manager::getFloat("framerate limit", "Video");
|
||||
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
|
||||
{
|
||||
double dt = frameTimer.time_s();
|
||||
|
@ -841,6 +840,8 @@ void OMW::Engine::go()
|
|||
|
||||
mViewer->advance(simulationTime);
|
||||
|
||||
mEnvironment.setFrameDuration(dt);
|
||||
|
||||
frame(dt);
|
||||
|
||||
if (!mEnvironment.getInputManager()->isWindowVisible())
|
||||
|
@ -858,15 +859,7 @@ void OMW::Engine::go()
|
|||
mViewer->renderingTraversals();
|
||||
}
|
||||
|
||||
if (framerateLimit > 0.f)
|
||||
{
|
||||
double thisFrameTime = frameTimer.time_s();
|
||||
double minFrameTime = 1.0 / framerateLimit;
|
||||
if (thisFrameTime < minFrameTime)
|
||||
{
|
||||
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
|
||||
}
|
||||
}
|
||||
mEnvironment.limitFrameRate(frameTimer.time_s());
|
||||
}
|
||||
|
||||
// Save user settings
|
||||
|
|
|
@ -148,7 +148,7 @@ namespace OMW
|
|||
/// Set resource dir
|
||||
void setResourceDir(const boost::filesystem::path& parResDir);
|
||||
|
||||
/// Set start cell name (only interiors for now)
|
||||
/// Set start cell name
|
||||
void setCell(const std::string& cellName);
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) || defined(__linux) || defined(__unix) || defined(__posix))
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) || (defined(__linux) && !defined(ANDROID)) || (defined(__unix) && !defined(ANDROID)) || defined(__posix))
|
||||
#define USE_CRASH_CATCHER 1
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <cassert>
|
||||
|
||||
#include <OpenThreads/Thread>
|
||||
|
||||
#include "world.hpp"
|
||||
#include "scriptmanager.hpp"
|
||||
#include "dialoguemanager.hpp"
|
||||
|
@ -17,7 +19,7 @@ MWBase::Environment *MWBase::Environment::sThis = 0;
|
|||
MWBase::Environment::Environment()
|
||||
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
|
||||
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mStateManager (0),
|
||||
mFrameDuration (0)
|
||||
mFrameDuration (0), mFrameRateLimit(0.f)
|
||||
{
|
||||
assert (!sThis);
|
||||
sThis = this;
|
||||
|
@ -79,6 +81,29 @@ void MWBase::Environment::setFrameDuration (float duration)
|
|||
mFrameDuration = duration;
|
||||
}
|
||||
|
||||
void MWBase::Environment::setFrameRateLimit(float limit)
|
||||
{
|
||||
mFrameRateLimit = limit;
|
||||
}
|
||||
|
||||
float MWBase::Environment::getFrameRateLimit() const
|
||||
{
|
||||
return mFrameRateLimit;
|
||||
}
|
||||
|
||||
void MWBase::Environment::limitFrameRate(double dt) const
|
||||
{
|
||||
if (mFrameRateLimit > 0.f)
|
||||
{
|
||||
double thisFrameTime = dt;
|
||||
double minFrameTime = 1.0 / static_cast<double>(mFrameRateLimit);
|
||||
if (thisFrameTime < minFrameTime)
|
||||
{
|
||||
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-thisFrameTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MWBase::World *MWBase::Environment::getWorld() const
|
||||
{
|
||||
assert (mWorld);
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace MWBase
|
|||
InputManager *mInputManager;
|
||||
StateManager *mStateManager;
|
||||
float mFrameDuration;
|
||||
float mFrameRateLimit;
|
||||
|
||||
Environment (const Environment&);
|
||||
///< not implemented
|
||||
|
@ -67,6 +68,10 @@ namespace MWBase
|
|||
void setFrameDuration (float duration);
|
||||
///< Set length of current frame in seconds.
|
||||
|
||||
void setFrameRateLimit(float frameRateLimit);
|
||||
float getFrameRateLimit() const;
|
||||
void limitFrameRate(double dt) const;
|
||||
|
||||
World *getWorld() const;
|
||||
|
||||
SoundManager *getSoundManager() const;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <set>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Vec3f;
|
||||
|
@ -231,6 +233,7 @@ namespace MWBase
|
|||
virtual void keepPlayerAlive() = 0;
|
||||
|
||||
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
|
||||
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
|
||||
|
||||
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;
|
||||
|
||||
|
@ -241,7 +244,7 @@ namespace MWBase
|
|||
/// Has the player stolen this item from the given owner?
|
||||
virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0;
|
||||
|
||||
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::CellRef& cellref, MWWorld::Ptr& victim) = 0;
|
||||
virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::ConstPtr& item, MWWorld::Ptr& victim) = 0;
|
||||
|
||||
/// Turn actor into werewolf or normal form.
|
||||
virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0;
|
||||
|
@ -251,6 +254,11 @@ namespace MWBase
|
|||
virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0;
|
||||
|
||||
virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0;
|
||||
|
||||
virtual bool isRunning(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -222,7 +222,10 @@ namespace MWBase
|
|||
virtual void setSpellVisibility(bool visible) = 0;
|
||||
virtual void setSneakVisibility(bool visible) = 0;
|
||||
|
||||
/// activate selected quick key
|
||||
virtual void activateQuickKey (int index) = 0;
|
||||
/// update activated quick key state (if action executing was delayed for some reason)
|
||||
virtual void updateActivatedQuickKey () = 0;
|
||||
|
||||
virtual std::string getSelectedSpell() = 0;
|
||||
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;
|
||||
|
|
|
@ -297,6 +297,16 @@ namespace MWBase
|
|||
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
||||
/// doPhysics.
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Make it possible to set the inertial force of a Ptr directly
|
||||
*/
|
||||
virtual void setInertialForce(const MWWorld::Ptr& ptr, const osg::Vec3f &force) = 0;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
||||
///< cast a Ray and return true if there is an object in the ray path.
|
||||
|
||||
|
|
|
@ -51,6 +51,11 @@ namespace MWClass
|
|||
return "";
|
||||
}
|
||||
|
||||
bool Activator::isActivator() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Activator::useAnim() const
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace MWClass
|
|||
|
||||
virtual bool useAnim() const;
|
||||
///< Whether or not to use animated variant of model (default false)
|
||||
|
||||
virtual bool isActivator() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -558,10 +559,27 @@ namespace MWClass
|
|||
return action;
|
||||
}
|
||||
|
||||
if(getCreatureStats(ptr).isDead())
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
if(stats.isDead())
|
||||
{
|
||||
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
|
||||
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (canLoot && !stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
|
||||
|
||||
// otherwise wait until death animation
|
||||
if(stats.isDeathAnimationFinished())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
|
||||
// death animation is not finished, do nothing
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
}
|
||||
|
||||
if(stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
|
||||
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
|
||||
}
|
||||
|
||||
|
@ -668,7 +686,11 @@ namespace MWClass
|
|||
return true;
|
||||
|
||||
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
||||
return !customData.mCreatureStats.getAiSequence().isInCombat() || customData.mCreatureStats.isDead();
|
||||
|
||||
if (customData.mCreatureStats.isDead() && customData.mCreatureStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
return !customData.mCreatureStats.getAiSequence().isInCombat();
|
||||
}
|
||||
|
||||
MWGui::ToolTipInfo Creature::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
|
||||
|
|
|
@ -88,6 +88,11 @@ namespace MWClass
|
|||
}
|
||||
}
|
||||
|
||||
bool Door::isDoor() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Door::useAnim() const
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -20,6 +20,8 @@ namespace MWClass
|
|||
|
||||
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
|
||||
|
||||
virtual bool isDoor() const;
|
||||
|
||||
virtual bool useAnim() const;
|
||||
|
||||
virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/npcstate.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -999,16 +1000,35 @@ namespace MWClass
|
|||
return action;
|
||||
}
|
||||
|
||||
if(getCreatureStats(ptr).isDead())
|
||||
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||
|
||||
if(stats.isDead())
|
||||
{
|
||||
bool canLoot = Settings::Manager::getBool ("can loot during death animation", "Game");
|
||||
|
||||
// by default user can loot friendly actors during death animation
|
||||
if (canLoot && !stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
if(ptr.getClass().getCreatureStats(ptr).getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
|
||||
|
||||
// otherwise wait until death animation
|
||||
if(stats.isDeathAnimationFinished())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
|
||||
|
||||
// death animation is not finished, do nothing
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
}
|
||||
|
||||
if(stats.getAiSequence().isInCombat())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction(""));
|
||||
|
||||
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)
|
||||
|| ptr.getClass().getCreatureStats(ptr).getKnockedDown())
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
|
||||
|
||||
// Can't talk to werewolfs
|
||||
if(ptr.getClass().isNpc() && ptr.getClass().getNpcStats(ptr).isWerewolf())
|
||||
return std::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction(""));
|
||||
|
||||
return std::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
|
||||
}
|
||||
|
||||
|
@ -1155,7 +1175,11 @@ namespace MWClass
|
|||
return true;
|
||||
|
||||
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
|
||||
return !customData.mNpcStats.getAiSequence().isInCombat() || customData.mNpcStats.isDead();
|
||||
|
||||
if (customData.mNpcStats.isDead() && customData.mNpcStats.isDeathAnimationFinished())
|
||||
return true;
|
||||
|
||||
return !customData.mNpcStats.getAiSequence().isInCombat();
|
||||
}
|
||||
|
||||
MWGui::ToolTipInfo Npc::getToolTipInfo (const MWWorld::ConstPtr& ptr, int count) const
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -373,6 +374,9 @@ namespace MWClass
|
|||
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
|
||||
return std::make_pair(0, "#{sInventoryMessage1}");
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(npc))
|
||||
return std::make_pair(0, "#{sCantEquipWeapWarning}");
|
||||
|
||||
std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr);
|
||||
|
||||
if (slots_.first.empty())
|
||||
|
|
|
@ -435,9 +435,15 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
|
|||
}
|
||||
else
|
||||
{
|
||||
// The section won't completely fit on the current page. Finish the current page and start a new one.
|
||||
mBook->mPages.push_back (Page (curPageStart, curPageStop));
|
||||
|
||||
curPageStart = i->mRect.top;
|
||||
curPageStop = i->mRect.bottom;
|
||||
|
||||
//split section
|
||||
int sectionHeightLeft = sectionHeight;
|
||||
while (sectionHeightLeft > mPageHeight)
|
||||
while (sectionHeightLeft >= mPageHeight)
|
||||
{
|
||||
// Adjust to the top of the first line that does not fit on the current page anymore
|
||||
int splitPos = curPageStop;
|
||||
|
|
|
@ -360,8 +360,9 @@ namespace MWGui
|
|||
if (msg.find("%s") != std::string::npos)
|
||||
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
||||
item.getClass().getValue(item), true);
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, item, mPtr, 1);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
return;
|
||||
|
|
|
@ -219,29 +219,33 @@ namespace MWGui
|
|||
|
||||
void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
int current = std::max(0, static_cast<int>(value.getCurrent()));
|
||||
int current = static_cast<int>(value.getCurrent());
|
||||
int modified = static_cast<int>(value.getModified());
|
||||
|
||||
// Fatigue can be negative
|
||||
if (id != "FBar")
|
||||
current = std::max(0, current);
|
||||
|
||||
MyGUI::Widget* w;
|
||||
std::string valStr = MyGUI::utility::toString(current) + " / " + MyGUI::utility::toString(modified);
|
||||
if (id == "HBar")
|
||||
{
|
||||
mHealth->setProgressRange(modified);
|
||||
mHealth->setProgressPosition(current);
|
||||
mHealth->setProgressRange(std::max(0, modified));
|
||||
mHealth->setProgressPosition(std::max(0, current));
|
||||
getWidget(w, "HealthFrame");
|
||||
w->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr);
|
||||
}
|
||||
else if (id == "MBar")
|
||||
{
|
||||
mMagicka->setProgressRange (modified);
|
||||
mMagicka->setProgressPosition (current);
|
||||
mMagicka->setProgressRange(std::max(0, modified));
|
||||
mMagicka->setProgressPosition(std::max(0, current));
|
||||
getWidget(w, "MagickaFrame");
|
||||
w->setUserString("Caption_HealthDescription", "#{sMagDesc}\n" + valStr);
|
||||
}
|
||||
else if (id == "FBar")
|
||||
{
|
||||
mStamina->setProgressRange (modified);
|
||||
mStamina->setProgressPosition (current);
|
||||
mStamina->setProgressRange(std::max(0, modified));
|
||||
mStamina->setProgressPosition(std::max(0, current));
|
||||
getWidget(w, "FatigueFrame");
|
||||
w->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "../mwrender/characterpreview.hpp"
|
||||
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "itemview.hpp"
|
||||
#include "inventoryitemmodel.hpp"
|
||||
|
@ -334,7 +335,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)
|
||||
|
@ -693,9 +694,18 @@ namespace MWGui
|
|||
|
||||
void InventoryWindow::cycle(bool next)
|
||||
{
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
|
||||
return;
|
||||
|
||||
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
|
||||
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
|
||||
return;
|
||||
|
||||
ItemModel::ModelIndex selected = -1;
|
||||
// not using mSortFilterModel as we only need sorting, not filtering
|
||||
SortFilterItemModel model(new InventoryItemModel(MWMechanics::getPlayer()));
|
||||
SortFilterItemModel model(new InventoryItemModel(player));
|
||||
model.setSortByType(false);
|
||||
model.update();
|
||||
if (model.getItemCount() == 0)
|
||||
|
|
|
@ -136,7 +136,7 @@ namespace MWGui
|
|||
*/
|
||||
value.setBase(std::min(100, value.getBase()+1));
|
||||
else
|
||||
value.setBase(value.getBase()-1);
|
||||
value.setBase(std::max(0, value.getBase()-1));
|
||||
}
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace
|
|||
|
||||
AddEntry::operator () (entry);
|
||||
|
||||
mTypesetter->sectionBreak (10);
|
||||
mTypesetter->sectionBreak (30);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -107,7 +107,7 @@ namespace
|
|||
mTypesetter->selectContent (mContentId);
|
||||
mTypesetter->write (mBodyStyle, 2, 3);// end quote
|
||||
|
||||
mTypesetter->sectionBreak (10);
|
||||
mTypesetter->sectionBreak (30);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,7 +121,7 @@ namespace
|
|||
void operator () (MWGui::JournalViewModel::Utf8Span topicName)
|
||||
{
|
||||
mTypesetter->write (mBodyStyle, topicName);
|
||||
mTypesetter->sectionBreak (10);
|
||||
mTypesetter->sectionBreak ();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -135,7 +135,7 @@ namespace
|
|||
void operator () (MWGui::JournalViewModel::Utf8Span topicName)
|
||||
{
|
||||
mTypesetter->write (mBodyStyle, topicName);
|
||||
mTypesetter->sectionBreak (10);
|
||||
mTypesetter->sectionBreak ();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ book JournalBooks::createTopicIndexBook ()
|
|||
BookTypesetter::Ptr JournalBooks::createTypesetter ()
|
||||
{
|
||||
//TODO: determine page size from layout...
|
||||
return BookTypesetter::create (240, 300);
|
||||
return BookTypesetter::create (240, 320);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace
|
|||
Book mTopicIndexBook;
|
||||
bool mQuestMode;
|
||||
bool mOptionsMode;
|
||||
bool mTopicsMode;
|
||||
bool mAllQuests;
|
||||
|
||||
template <typename T>
|
||||
|
@ -186,16 +187,17 @@ namespace
|
|||
}
|
||||
|
||||
adjustButton(TopicsBTN);
|
||||
int width = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width + getWidget<MyGUI::Widget>(QuestsBTN)->getSize().width;
|
||||
int topicsWidth = getWidget<MyGUI::Widget>(TopicsBTN)->getSize().width;
|
||||
int pageWidth = getWidget<MyGUI::Widget>(RightBookPage)->getSize().width;
|
||||
int cancelLeft = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left;
|
||||
int cancelRight = getWidget<MyGUI::Widget>(CancelBTN)->getPosition().left + getWidget<MyGUI::Widget>(CancelBTN)->getSize().width;
|
||||
|
||||
getWidget<MyGUI::Widget>(TopicsBTN)->setPosition((pageWidth - width)/2, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);
|
||||
getWidget<MyGUI::Widget>(QuestsBTN)->setPosition((pageWidth - width)/2 + topicsWidth, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);
|
||||
getWidget<MyGUI::Widget>(TopicsBTN)->setPosition(cancelLeft - topicsWidth, getWidget<MyGUI::Widget>(TopicsBTN)->getPosition().top);
|
||||
getWidget<MyGUI::Widget>(QuestsBTN)->setPosition(cancelRight, getWidget<MyGUI::Widget>(QuestsBTN)->getPosition().top);
|
||||
|
||||
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 ();
|
||||
}
|
||||
|
|
|
@ -102,6 +102,15 @@ namespace MWGui
|
|||
mBackgroundImage->setVisible(visible);
|
||||
}
|
||||
|
||||
double LoadingScreen::getTargetFrameRate() const
|
||||
{
|
||||
double frameRateLimit = MWBase::Environment::get().getFrameRateLimit();
|
||||
if (frameRateLimit > 0)
|
||||
return std::min(frameRateLimit, mTargetFrameRate);
|
||||
else
|
||||
return mTargetFrameRate;
|
||||
}
|
||||
|
||||
class CopyFramebufferToTextureCallback : public osg::Camera::DrawCallback
|
||||
{
|
||||
public:
|
||||
|
@ -141,7 +150,7 @@ namespace MWGui
|
|||
if (mViewer->getIncrementalCompileOperation())
|
||||
{
|
||||
mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100);
|
||||
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(mTargetFrameRate);
|
||||
mViewer->getIncrementalCompileOperation()->setTargetFrameRate(getTargetFrameRate());
|
||||
}
|
||||
|
||||
// Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading
|
||||
|
@ -210,7 +219,7 @@ namespace MWGui
|
|||
void LoadingScreen::setProgress (size_t value)
|
||||
{
|
||||
// skip expensive update if there isn't enough visible progress
|
||||
if (value - mProgress < mProgressBar->getScrollRange()/200.f)
|
||||
if (mProgressBar->getWidth() <= 0 || value - mProgress < mProgressBar->getScrollRange()/mProgressBar->getWidth())
|
||||
return;
|
||||
value = std::min(value, mProgressBar->getScrollRange()-1);
|
||||
mProgress = value;
|
||||
|
@ -231,7 +240,7 @@ namespace MWGui
|
|||
|
||||
bool LoadingScreen::needToDrawLoadingScreen()
|
||||
{
|
||||
if ( mTimer.time_m() <= mLastRenderTime + (1.0/mTargetFrameRate) * 1000.0)
|
||||
if ( mTimer.time_m() <= mLastRenderTime + (1.0/getTargetFrameRate()) * 1000.0)
|
||||
return false;
|
||||
|
||||
// the minimal delay before a loading screen shows
|
||||
|
|
|
@ -43,6 +43,8 @@ namespace MWGui
|
|||
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
double getTargetFrameRate() const;
|
||||
|
||||
private:
|
||||
void findSplashScreens();
|
||||
bool needToDrawLoadingScreen();
|
||||
|
@ -73,8 +75,6 @@ namespace MWGui
|
|||
|
||||
std::vector<std::string> mSplashScreens;
|
||||
|
||||
// TODO: add releaseGLObjects() for mTexture
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||
std::unique_ptr<MyGUI::ITexture> mGuiTexture;
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ namespace MWGui
|
|||
|
||||
|
||||
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
|
||||
: WindowModal("openmw_interactive_messagebox.layout")
|
||||
: WindowModal(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "openmw_interactive_messagebox_notransp.layout" : "openmw_interactive_messagebox.layout")
|
||||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mButtonPressed(-1)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
|
@ -36,6 +37,7 @@ namespace MWGui
|
|||
, mItemSelectionDialog(0)
|
||||
, mMagicSelectionDialog(0)
|
||||
, mSelectedIndex(-1)
|
||||
, mActivatedIndex(-1)
|
||||
{
|
||||
getWidget(mOkButton, "OKButton");
|
||||
getWidget(mInstructionLabel, "InstructionLabel");
|
||||
|
@ -69,6 +71,8 @@ namespace MWGui
|
|||
|
||||
void QuickKeysMenu::clear()
|
||||
{
|
||||
mActivatedIndex = -1;
|
||||
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
unassign(mQuickKeyButtons[i], i);
|
||||
|
@ -254,6 +258,15 @@ namespace MWGui
|
|||
mMagicSelectionDialog->setVisible(false);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::updateActivatedQuickKey()
|
||||
{
|
||||
// there is no delayed action, nothing to do.
|
||||
if (mActivatedIndex < 0)
|
||||
return;
|
||||
|
||||
activateQuickKey(mActivatedIndex);
|
||||
}
|
||||
|
||||
void QuickKeysMenu::activateQuickKey(int index)
|
||||
{
|
||||
assert (index-1 >= 0);
|
||||
|
@ -263,6 +276,27 @@ namespace MWGui
|
|||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
|
||||
const MWMechanics::CreatureStats &playerStats = player.getClass().getCreatureStats(player);
|
||||
|
||||
// Delay action executing,
|
||||
// if player is busy for now (casting a spell, attacking someone, etc.)
|
||||
bool isDelayNeeded = MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player)
|
||||
|| playerStats.getKnockedDown()
|
||||
|| playerStats.getHitRecovery();
|
||||
|
||||
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
|
||||
if (isReturnNeeded && type != Type_Item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDelayNeeded && type != Type_Item)
|
||||
{
|
||||
mActivatedIndex = index;
|
||||
return;
|
||||
}
|
||||
else
|
||||
mActivatedIndex = -1;
|
||||
|
||||
if (type == Type_Item || type == Type_MagicItem)
|
||||
{
|
||||
|
@ -309,6 +343,21 @@ namespace MWGui
|
|||
else if (type == Type_Item)
|
||||
{
|
||||
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
|
||||
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
|
||||
|
||||
// delay weapon switching if player is busy
|
||||
if (isDelayNeeded && isWeapon)
|
||||
{
|
||||
mActivatedIndex = index;
|
||||
return;
|
||||
}
|
||||
|
||||
// disable weapon switching if player is dead or paralyzed
|
||||
if (isReturnNeeded && isWeapon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->useItem(item);
|
||||
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
// change draw state only if the item is in player's right hand
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace MWGui
|
|||
void onAssignMagicCancel ();
|
||||
|
||||
void activateQuickKey(int index);
|
||||
void updateActivatedQuickKey();
|
||||
|
||||
/// @note This enum is serialized, so don't move the items around!
|
||||
enum QuickKeyType
|
||||
|
@ -64,7 +65,7 @@ namespace MWGui
|
|||
MagicSelectionDialog* mMagicSelectionDialog;
|
||||
|
||||
int mSelectedIndex;
|
||||
|
||||
int mActivatedIndex;
|
||||
|
||||
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
|
||||
void onOkButtonClicked(MyGUI::Widget* sender);
|
||||
|
|
|
@ -20,9 +20,8 @@ namespace MWGui
|
|||
{
|
||||
MWWorld::CellStore* playerCell = MWMechanics::getPlayer().getCell();
|
||||
|
||||
// check if player has changed cell, or count of the reference has become 0
|
||||
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)
|
||||
|| (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0))
|
||||
// check if count of the reference has become 0
|
||||
if (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0)
|
||||
{
|
||||
if (!mPtr.isEmpty())
|
||||
{
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace MWGui
|
|||
|
||||
void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
int current = std::max(0, static_cast<int>(value.getCurrent()));
|
||||
int current = static_cast<int>(value.getCurrent());
|
||||
int modified = static_cast<int>(value.getModified());
|
||||
|
||||
mFatigue->setValue(current, modified);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -216,6 +217,15 @@ namespace MWGui
|
|||
|
||||
void SpellWindow::cycle(bool next)
|
||||
{
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(player))
|
||||
return;
|
||||
|
||||
const MWMechanics::CreatureStats &stats = player.getClass().getCreatureStats(player);
|
||||
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead() || stats.getHitRecovery())
|
||||
return;
|
||||
|
||||
mSpellView->setModel(new SpellModel(MWMechanics::getPlayer()));
|
||||
|
||||
SpellModel::ModelIndex selected = 0;
|
||||
|
|
|
@ -102,12 +102,13 @@ namespace MWGui
|
|||
{
|
||||
MyGUI::ProgressBar* pt;
|
||||
getWidget(pt, name);
|
||||
pt->setProgressRange(max);
|
||||
pt->setProgressPosition(val);
|
||||
|
||||
std::stringstream out;
|
||||
out << val << "/" << max;
|
||||
setText(tname, out.str().c_str());
|
||||
|
||||
pt->setProgressRange(std::max(0, max));
|
||||
pt->setProgressPosition(std::max(0, val));
|
||||
}
|
||||
|
||||
void StatsWindow::setPlayerName(const std::string& playerName)
|
||||
|
@ -147,9 +148,13 @@ namespace MWGui
|
|||
|
||||
void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
int current = std::max(0, static_cast<int>(value.getCurrent()));
|
||||
int current = static_cast<int>(value.getCurrent());
|
||||
int modified = static_cast<int>(value.getModified());
|
||||
|
||||
// Fatigue can be negative
|
||||
if (id != "FBar")
|
||||
current = std::max(0, current);
|
||||
|
||||
setBar (id, id + "T", current, modified);
|
||||
|
||||
// health, magicka, fatigue tooltip
|
||||
|
|
|
@ -359,12 +359,11 @@ namespace MWGui
|
|||
{
|
||||
if(!mFocusObject.isEmpty())
|
||||
{
|
||||
const MWWorld::CellRef& cellref = mFocusObject.getCellRef();
|
||||
MWWorld::Ptr ptr = MWMechanics::getPlayer();
|
||||
MWWorld::Ptr victim;
|
||||
|
||||
MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager();
|
||||
bool allowed = mm->isAllowedToUse(ptr, cellref, victim);
|
||||
bool allowed = mm->isAllowedToUse(ptr, mFocusObject, victim);
|
||||
|
||||
return !allowed;
|
||||
}
|
||||
|
@ -378,17 +377,10 @@ namespace MWGui
|
|||
{
|
||||
mDynamicToolTipBox->setVisible(true);
|
||||
|
||||
if(mShowOwned == 1 || mShowOwned == 3)
|
||||
{
|
||||
if(isFocusObject && checkOwned())
|
||||
{
|
||||
mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp_Owned");
|
||||
}
|
||||
if((mShowOwned == 1 || mShowOwned == 3) && isFocusObject && checkOwned())
|
||||
mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp_Owned" : "HUD_Box_Owned");
|
||||
else
|
||||
{
|
||||
mDynamicToolTipBox->changeWidgetSkin("HUD_Box_NoTransp");
|
||||
}
|
||||
}
|
||||
mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp" : "HUD_Box");
|
||||
|
||||
std::string caption = info.caption;
|
||||
std::string image = info.icon;
|
||||
|
|
|
@ -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();
|
||||
|
@ -313,9 +311,9 @@ namespace MWGui
|
|||
if (msg.find("%s") != std::string::npos)
|
||||
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(msg);
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
|
||||
it->mBase.getClass().getValue(it->mBase)
|
||||
* it->mCount, true);
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItemToOwner(player, it->mBase, mPtr, it->mCount);
|
||||
|
||||
onCancelButtonClicked(mCancelButton);
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
return;
|
||||
|
@ -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)
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MWGui
|
|||
|
||||
void WaitDialog::startWaiting(int hoursToWait)
|
||||
{
|
||||
if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping
|
||||
if(Settings::Manager::getBool("autosave","Saves")) //autosaves when enabled
|
||||
MWBase::Environment::get().getStateManager()->quickSave("Autosave");
|
||||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
|
|
@ -502,11 +502,10 @@ namespace MWGui
|
|||
|
||||
if (mBarWidget)
|
||||
{
|
||||
mBarWidget->setProgressRange(mMax);
|
||||
mBarWidget->setProgressPosition(mValue);
|
||||
mBarWidget->setProgressRange(std::max(0, mMax));
|
||||
mBarWidget->setProgressPosition(std::max(0, mValue));
|
||||
}
|
||||
|
||||
|
||||
if (mBarTextWidget)
|
||||
{
|
||||
std::stringstream out;
|
||||
|
|
|
@ -529,6 +529,8 @@ namespace MWGui
|
|||
cleanupGarbage();
|
||||
|
||||
mHud->update();
|
||||
|
||||
updateActivatedQuickKey ();
|
||||
}
|
||||
|
||||
void WindowManager::updateVisible()
|
||||
|
@ -916,19 +918,30 @@ namespace MWGui
|
|||
|
||||
if (block)
|
||||
{
|
||||
osg::Timer frameTimer;
|
||||
while (mMessageBoxManager->readPressedButton(false) == -1
|
||||
&& !MWBase::Environment::get().getStateManager()->hasQuitRequest())
|
||||
{
|
||||
mMessageBoxManager->onFrame(0.f);
|
||||
MWBase::Environment::get().getInputManager()->update(0, true, false);
|
||||
double dt = frameTimer.time_s();
|
||||
frameTimer.setStartTick();
|
||||
|
||||
// at the time this function is called we are in the middle of a frame,
|
||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||
// refer to the advance() and frame() order in Engine::go()
|
||||
mMessageBoxManager->onFrame(dt);
|
||||
MWBase::Environment::get().getInputManager()->update(dt, true, false);
|
||||
|
||||
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
else
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
// at the time this function is called we are in the middle of a frame,
|
||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||
// refer to the advance() and frame() order in Engine::go()
|
||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||
|
||||
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1566,6 +1579,11 @@ namespace MWGui
|
|||
mHud->setCrosshairVisible (show && mCrosshairEnabled);
|
||||
}
|
||||
|
||||
void WindowManager::updateActivatedQuickKey ()
|
||||
{
|
||||
mQuickKeysMenu->updateActivatedQuickKey();
|
||||
}
|
||||
|
||||
void WindowManager::activateQuickKey (int index)
|
||||
{
|
||||
mQuickKeysMenu->activateQuickKey(index);
|
||||
|
@ -1869,18 +1887,28 @@ namespace MWGui
|
|||
if (mVideoWidget->hasAudioStream())
|
||||
MWBase::Environment::get().getSoundManager()->pauseSounds(
|
||||
MWBase::SoundManager::Play_TypeMask&(~MWBase::SoundManager::Play_TypeMovie));
|
||||
|
||||
osg::Timer frameTimer;
|
||||
while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
|
||||
{
|
||||
MWBase::Environment::get().getInputManager()->update(0, true, false);
|
||||
double dt = frameTimer.time_s();
|
||||
frameTimer.setStartTick();
|
||||
|
||||
// at the time this function is called we are in the middle of a frame,
|
||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||
// refer to the advance() and frame() order in Engine::go()
|
||||
MWBase::Environment::get().getInputManager()->update(dt, true, false);
|
||||
|
||||
if (!MWBase::Environment::get().getInputManager()->isWindowVisible())
|
||||
OpenThreads::Thread::microSleep(5000);
|
||||
else
|
||||
{
|
||||
mViewer->eventTraversal();
|
||||
mViewer->updateTraversal();
|
||||
mViewer->renderingTraversals();
|
||||
}
|
||||
// at the time this function is called we are in the middle of a frame,
|
||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||
// refer to the advance() and frame() order in Engine::go()
|
||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||
|
||||
MWBase::Environment::get().limitFrameRate(frameTimer.time_s());
|
||||
}
|
||||
mVideoWidget->stop();
|
||||
|
||||
|
@ -2021,12 +2049,8 @@ namespace MWGui
|
|||
char* text=0;
|
||||
text = SDL_GetClipboardText();
|
||||
if (text)
|
||||
{
|
||||
// MyGUI's clipboard might still have color information, to retain that information, only set the new text
|
||||
// if it actually changed (clipboard inserted by an external application)
|
||||
if (MyGUI::TextIterator::getOnlyText(_data) != text)
|
||||
_data = text;
|
||||
}
|
||||
_data = MyGUI::TextIterator::toTagsString(text);
|
||||
|
||||
SDL_free(text);
|
||||
}
|
||||
|
||||
|
@ -2072,7 +2096,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)
|
||||
|
|
|
@ -251,7 +251,10 @@ namespace MWGui
|
|||
virtual void setSpellVisibility(bool visible);
|
||||
virtual void setSneakVisibility(bool visible);
|
||||
|
||||
/// activate selected quick key
|
||||
virtual void activateQuickKey (int index);
|
||||
/// update activated quick key state (if action executing was delayed for some reason)
|
||||
virtual void updateActivatedQuickKey ();
|
||||
|
||||
virtual std::string getSelectedSpell() { return mSelectedSpell; }
|
||||
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/statemanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -966,6 +967,9 @@ namespace MWInput
|
|||
inventory.getSelectedEnchantItem() == inventory.end())
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
|
||||
return;
|
||||
|
||||
MWMechanics::DrawState_ state = mPlayer->getDrawState();
|
||||
if (state == MWMechanics::DrawState_Weapon || state == MWMechanics::DrawState_Nothing)
|
||||
mPlayer->setDrawState(MWMechanics::DrawState_Spell);
|
||||
|
@ -981,6 +985,9 @@ namespace MWInput
|
|||
if (!mControlSwitch["playerfighting"] || !mControlSwitch["playercontrols"])
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getMechanicsManager()->isAttackingOrSpell(mPlayer->getPlayer()))
|
||||
return;
|
||||
|
||||
MWMechanics::DrawState_ state = mPlayer->getDrawState();
|
||||
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
|
||||
mPlayer->setDrawState(MWMechanics::DrawState_Weapon);
|
||||
|
@ -1070,6 +1077,7 @@ namespace MWInput
|
|||
return;
|
||||
|
||||
if(MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Journal
|
||||
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_MainMenu
|
||||
&& MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->playSound ("book open");
|
||||
|
|
|
@ -222,10 +222,23 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void ActiveSpells::purgeAll(float chance)
|
||||
void ActiveSpells::purgeAll(float chance, bool spellOnly)
|
||||
{
|
||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); )
|
||||
{
|
||||
const std::string spellId = it->first;
|
||||
|
||||
// if spellOnly is true, dispell only spells. Leave potions, enchanted items etc.
|
||||
if (spellOnly)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(spellId);
|
||||
if (!spell || spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Misc::Rng::roll0to99() < chance)
|
||||
mSpells.erase(it++);
|
||||
else
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace MWMechanics
|
|||
void purgeEffect (short effectId, const std::string& sourceId);
|
||||
|
||||
/// Remove all active effects, if roll succeeds (for each effect)
|
||||
void purgeAll (float chance);
|
||||
void purgeAll(float chance, bool spellOnly = false);
|
||||
|
||||
/// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
|
||||
void purge (int casterActorId);
|
||||
|
|
|
@ -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"
|
||||
|
@ -830,7 +832,7 @@ namespace MWMechanics
|
|||
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
|
||||
if (ptr.getClass().hasInventoryStore(ptr))
|
||||
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
|
||||
updateSummonedCreatures.process();
|
||||
updateSummonedCreatures.process(mTimerDisposeSummonsCorpses == 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -849,6 +851,26 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
bool Actors::isRunning(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
if (it == mActors.end())
|
||||
return false;
|
||||
CharacterController* ctrl = it->second->getCharacterController();
|
||||
|
||||
return ctrl->isRunning();
|
||||
}
|
||||
|
||||
bool Actors::isSneaking(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
if (it == mActors.end())
|
||||
return false;
|
||||
CharacterController* ctrl = it->second->getCharacterController();
|
||||
|
||||
return ctrl->isSneaking();
|
||||
}
|
||||
|
||||
void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
|
@ -863,6 +885,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)
|
||||
|
@ -1069,7 +1100,9 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
Actors::Actors() {}
|
||||
Actors::Actors() {
|
||||
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
|
||||
}
|
||||
|
||||
Actors::~Actors()
|
||||
{
|
||||
|
@ -1138,6 +1171,7 @@ namespace MWMechanics
|
|||
// target lists get updated once every 1.0 sec
|
||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
|
@ -1307,6 +1341,7 @@ namespace MWMechanics
|
|||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
mTimerDisposeSummonsCorpses += duration;
|
||||
|
||||
// Looping magic VFX update
|
||||
// Note: we need to do this before any of the animations are updated.
|
||||
|
@ -1625,6 +1660,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();
|
||||
|
@ -1939,6 +1979,16 @@ namespace MWMechanics
|
|||
return it->second->getCharacterController()->isReadyToBlock();
|
||||
}
|
||||
|
||||
bool Actors::isAttackingOrSpell(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
PtrActorMap::const_iterator it = mActors.find(ptr);
|
||||
if (it == mActors.end())
|
||||
return false;
|
||||
CharacterController* ctrl = it->second->getCharacterController();
|
||||
|
||||
return ctrl->isAttackingOrSpell();
|
||||
}
|
||||
|
||||
void Actors::fastForwardAi()
|
||||
{
|
||||
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
|
|
|
@ -117,6 +117,9 @@ namespace MWMechanics
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
bool isRunning(const MWWorld::Ptr& ptr);
|
||||
bool isSneaking(const MWWorld::Ptr& ptr);
|
||||
|
||||
void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||
|
||||
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
|
||||
|
@ -157,9 +160,11 @@ namespace MWMechanics
|
|||
void clear(); // Clear death counter
|
||||
|
||||
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
||||
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
private:
|
||||
PtrActorMap mActors;
|
||||
float mTimerDisposeSummonsCorpses;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
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;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue