Merged files

This commit is contained in:
Jeffrey Haines 2014-05-04 11:33:27 -04:00
commit a65e8393bb
202 changed files with 2956 additions and 1738 deletions

View file

@ -86,8 +86,6 @@ option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock fram
# Sound source selection # Sound source selection
option(USE_FFMPEG "use ffmpeg for sound" ON) option(USE_FFMPEG "use ffmpeg for sound" ON)
option(USE_AUDIERE "use audiere for sound" ON)
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
# OS X deployment # OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF) option(OPENMW_OSX_DEPLOYMENT OFF)
@ -171,27 +169,6 @@ if (USE_FFMPEG)
endif (FFMPEG_FOUND) endif (FFMPEG_FOUND)
endif (USE_FFMPEG) endif (USE_FFMPEG)
if (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
find_package(Audiere)
if (AUDIERE_FOUND)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
set(GOT_SOUND_INPUT 1)
endif (AUDIERE_FOUND)
endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
if (USE_MPG123 AND NOT GOT_SOUND_INPUT)
find_package(MPG123 REQUIRED)
find_package(SNDFILE REQUIRED)
if (MPG123_FOUND AND SNDFILE_FOUND)
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
set(GOT_SOUND_INPUT 1)
endif (MPG123_FOUND AND SNDFILE_FOUND)
endif (USE_MPG123 AND NOT GOT_SOUND_INPUT)
if (NOT GOT_SOUND_INPUT) if (NOT GOT_SOUND_INPUT)
message(WARNING "--------------------") message(WARNING "--------------------")
message(WARNING "Failed to find any sound input packages") message(WARNING "Failed to find any sound input packages")
@ -434,7 +411,6 @@ IF(NOT WIN32 AND NOT APPLE)
# Install licenses # Install licenses
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
ENDIF (DPKG_PROGRAM) ENDIF (DPKG_PROGRAM)

View file

@ -333,7 +333,7 @@ int load(Arguments& info)
// Is the user interested in this record type? // Is the user interested in this record type?
bool interested = true; bool interested = true;
if (info.types.size() > 0) if (!info.types.empty())
{ {
std::vector<std::string>::iterator match; std::vector<std::string>::iterator match;
match = std::find(info.types.begin(), info.types.end(), match = std::find(info.types.begin(), info.types.end(),

View file

@ -124,7 +124,7 @@ void printEffectList(ESM::EffectList effects)
{ {
int i = 0; int i = 0;
std::vector<ESM::ENAMstruct>::iterator eit; std::vector<ESM::ENAMstruct>::iterator eit;
for (eit = effects.mList.begin(); eit != effects.mList.end(); eit++) for (eit = effects.mList.begin(); eit != effects.mList.end(); ++eit)
{ {
std::cout << " Effect[" << i << "]: " << magicEffectLabel(eit->mEffectID) std::cout << " Effect[" << i << "]: " << magicEffectLabel(eit->mEffectID)
<< " (" << eit->mEffectID << ")" << std::endl; << " (" << eit->mEffectID << ")" << std::endl;

View file

@ -214,13 +214,13 @@ QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre
uint row = 0; uint row = 0;
Ogre::ConfigOptionMap options = renderer->getConfigOptions(); Ogre::ConfigOptionMap options = renderer->getConfigOptions();
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++) for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); ++i, ++row)
{ {
Ogre::StringVector::iterator opt_it; Ogre::StringVector::iterator opt_it;
uint idx = 0; uint idx = 0;
for (opt_it = i->second.possibleValues.begin(); for (opt_it = i->second.possibleValues.begin();
opt_it != i->second.possibleValues.end(); opt_it++, idx++) opt_it != i->second.possibleValues.end(); ++opt_it, ++idx)
{ {
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) { if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified(); result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();

View file

@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable
) )
opencs_units (view/render opencs_units (view/render
@ -95,7 +95,9 @@ opencs_units (view/settings
booleanview booleanview
textview textview
listview listview
rangeview
resizeablestackedwidget resizeablestackedwidget
spinbox
) )
opencs_units_noqt (view/settings opencs_units_noqt (view/settings

View file

@ -121,7 +121,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
//iterate the data directories and add them to the file dialog for loading //iterate the data directories and add them to the file dialog for loading
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{ {
QString path = QString::fromStdString(iter->string()); QString path = QString::fromUtf8 (iter->string().c_str());
mFileDialog.addFiles(path); mFileDialog.addFiles(path);
} }
/* /*

View file

@ -1,29 +1,22 @@
#include "setting.hpp" #include "setting.hpp"
#include "support.hpp" #include "support.hpp"
CSMSettings::Setting::Setting()
{
buildDefaultSetting();
}
CSMSettings::Setting::Setting(SettingType typ, const QString &settingName, CSMSettings::Setting::Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QStringList &values) const QString &pageName)
: mIsEditorSetting (false) : mIsEditorSetting (false)
{ {
buildDefaultSetting(); buildDefaultSetting();
int vType = static_cast <int> (typ); int settingType = static_cast <int> (typ);
if ((vType % 2) == 0) //even-numbered setting types are multi-valued
setProperty (Property_IsMultiValue, if ((settingType % 2) == 0)
QVariant(true).toString()); setProperty (Property_IsMultiValue, QVariant(true).toString());
else
vType--;
setProperty (Property_ViewType, QVariant (vType / 2).toString()); //view type is related to setting type by an order of magnitude
setProperty (Property_SettingType, QVariant (settingType).toString());
setProperty (Property_Page, pageName); setProperty (Property_Page, pageName);
setProperty (Property_Name, settingName); setProperty (Property_Name, settingName);
setProperty (Property_DeclaredValues, values);
} }
void CSMSettings::Setting::buildDefaultSetting() void CSMSettings::Setting::buildDefaultSetting()
@ -74,6 +67,11 @@ int CSMSettings::Setting::columnSpan() const
return property (Property_ColumnSpan).at(0).toInt(); return property (Property_ColumnSpan).at(0).toInt();
} }
void CSMSettings::Setting::setDeclaredValues (QStringList list)
{
setProperty (Property_DeclaredValues, list);
}
QStringList CSMSettings::Setting::declaredValues() const QStringList CSMSettings::Setting::declaredValues() const
{ {
return property (Property_DeclaredValues); return property (Property_DeclaredValues);
@ -97,6 +95,16 @@ QStringList CSMSettings::Setting::property (SettingProperty prop) const
return mProperties.at(prop); return mProperties.at(prop);
} }
void CSMSettings::Setting::setDefaultValue (int value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (double value)
{
setDefaultValues (QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setDefaultValue (const QString &value) void CSMSettings::Setting::setDefaultValue (const QString &value)
{ {
setDefaultValues (QStringList() << value); setDefaultValues (QStringList() << value);
@ -166,6 +174,16 @@ bool CSMSettings::Setting::serializable() const
return (property (Property_Serializable).at(0) == "true"); return (property (Property_Serializable).at(0) == "true");
} }
void CSMSettings::Setting::setSpecialValueText(const QString &text)
{
setProperty (Property_SpecialValueText, text);
}
QString CSMSettings::Setting::specialValueText() const
{
return property (Property_SpecialValueText).at(0);
}
void CSMSettings::Setting::setName (const QString &value) void CSMSettings::Setting::setName (const QString &value)
{ {
setProperty (Property_Name, value); setProperty (Property_Name, value);
@ -186,6 +204,16 @@ QString CSMSettings::Setting::page() const
return property (Property_Page).at(0); return property (Property_Page).at(0);
} }
void CSMSettings::Setting::setPrefix (const QString &value)
{
setProperty (Property_Prefix, value);
}
QString CSMSettings::Setting::prefix() const
{
return property (Property_Prefix).at(0);
}
void CSMSettings::Setting::setRowSpan (const int value) void CSMSettings::Setting::setRowSpan (const int value)
{ {
setProperty (Property_RowSpan, value); setProperty (Property_RowSpan, value);
@ -196,15 +224,106 @@ int CSMSettings::Setting::rowSpan () const
return property (Property_RowSpan).at(0).toInt(); return property (Property_RowSpan).at(0).toInt();
} }
void CSMSettings::Setting::setViewType (int vType) void CSMSettings::Setting::setSingleStep (int value)
{ {
setProperty (Property_ViewType, vType); setProperty (Property_SingleStep, value);
}
void CSMSettings::Setting::setSingleStep (double value)
{
setProperty (Property_SingleStep, value);
}
QString CSMSettings::Setting::singleStep() const
{
return property (Property_SingleStep).at(0);
}
void CSMSettings::Setting::setSuffix (const QString &value)
{
setProperty (Property_Suffix, value);
}
QString CSMSettings::Setting::suffix() const
{
return property (Property_Suffix).at(0);
}
void CSMSettings::Setting::setTickInterval (int value)
{
setProperty (Property_TickInterval, value);
}
int CSMSettings::Setting::tickInterval () const
{
return property (Property_TickInterval).at(0).toInt();
}
void CSMSettings::Setting::setTicksAbove (bool state)
{
setProperty (Property_TicksAbove, state);
}
bool CSMSettings::Setting::ticksAbove() const
{
return (property (Property_TicksAbove).at(0) == "true");
}
void CSMSettings::Setting::setTicksBelow (bool state)
{
setProperty (Property_TicksBelow, state);
}
bool CSMSettings::Setting::ticksBelow() const
{
return (property (Property_TicksBelow).at(0) == "true");
}
void CSMSettings::Setting::setType (int settingType)
{
setProperty (Property_SettingType, settingType);
}
CSMSettings::SettingType CSMSettings::Setting::type() const
{
return static_cast <CSMSettings::SettingType> ( property (
Property_SettingType).at(0).toInt());
}
void CSMSettings::Setting::setMaximum (int value)
{
setProperty (Property_Maximum, value);
}
void CSMSettings::Setting::setMaximum (double value)
{
setProperty (Property_Maximum, value);
}
QString CSMSettings::Setting::maximum() const
{
return property (Property_Maximum).at(0);
}
void CSMSettings::Setting::setMinimum (int value)
{
setProperty (Property_Minimum, value);
}
void CSMSettings::Setting::setMinimum (double value)
{
setProperty (Property_Minimum, value);
}
QString CSMSettings::Setting::minimum() const
{
return property (Property_Minimum).at(0);
} }
CSVSettings::ViewType CSMSettings::Setting::viewType() const CSVSettings::ViewType CSMSettings::Setting::viewType() const
{ {
return static_cast <CSVSettings::ViewType> return static_cast <CSVSettings::ViewType> ( property (
(property(Property_ViewType).at(0).toInt()); Property_SettingType).at(0).toInt() / 10);
} }
void CSMSettings::Setting::setViewColumn (int value) void CSMSettings::Setting::setViewColumn (int value)
@ -242,6 +361,17 @@ int CSMSettings::Setting::widgetWidth() const
{ {
return property (Property_WidgetWidth).at(0).toInt(); return property (Property_WidgetWidth).at(0).toInt();
} }
void CSMSettings::Setting::setWrapping (bool state)
{
setProperty (Property_Wrapping, state);
}
bool CSMSettings::Setting::wrapping() const
{
return (property (Property_Wrapping).at(0) == "true");
}
void CSMSettings::Setting::setProperty (SettingProperty prop, bool value) void CSMSettings::Setting::setProperty (SettingProperty prop, bool value)
{ {
setProperty (prop, QStringList() << QVariant (value).toString()); setProperty (prop, QStringList() << QVariant (value).toString());
@ -252,6 +382,11 @@ void CSMSettings::Setting::setProperty (SettingProperty prop, int value)
setProperty (prop, QStringList() << QVariant (value).toString()); setProperty (prop, QStringList() << QVariant (value).toString());
} }
void CSMSettings::Setting::setProperty (SettingProperty prop, double value)
{
setProperty (prop, QStringList() << QVariant (value).toString());
}
void CSMSettings::Setting::setProperty (SettingProperty prop, void CSMSettings::Setting::setProperty (SettingProperty prop,
const QString &value) const QString &value)
{ {
@ -264,18 +399,3 @@ void CSMSettings::Setting::setProperty (SettingProperty prop,
if (prop < mProperties.size()) if (prop < mProperties.size())
mProperties.replace (prop, value); mProperties.replace (prop, value);
} }
QDataStream &operator <<(QDataStream &stream, const CSMSettings::Setting& setting)
{
stream << setting.properties();
stream << setting.proxies();
return stream;
}
QDataStream &operator >>(QDataStream& stream, CSMSettings::Setting& setting)
{
// stream >> setting.properties();
// stream >> setting.proxies();
return stream;
}

View file

@ -27,12 +27,8 @@ namespace CSMSettings
public: public:
explicit Setting();
explicit Setting(SettingType typ, const QString &settingName, explicit Setting(SettingType typ, const QString &settingName,
const QString &pageName, const QString &pageName);
const QStringList &values = QStringList());
void addProxy (const Setting *setting, const QStringList &vals); void addProxy (const Setting *setting, const QStringList &vals);
void addProxy (const Setting *setting, const QList <QStringList> &list); void addProxy (const Setting *setting, const QList <QStringList> &list);
@ -49,6 +45,8 @@ namespace CSMSettings
void setDefinedValues (QStringList list); void setDefinedValues (QStringList list);
QStringList definedValues() const; QStringList definedValues() const;
void setDefaultValue (int value);
void setDefaultValue (double value);
void setDefaultValue (const QString &value); void setDefaultValue (const QString &value);
void setDefaultValues (const QStringList &values); void setDefaultValues (const QStringList &values);
@ -66,12 +64,26 @@ namespace CSMSettings
void setIsMultiValue (bool state); void setIsMultiValue (bool state);
bool isMultiValue() const; bool isMultiValue() const;
void setMask (const QString &value);
QString mask() const;
void setMaximum (int value);
void setMaximum (double value);
QString maximum() const;
void setMinimum (int value);
void setMinimum (double value);
QString minimum() const;
void setName (const QString &value); void setName (const QString &value);
QString name() const; QString name() const;
void setPage (const QString &value); void setPage (const QString &value);
QString page() const; QString page() const;
void setPrefix (const QString &value);
QString prefix() const;
void setRowSpan (const int value); void setRowSpan (const int value);
int rowSpan() const; int rowSpan() const;
@ -80,6 +92,25 @@ namespace CSMSettings
void setSerializable (bool state); void setSerializable (bool state);
bool serializable() const; bool serializable() const;
void setSpecialValueText (const QString &text);
QString specialValueText() const;
void setSingleStep (int value);
void setSingleStep (double value);
QString singleStep() const;
void setSuffix (const QString &value);
QString suffix() const;
void setTickInterval (int value);
int tickInterval() const;
void setTicksAbove (bool state);
bool ticksAbove() const;
void setTicksBelow (bool state);
bool ticksBelow() const;
void setViewColumn (int value); void setViewColumn (int value);
int viewColumn() const; int viewColumn() const;
@ -88,9 +119,14 @@ namespace CSMSettings
void setViewRow (int value); void setViewRow (int value);
int viewRow() const; int viewRow() const;
void setViewType (int vType); void setType (int settingType);
CSMSettings::SettingType type() const;
CSVSettings::ViewType viewType() const; CSVSettings::ViewType viewType() const;
void setWrapping (bool state);
bool wrapping() const;
void setWidgetWidth (int value); void setWidgetWidth (int value);
int widgetWidth() const; int widgetWidth() const;
@ -100,6 +136,7 @@ namespace CSMSettings
///boilerplate code to convert setting values of common types ///boilerplate code to convert setting values of common types
void setProperty (SettingProperty prop, bool value); void setProperty (SettingProperty prop, bool value);
void setProperty (SettingProperty prop, int value); void setProperty (SettingProperty prop, int value);
void setProperty (SettingProperty prop, double value);
void setProperty (SettingProperty prop, const QString &value); void setProperty (SettingProperty prop, const QString &value);
void setProperty (SettingProperty prop, const QStringList &value); void setProperty (SettingProperty prop, const QStringList &value);
@ -111,9 +148,4 @@ namespace CSMSettings
}; };
} }
Q_DECLARE_METATYPE(CSMSettings::Setting)
QDataStream &operator <<(QDataStream &stream, const CSMSettings::Setting& setting);
QDataStream &operator >>(QDataStream &stream, CSMSettings::Setting& setting);
#endif // CSMSETTINGS_SETTING_HPP #endif // CSMSETTINGS_SETTING_HPP

View file

@ -30,8 +30,7 @@ void CSMSettings::SettingManager::dumpModel()
} }
CSMSettings::Setting *CSMSettings::SettingManager::createSetting CSMSettings::Setting *CSMSettings::SettingManager::createSetting
(CSMSettings::SettingType typ, const QString &page, const QString &name, (CSMSettings::SettingType typ, const QString &page, const QString &name)
const QStringList &values)
{ {
//get list of all settings for the current setting name //get list of all settings for the current setting name
if (findSetting (page, name)) if (findSetting (page, name))
@ -41,7 +40,8 @@ CSMSettings::Setting *CSMSettings::SettingManager::createSetting
return 0; return 0;
} }
Setting *setting = new Setting (typ, name, page, values); Setting *setting = new Setting (typ, name, page);
//add declaration to the model //add declaration to the model
mSettings.append (setting); mSettings.append (setting);

View file

@ -46,8 +46,7 @@ namespace CSMSettings
///add a new setting to the model and return it ///add a new setting to the model and return it
Setting *createSetting (CSMSettings::SettingType typ, Setting *createSetting (CSMSettings::SettingType typ,
const QString &page, const QString &name, const QString &page, const QString &name);
const QStringList &values = QStringList());
///add definitions to the settings specified in the page map ///add definitions to the settings specified in the page map
void addDefinitions (DefinitionPageMap &pageMap); void addDefinitions (DefinitionPageMap &pageMap);

View file

@ -27,7 +27,7 @@ namespace CSMSettings
{ {
Property_Name = 0, Property_Name = 0,
Property_Page = 1, Property_Page = 1,
Property_ViewType = 2, Property_SettingType = 2,
Property_IsMultiValue = 3, Property_IsMultiValue = 3,
Property_IsMultiLine = 4, Property_IsMultiLine = 4,
Property_WidgetWidth = 5, Property_WidgetWidth = 5,
@ -37,24 +37,49 @@ namespace CSMSettings
Property_Serializable = 9, Property_Serializable = 9,
Property_ColumnSpan = 10, Property_ColumnSpan = 10,
Property_RowSpan = 11, Property_RowSpan = 11,
Property_Minimum = 12,
Property_Maximum = 13,
Property_SpecialValueText = 14,
Property_Prefix = 15,
Property_Suffix = 16,
Property_SingleStep = 17,
Property_Wrapping = 18,
Property_TickInterval = 19,
Property_TicksAbove = 20,
Property_TicksBelow = 21,
//Stringlists should always be the last items //Stringlists should always be the last items
Property_DefaultValues = 12, Property_DefaultValues = 22,
Property_DeclaredValues = 13, Property_DeclaredValues = 23,
Property_DefinedValues = 14, Property_DefinedValues = 24,
Property_Proxies = 15 Property_Proxies = 25
}; };
enum SettingType enum SettingType
{ {
Type_MultiBool = 0, /*
Type_SingleBool = 1, * 0 - 9 - Boolean widgets
Type_MultiList = 2, * 10-19 - List widgets
Type_SingleList = 3, * 21-29 - Range widgets
Type_MultiRange = 4, * 31-39 - Text widgets
Type_SingleRange = 5, *
Type_MultiText = 6, * Each range corresponds to a View_Type enum by a factor of 10.
Type_SingleText = 7 *
* Even-numbered values are single-value widgets
* Odd-numbered values are multi-valued widgets
*/
Type_CheckBox = 0,
Type_RadioButton = 1,
Type_ListView = 10,
Type_ComboBox = 11,
Type_SpinBox = 21,
Type_DoubleSpinBox = 23,
Type_Slider = 25,
Type_Dial = 27,
Type_TextArea = 30,
Type_LineEdit = 31,
Type_Undefined = 40
}; };
enum MergeMethod enum MergeMethod
@ -84,7 +109,7 @@ namespace CSVSettings
}; };
} }
//
namespace CSMSettings namespace CSMSettings
{ {
struct PropertyDefaultValues struct PropertyDefaultValues
@ -96,9 +121,11 @@ namespace CSMSettings
const QString sPropertyNames[] = const QString sPropertyNames[] =
{ {
"name", "page", "view_type", "is_multi_value", "name", "page", "setting_type", "is_multi_value",
"is_multi_line", "widget_width", "view_row", "view_column", "delimiter", "is_multi_line", "widget_width", "view_row", "view_column", "delimiter",
"is_serializable","column_span", "row_span", "is_serializable","column_span", "row_span", "minimum", "maximum",
"special_value_text", "prefix", "suffix", "single_step", "wrapping",
"tick_interval", "ticks_above", "ticks_below",
"defaults", "declarations", "definitions", "proxies" "defaults", "declarations", "definitions", "proxies"
}; };
@ -106,16 +133,26 @@ namespace CSMSettings
{ {
"", //name "", //name
"", //page "", //page
"0", //view type "40", //setting type
"false", //multivalue "false", //multivalue
"false", //multiline "false", //multiline
"0", //widget width "7", //widget width
"-1", //view row "-1", //view row
"-1", //view column "-1", //view column
",", //delimiter ",", //delimiter
"true", //serialized "true", //serialized
"1", //column span "1", //column span
"1", //row span "1", //row span
"0", //value range
"0", //value minimum
"0", //value maximum
"", //special text
"", //prefix
"", //suffix
"false", //wrapping
"1", //tick interval
"false", //ticks above
"true", //ticks below
"", //default values "", //default values
"", //declared values "", //declared values
"", //defined values "", //defined values

View file

@ -47,11 +47,11 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
{ {
QString section = "Window Size"; QString section = "Window Size";
{ {
Setting *width = createSetting (Type_SingleText, section, "Width"); Setting *width = createSetting (Type_LineEdit, section, "Width");
Setting *height = createSetting (Type_SingleText, section, "Height"); Setting *height = createSetting (Type_LineEdit, section, "Height");
width->setWidgetWidth (5); width->setWidgetWidth (5);
height->setWidgetWidth (5); height->setWidgetWidth (8);
width->setDefaultValues (QStringList() << "1024"); width->setDefaultValues (QStringList() << "1024");
height->setDefaultValues (QStringList() << "768"); height->setDefaultValues (QStringList() << "768");
@ -65,14 +65,11 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
/* /*
*Create the proxy setting for predefined values *Create the proxy setting for predefined values
*/ */
Setting *preDefined = createSetting (Type_SingleList, section, Setting *preDefined = createSetting (Type_ComboBox, section,
"Pre-Defined", "Pre-Defined");
QStringList()
<< "640 x 480" preDefined->setDeclaredValues (QStringList() << "640 x 480"
<< "800 x 600" << "800 x 600" << "1024 x 768" << "1440 x 900");
<< "1024 x 768"
<< "1440 x 900"
);
preDefined->setViewLocation (1, 1); preDefined->setViewLocation (1, 1);
preDefined->setWidgetWidth (10); preDefined->setWidgetWidth (10);
@ -94,13 +91,14 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
QStringList values = QStringList() QStringList values = QStringList()
<< defaultValue << "Icon Only" << "Text Only"; << defaultValue << "Icon Only" << "Text Only";
Setting *rsd = createSetting (Type_SingleBool, Setting *rsd = createSetting (Type_RadioButton,
section, "Record Status Display", section, "Record Status Display");
values);
Setting *ritd = createSetting (Type_SingleBool, Setting *ritd = createSetting (Type_RadioButton,
section, "Referenceable ID Type Display", section, "Referenceable ID Type Display");
values);
rsd->setDeclaredValues (values);
ritd->setDeclaredValues (values);
rsd->setEditorSetting (true); rsd->setEditorSetting (true);
ritd->setEditorSetting (true); ritd->setEditorSetting (true);
@ -108,44 +106,67 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
section = "Proxy Selection Test"; section = "Proxy Selection Test";
{ {
//create three setting objects, specifying the basic widget type, /******************************************************************
//the setting view name, the page name, and the default value * There are three types of values:
Setting *masterBoolean = createSetting (Type_SingleBool, section, *
"Master Proxy", * Declared values
QStringList() *
* Pre-determined values, typically for
* combobox drop downs and boolean (radiobutton / checkbox) labels.
* These values represent the total possible list of values that
* may define a setting. No other values are allowed.
*
* Defined values
*
* Values which represent the actual, current value of
* a setting. For settings with declared values, this must be one or
* several declared values, as appropriate.
*
* Proxy values - values the proxy master updates the proxy slave when
* it's own definition is set / changed. These are definitions for
* proxy slave settings, but must match any declared values the proxy
* slave has, if any.
*******************************************************************/
//create setting objects, specifying the basic widget type,
//the page name, and the view name
/*
Setting *masterBoolean = createSetting (Type_RadioButton, section,
"Master Proxy");
Setting *slaveBoolean = createSetting (Type_CheckBox, section,
"Proxy Checkboxes");
Setting *slaveSingleText = createSetting (Type_LineEdit, section,
"Proxy TextBox 1");
Setting *slaveMultiText = createSetting (Type_LineEdit, section,
"ProxyTextBox 2");
Setting *slaveAlphaSpinbox = createSetting (Type_SpinBox, section,
"Alpha Spinbox");
Setting *slaveIntegerSpinbox = createSetting (Type_SpinBox, section,
"Int Spinbox");
Setting *slaveDoubleSpinbox = createSetting (Type_DoubleSpinBox,
section, "Double Spinbox");
Setting *slaveSlider = createSetting (Type_Slider, section, "Slider");
Setting *slaveDial = createSetting (Type_Dial, section, "Dial");
//set declared values for selected views
masterBoolean->setDeclaredValues (QStringList()
<< "Profile One" << "Profile Two" << "Profile One" << "Profile Two"
<< "Profile Three" << "Profile Four" << "Profile Three" << "Profile Four");
);
Setting *slaveBoolean = createSetting (Type_MultiBool, section, slaveBoolean->setDeclaredValues (QStringList()
"Proxy Checkboxes", << "One" << "Two" << "Three" << "Four" << "Five");
QStringList() << "One" << "Two"
<< "Three" << "Four" << "Five"
);
Setting *slaveSingleText = createSetting (Type_SingleText, section, slaveAlphaSpinbox->setDeclaredValues (QStringList()
"Proxy TextBox 1" << "One" << "Two" << "Three" << "Four");
);
Setting *slaveMultiText = createSetting (Type_SingleText, section,
"ProxyTextBox 2"
);
// There are three types of values:
//
// Declared values - Pre-determined values, typically for
// combobox drop downs and boolean (radiobutton / checkbox) labels.
// These values represent the total possible list of values that may
// define a setting. No other values are allowed.
//
// Defined values - Values which represent the atual, current value of
// a setting. For settings with declared values, this must be one or
// several declared values, as appropriate.
//
// Proxy values - values the proxy master updates the proxy slave when
// it's own definition is set / changed. These are definitions for
// proxy slave settings, but must match any declared values the proxy
// slave has, if any.
masterBoolean->addProxy (slaveBoolean, QList <QStringList>() masterBoolean->addProxy (slaveBoolean, QList <QStringList>()
<< (QStringList() << "One" << "Three") << (QStringList() << "One" << "Three")
@ -168,11 +189,47 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
<< (QStringList() << "Two" << "Four") << (QStringList() << "Two" << "Four")
); );
masterBoolean->addProxy (slaveAlphaSpinbox, QList <QStringList>()
<< (QStringList() << "Four")
<< (QStringList() << "Three")
<< (QStringList() << "Two")
<< (QStringList() << "One"));
masterBoolean->addProxy (slaveIntegerSpinbox, QList <QStringList> ()
<< (QStringList() << "0")
<< (QStringList() << "7")
<< (QStringList() << "14")
<< (QStringList() << "21"));
masterBoolean->addProxy (slaveDoubleSpinbox, QList <QStringList> ()
<< (QStringList() << "0.17")
<< (QStringList() << "0.34")
<< (QStringList() << "0.51")
<< (QStringList() << "0.68"));
masterBoolean->addProxy (slaveSlider, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
masterBoolean->addProxy (slaveDial, QList <QStringList> ()
<< (QStringList() << "25")
<< (QStringList() << "50")
<< (QStringList() << "75")
<< (QStringList() << "100")
);
//settings with proxies are not serialized by default //settings with proxies are not serialized by default
//other settings non-serialized for demo purposes //other settings non-serialized for demo purposes
slaveBoolean->setSerializable (false); slaveBoolean->setSerializable (false);
slaveSingleText->setSerializable (false); slaveSingleText->setSerializable (false);
slaveMultiText->setSerializable (false); slaveMultiText->setSerializable (false);
slaveAlphaSpinbox->setSerializable (false);
slaveIntegerSpinbox->setSerializable (false);
slaveDoubleSpinbox->setSerializable (false);
slaveSlider->setSerializable (false);
slaveBoolean->setDefaultValues (QStringList() slaveBoolean->setDefaultValues (QStringList()
<< "One" << "Three" << "Five"); << "One" << "Three" << "Five");
@ -184,6 +241,38 @@ void CSMSettings::UserSettings::buildSettingModelDefaults()
slaveSingleText->setWidgetWidth (24); slaveSingleText->setWidgetWidth (24);
slaveMultiText->setWidgetWidth (24); slaveMultiText->setWidgetWidth (24);
slaveAlphaSpinbox->setDefaultValue ("Two");
slaveAlphaSpinbox->setWidgetWidth (20);
//slaveAlphaSpinbox->setPrefix ("No. ");
//slaveAlphaSpinbox->setSuffix ("!");
slaveAlphaSpinbox->setWrapping (true);
slaveIntegerSpinbox->setDefaultValue (14);
slaveIntegerSpinbox->setMinimum (0);
slaveIntegerSpinbox->setMaximum (58);
slaveIntegerSpinbox->setPrefix ("$");
slaveIntegerSpinbox->setSuffix (".00");
slaveIntegerSpinbox->setWidgetWidth (10);
slaveIntegerSpinbox->setSpecialValueText ("Nothing!");
slaveDoubleSpinbox->setDefaultValue (0.51);
slaveDoubleSpinbox->setSingleStep(0.17);
slaveDoubleSpinbox->setMaximum(4.0);
slaveSlider->setMinimum (0);
slaveSlider->setMaximum (100);
slaveSlider->setDefaultValue (75);
slaveSlider->setWidgetWidth (100);
slaveSlider->setTicksAbove (true);
slaveSlider->setTickInterval (25);
slaveDial->setMinimum (0);
slaveDial->setMaximum (100);
slaveDial->setSingleStep (5);
slaveDial->setDefaultValue (75);
slaveDial->setTickInterval (25);
*/
} }
} }
@ -195,13 +284,13 @@ CSMSettings::UserSettings::~UserSettings()
void CSMSettings::UserSettings::loadSettings (const QString &fileName) void CSMSettings::UserSettings::loadSettings (const QString &fileName)
{ {
mUserFilePath = QString::fromUtf8 mUserFilePath = QString::fromUtf8
(mCfgMgr.getUserConfigPath().c_str()) + fileName.toUtf8(); (mCfgMgr.getUserConfigPath().string().c_str()) + fileName.toUtf8();
QString global = QString::fromUtf8 QString global = QString::fromUtf8
(mCfgMgr.getGlobalPath().c_str()) + fileName.toUtf8(); (mCfgMgr.getGlobalPath().string().c_str()) + fileName.toUtf8();
QString local = QString::fromUtf8 QString local = QString::fromUtf8
(mCfgMgr.getLocalPath().c_str()) + fileName.toUtf8(); (mCfgMgr.getLocalPath().string().c_str()) + fileName.toUtf8();
//open user and global streams //open user and global streams
QTextStream *userStream = openFilestream (mUserFilePath, true); QTextStream *userStream = openFilestream (mUserFilePath, true);

View file

@ -453,7 +453,7 @@ CSMWorld::IdCollection<CSMFilter::Filter>& CSMWorld::Data::getFilters()
return mFilters; return mFilters;
} }
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id)
{ {
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType()); std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());

View file

@ -1,6 +1,9 @@
#include "tablemimedata.hpp" #include "tablemimedata.hpp"
#include <string> #include <string>
#include <QDebug>
#include "universalid.hpp" #include "universalid.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
@ -11,7 +14,7 @@ mDocument(document)
mObjectsFormats << QString::fromUtf8 (("tabledata/" + id.getTypeName()).c_str()); mObjectsFormats << QString::fromUtf8 (("tabledata/" + id.getTypeName()).c_str());
} }
CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : CSMWorld::TableMimeData::TableMimeData (const std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) :
mUniversalId (id), mDocument(document) mUniversalId (id), mDocument(document)
{ {
for (std::vector<UniversalId>::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) for (std::vector<UniversalId>::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it)
@ -33,7 +36,8 @@ std::string CSMWorld::TableMimeData::getIcon() const
{ {
if (mUniversalId.empty()) if (mUniversalId.empty())
{ {
throw ("TableMimeData holds no UniversalId"); qDebug()<<"TableMimeData object does not hold any records!"; //because throwing in the event loop tends to be problematic
throw("TableMimeData object does not hold any records!");
} }
std::string tmpIcon; std::string tmpIcon;
@ -50,7 +54,7 @@ std::string CSMWorld::TableMimeData::getIcon() const
if (tmpIcon != mUniversalId[i].getIcon()) if (tmpIcon != mUniversalId[i].getIcon())
{ {
return ":/multitype.png"; //icon stolen from gnome return ":/multitype.png"; //icon stolen from gnome TODO: get new icon
} }
tmpIcon = mUniversalId[i].getIcon(); tmpIcon = mUniversalId[i].getIcon();

View file

@ -33,7 +33,7 @@ namespace CSMWorld
public: public:
TableMimeData(UniversalId id, const CSMDoc::Document& document); TableMimeData(UniversalId id, const CSMDoc::Document& document);
TableMimeData(std::vector<UniversalId>& id, const CSMDoc::Document& document); TableMimeData(const std::vector<UniversalId>& id, const CSMDoc::Document& document);
~TableMimeData(); ~TableMimeData();
@ -56,6 +56,7 @@ namespace CSMWorld
UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const;
static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type);
static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type);
private: private:

View file

@ -64,6 +64,7 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },

View file

@ -60,6 +60,7 @@ namespace CSMWorld
Type_Spell, Type_Spell,
Type_Cells, Type_Cells,
Type_Cell, Type_Cell,
Type_Cell_Missing, //For cells that does not exist yet.
Type_Referenceables, Type_Referenceables,
Type_Referenceable, Type_Referenceable,
Type_Activator, Type_Activator,

View file

@ -3,8 +3,12 @@
#include <sstream> #include <sstream>
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget *parent) #include <QtGui/qevent.h>
: WorldspaceWidget (parent)
#include <apps/opencs/model/world/tablemimedata.hpp>
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
: WorldspaceWidget (document, parent)
{} {}
void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
@ -45,3 +49,44 @@ void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSel
mSelection = selection; mSelection = selection;
emit cellSelectionChanged (mSelection); emit cellSelectionChanged (mSelection);
} }
std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (const std::string& record) const
{
std::istringstream stream (record.c_str());
char ignore;
int x, y;
stream >> ignore >> x >> y;
return std::make_pair(x, y);
}
void CSVRender::PagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld::UniversalId >& data)
{
bool selectionChanged = false;
for (unsigned i = 0; i < data.size(); ++i)
{
std::pair<int, int> coordinates(getCoordinatesFromId(data[i].getId()));
if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second)))
{
selectionChanged = true;
}
}
if (selectionChanged)
{
emit cellSelectionChanged(mSelection);
}
}
CSVRender::WorldspaceWidget::dropRequirments CSVRender::PagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::dropType type) const
{
switch (type)
{
case cellsExterior:
return canHandle;
case cellsInterior:
return needUnpaged;
default:
return ignored;
}
}

View file

@ -13,17 +13,25 @@ namespace CSVRender
CSMWorld::CellSelection mSelection; CSMWorld::CellSelection mSelection;
private:
std::pair<int, int> getCoordinatesFromId(const std::string& record) const;
public: public:
PagedWorldspaceWidget (QWidget *parent); PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
///< \note Sets the cell area selection to an invalid value to indicate that currently ///< \note Sets the cell area selection to an invalid value to indicate that currently
/// no cells are displayed. The cells to be displayed will be specified later through /// no cells are displayed. The cells to be displayed will be specified later through
/// hint system. /// hint system.
virtual void useViewHint (const std::string& hint); void useViewHint (const std::string& hint);
void setCellSelection (const CSMWorld::CellSelection& selection); void setCellSelection (const CSMWorld::CellSelection& selection);
virtual void handleDrop(const std::vector<CSMWorld::UniversalId>& data);
virtual dropRequirments getDropRequirements(dropType type) const;
signals: signals:
void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::CellSelection& selection);

View file

@ -320,7 +320,7 @@ namespace CSVRender
} }
if (mUpdate) if (mUpdate && mWindow)
{ {
mUpdate = false; mUpdate = false;
mWindow->update(); mWindow->update();

View file

@ -3,10 +3,13 @@
#include <OgreColourValue.h> #include <OgreColourValue.h>
#include <QtGui/qevent.h>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/tablemimedata.hpp"
void CSVRender::UnpagedWorldspaceWidget::update() void CSVRender::UnpagedWorldspaceWidget::update()
{ {
@ -20,9 +23,8 @@ void CSVRender::UnpagedWorldspaceWidget::update()
/// \todo deal with mSunlight and mFog/mForDensity /// \todo deal with mSunlight and mFog/mForDensity
} }
CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent)
CSMDoc::Document& document, QWidget *parent) : WorldspaceWidget (document, parent), mCellId (cellId)
: WorldspaceWidget (parent), mCellId (cellId)
{ {
mCellsModel = &dynamic_cast<CSMWorld::IdTable&> ( mCellsModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
@ -64,3 +66,25 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI
if (cellIndex.row()>=start && cellIndex.row()<=end) if (cellIndex.row()>=start && cellIndex.row()<=end)
emit closeRequest(); emit closeRequest();
} }
void CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld::UniversalId >& data)
{
mCellId = data.begin()->getId();
update();
emit cellChanged(*data.begin());
}
CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::dropType type) const
{
switch(type)
{
case cellsInterior:
return canHandle;
case cellsExterior:
return needPaged;
default:
return ignored;
}
}

View file

@ -33,11 +33,19 @@ namespace CSVRender
UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document,
QWidget *parent); QWidget *parent);
virtual dropRequirments getDropRequirements(dropType type) const;
virtual void handleDrop(const std::vector<CSMWorld::UniversalId>& data);
private slots: private slots:
void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
signals:
void cellChanged(const CSMWorld::UniversalId& id);
}; };
} }

View file

@ -5,15 +5,20 @@
#include <OgreSceneManager.h> #include <OgreSceneManager.h>
#include <OgreEntity.h> #include <OgreEntity.h>
#include "../world/scenetoolmode.hpp" #include <QtGui/qevent.h>
CSVRender::WorldspaceWidget::WorldspaceWidget (QWidget *parent) #include "../world/scenetoolmode.hpp"
: SceneWidget (parent) #include <apps/opencs/model/world/universalid.hpp>
CSVRender::WorldspaceWidget::WorldspaceWidget (const CSMDoc::Document& document, QWidget* parent)
: SceneWidget (parent), mDocument(document)
{ {
Ogre::Entity* ent = getSceneManager()->createEntity("cube", Ogre::SceneManager::PT_CUBE); Ogre::Entity* ent = getSceneManager()->createEntity("cube", Ogre::SceneManager::PT_CUBE);
ent->setMaterialName("BaseWhite"); ent->setMaterialName("BaseWhite");
getSceneManager()->getRootSceneNode()->attachObject(ent); getSceneManager()->getRootSceneNode()->attachObject(ent);
setAcceptDrops(true);
} }
void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode)
@ -47,3 +52,77 @@ CSVWorld::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
return tool; return tool;
} }
CSVRender::WorldspaceWidget::dropType CSVRender::WorldspaceWidget::getDropType (
const std::vector< CSMWorld::UniversalId >& data)
{
dropType output = notCells;
bool firstIteration = true;
for (unsigned i = 0; i < data.size(); ++i)
{
if (data[i].getType() == CSMWorld::UniversalId::Type_Cell ||
data[i].getType() == CSMWorld::UniversalId::Type_Cell_Missing)
{
if (*(data[i].getId().begin()) == '#') //exterior
{
if (firstIteration)
{
output = cellsExterior;
firstIteration = false;
continue;
}
if (output == cellsInterior)
{
output = cellsMixed;
break;
} else {
output = cellsInterior;
}
} else //interior
{
if (firstIteration)
{
output = cellsInterior;
firstIteration = false;
continue;
}
if (output == cellsExterior)
{
output = cellsMixed;
break;
} else {
output = cellsInterior;
}
}
} else {
output = notCells;
break;
}
}
return output;
}
void CSVRender::WorldspaceWidget::dragEnterEvent (QDragEnterEvent* event)
{
event->accept();
}
void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event)
{
event->accept();
}
void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (mime->fromDocument (mDocument))
{
emit dataDropped(mime->getData());
} //not handling drops from different documents at the moment
}

View file

@ -6,7 +6,13 @@
#include "navigation1st.hpp" #include "navigation1st.hpp"
#include "navigationfree.hpp" #include "navigationfree.hpp"
#include "navigationorbit.hpp" #include "navigationorbit.hpp"
#include <apps/opencs/model/doc/document.hpp>
#include <apps/opencs/model/world/tablemimedata.hpp>
namespace CSMWorld
{
class UniversalId;
}
namespace CSVWorld namespace CSVWorld
{ {
class SceneToolMode; class SceneToolMode;
@ -25,7 +31,23 @@ namespace CSVRender
public: public:
WorldspaceWidget (QWidget *parent = 0); enum dropType
{
cellsMixed,
cellsInterior,
cellsExterior,
notCells
};
enum dropRequirments
{
canHandle,
needPaged,
needUnpaged,
ignored //either mixed cells, or not cells
};
WorldspaceWidget (const CSMDoc::Document& document, QWidget *parent = 0);
CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that ///< \attention The created tool is not added to the toolbar (via addTool). Doing that
@ -33,9 +55,26 @@ namespace CSVRender
void selectDefaultNavigationMode(); void selectDefaultNavigationMode();
static dropType getDropType(const std::vector<CSMWorld::UniversalId>& data);
virtual dropRequirments getDropRequirements(dropType type) const = 0;
virtual void useViewHint (const std::string& hint); virtual void useViewHint (const std::string& hint);
///< Default-implementation: ignored. ///< Default-implementation: ignored.
virtual void handleDrop(const std::vector<CSMWorld::UniversalId>& data) = 0;
protected:
const CSMDoc::Document& mDocument; //for checking if drop comes from same document
private:
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent* event);
void dragMoveEvent(QDragMoveEvent *event);
private slots: private slots:
void selectNavigationMode (const std::string& mode); void selectNavigationMode (const std::string& mode);
@ -43,6 +82,7 @@ namespace CSVRender
signals: signals:
void closeRequest(); void closeRequest();
void dataDropped(const std::vector<CSMWorld::UniversalId>& data);
}; };
} }

View file

@ -18,10 +18,19 @@ CSVSettings::BooleanView::BooleanView (CSMSettings::Setting *setting,
{ {
QAbstractButton *button = 0; QAbstractButton *button = 0;
if (isMultiValue()) switch (setting->type())
{
case CSMSettings::Type_CheckBox:
button = new QCheckBox (value, this); button = new QCheckBox (value, this);
else break;
case CSMSettings::Type_RadioButton:
button = new QRadioButton (value, this); button = new QRadioButton (value, this);
break;
default:
break;
}
connect (button, SIGNAL (clicked (bool)), connect (button, SIGNAL (clicked (bool)),
this, SLOT (slotToggled (bool))); this, SLOT (slotToggled (bool)));

View file

@ -1,5 +1,5 @@
#ifndef CSVSETTINGS_BOOLEANVIEW_HPP #ifndef CSVSETTINGS_BOOLEANVIEW_HPP
#define CSVSETTINGS_BOOELANVIEW_HPP #define CSVSETTINGS_BOOLEANVIEW_HPP
#include <QWidget> #include <QWidget>
#include <QAbstractButton> #include <QAbstractButton>

View file

@ -123,10 +123,8 @@ void CSVSettings::Dialog::show()
{ {
if (pages().isEmpty()) if (pages().isEmpty())
buildPages(); buildPages();
QPoint screenCenter = QApplication::desktop()->screenGeometry().center(); QPoint screenCenter = QApplication::desktop()->screenGeometry().center();
move (screenCenter - geometry().center()); move (screenCenter - geometry().center());
QWidget::show(); QWidget::show();
} }

View file

@ -60,9 +60,11 @@ void CSVSettings::Frame::showWidgets()
QWidget *widg = static_cast <QWidget *> (obj); QWidget *widg = static_cast <QWidget *> (obj);
if (widg->property("sizePolicy").isValid()) if (widg->property("sizePolicy").isValid())
{
widg->setSizePolicy widg->setSizePolicy
(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
} }
}
layout()->activate(); layout()->activate();
setFixedSize(minimumSizeHint()); setFixedSize(minimumSizeHint());
} }

View file

@ -3,9 +3,12 @@
#include "booleanview.hpp" #include "booleanview.hpp"
#include "textview.hpp" #include "textview.hpp"
#include "listview.hpp" #include "listview.hpp"
#include "rangeview.hpp"
#include "../../model/settings/usersettings.hpp" #include "../../model/settings/usersettings.hpp"
#include "../../model/settings/connector.hpp" #include "../../model/settings/connector.hpp"
#include "../../model/settings/support.hpp"
#include "settingwindow.hpp" #include "settingwindow.hpp"
QMap <CSVSettings::ViewType, CSVSettings::IViewFactory *> QMap <CSVSettings::ViewType, CSVSettings::IViewFactory *>
@ -85,4 +88,5 @@ void CSVSettings::Page::buildFactories()
mViewFactories[ViewType_Boolean] = new BooleanViewFactory (this); mViewFactories[ViewType_Boolean] = new BooleanViewFactory (this);
mViewFactories[ViewType_Text] = new TextViewFactory (this); mViewFactories[ViewType_Text] = new TextViewFactory (this);
mViewFactories[ViewType_List] = new ListViewFactory (this); mViewFactories[ViewType_List] = new ListViewFactory (this);
mViewFactories[ViewType_Range] = new RangeViewFactory (this);
} }

View file

@ -0,0 +1,204 @@
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QAbstractSpinBox>
#include <QAbstractSlider>
#include <QDial>
#include <QSlider>
#include "rangeview.hpp"
#include "spinbox.hpp"
#include "../../model/settings/setting.hpp"
#include "../../model/settings/support.hpp"
CSVSettings::RangeView::RangeView (CSMSettings::Setting *setting,
Page *parent)
: mRangeWidget (0), mRangeType (setting->type()), View (setting, parent)
{
mRangeWidget = 0;
if (isMultiValue())
return;
switch (mRangeType)
{
case CSMSettings::Type_SpinBox:
case CSMSettings::Type_DoubleSpinBox:
buildSpinBox (setting);
break;
case CSMSettings::Type_Dial:
case CSMSettings::Type_Slider:
buildSlider (setting);
break;
default:
break;
}
mRangeWidget->setFixedWidth (widgetWidth (setting->widgetWidth()));
mRangeWidget->setObjectName (setting->name());
addWidget (mRangeWidget);
}
void CSVSettings::RangeView::buildSlider (CSMSettings::Setting *setting)
{
switch (setting->type())
{
case CSMSettings::Type_Slider:
mRangeWidget = new QSlider (Qt::Horizontal, this);
mRangeWidget->setProperty ("tickInterval", setting->tickInterval());
if (setting->ticksAbove())
{
if (setting->ticksBelow())
mRangeWidget->setProperty ("tickPosition", QSlider::TicksBothSides);
else
mRangeWidget->setProperty ("tickPosition", QSlider::TicksAbove);
}
else if (setting->ticksBelow())
mRangeWidget->setProperty ("tickPosition", QSlider::TicksBelow);
else
mRangeWidget->setProperty ("tickPosition", QSlider::NoTicks);
break;
case CSMSettings::Type_Dial:
mRangeWidget = new QDial (this);
mRangeWidget->setProperty ("wrapping", setting->wrapping());
mRangeWidget->setProperty ("notchesVisible",
(setting->ticksAbove() || setting->ticksBelow()));
break;
default:
break;
}
mRangeWidget->setProperty ("minimum", setting->minimum());
mRangeWidget->setProperty ("maximum", setting->maximum());
mRangeWidget->setProperty ("tracking", false);
mRangeWidget->setProperty ("singleStep", setting->singleStep());
connect (mRangeWidget, SIGNAL (valueChanged (int)),
this, SLOT (slotUpdateView (int)));
}
void CSVSettings::RangeView::buildSpinBox (CSMSettings::Setting *setting)
{
SpinBox *sb = 0;
switch (setting->type())
{
case CSMSettings::Type_SpinBox:
sb = new SpinBox (this);
if (!setting->declaredValues().isEmpty())
sb->setValueList (setting->declaredValues());
mRangeWidget = sb;
connect (mRangeWidget, SIGNAL (valueChanged (int)),
this, SLOT (slotUpdateView (int)));
break;
case CSMSettings::Type_DoubleSpinBox:
mRangeWidget = new QDoubleSpinBox (this);
connect (mRangeWidget, SIGNAL (valueChanged (double)),
this, SLOT (slotUpdateView (double)));
break;
default:
break;
}
//min / max values are set automatically in AlphaSpinBox
if (setting->declaredValues().isEmpty())
{
mRangeWidget->setProperty ("minimum", setting->minimum());
mRangeWidget->setProperty ("maximum", setting->maximum());
mRangeWidget->setProperty ("singleStep", setting->singleStep());
mRangeWidget->setProperty ("specialValueText",
setting->specialValueText());
}
mRangeWidget->setProperty ("prefix", setting->prefix());
mRangeWidget->setProperty ("suffix", setting->suffix());
mRangeWidget->setProperty ("wrapping", setting->wrapping());
}
void CSVSettings::RangeView::slotUpdateView (int value)
{
QString textValue = "";
QStringList list;
switch (mRangeType)
{
case CSMSettings::Type_SpinBox:
list = static_cast <SpinBox *> (mRangeWidget)->valueList();
if (!list.isEmpty())
textValue = list.at(value);
break;
default:
break;
}
if (textValue.isEmpty())
textValue = QVariant (value).toString();
setSelectedValue (textValue, false);
View::updateView();
}
void CSVSettings::RangeView::slotUpdateView (double value)
{
setSelectedValue (QVariant(value).toString(), false);
View::updateView();
}
void CSVSettings::RangeView::updateView (bool signalUpdate) const
{
QString value;
if (!selectedValues().isEmpty())
value = selectedValues().at(0);
switch (mRangeType)
{
case CSMSettings::Type_SpinBox:
static_cast <SpinBox *> (mRangeWidget)->setValue (value);
break;
case CSMSettings::Type_DoubleSpinBox:
static_cast <QDoubleSpinBox *> (mRangeWidget)->setValue (value.toDouble());
break;
case CSMSettings::Type_Slider:
case CSMSettings::Type_Dial:
mRangeWidget->setProperty ("value", value.toInt());
mRangeWidget->setProperty ("sliderPosition", value.toInt());
break;
default:
break;
}
View::updateView (signalUpdate);
}
CSVSettings::RangeView *CSVSettings::RangeViewFactory::createView
(CSMSettings::Setting *setting,
Page *parent)
{
return new RangeView (setting, parent);
}

View file

@ -0,0 +1,49 @@
#ifndef CSVSETTINGS_RANGEVIEW_HPP
#define CSVSETTINGS_RANGEVIEW_HPP
#include "view.hpp"
#include "../../model/settings/support.hpp"
class QStringListModel;
class QAbstractSpinBox;
namespace CSVSettings
{
class RangeView : public View
{
Q_OBJECT
QWidget *mRangeWidget;
CSMSettings::SettingType mRangeType;
public:
explicit RangeView (CSMSettings::Setting *setting,
Page *parent);
protected:
void updateView (bool signalUpdate = true) const;
void buildSlider (CSMSettings::Setting *setting);
void buildSpinBox (CSMSettings::Setting *setting);
private slots:
void slotUpdateView (int value);
void slotUpdateView (double value);
};
class RangeViewFactory : public QObject, public IViewFactory
{
Q_OBJECT
public:
explicit RangeViewFactory (QWidget *parent = 0)
: QObject (parent)
{}
RangeView *createView (CSMSettings::Setting *setting,
Page *parent);
};
}
#endif // CSVSETTINGS_RANGEVIEW_HPP

View file

@ -0,0 +1,51 @@
#include "spinbox.hpp"
#include <QSpinBox>
#include <QLineEdit>
CSVSettings::SpinBox::SpinBox(QWidget *parent)
: mValueList(QStringList()), QSpinBox(parent)
{
setRange (0, 0);
}
QString CSVSettings::SpinBox::textFromValue(int val) const
{
if (mValueList.isEmpty())
return QVariant (val).toString();
QString value = "";
if (val < mValueList.size())
value = mValueList.at (val);
return value;
}
int CSVSettings::SpinBox::valueFromText(const QString &text) const
{
if (mValueList.isEmpty())
return -1;
if (mValueList.contains (text))
return mValueList.indexOf(text);
return -1;
}
void CSVSettings::SpinBox::setValue (const QString &value)
{
if (!mValueList.isEmpty())
{
lineEdit()->setText (value);
QSpinBox::setValue(valueFromText(value));
}
else
QSpinBox::setValue (value.toInt());
}
void CSVSettings::SpinBox::setValueList (const QStringList &list)
{
mValueList = list;
setMaximum (list.size() - 1);
}

View file

@ -0,0 +1,31 @@
#ifndef CSVSETTINGS_SPINBOX_HPP
#define CSVSETTINGS_SPINBOX_HPP
#include <QObject>
#include <QStringList>
#include <QSpinBox>
namespace CSVSettings
{
class SpinBox : public QSpinBox
{
Q_OBJECT
QStringList mValueList;
public:
explicit SpinBox(QWidget *parent = 0);
void setObjectName (const QString &name);
void setValue (const QString &value);
void setValueList (const QStringList &list);
const QStringList &valueList() const { return mValueList; }
protected:
QString textFromValue (int val) const;
int valueFromText (const QString &text) const;
};
}
#endif // CSVSETTINGS_SPINBOX_HPP

View file

@ -28,11 +28,6 @@ bool CSVSettings::TextView::isEquivalent
return (lhs.trimmed() == rhs.trimmed()); return (lhs.trimmed() == rhs.trimmed());
} }
void CSVSettings::TextView::setWidgetText (const QString &value) const
{
mTextWidget->setProperty ("text", value);
}
void CSVSettings::TextView::slotTextEdited (QString value) void CSVSettings::TextView::slotTextEdited (QString value)
{ {
QStringList values = value.split (mDelimiter, QString::SkipEmptyParts); QStringList values = value.split (mDelimiter, QString::SkipEmptyParts);
@ -51,19 +46,14 @@ void CSVSettings::TextView::updateView(bool signalUpdate) const
{ {
QString values = selectedValues().join (mDelimiter); QString values = selectedValues().join (mDelimiter);
if (isEquivalent (widgetText(), values)) if (isEquivalent (mTextWidget->property("text").toString(), values))
return; return;
setWidgetText (values); mTextWidget->setProperty("text", values);
View::updateView (signalUpdate); View::updateView (signalUpdate);
} }
QString CSVSettings::TextView::widgetText() const
{
return mTextWidget->property("text").toString();
}
CSVSettings::TextView *CSVSettings::TextViewFactory::createView CSVSettings::TextView *CSVSettings::TextViewFactory::createView
(CSMSettings::Setting *setting, (CSMSettings::Setting *setting,
Page *parent) Page *parent)

View file

@ -32,12 +32,6 @@ namespace CSVSettings
///Comparison function that returns true if the trimmed() strings ///Comparison function that returns true if the trimmed() strings
///are equal ///are equal
bool isEquivalent (const QString &lhs, const QString &rhs) const; bool isEquivalent (const QString &lhs, const QString &rhs) const;
///Convenience function to return the text of the widget
QString widgetText() const;
///Convenience function to set the text of the widget
void setWidgetText (const QString &value) const;
}; };
class TextViewFactory : public QObject, public IViewFactory class TextViewFactory : public QObject, public IViewFactory

View file

@ -192,6 +192,7 @@ bool CSVSettings::View::stringListsMatch (
QList <QStandardItem *> CSVSettings::View::toStandardItemList QList <QStandardItem *> CSVSettings::View::toStandardItemList
(const QStringList &list) const (const QStringList &list) const
{ {
QList <QStandardItem *> itemList; QList <QStandardItem *> itemList;
foreach (const QString &value, list) foreach (const QString &value, list)

View file

@ -101,7 +101,7 @@ namespace CSVSettings
void showEvent ( QShowEvent * event ); void showEvent ( QShowEvent * event );
///Virtual for updating a specific View subclass ///Virtual for updating a specific View subclass
///bool indicates whether a signal is emitted that the view was updated ///bool indicates whether viewUpdated() signal is emitted
virtual void updateView (bool signalUpdate = true) const; virtual void updateView (bool signalUpdate = true) const;
///Returns the pixel width corresponding to the specified number of ///Returns the pixel width corresponding to the specified number of

4
apps/opencs/view/world/datadisplaydelegate.cpp Executable file → Normal file
View file

@ -25,7 +25,7 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values,
void CSVWorld::DataDisplayDelegate::buildPixmaps () void CSVWorld::DataDisplayDelegate::buildPixmaps ()
{ {
if (mPixmaps.size() > 0) if (!mPixmaps.empty())
mPixmaps.clear(); mPixmaps.clear();
IconList::iterator it = mIcons.begin(); IconList::iterator it = mIcons.begin();
@ -33,7 +33,7 @@ void CSVWorld::DataDisplayDelegate::buildPixmaps ()
while (it != mIcons.end()) while (it != mIcons.end())
{ {
mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) ); mPixmaps.push_back (std::make_pair (it->first, it->second.pixmap (mIconSize) ) );
it++; ++it;
} }
} }

View file

@ -183,7 +183,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS
{ {
delegate = CommandDelegateFactoryCollection::get().makeDelegate ( delegate = CommandDelegateFactoryCollection::get().makeDelegate (
display, mUndoStack, mParent); display, mUndoStack, mParent);
mDelegates.insert(std::make_pair<int, CommandDelegate*>(display, delegate)); mDelegates.insert(std::make_pair(display, delegate));
} else } else
{ {
delegate = delegateIt->second; delegate = delegateIt->second;

View file

@ -0,0 +1,38 @@
#include <QDrag>
#include "../../model/world/tablemimedata.hpp"
#include "dragrecordtable.hpp"
void CSVWorld::DragRecordTable::startDrag (const CSVWorld::DragRecordTable& table)
{
CSMWorld::TableMimeData* mime = new CSMWorld::TableMimeData (table.getDraggedRecords(), mDocument);
if (mime)
{
QDrag* drag = new QDrag (this);
drag->setMimeData (mime);
drag->setPixmap (QString::fromUtf8 (mime->getIcon().c_str()));
drag->exec (Qt::CopyAction);
}
}
CSVWorld::DragRecordTable::DragRecordTable (CSMDoc::Document& document, QWidget* parent) :
mDocument(document),
QTableView(parent),
mEditLock(false)
{}
void CSVWorld::DragRecordTable::setEditLock (bool locked)
{
mEditLock = locked;
}
void CSVWorld::DragRecordTable::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void CSVWorld::DragRecordTable::dragMoveEvent(QDragMoveEvent *event)
{
event->accept();
}

View file

@ -0,0 +1,45 @@
#ifndef CSV_WORLD_DRAGRECORDTABLE_H
#define CSV_WORLD_DRAGRECORDTABLE_H
#include <QTableView>
#include <QtGui/qevent.h>
class QWidget;
class QAction;
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class UniversalId;
}
namespace CSVWorld
{
class DragRecordTable : public QTableView
{
protected:
CSMDoc::Document& mDocument;
bool mEditLock;
public:
DragRecordTable(CSMDoc::Document& document, QWidget* parent = NULL);
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const = 0;
void setEditLock(bool locked);
protected:
void startDrag(const DragRecordTable& table);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
};
}
#endif

View file

@ -17,6 +17,7 @@
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/commands.hpp" #include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/tablemimedata.hpp"
void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event)
{ {
@ -180,7 +181,7 @@ void CSVWorld::RegionMap::setRegion (const std::string& regionId)
CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId,
CSMDoc::Document& document, QWidget *parent) CSMDoc::Document& document, QWidget *parent)
: QTableView (parent), mEditLock (false), mDocument (document) : DragRecordTable(document, parent)
{ {
verticalHeader()->hide(); verticalHeader()->hide();
horizontalHeader()->hide(); horizontalHeader()->hide();
@ -223,11 +224,8 @@ CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId,
mViewInTableAction = new QAction (tr ("View Cells in Table"), this); mViewInTableAction = new QAction (tr ("View Cells in Table"), this);
connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable())); connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable()));
addAction (mViewInTableAction); addAction (mViewInTableAction);
}
void CSVWorld::RegionMap::setEditLock (bool locked) setAcceptDrops(true);
{
mEditLock = locked;
} }
void CSVWorld::RegionMap::selectAll() void CSVWorld::RegionMap::selectAll()
@ -344,3 +342,64 @@ void CSVWorld::RegionMap::viewInTable()
emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str()); emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str());
} }
void CSVWorld::RegionMap::mouseMoveEvent (QMouseEvent* event)
{
startDrag(*this);
}
std::vector< CSMWorld::UniversalId > CSVWorld::RegionMap::getDraggedRecords() const
{
QModelIndexList selected(getSelectedCells(true, false));
std::vector<CSMWorld::UniversalId> ids;
foreach (QModelIndex it, selected)
{
ids.push_back(
CSMWorld::UniversalId(
CSMWorld::UniversalId::Type_Cell,
model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()));
}
selected = getSelectedCells(false, true);
foreach (QModelIndex it, selected)
{
ids.push_back(
CSMWorld::UniversalId(
CSMWorld::UniversalId::Type_Cell_Missing,
model()->data(it, CSMWorld::RegionMap::Role_CellId).toString().toUtf8().constData()));
}
return ids;
}
void CSVWorld::RegionMap::dropEvent (QDropEvent* event)
{
QModelIndex index = indexAt (event->pos());
bool exists = QTableView::model()->data(index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern);
if (!index.isValid() || !exists)
{
return;
}
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region))
{
CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region));
QAbstractItemModel *regionModel = model();
CSMWorld::IdTable *cellsModel = &dynamic_cast<CSMWorld::IdTable&> (*
mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
std::string cellId(regionModel->data (index, CSMWorld::RegionMap::Role_CellId).
toString().toUtf8().constData());
QModelIndex index2(cellsModel->getModelIndex (cellId,
cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)));
mDocument.getUndoStack().push(new CSMWorld::ModifyCommand
(*cellsModel, index2, QString::fromUtf8(record.getId().c_str())));
mRegionId = record.getId();
}
}

View file

@ -1,8 +1,14 @@
#ifndef CSV_WORLD_REGIONMAP_H #ifndef CSV_WORLD_REGIONMAP_H
#define CSV_WORLD_REGIONMAP_H #define CSV_WORLD_REGIONMAP_H
#include <cstddef>
#include <vector>
#include <QObject>
#include <QTableView> #include <QTableView>
#include "./dragrecordtable.hpp"
class QAction; class QAction;
namespace CSMDoc namespace CSMDoc
@ -17,7 +23,7 @@ namespace CSMWorld
namespace CSVWorld namespace CSVWorld
{ {
class RegionMap : public QTableView class RegionMap : public DragRecordTable
{ {
Q_OBJECT Q_OBJECT
@ -29,8 +35,6 @@ namespace CSVWorld
QAction *mUnsetRegionAction; QAction *mUnsetRegionAction;
QAction *mViewAction; QAction *mViewAction;
QAction *mViewInTableAction; QAction *mViewInTableAction;
bool mEditLock;
CSMDoc::Document& mDocument;
std::string mRegionId; std::string mRegionId;
private: private:
@ -50,12 +54,16 @@ namespace CSVWorld
void setRegion (const std::string& regionId); void setRegion (const std::string& regionId);
///< Set region Id of selected cells. ///< Set region Id of selected cells.
void mouseMoveEvent(QMouseEvent *event);
void dropEvent(QDropEvent* event);
public: public:
RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document,
QWidget *parent = 0); QWidget *parent = 0);
void setEditLock (bool locked); virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
signals: signals:

View file

@ -6,6 +6,7 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <cassert>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
@ -18,11 +19,10 @@
#include "tablebottombox.hpp" #include "tablebottombox.hpp"
#include "creator.hpp" #include "creator.hpp"
#include "scenetoolbar.hpp"
#include "scenetoolmode.hpp" #include "scenetoolmode.hpp"
CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
: SubView (id) : SubView (id), mLayout(new QHBoxLayout), mDocument(document), mScene(NULL), mToolbar(NULL)
{ {
QVBoxLayout *layout = new QVBoxLayout; QVBoxLayout *layout = new QVBoxLayout;
@ -32,33 +32,35 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
new TableBottomBox (NullCreatorFactory(), document.getData(), document.getUndoStack(), id, new TableBottomBox (NullCreatorFactory(), document.getData(), document.getUndoStack(), id,
this), 0); this), 0);
QHBoxLayout *layout2 = new QHBoxLayout; mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
layout2->setContentsMargins (QMargins (0, 0, 0, 0)); CSVRender::WorldspaceWidget* wordspaceWidget = NULL;
widgetType whatWidget;
SceneToolbar *toolbar = new SceneToolbar (48+6, this);
if (id.getId()=="sys::default") if (id.getId()=="sys::default")
{ {
CSVRender::PagedWorldspaceWidget *widget = new CSVRender::PagedWorldspaceWidget (this); whatWidget = widget_Paged;
mScene = widget;
connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)), CSVRender::PagedWorldspaceWidget *newWidget = new CSVRender::PagedWorldspaceWidget (this, document);
this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&)));
wordspaceWidget = newWidget;
makeConnections(newWidget);
} }
else else
mScene = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this); {
whatWidget = widget_Unpaged;
SceneToolMode *navigationTool = mScene->makeNavigationSelector (toolbar); CSVRender::UnpagedWorldspaceWidget *newWidget = new CSVRender::UnpagedWorldspaceWidget (id.getId(), document, this);
toolbar->addTool (navigationTool);
SceneToolMode *lightingTool = mScene->makeLightingSelector (toolbar); wordspaceWidget = newWidget;
toolbar->addTool (lightingTool);
layout2->addWidget (toolbar, 0); makeConnections(newWidget);
}
layout2->addWidget (mScene, 1); replaceToolbarAndWorldspace(wordspaceWidget, makeToolbar(wordspaceWidget, whatWidget));
layout->insertLayout (0, layout2, 1); layout->insertLayout (0, mLayout, 1);
CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this);
@ -69,10 +71,53 @@ CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::D
widget->setLayout (layout); widget->setLayout (layout);
setWidget (widget); setWidget (widget);
}
mScene->selectDefaultNavigationMode(); void CSVWorld::SceneSubView::makeConnections (CSVRender::UnpagedWorldspaceWidget* widget)
{
connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
connect (mScene, SIGNAL (closeRequest()), this, SLOT (closeRequest())); connect(widget, SIGNAL(dataDropped(const std::vector<CSMWorld::UniversalId>&)),
this, SLOT(handleDrop(const std::vector<CSMWorld::UniversalId>&)));
connect(widget, SIGNAL(cellChanged(const CSMWorld::UniversalId&)),
this, SLOT(cellSelectionChanged(const CSMWorld::UniversalId&)));
}
void CSVWorld::SceneSubView::makeConnections (CSVRender::PagedWorldspaceWidget* widget)
{
connect (widget, SIGNAL (closeRequest()), this, SLOT (closeRequest()));
connect(widget, SIGNAL(dataDropped(const std::vector<CSMWorld::UniversalId>&)),
this, SLOT(handleDrop(const std::vector<CSMWorld::UniversalId>&)));
connect (widget, SIGNAL (cellSelectionChanged (const CSMWorld::CellSelection&)),
this, SLOT (cellSelectionChanged (const CSMWorld::CellSelection&)));
}
CSVWorld::SceneToolbar* CSVWorld::SceneSubView::makeToolbar (CSVRender::WorldspaceWidget* widget, widgetType type)
{
CSVWorld::SceneToolbar* toolbar = new SceneToolbar (48+6, this);
SceneToolMode *navigationTool = widget->makeNavigationSelector (toolbar);
toolbar->addTool (navigationTool);
SceneToolMode *lightingTool = widget->makeLightingSelector (toolbar);
toolbar->addTool (lightingTool);
/* Add buttons specific to the type. For now no need for it.
*
switch (type)
{
case widget_Paged:
break;
case widget_Unpaged:
break;
}
*/
return toolbar;
} }
void CSVWorld::SceneSubView::setEditLock (bool locked) void CSVWorld::SceneSubView::setEditLock (bool locked)
@ -102,8 +147,19 @@ void CSVWorld::SceneSubView::closeRequest()
deleteLater(); deleteLater();
} }
void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::UniversalId& id)
{
setUniversalId(id);
std::ostringstream stream;
stream << "Scene: " << getUniversalId().getId();
setWindowTitle (QString::fromUtf8 (stream.str().c_str()));
}
void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection) void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection& selection)
{ {
setUniversalId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Scene, "sys::default"));
int size = selection.getSize(); int size = selection.getSize();
std::ostringstream stream; std::ostringstream stream;
@ -127,3 +183,61 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection
setWindowTitle (QString::fromUtf8 (stream.str().c_str())); setWindowTitle (QString::fromUtf8 (stream.str().c_str()));
} }
void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data)
{
CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL;
CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL;
SceneToolbar* toolbar = NULL;
switch (mScene->getDropRequirements(CSVRender::WorldspaceWidget::getDropType(data)))
{
case CSVRender::WorldspaceWidget::canHandle:
mScene->handleDrop(data);
break;
case CSVRender::WorldspaceWidget::needPaged:
pagedNewWidget = new CSVRender::PagedWorldspaceWidget(this, mDocument);
toolbar = makeToolbar(pagedNewWidget, widget_Paged);
makeConnections(pagedNewWidget);
replaceToolbarAndWorldspace(pagedNewWidget, toolbar);
mScene->handleDrop(data);
break;
case CSVRender::WorldspaceWidget::needUnpaged:
unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(data.begin()->getId(), mDocument, this);
toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged);
makeConnections(unPagedNewWidget);
replaceToolbarAndWorldspace(unPagedNewWidget, toolbar);
cellSelectionChanged(*(data.begin()));
break;
case CSVRender::WorldspaceWidget::ignored:
return;
}
}
void CSVWorld::SceneSubView::replaceToolbarAndWorldspace (CSVRender::WorldspaceWidget* widget, CSVWorld::SceneToolbar* toolbar)
{
assert(mLayout);
if (mScene)
{
mLayout->removeWidget(mScene);
mScene->deleteLater();
}
if (mToolbar)
{
mLayout->removeWidget(mToolbar);
mToolbar->deleteLater();
}
mScene = widget;
mToolbar = toolbar;
mLayout->addWidget (mToolbar, 0);
mLayout->addWidget (mScene, 1);
mScene->selectDefaultNavigationMode();
}

View file

@ -1,7 +1,10 @@
#ifndef CSV_WORLD_SCENESUBVIEW_H #ifndef CSV_WORLD_SCENESUBVIEW_H
#define CSV_WORLD_SCENESUBVIEW_H #define CSV_WORLD_SCENESUBVIEW_H
#include <QHBoxLayout>
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
#include "scenetoolbar.hpp"
class QModelIndex; class QModelIndex;
@ -18,6 +21,8 @@ namespace CSMDoc
namespace CSVRender namespace CSVRender
{ {
class WorldspaceWidget; class WorldspaceWidget;
class PagedWorldspaceWidget;
class UnpagedWorldspaceWidget;
} }
namespace CSVWorld namespace CSVWorld
@ -32,6 +37,9 @@ namespace CSVWorld
TableBottomBox *mBottom; TableBottomBox *mBottom;
CSVRender::WorldspaceWidget *mScene; CSVRender::WorldspaceWidget *mScene;
QHBoxLayout* mLayout;
CSMDoc::Document& mDocument;
SceneToolbar* mToolbar;
public: public:
@ -45,11 +53,30 @@ namespace CSVWorld
virtual void useHint (const std::string& hint); virtual void useHint (const std::string& hint);
private:
void makeConnections(CSVRender::PagedWorldspaceWidget* widget);
void makeConnections(CSVRender::UnpagedWorldspaceWidget* widget);
void replaceToolbarAndWorldspace(CSVRender::WorldspaceWidget* widget, SceneToolbar* toolbar);
enum widgetType
{
widget_Paged,
widget_Unpaged
};
SceneToolbar* makeToolbar(CSVRender::WorldspaceWidget* widget, widgetType type);
private slots: private slots:
void closeRequest(); void closeRequest();
void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::CellSelection& selection);
void cellSelectionChanged (const CSMWorld::UniversalId& id);
void handleDrop(const std::vector<CSMWorld::UniversalId>& data);
}; };
} }

View file

@ -188,8 +188,8 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), : mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0),
mDocument (document) DragRecordTable(document)
{ {
mModel = &dynamic_cast<CSMWorld::IdTable&> (*mDocument.getData().getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTable&> (*mDocument.getData().getTableModel (id));
@ -282,7 +282,7 @@ void CSVWorld::Table::setEditLock (bool locked)
for (std::vector<CommandDelegate *>::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) for (std::vector<CommandDelegate *>::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter)
(*iter)->setEditLock (locked); (*iter)->setEditLock (locked);
mEditLock = locked; DragRecordTable::setEditLock(locked);
} }
CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
@ -298,7 +298,7 @@ void CSVWorld::Table::revertRecord()
{ {
std::vector<std::string> revertableIds = listRevertableSelectedIds(); std::vector<std::string> revertableIds = listRevertableSelectedIds();
if (revertableIds.size()>0) if (!revertableIds.empty())
{ {
if (revertableIds.size()>1) if (revertableIds.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
@ -318,7 +318,7 @@ void CSVWorld::Table::deleteRecord()
{ {
std::vector<std::string> deletableIds = listDeletableSelectedIds(); std::vector<std::string> deletableIds = listDeletableSelectedIds();
if (deletableIds.size()>0) if (!deletableIds.empty())
{ {
if (deletableIds.size()>1) if (deletableIds.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
@ -518,42 +518,8 @@ void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event)
{ {
if (event->buttons() & Qt::LeftButton) if (event->buttons() & Qt::LeftButton)
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); startDrag(*this);
if (selectedRows.size() == 0)
{
return;
} }
QDrag* drag = new QDrag (this);
CSMWorld::TableMimeData* mime = NULL;
if (selectedRows.size() == 1)
{
mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument);
}
else
{
std::vector<CSMWorld::UniversalId> idToDrag;
foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW.
{
idToDrag.push_back (getUniversalId (it.row()));
}
mime = new CSMWorld::TableMimeData (idToDrag, mDocument);
}
drag->setMimeData (mime);
drag->setPixmap (QString::fromUtf8 (mime->getIcon().c_str()));
drag->exec(Qt::CopyAction);
}
}
void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
} }
void CSVWorld::Table::dropEvent(QDropEvent *event) void CSVWorld::Table::dropEvent(QDropEvent *event)
@ -583,11 +549,6 @@ void CSVWorld::Table::dropEvent(QDropEvent *event)
} //TODO handle drops from different document } //TODO handle drops from different document
} }
void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event)
{
event->accept();
}
std::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const std::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const
{ {
const int count = mModel->columnCount(); const int count = mModel->columnCount();
@ -605,3 +566,18 @@ std::vector<std::string> CSVWorld::Table::getColumnsWithDisplay(CSMWorld::Column
} }
return titles; return titles;
} }
std::vector< CSMWorld::UniversalId > CSVWorld::Table::getDraggedRecords() const
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<CSMWorld::UniversalId> idToDrag;
foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW.
{
idToDrag.push_back (getUniversalId (it.row()));
}
return idToDrag;
}

View file

@ -4,11 +4,11 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <QTableView>
#include <QtGui/qevent.h> #include <QtGui/qevent.h>
#include "../../model/filter/node.hpp" #include "../../model/filter/node.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "dragrecordtable.hpp"
class QUndoStack; class QUndoStack;
class QAction; class QAction;
@ -31,7 +31,7 @@ namespace CSVWorld
class CommandDelegate; class CommandDelegate;
///< Table widget ///< Table widget
class Table : public QTableView class Table : public DragRecordTable
{ {
Q_OBJECT Q_OBJECT
@ -47,9 +47,7 @@ namespace CSVWorld
QAction *mPreviewAction; QAction *mPreviewAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
bool mEditLock;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMDoc::Document& mDocument;
private: private:
@ -61,10 +59,6 @@ namespace CSVWorld
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event); void dropEvent(QDropEvent *event);
public: public:
@ -74,12 +68,14 @@ namespace CSVWorld
///< \param createAndDelete Allow creation and deletion of records. ///< \param createAndDelete Allow creation and deletion of records.
/// \param sorting Allow changing order of rows in the view via column headers. /// \param sorting Allow changing order of rows in the view via column headers.
void setEditLock (bool locked); virtual void setEditLock (bool locked);
CSMWorld::UniversalId getUniversalId (int row) const; CSMWorld::UniversalId getUniversalId (int row) const;
std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; std::vector<std::string> getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const;
virtual std::vector<CSMWorld::UniversalId> getDraggedRecords() const;
signals: signals:
void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); void editRequest (const CSMWorld::UniversalId& id, const std::string& hint);
@ -92,6 +88,7 @@ namespace CSVWorld
/// \param modified Number of added and modified records /// \param modified Number of added and modified records
void createRequest(); void createRequest();
void cloneRequest(const CSMWorld::UniversalId&); void cloneRequest(const CSMWorld::UniversalId&);
private slots: private slots:

View file

@ -48,7 +48,7 @@ add_openmw_dir (mwscript
) )
add_openmw_dir (mwsound add_openmw_dir (mwsound
soundmanagerimp openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder soundmanagerimp openal_output ffmpeg_decoder
) )
add_openmw_dir (mwworld add_openmw_dir (mwworld
@ -67,7 +67,7 @@ add_openmw_dir (mwclass
add_openmw_dir (mwmechanics add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipersue alchemy aiwander aitravel aifollow drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle disease pickpocket levelledlist combat steering obstacle
) )

View file

@ -5,6 +5,11 @@
#include <stdint.h> #include <stdint.h>
namespace Loading
{
class Listener;
}
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -45,9 +50,6 @@ namespace MWBase
virtual void goodbye() = 0; virtual void goodbye() = 0;
virtual MWWorld::Ptr getActor() const = 0;
///< Return the actor the player is currently talking to.
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0;
//calbacks for the GUI //calbacks for the GUI
@ -63,7 +65,7 @@ namespace MWBase
virtual int countSavedGameRecords() const = 0; virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };

View file

@ -11,6 +11,11 @@
#include "../mwdialogue/topic.hpp" #include "../mwdialogue/topic.hpp"
#include "../mwdialogue/quest.hpp" #include "../mwdialogue/quest.hpp"
namespace Loading
{
class Listener;
}
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -80,7 +85,7 @@ namespace MWBase
virtual int countSavedGameRecords() const = 0; virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };

View file

@ -55,6 +55,8 @@ namespace MWBase
virtual void endGame() = 0; virtual void endGame() = 0;
virtual void deleteGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0; virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
///< Write a saved game to \a slot or create a new slot if \a slot == 0. ///< Write a saved game to \a slot or create a new slot if \a slot == 0.
/// ///

View file

@ -156,8 +156,9 @@ namespace MWBase
virtual void setValue (const std::string& id, int value) = 0; virtual void setValue (const std::string& id, int value) = 0;
/// Set time left for the player to start drowning (update the drowning bar) /// Set time left for the player to start drowning (update the drowning bar)
/// @param time value from [0,20] /// @param time time left to start drowning
virtual void setDrowningTimeLeft (float time) =0; /// @param maxTime how long we can be underwater (in total) until drowning starts
virtual void setDrowningTimeLeft (float time, float maxTime) = 0;
virtual void setPlayerClass (const ESM::Class &class_) = 0; virtual void setPlayerClass (const ESM::Class &class_) = 0;
///< set current class of player ///< set current class of player
@ -302,8 +303,12 @@ namespace MWBase
/// Clear all savegame-specific data /// Clear all savegame-specific data
virtual void clear() = 0; virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
virtual int countSavedGameRecords() const = 0;
/// Does the current stack of GUI-windows permit saving?
virtual bool isSavingAllowed() const = 0;
}; };
} }

View file

@ -108,7 +108,7 @@ namespace MWBase
virtual int countSavedGameRecords() const = 0; virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type, virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 0; const std::map<int, int>& contentFileMap) = 0;
@ -407,6 +407,8 @@ namespace MWBase
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0; virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0;
///< get Line of Sight (morrowind stupid implementation) ///< get Line of Sight (morrowind stupid implementation)
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0; virtual void enableActorCollision(const MWWorld::Ptr& actor, bool enable) = 0;
virtual int canRest() = 0; virtual int canRest() = 0;

View file

@ -624,6 +624,8 @@ namespace MWClass
if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
getCreatureStats(ptr).setAttacked(true);
if(!successful) if(!successful)
{ {
// TODO: Handle HitAttemptOnMe script function // TODO: Handle HitAttemptOnMe script function
@ -659,7 +661,6 @@ namespace MWClass
{ {
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit"); MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
} }
getCreatureStats(ptr).setAttacked(true);
// Check for knockdown // Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();

View file

@ -170,6 +170,14 @@ namespace MWClass
virtual int getBaseGold(const MWWorld::Ptr& ptr) const; virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const;
virtual bool canSwim (const MWWorld::Ptr &ptr) const {
return true;
}
virtual bool canWalk (const MWWorld::Ptr &ptr) const {
return true;
}
}; };
} }

View file

@ -468,11 +468,6 @@ namespace MWDialogue
mIsInChoice = true; mIsInChoice = true;
} }
MWWorld::Ptr DialogueManager::getActor() const
{
return mActor;
}
void DialogueManager::goodbye() void DialogueManager::goodbye()
{ {
mIsInChoice = true; mIsInChoice = true;
@ -614,7 +609,7 @@ namespace MWDialogue
return 1; // known topics return 1; // known topics
} }
void DialogueManager::write (ESM::ESMWriter& writer) const void DialogueManager::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{ {
ESM::DialogueState state; ESM::DialogueState state;
@ -626,6 +621,7 @@ namespace MWDialogue
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
state.save (writer); state.save (writer);
writer.endRecord (ESM::REC_DIAS); writer.endRecord (ESM::REC_DIAS);
progress.increaseProgress();
} }
void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type) void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type)

View file

@ -68,9 +68,6 @@ namespace MWDialogue
virtual void goodbye(); virtual void goodbye();
virtual MWWorld::Ptr getActor() const;
///< Return the actor the player is currently talking to.
virtual bool checkServiceRefused (); virtual bool checkServiceRefused ();
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const; virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const;
@ -86,7 +83,7 @@ namespace MWDialogue
virtual int countSavedGameRecords() const; virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void readRecord (ESM::ESMReader& reader, int32_t type);
}; };

View file

@ -111,8 +111,14 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
// check cell // check cell
if (!info.mCell.empty()) if (!info.mCell.empty())
if (!Misc::StringUtils::ciEqual(player.getCell()->getCell()->mName, info.mCell)) {
// supports partial matches, just like getPcCell
const std::string& playerCell = player.getCell()->getCell()->mName;
bool match = playerCell.length()>=info.mCell.length() &&
Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell);
if (!match)
return false; return false;
}
return true; return true;
} }

View file

@ -167,7 +167,7 @@ namespace MWDialogue
return count; return count;
} }
void Journal::write (ESM::ESMWriter& writer) const void Journal::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
{ {
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter) for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
{ {
@ -178,6 +178,7 @@ namespace MWDialogue
writer.startRecord (ESM::REC_QUES); writer.startRecord (ESM::REC_QUES);
state.save (writer); state.save (writer);
writer.endRecord (ESM::REC_QUES); writer.endRecord (ESM::REC_QUES);
progress.increaseProgress();
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter) for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
{ {
@ -188,6 +189,7 @@ namespace MWDialogue
writer.startRecord (ESM::REC_JOUR); writer.startRecord (ESM::REC_JOUR);
entry.save (writer); entry.save (writer);
writer.endRecord (ESM::REC_JOUR); writer.endRecord (ESM::REC_JOUR);
progress.increaseProgress();
} }
} }
@ -199,6 +201,7 @@ namespace MWDialogue
writer.startRecord (ESM::REC_JOUR); writer.startRecord (ESM::REC_JOUR);
entry.save (writer); entry.save (writer);
writer.endRecord (ESM::REC_JOUR); writer.endRecord (ESM::REC_JOUR);
progress.increaseProgress();
} }
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter) for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
@ -214,6 +217,7 @@ namespace MWDialogue
writer.startRecord (ESM::REC_JOUR); writer.startRecord (ESM::REC_JOUR);
entry.save (writer); entry.save (writer);
writer.endRecord (ESM::REC_JOUR); writer.endRecord (ESM::REC_JOUR);
progress.increaseProgress();
} }
} }
} }

View file

@ -64,7 +64,7 @@ namespace MWDialogue
virtual int countSavedGameRecords() const; virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void readRecord (ESM::ESMReader& reader, int32_t type);
}; };

View file

@ -188,12 +188,13 @@ namespace MWGui
break; break;
case GM_ClassCreate: case GM_ClassCreate:
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); if (!mCreateClassDialog)
mCreateClassDialog = 0; {
mCreateClassDialog = new CreateClassDialog(); mCreateClassDialog = new CreateClassDialog();
mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);
mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
}
mCreateClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);
mCreateClassDialog->setVisible(true); mCreateClassDialog->setVisible(true);
if (mCreationStage < CSE_RaceChosen) if (mCreationStage < CSE_RaceChosen)
mCreationStage = CSE_RaceChosen; mCreationStage = CSE_RaceChosen;
@ -531,8 +532,8 @@ namespace MWGui
mPlayerClass = klass; mPlayerClass = klass;
MWBase::Environment::get().getWindowManager()->setPlayerClass(klass); MWBase::Environment::get().getWindowManager()->setPlayerClass(klass);
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); // Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later
mCreateClassDialog = 0; mCreateClassDialog->setVisible(false);
} }
updatePlayerHealth(); updatePlayerHealth();
@ -554,8 +555,8 @@ namespace MWGui
void CharacterCreation::onCreateClassDialogBack() void CharacterCreation::onCreateClassDialogBack()
{ {
MWBase::Environment::get().getWindowManager()->removeDialog(mCreateClassDialog); // Do not delete dialog, so that choices are rembered in case we want to go back and adjust them later
mCreateClassDialog = 0; mCreateClassDialog->setVisible(false);
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class);

View file

@ -10,7 +10,7 @@ namespace MWGui
{ {
} }
void CompanionItemModel::copyItem (const ItemStack& item, size_t count) void CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
{ {
if (mActor.getClass().isNpc()) if (mActor.getClass().isNpc())
{ {
@ -18,7 +18,7 @@ namespace MWGui
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
} }
InventoryItemModel::copyItem(item, count); InventoryItemModel::copyItem(item, count, setNewOwner);
} }
void CompanionItemModel::removeItem (const ItemStack& item, size_t count) void CompanionItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -13,7 +13,7 @@ namespace MWGui
public: public:
CompanionItemModel (const MWWorld::Ptr& actor); CompanionItemModel (const MWWorld::Ptr& actor);
virtual void copyItem (const ItemStack& item, size_t count); virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
}; };

View file

@ -215,16 +215,22 @@ namespace MWGui
{ {
std::vector<std::string> matches; std::vector<std::string> matches;
listNames(); listNames();
mCommandLine->setCaption(complete( mCommandLine->getOnlyText(), matches )); std::string oldCaption = mCommandLine->getCaption();
#if 0 std::string newCaption = complete( mCommandLine->getOnlyText(), matches );
mCommandLine->setCaption(newCaption);
// List candidates if repeatedly pressing tab
if (oldCaption == newCaption && matches.size())
{
int i = 0; int i = 0;
printOK("");
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); ++it,++i ) for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); ++it,++i )
{ {
printOK( *it ); printOK( *it );
if( i == 50 ) if( i == 50 )
break; break;
} }
#endif }
} }
if(mCommandHistory.empty()) return; if(mCommandHistory.empty()) return;

View file

@ -13,6 +13,7 @@
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwmechanics/pickpocket.hpp" #include "../mwmechanics/pickpocket.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "countdialog.hpp" #include "countdialog.hpp"
#include "tradewindow.hpp" #include "tradewindow.hpp"
@ -84,8 +85,7 @@ namespace MWGui
// otherwise, do the transfer // otherwise, do the transfer
if (targetModel != mSourceModel) if (targetModel != mSourceModel)
{ {
targetModel->copyItem(mItem, mDraggedCount); mSourceModel->moveItem(mItem, mDraggedCount, targetModel);
mSourceModel->removeItem(mItem, mDraggedCount);
} }
mSourceModel->update(); mSourceModel->update();
@ -292,8 +292,7 @@ namespace MWGui
if (!onTakeItem(item, item.mCount)) if (!onTakeItem(item, item.mCount))
break; break;
playerModel->copyItem(item, item.mCount); mModel->moveItem(item, item.mCount, playerModel);
mModel->removeItem(item, item.mCount);
} }
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
@ -341,6 +340,10 @@ namespace MWGui
} }
else else
{ {
// Looting a dead corpse is considered OK
if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead())
return true;
else
MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count);
} }
return true; return true;

View file

@ -71,7 +71,7 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
void ContainerItemModel::copyItem (const ItemStack& item, size_t count) void ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))

View file

@ -21,7 +21,7 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual void copyItem (const ItemStack& item, size_t count); virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual void update(); virtual void update();

View file

@ -196,6 +196,16 @@ namespace MWGui
bitmapFile->read(&textureData[0], width*height*4); bitmapFile->read(&textureData[0], width*height*4);
bitmapFile->close(); bitmapFile->close();
std::string resourceName;
if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic"))
resourceName = "Magic Cards";
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century"))
resourceName = "Century Gothic";
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric"))
resourceName = "Daedric";
else
return; // no point in loading it, since there is no way of using additional fonts
std::string textureName = name; std::string textureName = name;
Ogre::Image image; Ogre::Image image;
image.loadDynamicImage(&textureData[0], width, height, Ogre::PF_BYTE_RGBA); image.loadDynamicImage(&textureData[0], width, height, Ogre::PF_BYTE_RGBA);
@ -208,18 +218,11 @@ namespace MWGui
// Register the font with MyGUI // Register the font with MyGUI
MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>( MyGUI::ResourceManualFont* font = static_cast<MyGUI::ResourceManualFont*>(
MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont")); MyGUI::FactoryManager::getInstance().createObject("Resource", "ResourceManualFont"));
// We need to emulate loading from XML because the data members are private as of mygui 3.2.0 // We need to emulate loading from XML because the data members are private as of mygui 3.2.0
MyGUI::xml::Document xmlDocument; MyGUI::xml::Document xmlDocument;
MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont"); MyGUI::xml::ElementPtr root = xmlDocument.createRoot("ResourceManualFont");
root->addAttribute("name", resourceName);
if (name.size() >= 5 && Misc::StringUtils::ciEqual(name.substr(0, 5), "magic"))
root->addAttribute("name", "Magic Cards");
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "century"))
root->addAttribute("name", "Century Gothic");
else if (name.size() >= 7 && Misc::StringUtils::ciEqual(name.substr(0, 7), "daedric"))
root->addAttribute("name", "Daedric");
else
return; // no point in loading it, since there is no way of using additional fonts
MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property"); MyGUI::xml::ElementPtr defaultHeight = root->createChild("Property");
defaultHeight->addAttribute("key", "DefaultHeight"); defaultHeight->addAttribute("key", "DefaultHeight");
@ -285,6 +288,7 @@ namespace MWGui
font->deserialization(root, MyGUI::Version(3,2,0)); font->deserialization(root, MyGUI::Version(3,2,0));
MyGUI::ResourceManager::getInstance().removeByName(font->getResourceName());
MyGUI::ResourceManager::getInstance().addResource(font); MyGUI::ResourceManager::getInstance().addResource(font);
} }

View file

@ -52,7 +52,7 @@ namespace MWGui
, mWeaponVisible(true) , mWeaponVisible(true)
, mSpellVisible(true) , mSpellVisible(true)
, mWorldMouseOver(false) , mWorldMouseOver(false)
, mEnemyHealthTimer(0) , mEnemyHealthTimer(-1)
, mIsDrowning(false) , mIsDrowning(false)
, mWeaponSpellTimer(0.f) , mWeaponSpellTimer(0.f)
, mDrowningFlashTheta(0.f) , mDrowningFlashTheta(0.f)
@ -203,9 +203,9 @@ namespace MWGui
} }
} }
void HUD::setDrowningTimeLeft(float time) void HUD::setDrowningTimeLeft(float time, float maxTime)
{ {
size_t progress = time/20.0*200.0; size_t progress = time/maxTime*200.0;
mDrowning->setProgressPosition(progress); mDrowning->setProgressPosition(progress);
bool isDrowning = (progress == 0); bool isDrowning = (progress == 0);
@ -625,7 +625,7 @@ namespace MWGui
if (mIsDrowning) if (mIsDrowning)
{ {
float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f; float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f;
mDrowningFlash->setColour(MyGUI::Colour(intensity, intensity, intensity)); mDrowningFlash->setColour(MyGUI::Colour(intensity, 0, 0));
} }
} }
@ -639,4 +639,10 @@ namespace MWGui
updateEnemyHealthBar(); updateEnemyHealthBar();
} }
void HUD::resetEnemy()
{
mEnemy = MWWorld::Ptr();
mEnemyHealthTimer = -1;
}
} }

View file

@ -22,8 +22,9 @@ namespace MWGui
void setBatchCount(unsigned int count); void setBatchCount(unsigned int count);
/// Set time left for the player to start drowning /// Set time left for the player to start drowning
/// @param time value from [0,20] /// @param time time left to start drowning
void setDrowningTimeLeft(float time); /// @param maxTime how long we can be underwater (in total) until drowning starts
void setDrowningTimeLeft(float time, float maxTime);
void setDrowningBarVisible(bool visible); void setDrowningBarVisible(bool visible);
void setHmsVisible(bool visible); void setHmsVisible(bool visible);
@ -56,6 +57,7 @@ namespace MWGui
void update(); void update();
void setEnemy(const MWWorld::Ptr& enemy); void setEnemy(const MWWorld::Ptr& enemy);
void resetEnemy();
private: private:
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;

View file

@ -4,6 +4,8 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/creaturestats.hpp"
namespace MWGui namespace MWGui
{ {
@ -38,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
return -1; return -1;
} }
void InventoryItemModel::copyItem (const ItemStack& item, size_t count) void InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
throw std::runtime_error("Item to copy needs to be from a different container!"); throw std::runtime_error("Item to copy needs to be from a different container!");
mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor); mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
} }
@ -57,6 +59,18 @@ void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
throw std::runtime_error("Not enough items in the stack to remove"); throw std::runtime_error("Not enough items in the stack to remove");
} }
void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
{
bool setNewOwner = false;
// Are you dead? Then you wont need that anymore
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead())
setNewOwner = true;
otherModel->copyItem(item, count, setNewOwner);
removeItem(item, count);
}
void InventoryItemModel::update() void InventoryItemModel::update()
{ {
MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor);

View file

@ -15,9 +15,12 @@ namespace MWGui
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);
virtual size_t getItemCount(); virtual size_t getItemCount();
virtual void copyItem (const ItemStack& item, size_t count); virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
/// Move items from this model to \a otherModel.
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
virtual void update(); virtual void update();
protected: protected:

View file

@ -35,7 +35,7 @@ namespace MWGui
, mTrading(false) , mTrading(false)
, mLastXSize(0) , mLastXSize(0)
, mLastYSize(0) , mLastYSize(0)
, mPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()) , mPreview(new MWRender::InventoryPreview(MWBase::Environment::get().getWorld ()->getPlayerPtr()))
, mPreviewDirty(true) , mPreviewDirty(true)
, mDragAndDrop(dragAndDrop) , mDragAndDrop(dragAndDrop)
, mSelectedItem(-1) , mSelectedItem(-1)
@ -91,8 +91,8 @@ namespace MWGui
mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr()); mTradeModel = new TradeItemModel(new InventoryItemModel(mPtr), MWWorld::Ptr());
mSortModel = new SortFilterItemModel(mTradeModel); mSortModel = new SortFilterItemModel(mTradeModel);
mItemView->setModel(mSortModel); mItemView->setModel(mSortModel);
mPreview = MWRender::InventoryPreview(mPtr); mPreview.reset(new MWRender::InventoryPreview(mPtr));
mPreview.setup(); mPreview->setup();
} }
void InventoryWindow::setGuiMode(GuiMode mode) void InventoryWindow::setGuiMode(GuiMode mode)
@ -444,7 +444,7 @@ namespace MWGui
MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y) MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y)
{ {
int slot = mPreview.getSlotSelected (x, y); int slot = mPreview->getSlotSelected (x, y);
if (slot == -1) if (slot == -1)
return MWWorld::Ptr(); return MWWorld::Ptr();
@ -493,7 +493,7 @@ namespace MWGui
mPreviewDirty = false; mPreviewDirty = false;
MyGUI::IntSize size = mAvatarImage->getSize(); MyGUI::IntSize size = mAvatarImage->getSize();
mPreview.update (size.width, size.height); mPreview->update (size.width, size.height);
mAvatarImage->setImageTexture("CharacterPreview"); mAvatarImage->setImageTexture("CharacterPreview");
mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height))); mAvatarImage->setImageCoord(MyGUI::IntCoord(0, 0, std::min(512, size.width), std::min(1024, size.height)));

View file

@ -34,7 +34,7 @@ namespace MWGui
MWWorld::Ptr getAvatarSelectedItem(int x, int y); MWWorld::Ptr getAvatarSelectedItem(int x, int y);
void rebuildAvatar() { void rebuildAvatar() {
mPreview.rebuild(); mPreview->rebuild();
} }
TradeItemModel* getTradeModel(); TradeItemModel* getTradeModel();
@ -81,7 +81,7 @@ namespace MWGui
int mLastXSize; int mLastXSize;
int mLastYSize; int mLastYSize;
MWRender::InventoryPreview mPreview; std::auto_ptr<MWRender::InventoryPreview> mPreview;
bool mTrading; bool mTrading;

View file

@ -71,16 +71,22 @@ namespace MWGui
{ {
} }
void ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
{
otherModel->copyItem(item, count);
removeItem(item, count);
}
ProxyItemModel::~ProxyItemModel() ProxyItemModel::~ProxyItemModel()
{ {
delete mSourceModel; delete mSourceModel;
} }
void ProxyItemModel::copyItem (const ItemStack& item, size_t count) void ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
{ {
// no need to use mapToSource since itemIndex refers to an index in the sourceModel // no need to use mapToSource since itemIndex refers to an index in the sourceModel
mSourceModel->copyItem (item, count); mSourceModel->copyItem (item, count, setNewOwner);
} }
void ProxyItemModel::removeItem (const ItemStack& item, size_t count) void ProxyItemModel::removeItem (const ItemStack& item, size_t count)

View file

@ -55,7 +55,11 @@ namespace MWGui
virtual void update() = 0; virtual void update() = 0;
virtual void copyItem (const ItemStack& item, size_t count) = 0; /// Move items from this model to \a otherModel.
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
/// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner?
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
virtual void removeItem (const ItemStack& item, size_t count) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0;
private: private:
@ -69,7 +73,7 @@ namespace MWGui
{ {
public: public:
virtual ~ProxyItemModel(); virtual ~ProxyItemModel();
virtual void copyItem (const ItemStack& item, size_t count); virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
virtual void removeItem (const ItemStack& item, size_t count); virtual void removeItem (const ItemStack& item, size_t count);
virtual ModelIndex getIndex (ItemStack item); virtual ModelIndex getIndex (ItemStack item);

View file

@ -48,7 +48,7 @@ namespace MWGui
void MWList::redraw(bool scrollbarShown) void MWList::redraw(bool scrollbarShown)
{ {
const int _scrollBarWidth = 24; // fetch this from skin? const int _scrollBarWidth = 20; // fetch this from skin?
const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0; const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;
const int spacing = 3; const int spacing = 3;
size_t scrollbarPosition = mScrollView->getScrollPosition(); size_t scrollbarPosition = mScrollView->getScrollPosition();
@ -83,7 +83,7 @@ namespace MWGui
else else
{ {
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine", MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine",
MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18), MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth() - scrollBarWidth - 4, 18),
MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch); MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);
separator->setNeedMouseFocus(false); separator->setNeedMouseFocus(false);
@ -91,7 +91,7 @@ namespace MWGui
} }
++i; ++i;
} }
mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height)); mScrollView->setCanvasSize(mClient->getSize().width, std::max(mItemHeight, mClient->getSize().height));
if (!scrollbarShown && mItemHeight > mClient->getSize().height) if (!scrollbarShown && mItemHeight > mClient->getSize().height)
redraw(true); redraw(true);

View file

@ -160,7 +160,6 @@ namespace MWGui
void LoadingScreen::setProgress (size_t value) void LoadingScreen::setProgress (size_t value)
{ {
assert(value < mProgressBar->getScrollRange());
if (value - mProgress < mProgressBar->getScrollRange()/100.f) if (value - mProgress < mProgressBar->getScrollRange()/100.f)
return; return;
mProgress = value; mProgress = value;
@ -174,7 +173,6 @@ namespace MWGui
mProgressBar->setScrollPosition(0); mProgressBar->setScrollPosition(0);
size_t value = mProgress + increase; size_t value = mProgress + increase;
mProgress = value; mProgress = value;
assert(mProgress < mProgressBar->getScrollRange());
mProgressBar->setTrackSize(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize()); mProgressBar->setTrackSize(value / (float)(mProgressBar->getScrollRange()) * mProgressBar->getLineSize());
draw(); draw();
} }

View file

@ -25,7 +25,7 @@ namespace MWGui
virtual void setProgressRange (size_t range); virtual void setProgressRange (size_t range);
virtual void setProgress (size_t value); virtual void setProgress (size_t value);
virtual void increaseProgress (size_t increase); virtual void increaseProgress (size_t increase=1);
virtual void setVisible(bool visible); virtual void setVisible(bool visible);

View file

@ -28,7 +28,7 @@ namespace MWGui
{ {
getWidget(mVersionText, "VersionText"); getWidget(mVersionText, "VersionText");
std::stringstream sstream; std::stringstream sstream;
sstream << "OpenMW version: " << OPENMW_VERSION; sstream << "OpenMW Version: " << OPENMW_VERSION;
// adding info about git hash if available // adding info about git hash if available
std::string rev = OPENMW_VERSION_COMMITHASH; std::string rev = OPENMW_VERSION_COMMITHASH;
@ -36,7 +36,7 @@ namespace MWGui
if (!rev.empty() && !tag.empty()) if (!rev.empty() && !tag.empty())
{ {
rev = rev.substr(0,10); rev = rev.substr(0,10);
sstream << "\nrevision: " << rev; sstream << "\nRevision: " << rev;
} }
std::string output = sstream.str(); std::string output = sstream.str();
@ -170,7 +170,8 @@ namespace MWGui
buttons.push_back("loadgame"); buttons.push_back("loadgame");
if (state==MWBase::StateManager::State_Running && if (state==MWBase::StateManager::State_Running &&
MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1) MWBase::Environment::get().getWorld()->getGlobalInt ("chargenstate")==-1 &&
MWBase::Environment::get().getWindowManager()->isSavingAllowed())
buttons.push_back("savegame"); buttons.push_back("savegame");
buttons.push_back("options"); buttons.push_back("options");

View file

@ -14,6 +14,8 @@
#include "../mwrender/globalmap.hpp" #include "../mwrender/globalmap.hpp"
#include "../components/esm/globalmap.hpp"
#include "widgets.hpp" #include "widgets.hpp"
namespace MWGui namespace MWGui
@ -436,7 +438,6 @@ namespace MWGui
worldY * mGlobalMapRender->getHeight()+6, worldY * mGlobalMapRender->getHeight()+6,
12, 12); 12, 12);
static int _counter=0; static int _counter=0;
MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage", MyGUI::Button* markerWidget = mGlobalMapOverlay->createWidget<MyGUI::Button>("ButtonImage",
widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter)); widgetCoord, MyGUI::Align::Default, "Door" + boost::lexical_cast<std::string>(_counter));
@ -452,6 +453,11 @@ namespace MWGui
markerWidget->setUserString("ToolTipType", "Layout"); markerWidget->setUserString("ToolTipType", "Layout");
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine"); markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
markerWidget->setUserString("Caption_TextOneLine", name); markerWidget->setUserString("Caption_TextOneLine", name);
CellId cell;
cell.first = x;
cell.second = y;
mMarkers.push_back(cell);
} }
void MapWindow::cellExplored(int x, int y) void MapWindow::cellExplored(int x, int y)
@ -580,6 +586,7 @@ namespace MWGui
void MapWindow::clear() void MapWindow::clear()
{ {
mMarkers.clear();
mGlobalMapRender->clear(); mGlobalMapRender->clear();
while (mEventBoxGlobal->getChildCount()) while (mEventBoxGlobal->getChildCount())
@ -588,21 +595,34 @@ namespace MWGui
MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(mGlobalMapOverlay->getChildAt(0));
} }
void MapWindow::write(ESM::ESMWriter &writer) void MapWindow::write(ESM::ESMWriter &writer, Loading::Listener& progress)
{ {
mGlobalMapRender->write(writer); ESM::GlobalMap map;
mGlobalMapRender->write(map);
map.mMarkers = mMarkers;
writer.startRecord(ESM::REC_GMAP);
map.save(writer);
writer.endRecord(ESM::REC_GMAP);
progress.increaseProgress();
} }
void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type) void MapWindow::readRecord(ESM::ESMReader &reader, int32_t type)
{ {
std::vector<std::pair<int, int> > exploredCells; if (type == ESM::REC_GMAP)
mGlobalMapRender->readRecord(reader, type, exploredCells); {
ESM::GlobalMap map;
map.load(reader);
for (std::vector<std::pair<int, int> >::iterator it = exploredCells.begin(); it != exploredCells.end(); ++it) mGlobalMapRender->read(map);
for (std::vector<ESM::GlobalMap::CellId>::iterator it = map.mMarkers.begin(); it != map.mMarkers.end(); ++it)
{ {
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second); const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(it->first, it->second);
if (cell && !cell->mName.empty()) if (cell && !cell->mName.empty())
addVisitedLocation(cell->mName, it->first, it->second); addVisitedLocation(cell->mName, it->first, it->second);
} }
} }
}
} }

View file

@ -55,6 +55,9 @@ namespace MWGui
bool mChanged; bool mChanged;
bool mFogOfWar; bool mFogOfWar;
typedef std::pair<int, int> CellId;
std::vector<CellId> mMarkers;
std::vector<MyGUI::ImageBox*> mMapWidgets; std::vector<MyGUI::ImageBox*> mMapWidgets;
std::vector<MyGUI::ImageBox*> mFogWidgets; std::vector<MyGUI::ImageBox*> mFogWidgets;
@ -105,7 +108,7 @@ namespace MWGui
/// Clear all savegame-specific data /// Clear all savegame-specific data
void clear(); void clear();
void write (ESM::ESMWriter& writer); void write (ESM::ESMWriter& writer, Loading::Listener& progress);
void readRecord (ESM::ESMReader& reader, int32_t type); void readRecord (ESM::ESMReader& reader, int32_t type);
private: private:

View file

@ -2,6 +2,8 @@
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <components/esm/quickkeys.hpp>
#include "../mwworld/inventorystore.hpp" #include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -55,6 +57,14 @@ namespace MWGui
} }
} }
void QuickKeysMenu::clear()
{
for (int i=0; i<10; ++i)
{
unassign(mQuickKeyButtons[i], i);
}
}
QuickKeysMenu::~QuickKeysMenu() QuickKeysMenu::~QuickKeysMenu()
{ {
delete mAssignDialog; delete mAssignDialog;
@ -154,8 +164,6 @@ namespace MWGui
frame->setUserString ("ToolTipType", "ItemPtr"); frame->setUserString ("ToolTipType", "ItemPtr");
frame->setUserData(item); frame->setUserData(item);
frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\"); std::string path = std::string("icons\\");
path += MWWorld::Class::get(item).getInventoryIcon(item); path += MWWorld::Class::get(item).getInventoryIcon(item);
@ -165,6 +173,7 @@ namespace MWGui
image->setImageTexture (path); image->setImageTexture (path);
image->setNeedMouseFocus (false); image->setNeedMouseFocus (false);
if (mItemSelectionDialog)
mItemSelectionDialog->setVisible(false); mItemSelectionDialog->setVisible(false);
} }
@ -198,6 +207,7 @@ namespace MWGui
image->setImageTexture (path); image->setImageTexture (path);
image->setNeedMouseFocus (false); image->setNeedMouseFocus (false);
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);
} }
@ -239,6 +249,7 @@ namespace MWGui
image->setImageTexture (path); image->setImageTexture (path);
image->setNeedMouseFocus (false); image->setNeedMouseFocus (false);
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);
} }
@ -325,7 +336,6 @@ namespace MWGui
} }
store.setSelectedEnchantItem(it); store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
} }
} }
@ -375,6 +385,110 @@ namespace MWGui
center(); center();
} }
void QuickKeysMenu::write(ESM::ESMWriter &writer)
{
writer.startRecord(ESM::REC_KEYS);
ESM::QuickKeys keys;
for (int i=0; i<10; ++i)
{
MyGUI::Button* button = mQuickKeyButtons[i];
int type = *button->getUserData<QuickKeyType>();
ESM::QuickKeys::QuickKey key;
key.mType = type;
switch (type)
{
case Type_Unassigned:
break;
case Type_Item:
case Type_MagicItem:
{
MWWorld::Ptr item = *button->getChildAt(0)->getUserData<MWWorld::Ptr>();
key.mId = item.getCellRef().mRefID;
break;
}
case Type_Magic:
std::string spellId = button->getChildAt(0)->getUserString("Spell");
key.mId = spellId;
break;
}
keys.mKeys.push_back(key);
}
keys.save(writer);
writer.endRecord(ESM::REC_KEYS);
}
void QuickKeysMenu::readRecord(ESM::ESMReader &reader, int32_t type)
{
if (type != ESM::REC_KEYS)
return;
ESM::QuickKeys keys;
keys.load(reader);
int i=0;
for (std::vector<ESM::QuickKeys::QuickKey>::const_iterator it = keys.mKeys.begin(); it != keys.mKeys.end(); ++it)
{
if (i >= 10)
return;
mSelectedIndex = i;
int keyType = it->mType;
std::string id = it->mId;
MyGUI::Button* button = mQuickKeyButtons[i];
switch (keyType)
{
case Type_Magic:
onAssignMagic(id);
break;
case Type_Item:
case Type_MagicItem:
{
// Find the item by id
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
MWWorld::Ptr item;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id))
{
if (item.isEmpty() ||
// Prefer the stack with the lowest remaining uses
(it->getCellRef().mCharge != -1 && (item.getCellRef().mCharge == -1 || it->getCellRef().mCharge < item.getCellRef().mCharge) ))
{
item = *it;
}
}
}
if (item.isEmpty())
unassign(button, i);
else
{
if (keyType == Type_Item)
onAssignItem(item);
else if (keyType == Type_MagicItem)
onAssignMagicItem(item);
}
break;
}
case Type_Unassigned:
unassign(button, i);
break;
}
++i;
}
}
// --------------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------

View file

@ -41,6 +41,11 @@ namespace MWGui
}; };
void write (ESM::ESMWriter& writer);
void readRecord (ESM::ESMReader& reader, int32_t type);
void clear();
private: private:
MyGUI::EditBox* mInstructionLabel; MyGUI::EditBox* mInstructionLabel;
MyGUI::Button* mOkButton; MyGUI::Button* mOkButton;

View file

@ -17,6 +17,8 @@ namespace MWGui
void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable void checkReferenceAvailable(); ///< closes the window, if the MW-reference has become unavailable
void resetReference() { mPtr = MWWorld::Ptr(); mCurrentPlayerCell = NULL; }
protected: protected:
virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable virtual void onReferenceUnavailable() = 0; ///< called when reference has become unavailable

View file

@ -23,6 +23,7 @@ namespace MWGui
: WindowModal("openmw_savegame_dialog.layout") : WindowModal("openmw_savegame_dialog.layout")
, mSaving(true) , mSaving(true)
, mCurrentCharacter(NULL) , mCurrentCharacter(NULL)
, mCurrentSlot(NULL)
{ {
getWidget(mScreenshot, "Screenshot"); getWidget(mScreenshot, "Screenshot");
getWidget(mCharacterSelection, "SelectCharacter"); getWidget(mCharacterSelection, "SelectCharacter");
@ -36,6 +37,7 @@ namespace MWGui
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept); mSaveNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onEditSelectAccept);
mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged); mSaveNameEdit->eventEditTextChange += MyGUI::newDelegate(this, &SaveGameDialog::onSaveNameChanged);
@ -47,6 +49,37 @@ namespace MWGui
accept(); accept();
} }
void SaveGameDialog::onSlotMouseClick(MyGUI::ListBox* sender, size_t pos)
{
onSlotSelected(sender, pos);
if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed())
{
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sMessage3}");
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed);
dialog->eventCancelClicked.clear();
}
}
void SaveGameDialog::onDeleteSlotConfirmed()
{
MWBase::Environment::get().getStateManager()->deleteGame (mCurrentCharacter, mCurrentSlot);
mSaveList->removeItemAt(mSaveList->getIndexSelected());
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
// The character might be deleted now
size_t previousIndex = mCharacterSelection->getIndexSelected();
open();
if (mCharacterSelection->getItemCount())
{
size_t nextCharacter = std::min(previousIndex, mCharacterSelection->getItemCount()-1);
mCharacterSelection->setIndexSelected(nextCharacter);
onCharacterSelected(mCharacterSelection, nextCharacter);
}
}
void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender) void SaveGameDialog::onSaveNameChanged(MyGUI::EditBox *sender)
{ {
// This might have previously been a save slot from the list. If so, that is no longer the case // This might have previously been a save slot from the list. If so, that is no longer the case
@ -69,6 +102,12 @@ namespace MWGui
center(); center();
mCharacterSelection->setCaption("");
mCharacterSelection->removeAllItems();
mCurrentCharacter = NULL;
mCurrentSlot = NULL;
mSaveList->removeAllItems();
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd()) if (mgr->characterBegin() == mgr->characterEnd())
return; return;
@ -78,8 +117,6 @@ namespace MWGui
std::string directory = std::string directory =
Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves")); Misc::StringUtils::lowerCase (Settings::Manager::getString ("character", "Saves"));
mCharacterSelection->removeAllItems();
int selectedIndex = MyGUI::ITEM_NONE; int selectedIndex = MyGUI::ITEM_NONE;
for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it) for (MWBase::StateManager::CharacterIterator it = mgr->characterBegin(); it != mgr->characterEnd(); ++it)
@ -152,23 +189,10 @@ namespace MWGui
{ {
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL);
// Get the selected slot, if any
unsigned int i=0;
const MWState::Slot* slot = NULL;
if (mCurrentCharacter)
{
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it,++i)
{
if (i == mSaveList->getIndexSelected())
slot = &*it;
}
}
if (mSaving) if (mSaving)
{ {
// If overwriting an existing slot, ask for confirmation first // If overwriting an existing slot, ask for confirmation first
if (slot != NULL && !reallySure) if (mCurrentSlot != NULL && !reallySure)
{ {
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sMessage4}"); dialog->open("#{sMessage4}");
@ -182,18 +206,22 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage65}");
return; return;
} }
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), slot);
}
else
{
if (mCurrentCharacter && slot)
{
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, slot);
MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);
}
} }
setVisible(false); setVisible(false);
MWBase::Environment::get().getWindowManager()->removeGuiMode (MWGui::GM_MainMenu);
if (mSaving)
{
MWBase::Environment::get().getStateManager()->saveGame (mSaveNameEdit->getCaption(), mCurrentSlot);
}
else
{
if (mCurrentCharacter && mCurrentSlot)
{
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot);
}
}
if (MWBase::Environment::get().getStateManager()->getState()== if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_NoGame) MWBase::StateManager::State_NoGame)
@ -221,6 +249,7 @@ namespace MWGui
assert(character && "Can't find selected character"); assert(character && "Can't find selected character");
mCurrentCharacter = character; mCurrentCharacter = character;
mCurrentSlot = NULL;
fillSaveList(); fillSaveList();
} }
@ -240,6 +269,7 @@ namespace MWGui
{ {
if (pos == MyGUI::ITEM_NONE) if (pos == MyGUI::ITEM_NONE)
{ {
mCurrentSlot = NULL;
mInfoText->setCaption(""); mInfoText->setCaption("");
mScreenshot->setImageTexture(""); mScreenshot->setImageTexture("");
return; return;
@ -248,17 +278,17 @@ namespace MWGui
if (mSaving) if (mSaving)
mSaveNameEdit->setCaption(sender->getItemNameAt(pos)); mSaveNameEdit->setCaption(sender->getItemNameAt(pos));
const MWState::Slot* slot = NULL; mCurrentSlot = NULL;
unsigned int i=0; unsigned int i=0;
for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i) for (MWState::Character::SlotIterator it = mCurrentCharacter->begin(); it != mCurrentCharacter->end(); ++it, ++i)
{ {
if (i == pos) if (i == pos)
slot = &*it; mCurrentSlot = &*it;
} }
assert(slot && "Can't find selected slot"); assert(mCurrentSlot && "Can't find selected slot");
std::stringstream text; std::stringstream text;
time_t time = slot->mTimeStamp; time_t time = mCurrentSlot->mTimeStamp;
struct tm* timeinfo; struct tm* timeinfo;
timeinfo = localtime(&time); timeinfo = localtime(&time);
@ -269,24 +299,24 @@ namespace MWGui
char buffer[size]; char buffer[size];
if (std::strftime(buffer, size, "%x %X", timeinfo) > 0) if (std::strftime(buffer, size, "%x %X", timeinfo) > 0)
text << buffer << "\n"; text << buffer << "\n";
text << "Level " << slot->mProfile.mPlayerLevel << "\n"; text << "Level " << mCurrentSlot->mProfile.mPlayerLevel << "\n";
text << slot->mProfile.mPlayerCell << "\n"; text << mCurrentSlot->mProfile.mPlayerCell << "\n";
// text << "Time played: " << slot->mProfile.mTimePlayed << "\n"; // text << "Time played: " << slot->mProfile.mTimePlayed << "\n";
int hour = int(slot->mProfile.mInGameTime.mGameHour); int hour = int(mCurrentSlot->mProfile.mInGameTime.mGameHour);
bool pm = hour >= 12; bool pm = hour >= 12;
if (hour >= 13) hour -= 12; if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12; if (hour == 0) hour = 12;
text text
<< slot->mProfile.mInGameTime.mDay << " " << mCurrentSlot->mProfile.mInGameTime.mDay << " "
<< MWBase::Environment::get().getWorld()->getMonthName(slot->mProfile.mInGameTime.mMonth) << MWBase::Environment::get().getWorld()->getMonthName(mCurrentSlot->mProfile.mInGameTime.mMonth)
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); << " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
mInfoText->setCaptionWithReplacing(text.str()); mInfoText->setCaptionWithReplacing(text.str());
// Decode screenshot // Decode screenshot
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :( std::vector<char> data = mCurrentSlot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size())); Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
Ogre::Image image; Ogre::Image image;
image.load(stream, "jpg"); image.load(stream, "jpg");

View file

@ -6,6 +6,7 @@
namespace MWState namespace MWState
{ {
class Character; class Character;
class Slot;
} }
namespace MWGui namespace MWGui
@ -24,8 +25,15 @@ namespace MWGui
void onCancelButtonClicked (MyGUI::Widget* sender); void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
// Slot selected (mouse click or arrow keys)
void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
// Slot activated (double click or enter key)
void onSlotActivated (MyGUI::ListBox* sender, size_t pos); void onSlotActivated (MyGUI::ListBox* sender, size_t pos);
// Slot clicked with mouse
void onSlotMouseClick(MyGUI::ListBox* sender, size_t pos);
void onDeleteSlotConfirmed();
void onEditSelectAccept (MyGUI::EditBox* sender); void onEditSelectAccept (MyGUI::EditBox* sender);
void onSaveNameChanged (MyGUI::EditBox* sender); void onSaveNameChanged (MyGUI::EditBox* sender);
void onConfirmationGiven(); void onConfirmationGiven();
@ -46,6 +54,7 @@ namespace MWGui
MyGUI::Widget* mSpacer; MyGUI::Widget* mSpacer;
const MWState::Character* mCurrentCharacter; const MWState::Character* mCurrentCharacter;
const MWState::Slot* mCurrentSlot;
}; };

View file

@ -322,7 +322,6 @@ namespace MWGui
} }
store.setSelectedEnchantItem(it); store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
updateSpells(); updateSpells();
} }

View file

@ -475,7 +475,7 @@ namespace MWGui
text += std::string("#DDC79E") + faction->mName; text += std::string("#DDC79E") + faction->mName;
if (expelled.find(it->first) != expelled.end()) if (expelled.find(it->first) != expelled.end())
text += "\n#{sExpelled}"; text += "\n#BF9959#{sExpelled}";
else else
{ {
text += std::string("\n#BF9959") + faction->mRanks[it->second]; text += std::string("\n#BF9959") + faction->mRanks[it->second];

View file

@ -120,14 +120,11 @@ namespace MWGui
if (i == sourceModel->getItemCount()) if (i == sourceModel->getItemCount())
throw std::runtime_error("The borrowed item disappeared"); throw std::runtime_error("The borrowed item disappeared");
// reset owner before copying // reset owner while copying, but only for items bought by the player
bool setNewOwner = (mMerchant.isEmpty());
const ItemStack& item = sourceModel->getItem(i); const ItemStack& item = sourceModel->getItem(i);
std::string owner = item.mBase.getCellRef().mOwner;
if (mMerchant.isEmpty()) // only for items bought by player
item.mBase.getCellRef().mOwner = "";
// copy the borrowed items to our model // copy the borrowed items to our model
copyItem(item, it->mCount); copyItem(item, it->mCount, setNewOwner);
item.mBase.getCellRef().mOwner = owner;
// then remove them from the source model // then remove them from the source model
sourceModel->removeItem(item, it->mCount); sourceModel->removeItem(item, it->mCount);
} }

View file

@ -245,7 +245,7 @@ namespace MWGui
// were there any items traded at all? // were there any items traded at all?
std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs(); std::vector<ItemStack> playerBought = playerItemModel->getItemsBorrowedToUs();
std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs(); std::vector<ItemStack> merchantBought = mTradeModel->getItemsBorrowedToUs();
if (!playerBought.size() && !merchantBought.size()) if (playerBought.empty() && merchantBought.empty())
{ {
// user notification // user notification
MWBase::Environment::get().getWindowManager()-> MWBase::Environment::get().getWindowManager()->

View file

@ -187,7 +187,7 @@ namespace MWGui
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
SDL_ShowCursor(false); //SDL_ShowCursor(false);
mCursorManager->setEnabled(true); mCursorManager->setEnabled(true);
@ -289,6 +289,10 @@ namespace MWGui
void WindowManager::setNewGame(bool newgame) void WindowManager::setNewGame(bool newgame)
{ {
// This method will always be called after loading a savegame or starting a new game
// Reset enemy, it could be a dangling pointer from a previous game
mHud->resetEnemy();
if (newgame) if (newgame)
{ {
disallowAll(); disallowAll();
@ -621,9 +625,9 @@ namespace MWGui
mStatsWindow->setValue (id, value); mStatsWindow->setValue (id, value);
} }
void WindowManager::setDrowningTimeLeft (float time) void WindowManager::setDrowningTimeLeft (float time, float maxTime)
{ {
mHud->setDrowningTimeLeft(time); mHud->setDrowningTimeLeft(time, maxTime);
} }
void WindowManager::setPlayerClass (const ESM::Class &class_) void WindowManager::setPlayerClass (const ESM::Class &class_)
@ -1401,16 +1405,49 @@ namespace MWGui
void WindowManager::clear() void WindowManager::clear()
{ {
mMap->clear(); mMap->clear();
mQuickKeysMenu->clear();
mTrainingWindow->resetReference();
mDialogueWindow->resetReference();
mTradeWindow->resetReference();
mSpellBuyingWindow->resetReference();
mSpellCreationDialog->resetReference();
mEnchantingDialog->resetReference();
mContainerWindow->resetReference();
mCompanionWindow->resetReference();
mConsole->resetReference();
mGuiModes.clear();
updateVisible();
} }
void WindowManager::write(ESM::ESMWriter &writer) void WindowManager::write(ESM::ESMWriter &writer, Loading::Listener& progress)
{ {
mMap->write(writer); mMap->write(writer, progress);
mQuickKeysMenu->write(writer);
progress.increaseProgress();
} }
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type) void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
{ {
if (type == ESM::REC_GMAP)
mMap->readRecord(reader, type); mMap->readRecord(reader, type);
else if (type == ESM::REC_KEYS)
mQuickKeysMenu->readRecord(reader, type);
}
int WindowManager::countSavedGameRecords() const
{
return 1 // Global map
+ 1; // QuickKeysMenu
}
bool WindowManager::isSavingAllowed() const
{
return !MyGUI::InputManager::getInstance().isModalAny()
// TODO: remove this, once we have properly serialized the state of open windows
&& (!isGuiMode() || (mGuiModes.size() == 1 && getMode() == GM_MainMenu));
} }
void WindowManager::playVideo(const std::string &name, bool allowSkipping) void WindowManager::playVideo(const std::string &name, bool allowSkipping)

View file

@ -166,8 +166,9 @@ namespace MWGui
virtual void setValue (const std::string& id, int value); virtual void setValue (const std::string& id, int value);
/// Set time left for the player to start drowning (update the drowning bar) /// Set time left for the player to start drowning (update the drowning bar)
/// @param time value from [0,20] /// @param time time left to start drowning
virtual void setDrowningTimeLeft (float time); /// @param maxTime how long we can be underwater (in total) until drowning starts
virtual void setDrowningTimeLeft (float time, float maxTime);
virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player
virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group.
@ -290,8 +291,12 @@ namespace MWGui
/// Clear all savegame-specific data /// Clear all savegame-specific data
virtual void clear(); virtual void clear();
virtual void write (ESM::ESMWriter& writer); virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress);
virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void readRecord (ESM::ESMReader& reader, int32_t type);
virtual int countSavedGameRecords() const;
/// Does the current stack of GUI-windows permit saving?
virtual bool isSavingAllowed() const;
private: private:
bool mConsoleOnlyScripts; bool mConsoleOnlyScripts;

View file

@ -114,6 +114,7 @@ namespace MWInput
, mTimeIdle(0.f) , mTimeIdle(0.f)
, mOverencumberedMessageDelay(0.f) , mOverencumberedMessageDelay(0.f)
, mAlwaysRunActive(false) , mAlwaysRunActive(false)
, mControlsDisabled(false)
{ {
Ogre::RenderWindow* window = ogre.getWindow (); Ogre::RenderWindow* window = ogre.getWindow ();
@ -265,19 +266,20 @@ namespace MWInput
void InputManager::update(float dt, bool disableControls, bool disableEvents) void InputManager::update(float dt, bool disableControls, bool disableEvents)
{ {
mControlsDisabled = disableControls;
mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible()); mInputManager->setMouseVisible(MWBase::Environment::get().getWindowManager()->getCursorVisible());
mInputManager->capture(disableEvents); mInputManager->capture(disableEvents);
// inject some fake mouse movement to force updating MyGUI's widget states // inject some fake mouse movement to force updating MyGUI's widget states
MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel);
// update values of channels (as a result of pressed keys) if (mControlsDisabled)
if (!disableControls)
mInputBinder->update(dt);
if (disableControls)
return; return;
// update values of channels (as a result of pressed keys)
mInputBinder->update(dt);
bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) bool grab = !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)
&& MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console; && MWBase::Environment::get().getWindowManager()->getMode() != MWGui::GM_Console;
@ -484,7 +486,7 @@ namespace MWInput
if (text) if (text)
{ {
edit->addText(MyGUI::UString(text)); edit->insertText(MyGUI::UString(text), edit->getTextCursor());
SDL_free(text); SDL_free(text);
} }
} }
@ -509,6 +511,7 @@ namespace MWInput
} }
} }
if (!mControlsDisabled)
mInputBinder->keyPressed (arg); mInputBinder->keyPressed (arg);
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);

View file

@ -68,7 +68,7 @@ namespace MWInput
/// Clear all savegame-specific data /// Clear all savegame-specific data
virtual void clear(); virtual void clear();
virtual void update(float dt, bool disableControls, bool disableEvents=false); virtual void update(float dt, bool disableControls=false, bool disableEvents=false);
void setPlayer (MWWorld::Player* player) { mPlayer = player; } void setPlayer (MWWorld::Player* player) { mPlayer = player; }
@ -145,6 +145,8 @@ namespace MWInput
bool mInvertY; bool mInvertY;
bool mControlsDisabled;
float mCameraSensitivity; float mCameraSensitivity;
float mUISensitivity; float mUISensitivity;
float mCameraYMultiplier; float mCameraYMultiplier;

View file

@ -206,7 +206,7 @@ namespace MWMechanics
if (effectIt->mKey.mId == effectId) if (effectIt->mKey.mId == effectId)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
else else
effectIt++; ++effectIt;
} }
} }
mSpellsChanged = true; mSpellsChanged = true;
@ -224,7 +224,7 @@ namespace MWMechanics
&& it->second.mCasterHandle == actorHandle) && it->second.mCasterHandle == actorHandle)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
else else
effectIt++; ++effectIt;
} }
} }
mSpellsChanged = true; mSpellsChanged = true;

View file

@ -29,7 +29,7 @@
#include "aicombat.hpp" #include "aicombat.hpp"
#include "aifollow.hpp" #include "aifollow.hpp"
#include "aipersue.hpp" #include "aipursue.hpp"
namespace namespace
{ {
@ -194,21 +194,11 @@ namespace MWMechanics
+(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1]) +(actorpos.pos[1] - playerpos.pos[1])*(actorpos.pos[1] - playerpos.pos[1])
+(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2])); +(actorpos.pos[2] - playerpos.pos[2])*(actorpos.pos[2] - playerpos.pos[2]));
float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified(); float fight = ptr.getClass().getCreatureStats(ptr).getAiSetting(CreatureStats::AI_Fight).getModified();
float disp = 100; //creatures don't have disposition, so set it to 100 by default
if(ptr.getTypeName() == typeid(ESM::NPC).name())
{
disp = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr);
}
if( (fight == 100 ) if( (fight == 100 )
|| (fight >= 95 && d <= 3000) || (fight >= 95 && d <= 3000)
|| (fight >= 90 && d <= 2000) || (fight >= 90 && d <= 2000)
|| (fight >= 80 && d <= 1000) || (fight >= 80 && d <= 1000)
|| (fight >= 80 && disp <= 40)
|| (fight >= 70 && disp <= 35 && d <= 1000)
|| (fight >= 60 && disp <= 30 && d <= 1000)
|| (fight >= 50 && disp == 0)
|| (fight >= 40 && disp <= 10 && d <= 500)
) )
{ {
bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player) bool LOS = MWBase::Environment::get().getWorld()->getLOS(ptr,player)
@ -216,7 +206,7 @@ namespace MWMechanics
if (LOS) if (LOS)
{ {
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr())); creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()), ptr);
creatureStats.setHostile(true); creatureStats.setHostile(true);
} }
} }
@ -548,7 +538,7 @@ namespace MWMechanics
// TODO: Add AI to follow player and fight for him // TODO: Add AI to follow player and fight for him
AiFollow package(ptr.getRefData().getHandle()); AiFollow package(ptr.getRefData().getHandle());
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package); MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr);
// TODO: VFX_SummonStart, VFX_SummonEnd // TODO: VFX_SummonStart, VFX_SummonEnd
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle())); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
@ -751,34 +741,33 @@ namespace MWMechanics
CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); CreatureStats& creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr); NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr);
// If I'm a guard and I'm not hostile if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.isHostile())
if (ptr.getClass().isClass(ptr, "Guard") && !creatureStats.isHostile())
{ {
/// \todo Move me! I shouldn't be here... /// \todo Move me! I shouldn't be here...
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt()) * float cutoff = float(esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt());
float(esmStore.get<ESM::GameSetting>().find("iCrimeThresholdMultiplier")->getInt()) * // Force dialogue on sight if bounty is greater than the cutoff
esmStore.get<ESM::GameSetting>().find("fCrimeGoldDiscountMult")->getFloat(); // In vanilla morrowind, the greeting dialogue is scripted to either arrest the player (< 5000 bounty) or attack (>= 5000 bounty)
// Attack on sight if bounty is greater than the cutoff
if ( player.getClass().getNpcStats(player).getBounty() >= cutoff if ( player.getClass().getNpcStats(player).getBounty() >= cutoff
// TODO: do not run these two every frame. keep an Aware state for each actor and update it every 0.2 s or so?
&& MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getWorld()->getLOS(ptr, player)
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr))
{ {
creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);
creatureStats.setHostile(true); creatureStats.setAlarmed(true);
npcStats.setCrimeId( MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId());
} }
} }
// if I was a witness to a crime // if I was a witness to a crime
if (npcStats.getCrimeId() != -1) if (npcStats.getCrimeId() != -1)
{ {
// if you've payed for your crimes and I havent noticed // if you've paid for your crimes and I havent noticed
if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() ) if( npcStats.getCrimeId() <= MWBase::Environment::get().getWorld()->getPlayer().getCrimeId() )
{ {
// Calm witness down // Calm witness down
if (ptr.getClass().isClass(ptr, "Guard")) if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stopPersue(); creatureStats.getAiSequence().stopPursuit();
creatureStats.getAiSequence().stopCombat(); creatureStats.getAiSequence().stopCombat();
// Reset factors to attack // Reset factors to attack
@ -793,17 +782,16 @@ namespace MWMechanics
else if (!creatureStats.isHostile()) else if (!creatureStats.isHostile())
{ {
if (ptr.getClass().isClass(ptr, "Guard")) if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPersue(player.getClass().getId(player))); creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);
else else
creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.getAiSequence().stack(AiCombat(player), ptr);
creatureStats.setHostile(true); creatureStats.setHostile(true);
} }
} }
// if I didn't report a crime was I attacked? // if I didn't report a crime was I attacked?
else if (creatureStats.getAttacked() && !creatureStats.isHostile()) else if (creatureStats.getAttacked() && !creatureStats.isHostile())
{ {
creatureStats.getAiSequence().stack(AiCombat(player)); creatureStats.getAiSequence().stack(AiCombat(player), ptr);
creatureStats.setHostile(true); creatureStats.setHostile(true);
} }
} }
@ -911,12 +899,23 @@ namespace MWMechanics
iter->second->update(duration); iter->second->update(duration);
} }
// Kill dead actors // Kill dead actors, update some variables
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{ {
const MWWorld::Class &cls = MWWorld::Class::get(iter->first); const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
CreatureStats &stats = cls.getCreatureStats(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first);
//KnockedOutOneFrameLogic
//Used for "OnKnockedOut" command
//Put here to ensure that it's run for PRECISELY one frame.
if(stats.getKnockedDown() && !stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Start it for one frame if nessesary
stats.setKnockedDownOneFrame(true);
}
else if (stats.getKnockedDownOneFrame() && !stats.getKnockedDownOverOneFrame()) { //Turn off KnockedOutOneframe
stats.setKnockedDownOneFrame(false);
stats.setKnockedDownOverOneFrame(true);
}
if(!stats.isDead()) if(!stats.isDead())
{ {
if(iter->second->isDead()) if(iter->second->isDead())
@ -1050,8 +1049,7 @@ namespace MWMechanics
{ {
const MWWorld::Class &cls = MWWorld::Class::get(iter->first); const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
CreatureStats &stats = cls.getCreatureStats(iter->first); CreatureStats &stats = cls.getCreatureStats(iter->first);
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow)
if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdFollow)
{ {
MWMechanics::AiFollow* package = static_cast<MWMechanics::AiFollow*>(stats.getAiSequence().getActivePackage()); MWMechanics::AiFollow* package = static_cast<MWMechanics::AiFollow*>(stats.getAiSequence().getActivePackage());
if(package->getFollowedActor() == actor.getCellRef().mRefID) if(package->getFollowedActor() == actor.getCellRef().mRefID)
@ -1072,8 +1070,7 @@ namespace MWMechanics
{ {
const MWWorld::Class &cls = MWWorld::Class::get(*iter); const MWWorld::Class &cls = MWWorld::Class::get(*iter);
CreatureStats &stats = cls.getCreatureStats(*iter); CreatureStats &stats = cls.getCreatureStats(*iter);
if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
if(stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat)
{ {
MWMechanics::AiCombat* package = static_cast<MWMechanics::AiCombat*>(stats.getAiSequence().getActivePackage()); MWMechanics::AiCombat* package = static_cast<MWMechanics::AiCombat*>(stats.getAiSequence().getActivePackage());
if(package->getTargetId() == actor.getCellRef().mRefID) if(package->getTargetId() == actor.getCellRef().mRefID)

View file

@ -31,10 +31,52 @@ namespace
//chooses an attack depending on probability to avoid uniformity //chooses an attack depending on probability to avoid uniformity
void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement); void chooseBestAttack(const ESM::Weapon* weapon, MWMechanics::Movement &movement);
float getZAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
{
float len = (dirLen > 0.0f)? dirLen : dir.length();
return Ogre::Radian( Ogre::Math::ACos(dir.y / len) * sgn(Ogre::Math::ASin(dir.x / len)) ).valueDegrees();
}
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
{
float len = (dirLen > 0.0f)? dirLen : dir.length();
return Ogre::Radian(-Ogre::Math::ASin(dir.z / len)).valueDegrees();
}
const float PATHFIND_Z_REACH = 50.0f;
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
const float PATHFIND_CAUTION_DIST = 500.0f;
// distance after which actor (failed previously to shortcut) will try again
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f;
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
bool checkWayIsClear(const Ogre::Vector3& from, const Ogre::Vector3& to, float offset)
{
if((to - from).length() >= PATHFIND_CAUTION_DIST || abs(from.z - to.z) <= PATHFIND_Z_REACH)
{
Ogre::Vector3 dir = to - from;
dir.z = 0;
dir.normalise();
float verticalOffset = 200; // instead of '200' here we want the height of the actor
Ogre::Vector3 _from = from + dir*offset + Ogre::Vector3::UNIT_Z * verticalOffset;
// cast up-down ray and find height in world space of hit
float h = _from.z - MWBase::Environment::get().getWorld()->getDistToNearestRayHit(_from, -Ogre::Vector3::UNIT_Z, verticalOffset + PATHFIND_Z_REACH + 1);
if(abs(from.z - h) <= PATHFIND_Z_REACH)
return true;
}
return false;
}
} }
namespace MWMechanics namespace MWMechanics
{ {
static const float MAX_ATTACK_DURATION = 0.35f;
static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander static const float DOOR_CHECK_INTERVAL = 1.5f; // same as AiWander
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
@ -45,16 +87,16 @@ namespace MWMechanics
mTimerCombatMove(0), mTimerCombatMove(0),
mFollowTarget(false), mFollowTarget(false),
mReadyToAttack(false), mReadyToAttack(false),
mStrike(false), mAttack(false),
mCombatMove(false), mCombatMove(false),
mBackOffDoor(false),
mRotate(false),
mMovement(), mMovement(),
mForceNoShortcut(false),
mShortcutFailPos(),
mBackOffDoor(false),
mCell(NULL), mCell(NULL),
mDoorIter(actor.getCell()->get<ESM::Door>().mList.end()), mDoorIter(actor.getCell()->get<ESM::Door>().mList.end()),
mDoors(actor.getCell()->get<ESM::Door>()), mDoors(actor.getCell()->get<ESM::Door>()),
mDoorCheckDuration(0), mDoorCheckDuration(0)
mTargetAngle(0)
{ {
} }
@ -127,15 +169,21 @@ namespace MWMechanics
} }
actor.getClass().getMovementSettings(actor) = mMovement; actor.getClass().getMovementSettings(actor) = mMovement;
actor.getClass().getMovementSettings(actor).mRotation[0] = 0;
actor.getClass().getMovementSettings(actor).mRotation[2] = 0;
if (mRotate) if(mMovement.mRotation[2] != 0)
{ {
if (zTurn(actor, Ogre::Degree(mTargetAngle))) if(zTurn(actor, Ogre::Degree(mMovement.mRotation[2]))) mMovement.mRotation[2] = 0;
mRotate = false; }
if(mMovement.mRotation[0] != 0)
{
if(smoothTurn(actor, Ogre::Degree(mMovement.mRotation[0]), 0)) mMovement.mRotation[0] = 0;
} }
mTimerAttack -= duration; mTimerAttack -= duration;
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike); actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mAttack);
float tReaction = 0.25f; float tReaction = 0.25f;
if(mTimerReact < tReaction) if(mTimerReact < tReaction)
@ -156,16 +204,16 @@ namespace MWMechanics
//actual attacking logic //actual attacking logic
//TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f
float attackPeriod = 1.0f; float attacksPeriod = 1.0f;
if(mReadyToAttack) if(mReadyToAttack)
{ {
if(mTimerAttack <= -attackPeriod) if(mTimerAttack <= -attacksPeriod)
{ {
//TODO: should depend on time between 'start' to 'min attack' //TODO: should depend on time between 'start' to 'min attack'
//for better controlling of NPCs' attack strength. //for better controlling of NPCs' attack strength.
//Also it seems that this time is different for slash/thrust/chop //Also it seems that this time is different for slash/thrust/chop
mTimerAttack = 0.35f * static_cast<float>(rand())/RAND_MAX; mTimerAttack = MAX_ATTACK_DURATION * static_cast<float>(rand())/RAND_MAX;
mStrike = true; mAttack = true;
//say a provoking combat phrase //say a provoking combat phrase
if (actor.getClass().isNpc()) if (actor.getClass().isNpc())
@ -180,30 +228,31 @@ namespace MWMechanics
} }
} }
else if (mTimerAttack <= 0) else if (mTimerAttack <= 0)
mStrike = false; mAttack = false;
} }
else else
{ {
mTimerAttack = -attackPeriod; mTimerAttack = -attacksPeriod;
mStrike = false; mAttack = false;
} }
const MWWorld::Class &cls = actor.getClass(); const MWWorld::Class &actorCls = actor.getClass();
const ESM::Weapon *weapon = NULL; const ESM::Weapon *weapon = NULL;
MWMechanics::WeaponType weaptype; MWMechanics::WeaponType weaptype;
float weapRange, weapSpeed = 1.0f; float weapRange, weapSpeed = 1.0f;
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actorCls.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
if (actor.getClass().hasInventoryStore(actor)) // Get weapon characteristics
if (actorCls.hasInventoryStore(actor))
{ {
MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); MWMechanics::DrawState_ state = actorCls.getCreatureStats(actor).getDrawState();
if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing)
actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); actorCls.getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon);
//Get weapon speed and range //Get weapon speed and range
MWWorld::ContainerStoreIterator weaponSlot = MWWorld::ContainerStoreIterator weaponSlot =
MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype); MWMechanics::getActiveWeapon(actorCls.getCreatureStats(actor), actorCls.getInventoryStore(actor), &weaptype);
if (weaptype == WeapType_HandToHand) if (weaptype == WeapType_HandToHand)
{ {
@ -225,36 +274,27 @@ namespace MWMechanics
weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit) weapRange = 150; //TODO: use true attack range (the same problem in Creature::hit)
} }
ESM::Position pos = actor.getRefData().getPosition();
/* /*
* Some notes on meanings of variables: * Some notes on meanings of variables:
* *
* rangeMelee: * rangeAttack:
* *
* - Distance where attack using the actor's weapon is possible * - Distance where attack using the actor's weapon is possible:
* - longer for ranged weapons (obviously?) vs. melee weapons * longer for ranged weapons (obviously?) vs. melee weapons
* - Determined by weapon's reach parameter; hardcoded value
* for ranged weapon and for creatures
* - Once within this distance mFollowTarget is triggered * - Once within this distance mFollowTarget is triggered
* (TODO: check whether the follow logic still works for ranged
* weapons, since rangeCloseup is set to zero)
* - TODO: The variable name is confusing. It was ok when AiCombat only
* had melee weapons but now that ranged weapons are supported that is
* no longer the case. It should really be renamed to something
* like rangeStrike - alternatively, keep this name for melee
* weapons and use a different variable for tracking ranged weapon
* distance (rangeRanged maybe?)
* *
* rangeCloseup: * rangeFollow:
* *
* - Applies to melee weapons or hand to hand only (or creatures without * - Applies to melee weapons or hand to hand only (or creatures without
* weapons) * weapons)
* - Distance a little further away from the actor's weapon strike * - Distance a little further away than the actor's weapon reach
* i.e. rangeCloseup > rangeMelee for melee weapons * i.e. rangeFollow > rangeAttack for melee weapons
* (the variable names make this simple concept counter-intuitive, * - Hardcoded value (0 for ranged weapons)
* something like rangeMelee > rangeStrike may be better)
* - Once the target gets beyond this distance mFollowTarget is cleared * - Once the target gets beyond this distance mFollowTarget is cleared
* and a path to the target needs to be found * and a path to the target needs to be found
* - TODO: Possibly rename this variable to rangeMelee or even rangeFollow
* *
* mFollowTarget: * mFollowTarget:
* *
@ -263,58 +303,72 @@ namespace MWMechanics
* available, since the default path without pathgrids is direct to * available, since the default path without pathgrids is direct to
* target even if LOS is not achieved) * target even if LOS is not achieved)
*/ */
float rangeMelee;
float rangeCloseUp; float rangeAttack;
float rangeFollow;
bool distantCombat = false; bool distantCombat = false;
if (weaptype==WeapType_BowAndArrow || weaptype==WeapType_Crossbow || weaptype==WeapType_Thrown) if (weaptype == WeapType_BowAndArrow || weaptype == WeapType_Crossbow || weaptype == WeapType_Thrown)
{ {
rangeMelee = 1000; // TODO: should depend on archer skill rangeAttack = 1000; // TODO: should depend on archer skill
rangeCloseUp = 0; //doesn't needed when attacking from distance rangeFollow = 0; // not needed in ranged combat
distantCombat = true; distantCombat = true;
} }
else else
{ {
rangeMelee = weapRange; rangeAttack = weapRange;
rangeCloseUp = 300; rangeFollow = 300;
} }
Ogre::Vector3 vStart(pos.pos[0], pos.pos[1], pos.pos[2]); ESM::Position pos = actor.getRefData().getPosition();
ESM::Position targetPos = mTarget.getRefData().getPosition(); Ogre::Vector3 vActorPos(pos.pos);
Ogre::Vector3 vDest(targetPos.pos[0], targetPos.pos[1], targetPos.pos[2]); Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos);
Ogre::Vector3 vDir = vDest - vStart; Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
float distBetween = vDir.length();
bool isStuck = false;
float speed = 0.0f;
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = actorCls.getSpeed(actor)) / 10.0f)
isStuck = true;
mLastPos = pos;
// check if actor can move along z-axis
bool canMoveByZ = (actorCls.canSwim(actor) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|| MWBase::Environment::get().getWorld()->isFlying(actor);
// determine vertical angle to target
// if actor can move along z-axis it will control movement dir
// if can't - it will control correct aiming
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget);
vDirToTarget.z = 0;
float distToTarget = vDirToTarget.length();
// (within strike dist) || (not quite strike dist while following) // (within strike dist) || (not quite strike dist while following)
if(distBetween < rangeMelee || (distBetween <= rangeCloseUp && mFollowTarget) ) if(distToTarget < rangeAttack || (distToTarget <= rangeFollow && mFollowTarget && !isStuck) )
{ {
//Melee and Close-up combat //Melee and Close-up combat
vDir.z = 0; mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
float dirLen = vDir.length();
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / dirLen) * sgn(Ogre::Math::ASin(vDir.x / dirLen)) ).valueDegrees();
mRotate = true;
//bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
// (not quite strike dist while following) // (not quite strike dist while following)
if (mFollowTarget && distBetween > rangeMelee) if (mFollowTarget && distToTarget > rangeAttack)
{ {
//Close-up combat: just run up on target //Close-up combat: just run up on target
mMovement.mPosition[1] = 1; mMovement.mPosition[1] = 1;
} }
else // (within strike dist) else // (within strike dist)
{ {
//Melee: stop running and attack
mMovement.mPosition[1] = 0; mMovement.mPosition[1] = 0;
// When attacking with a weapon, choose between slash, thrust or chop // set slash/thrust/chop attack
if (actor.getClass().hasInventoryStore(actor)) if (mAttack && !distantCombat) chooseBestAttack(weapon, mMovement);
chooseBestAttack(weapon, mMovement);
if(mMovement.mPosition[0] || mMovement.mPosition[1]) if(mMovement.mPosition[0] || mMovement.mPosition[1])
{ {
mTimerCombatMove = 0.1f + 0.1f * static_cast<float>(rand())/RAND_MAX; mTimerCombatMove = 0.1f + 0.1f * static_cast<float>(rand())/RAND_MAX;
mCombatMove = true; mCombatMove = true;
} }
else if(actor.getClass().isNpc() && (!distantCombat || (distantCombat && rangeMelee/5))) // only NPCs are smart enough to use dodge movements
else if(actorCls.isNpc() && (!distantCombat || (distantCombat && distToTarget < rangeAttack/2)))
{ {
//apply sideway movement (kind of dodging) with some probability //apply sideway movement (kind of dodging) with some probability
if(static_cast<float>(rand())/RAND_MAX < 0.25) if(static_cast<float>(rand())/RAND_MAX < 0.25)
@ -325,7 +379,7 @@ namespace MWMechanics
} }
} }
if(distantCombat && distBetween < rangeMelee/4) if(distantCombat && distToTarget < rangeAttack/4)
{ {
mMovement.mPosition[1] = -1; mMovement.mPosition[1] = -1;
} }
@ -335,56 +389,105 @@ namespace MWMechanics
mFollowTarget = true; mFollowTarget = true;
} }
} }
else else // remote pathfinding
{ {
//target is at far distance: build path to target bool preferShortcut = false;
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
if(mReadyToAttack) isStuck = false;
// check if shortcut is available
if(!isStuck
&& (!mForceNoShortcut
|| (Ogre::Vector3(mShortcutFailPos.pos) - vActorPos).length() >= PATHFIND_SHORTCUT_RETRY_DIST)
&& inLOS)
{
if(speed == 0.0f) speed = actorCls.getSpeed(actor);
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2; // *2 - for reliability
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
}
// don't use pathgrid when actor can move in 3 dimensions
if(canMoveByZ) preferShortcut = true;
if(preferShortcut)
{
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
mForceNoShortcut = false;
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
mPathFinder.clearPath();
}
else // if shortcut failed stick to path grid
{
if(!isStuck && mShortcutFailPos.pos[0] == 0.0f && mShortcutFailPos.pos[1] == 0.0f && mShortcutFailPos.pos[2] == 0.0f)
{
mForceNoShortcut = true;
mShortcutFailPos = pos;
}
mFollowTarget = false; mFollowTarget = false;
buildNewPath(actor); //may fail to build a path, check before use buildNewPath(actor); //may fail to build a path, check before use
//delete visited path node //delete visited path node
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
//if no new path leave mTargetAngle unchanged // This works on the borders between the path grid and areas with no waypoints.
if(!mPathFinder.getPath().empty()) if(inLOS && mPathFinder.getPath().size() > 1)
{ {
//try shortcut // get point just before target
if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) std::list<ESM::Pathgrid::Point>::const_iterator pntIter = --mPathFinder.getPath().end();
mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); --pntIter;
Ogre::Vector3 vBeforeTarget = Ogre::Vector3(pntIter->mX, pntIter->mY, pntIter->mZ);
// if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
if(distToTarget <= (vTargetPos - vBeforeTarget).length())
{
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
preferShortcut = true;
}
}
// if there is no new path, then go straight on target
if(!preferShortcut)
{
if(!mPathFinder.getPath().empty())
mMovement.mRotation[2] = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
else else
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
mRotate = true; }
} }
mMovement.mPosition[1] = 1; mMovement.mPosition[1] = 1;
mReadyToAttack = false; mReadyToAttack = false;
} }
if(distBetween > rangeMelee) if(distToTarget > rangeAttack && !distantCombat)
{ {
//special run attack; it shouldn't affect melee combat tactics //special run attack; it shouldn't affect melee combat tactics
if(actor.getClass().getMovementSettings(actor).mPosition[1] == 1) if(actorCls.getMovementSettings(actor).mPosition[1] == 1)
{ {
//check if actor can overcome the distance = distToTarget - attackerWeapRange //check if actor can overcome the distance = distToTarget - attackerWeapRange
//less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing)
//then start attacking //then start attacking
float speed1 = cls.getSpeed(actor); float speed1 = actorCls.getSpeed(actor);
float speed2 = mTarget.getClass().getSpeed(mTarget); float speed2 = mTarget.getClass().getSpeed(mTarget);
if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0
&& mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
speed2 = 0; speed2 = 0;
float s1 = distBetween - weapRange; float s1 = distToTarget - weapRange;
float t = s1/speed1; float t = s1/speed1;
float s2 = speed2 * t; float s2 = speed2 * t;
float t_swing = 0.17f/weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags float t_swing = (MAX_ATTACK_DURATION/2) / weapSpeed;//instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
if (t + s2/speed1 <= t_swing) if (t + s2/speed1 <= t_swing)
{ {
mReadyToAttack = true; mReadyToAttack = true;
if(mTimerAttack <= -attackPeriod) if(mTimerAttack <= -attacksPeriod)
{ {
mTimerAttack = 0.3f*static_cast<float>(rand())/RAND_MAX; mTimerAttack = MAX_ATTACK_DURATION * static_cast<float>(rand())/RAND_MAX;
mStrike = true; mAttack = true;
} }
} }
} }
@ -394,7 +497,7 @@ namespace MWMechanics
// coded at 250ms or 1/4 second // coded at 250ms or 1/4 second
// //
// TODO: Add a parameter to vary DURATION_SAME_SPOT? // TODO: Add a parameter to vary DURATION_SAME_SPOT?
if((distBetween > rangeMelee || mFollowTarget) && if((distToTarget > rangeAttack || mFollowTarget) &&
mObstacleCheck.check(actor, tReaction)) // check if evasive action needed mObstacleCheck.check(actor, tReaction)) // check if evasive action needed
{ {
// first check if we're walking into a door // first check if we're walking into a door
@ -406,12 +509,11 @@ namespace MWMechanics
// Check all the doors in this cell // Check all the doors in this cell
mDoors = cell->get<ESM::Door>(); // update mDoors = cell->get<ESM::Door>(); // update
mDoorIter = mDoors.mList.begin(); mDoorIter = mDoors.mList.begin();
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
for (; mDoorIter != mDoors.mList.end(); ++mDoorIter) for (; mDoorIter != mDoors.mList.end(); ++mDoorIter)
{ {
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter; MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility
if(actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr &&
ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening
{ {
//std::cout<<"closed door id \""<<ref.mRef.mRefID<<"\""<<std::endl; //std::cout<<"closed door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
@ -441,11 +543,10 @@ namespace MWMechanics
} }
MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter; MWWorld::LiveCellRef<ESM::Door>& ref = *mDoorIter;
Ogre::Vector3 actorPos(actor.getRefData().getPosition().pos);
float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility
// TODO: add reaction to checking open doors // TODO: add reaction to checking open doors
if(mBackOffDoor && if(mBackOffDoor &&
actorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr)
{ {
mMovement.mPosition[1] = -0.2; // back off, but slowly mMovement.mPosition[1] = -0.2; // back off, but slowly
} }
@ -458,43 +559,34 @@ namespace MWMechanics
//std::cout<<"open door id \""<<ref.mRef.mRefID<<"\""<<std::endl; //std::cout<<"open door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
mMovement.mPosition[1] = 1; mMovement.mPosition[1] = 1;
} }
else // these lines break ranged combat distance keeping
{ //else
mMovement.mPosition[1] = 1; // FIXME: oscillation? //{
} // mMovement.mPosition[1] = 1; // FIXME: oscillation?
//}
actor.getClass().getMovementSettings(actor) = mMovement;
return false; return false;
} }
void AiCombat::buildNewPath(const MWWorld::Ptr& actor) void AiCombat::buildNewPath(const MWWorld::Ptr& actor)
{ {
//Construct path to target Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos);
ESM::Pathgrid::Point dest;
dest.mX = mTarget.getRefData().getPosition().pos[0]; float dist;
dest.mY = mTarget.getRefData().getPosition().pos[1];
dest.mZ = mTarget.getRefData().getPosition().pos[2];
Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ);
float dist = -1; //hack to indicate first time, to construct a new path
if(!mPathFinder.getPath().empty()) if(!mPathFinder.getPath().empty())
{ {
ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back();
Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ);
dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); dist = (newPathTarget - currPathTarget).length();
} }
else dist = 1e+38F; // necessarily construct a new path
float targetPosThreshold; float targetPosThreshold = (actor.getCell()->getCell()->isExterior())? 300 : 100;
bool isOutside = actor.getCell()->getCell()->isExterior();
if (isOutside)
targetPosThreshold = 300;
else
targetPosThreshold = 100;
if((dist < 0) || (dist > targetPosThreshold)) //construct new path only if target has moved away more than on [targetPosThreshold]
if(dist > targetPosThreshold)
{ {
//construct new path only if target has moved away more than on <targetPosThreshold>
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
ESM::Pathgrid::Point start; ESM::Pathgrid::Point start;
@ -502,17 +594,18 @@ namespace MWMechanics
start.mY = pos.pos[1]; start.mY = pos.pos[1];
start.mZ = pos.pos[2]; start.mZ = pos.pos[2];
ESM::Pathgrid::Point dest;
dest.mX = newPathTarget.x;
dest.mY = newPathTarget.y;
dest.mZ = newPathTarget.z;
if(!mPathFinder.isPathConstructed()) if(!mPathFinder.isPathConstructed())
mPathFinder.buildPath(start, dest, actor.getCell(), isOutside); mPathFinder.buildPath(start, dest, actor.getCell(), false);
else else
{ {
PathFinder newPathFinder; PathFinder newPathFinder;
newPathFinder.buildPath(start, dest, actor.getCell(), isOutside); newPathFinder.buildPath(start, dest, actor.getCell(), false);
//TO EXPLORE:
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
//not the actual path length. Here we should know if the new path is actually more effective.
//if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
if(!mPathFinder.getPath().empty()) if(!mPathFinder.getPath().empty())
{ {
newPathFinder.syncStart(mPathFinder.getPath()); newPathFinder.syncStart(mPathFinder.getPath());

View file

@ -39,17 +39,16 @@ namespace MWMechanics
// when mCombatMove is true // when mCombatMove is true
float mTimerCombatMove; float mTimerCombatMove;
// the z rotation angle (degrees) we want to reach
// used every frame when mRotate is true
float mTargetAngle;
// AiCombat states // AiCombat states
bool mReadyToAttack, mStrike; bool mReadyToAttack, mAttack;
bool mFollowTarget; bool mFollowTarget;
bool mCombatMove; bool mCombatMove;
bool mBackOffDoor; bool mBackOffDoor;
bool mRotate;
bool mForceNoShortcut;
ESM::Position mShortcutFailPos;
ESM::Position mLastPos;
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
MWWorld::Ptr mTarget; MWWorld::Ptr mTarget;

View file

@ -20,7 +20,7 @@ namespace MWMechanics
TypeIdFollow = 3, TypeIdFollow = 3,
TypeIdActivate = 4, TypeIdActivate = 4,
TypeIdCombat = 5, TypeIdCombat = 5,
TypeIdPersue = 6 TypeIdPursue = 6
}; };
virtual ~AiPackage(); virtual ~AiPackage();

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