mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-29 22:36:43 +00:00
Add OpenMW commits up to 20 Oct 2019
# Conflicts: # apps/openmw/mwgui/recharge.cpp # apps/openmw/mwrender/globalmap.cpp # apps/openmw/mwrender/globalmap.hpp # apps/openmw/mwworld/inventorystore.cpp
This commit is contained in:
commit
bde9f7b817
53 changed files with 929 additions and 496 deletions
|
@ -2,6 +2,7 @@
|
||||||
------
|
------
|
||||||
|
|
||||||
Bug #1515: Opening console masks dialogue, inventory menu
|
Bug #1515: Opening console masks dialogue, inventory menu
|
||||||
|
Bug #1933: Actors can have few stocks of the same item
|
||||||
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
|
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
|
||||||
Bug #2969: Scripted items can stack
|
Bug #2969: Scripted items can stack
|
||||||
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
|
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
|
||||||
|
@ -18,8 +19,10 @@
|
||||||
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation
|
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation
|
||||||
Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled
|
Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled
|
||||||
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
|
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
|
||||||
|
Bug #4077: Enchanted items are not recharged if they are not in the player's inventory
|
||||||
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
|
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
|
||||||
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
|
||||||
|
Bug #4262: Rain settings are hardcoded
|
||||||
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
||||||
Bug #4276: Resizing character window differs from vanilla
|
Bug #4276: Resizing character window differs from vanilla
|
||||||
Bug #4329: Removed birthsign abilities are restored after reloading the save
|
Bug #4329: Removed birthsign abilities are restored after reloading the save
|
||||||
|
@ -27,8 +30,10 @@
|
||||||
Bug #4383: Bow model obscures crosshair when arrow is drawn
|
Bug #4383: Bow model obscures crosshair when arrow is drawn
|
||||||
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons
|
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons
|
||||||
Bug #4411: Reloading a saved game while falling prevents damage in some cases
|
Bug #4411: Reloading a saved game while falling prevents damage in some cases
|
||||||
|
Bug #4449: Value returned by GetWindSpeed is incorrect
|
||||||
Bug #4456: AiActivate should not be cancelled after target activation
|
Bug #4456: AiActivate should not be cancelled after target activation
|
||||||
Bug #4540: Rain delay when exiting water
|
Bug #4540: Rain delay when exiting water
|
||||||
|
Bug #4594: Actors without AI packages don't use Hello dialogue
|
||||||
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
||||||
Bug #4639: Black screen after completing first mages guild mission + training
|
Bug #4639: Black screen after completing first mages guild mission + training
|
||||||
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
||||||
|
@ -77,6 +82,7 @@
|
||||||
Bug #4888: Global variable stray explicit reference calls break script compilation
|
Bug #4888: Global variable stray explicit reference calls break script compilation
|
||||||
Bug #4896: Title screen music doesn't loop
|
Bug #4896: Title screen music doesn't loop
|
||||||
Bug #4902: Using scrollbars in settings causes resolution to change
|
Bug #4902: Using scrollbars in settings causes resolution to change
|
||||||
|
Bug #4904: Editor: Texture painting with duplicate of a base-version texture
|
||||||
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
|
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
|
||||||
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.
|
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.
|
||||||
Bug #4918: Abilities don't play looping VFX when they're initially applied
|
Bug #4918: Abilities don't play looping VFX when they're initially applied
|
||||||
|
@ -155,6 +161,8 @@
|
||||||
Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn
|
Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn
|
||||||
Bug #5175: Random script function returns an integer value
|
Bug #5175: Random script function returns an integer value
|
||||||
Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
|
Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
|
||||||
|
Bug #5182: OnPCEquip doesn't trigger on skipped beast race attempts to equip something not equippable by beasts
|
||||||
|
Bug #5186: Equipped item enchantments don't affect creatures
|
||||||
Feature #1774: Handle AvoidNode
|
Feature #1774: Handle AvoidNode
|
||||||
Feature #2229: Improve pathfinding AI
|
Feature #2229: Improve pathfinding AI
|
||||||
Feature #3025: Analogue gamepad movement controls
|
Feature #3025: Analogue gamepad movement controls
|
||||||
|
|
|
@ -101,6 +101,7 @@ Editor Bug Fixes:
|
||||||
- Colour fields in interior-cell records now also use the colour picker widget (#4745)
|
- Colour fields in interior-cell records now also use the colour picker widget (#4745)
|
||||||
- Cloned, added, or moved instances no longer disappear at load-time (#4748)
|
- Cloned, added, or moved instances no longer disappear at load-time (#4748)
|
||||||
- "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757)
|
- "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757)
|
||||||
|
- Terrain texture editing for plugins now correctly handles drags from base file (#4904)
|
||||||
- Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911)
|
- Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911)
|
||||||
- Minimap doesn't get corrupted, when editing new omwgame (#5177)
|
- Minimap doesn't get corrupted, when editing new omwgame (#5177)
|
||||||
|
|
||||||
|
|
|
@ -41,9 +41,9 @@
|
||||||
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
|
CSVRender::TerrainTextureMode::TerrainTextureMode (WorldspaceWidget *worldspaceWidget, osg::Group* parentNode, QWidget *parent)
|
||||||
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
|
: EditMode (worldspaceWidget, QIcon {":scenetoolbar/editing-terrain-texture"}, Mask_Terrain | Mask_Reference, "Terrain texture editing", parent),
|
||||||
mBrushTexture("L0#0"),
|
mBrushTexture("L0#0"),
|
||||||
mBrushSize(0),
|
mBrushSize(1),
|
||||||
mBrushShape(0),
|
mBrushShape(0),
|
||||||
mTextureBrushScenetool(0),
|
mTextureBrushScenetool(nullptr),
|
||||||
mDragMode(InteractionType_None),
|
mDragMode(InteractionType_None),
|
||||||
mParentNode(parentNode),
|
mParentNode(parentNode),
|
||||||
mIsEditing(false)
|
mIsEditing(false)
|
||||||
|
@ -82,7 +82,7 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar)
|
||||||
{
|
{
|
||||||
toolbar->removeTool (mTextureBrushScenetool);
|
toolbar->removeTool (mTextureBrushScenetool);
|
||||||
delete mTextureBrushScenetool;
|
delete mTextureBrushScenetool;
|
||||||
mTextureBrushScenetool = 0;
|
mTextureBrushScenetool = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTerrainTextureSelection)
|
if (mTerrainTextureSelection)
|
||||||
|
|
|
@ -51,36 +51,37 @@ namespace CSVRender
|
||||||
/// \brief Editmode for terrain texture grid
|
/// \brief Editmode for terrain texture grid
|
||||||
TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
|
TerrainTextureMode(WorldspaceWidget*, osg::Group* parentNode, QWidget* parent = nullptr);
|
||||||
|
|
||||||
void primaryOpenPressed (const WorldspaceHitResult& hit);
|
void primaryOpenPressed (const WorldspaceHitResult& hit) final;
|
||||||
|
|
||||||
/// \brief Create single command for one-click texture editing
|
/// \brief Create single command for one-click texture editing
|
||||||
void primaryEditPressed (const WorldspaceHitResult& hit);
|
void primaryEditPressed (const WorldspaceHitResult& hit) final;
|
||||||
|
|
||||||
/// \brief Open brush settings window
|
/// \brief Open brush settings window
|
||||||
void primarySelectPressed(const WorldspaceHitResult&);
|
void primarySelectPressed(const WorldspaceHitResult&) final;
|
||||||
|
|
||||||
void secondarySelectPressed(const WorldspaceHitResult&);
|
void secondarySelectPressed(const WorldspaceHitResult&) final;
|
||||||
|
|
||||||
void activate(CSVWidget::SceneToolbar*);
|
void activate(CSVWidget::SceneToolbar*) final;
|
||||||
void deactivate(CSVWidget::SceneToolbar*);
|
void deactivate(CSVWidget::SceneToolbar*) final;
|
||||||
|
|
||||||
/// \brief Start texture editing command macro
|
/// \brief Start texture editing command macro
|
||||||
virtual bool primaryEditStartDrag (const QPoint& pos);
|
bool primaryEditStartDrag (const QPoint& pos) final;
|
||||||
|
|
||||||
virtual bool secondaryEditStartDrag (const QPoint& pos);
|
bool secondaryEditStartDrag (const QPoint& pos) final;
|
||||||
virtual bool primarySelectStartDrag (const QPoint& pos);
|
bool primarySelectStartDrag (const QPoint& pos) final;
|
||||||
virtual bool secondarySelectStartDrag (const QPoint& pos);
|
bool secondarySelectStartDrag (const QPoint& pos) final;
|
||||||
|
|
||||||
/// \brief Handle texture edit behavior during dragging
|
/// \brief Handle texture edit behavior during dragging
|
||||||
virtual void drag (const QPoint& pos, int diffX, int diffY, double speedFactor);
|
void drag (const QPoint& pos, int diffX, int diffY, double speedFactor) final;
|
||||||
|
|
||||||
/// \brief End texture editing command macro
|
/// \brief End texture editing command macro
|
||||||
virtual void dragCompleted(const QPoint& pos);
|
void dragCompleted(const QPoint& pos) final;
|
||||||
|
|
||||||
virtual void dragAborted();
|
void dragAborted() final;
|
||||||
virtual void dragWheel (int diff, double speedFactor);
|
void dragWheel (int diff, double speedFactor) final;
|
||||||
virtual void dragMoveEvent (QDragMoveEvent *event);
|
void dragMoveEvent (QDragMoveEvent *event) final;
|
||||||
|
|
||||||
|
private:
|
||||||
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
|
/// \brief Handle brush mechanics, maths regarding worldspace hit etc.
|
||||||
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
|
void editTerrainTextureGrid (const WorldspaceHitResult& hit);
|
||||||
|
|
||||||
|
@ -100,7 +101,6 @@ namespace CSVRender
|
||||||
/// \brief Create new cell and land if needed
|
/// \brief Create new cell and land if needed
|
||||||
bool allowLandTextureEditing(std::string textureFileName);
|
bool allowLandTextureEditing(std::string textureFileName);
|
||||||
|
|
||||||
private:
|
|
||||||
std::string mCellId;
|
std::string mCellId;
|
||||||
std::string mBrushTexture;
|
std::string mBrushTexture;
|
||||||
int mBrushSize;
|
int mBrushSize;
|
||||||
|
@ -113,7 +113,6 @@ namespace CSVRender
|
||||||
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
|
std::unique_ptr<TerrainSelection> mTerrainTextureSelection;
|
||||||
|
|
||||||
const int cellSize {ESM::Land::REAL_SIZE};
|
const int cellSize {ESM::Land::REAL_SIZE};
|
||||||
const int landSize {ESM::Land::LAND_SIZE};
|
|
||||||
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
|
const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE};
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -33,19 +33,19 @@
|
||||||
|
|
||||||
|
|
||||||
CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent)
|
CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *parent)
|
||||||
: QGroupBox(title, parent)
|
: QGroupBox(title, parent),
|
||||||
|
mLayoutSliderSize(new QHBoxLayout),
|
||||||
|
mBrushSizeSlider(new QSlider(Qt::Horizontal)),
|
||||||
|
mBrushSizeSpinBox(new QSpinBox)
|
||||||
{
|
{
|
||||||
mBrushSizeSlider = new QSlider(Qt::Horizontal);
|
|
||||||
mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);
|
mBrushSizeSlider->setTickPosition(QSlider::TicksBothSides);
|
||||||
mBrushSizeSlider->setTickInterval(10);
|
mBrushSizeSlider->setTickInterval(10);
|
||||||
mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
|
mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
|
||||||
mBrushSizeSlider->setSingleStep(1);
|
mBrushSizeSlider->setSingleStep(1);
|
||||||
|
|
||||||
mBrushSizeSpinBox = new QSpinBox;
|
|
||||||
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
|
mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt());
|
||||||
mBrushSizeSpinBox->setSingleStep(1);
|
mBrushSizeSpinBox->setSingleStep(1);
|
||||||
|
|
||||||
mLayoutSliderSize = new QHBoxLayout;
|
|
||||||
mLayoutSliderSize->addWidget(mBrushSizeSlider);
|
mLayoutSliderSize->addWidget(mBrushSizeSlider);
|
||||||
mLayoutSliderSize->addWidget(mBrushSizeSpinBox);
|
mLayoutSliderSize->addWidget(mBrushSizeSpinBox);
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *p
|
||||||
CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)
|
CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent)
|
||||||
: QFrame(parent, Qt::Popup),
|
: QFrame(parent, Qt::Popup),
|
||||||
mBrushShape(0),
|
mBrushShape(0),
|
||||||
mBrushSize(0),
|
mBrushSize(1),
|
||||||
mBrushTexture("L0#0"),
|
mBrushTexture("L0#0"),
|
||||||
mDocument(document)
|
mDocument(document)
|
||||||
{
|
{
|
||||||
|
@ -142,60 +142,61 @@ void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton *
|
||||||
|
|
||||||
void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
|
void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
|
||||||
{
|
{
|
||||||
mBrushTexture = brushTexture;
|
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
|
||||||
|
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
|
||||||
|
QUndoStack& undoStack = mDocument.getUndoStack();
|
||||||
|
|
||||||
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
|
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
|
||||||
|
|
||||||
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
|
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
|
||||||
int columnModification = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Modification);
|
|
||||||
int index = landtexturesCollection.searchId(mBrushTexture);
|
|
||||||
|
|
||||||
// Check if texture exists in current plugin
|
int index = 0;
|
||||||
if(landtexturesCollection.getData(index, columnModification).value<int>() == 0)
|
int pluginInDragged = 0;
|
||||||
|
CSMWorld::LandTexture::parseUniqueRecordId(brushTexture, pluginInDragged, index);
|
||||||
|
std::string newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, index);
|
||||||
|
int rowInBase = landtexturesCollection.searchId(brushTexture);
|
||||||
|
int rowInNew = landtexturesCollection.searchId(newBrushTextureId);
|
||||||
|
|
||||||
|
// Check if texture exists in current plugin, and clone if id found in base, otherwise reindex the texture
|
||||||
|
// TO-DO: Handle case when texture is not found in neither base or plugin properly (finding new index is not enough)
|
||||||
|
// TO-DO: Handle conflicting plugins properly
|
||||||
|
if (rowInNew == -1)
|
||||||
{
|
{
|
||||||
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&> (
|
if (rowInBase == -1)
|
||||||
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
|
|
||||||
|
|
||||||
QUndoStack& undoStack = mDocument.getUndoStack();
|
|
||||||
|
|
||||||
QVariant textureFileNameVariant;
|
|
||||||
textureFileNameVariant.setValue(landtexturesCollection.getData(index, landTextureFilename).value<QString>());
|
|
||||||
|
|
||||||
std::size_t hashlocation = mBrushTexture.find("#");
|
|
||||||
std::string mBrushTexturePlugin = "L0#" + mBrushTexture.substr (hashlocation+1);
|
|
||||||
int indexPlugin = landtexturesCollection.searchId(mBrushTexturePlugin);
|
|
||||||
|
|
||||||
// Reindex texture if needed
|
|
||||||
if (indexPlugin != -1 && !landtexturesCollection.getRecord(indexPlugin).isDeleted())
|
|
||||||
{
|
{
|
||||||
int counter=0;
|
int counter=0;
|
||||||
bool freeIndexFound = false;
|
bool freeIndexFound = false;
|
||||||
|
const int maxCounter = std::numeric_limits<uint16_t>::max() - 1;
|
||||||
do {
|
do {
|
||||||
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
|
newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
|
||||||
mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter);
|
if (landtexturesCollection.searchId(brushTexture) != -1 &&
|
||||||
if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter;
|
landtexturesCollection.getRecord(brushTexture).isDeleted() == 0 &&
|
||||||
|
landtexturesCollection.searchId(newBrushTextureId) != -1 &&
|
||||||
|
landtexturesCollection.getRecord(newBrushTextureId).isDeleted() == 0)
|
||||||
|
counter = (counter + 1) % maxCounter;
|
||||||
else freeIndexFound = true;
|
else freeIndexFound = true;
|
||||||
} while (freeIndexFound == false);
|
} while (freeIndexFound == false || counter < maxCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
undoStack.beginMacro ("Add land texture record");
|
undoStack.beginMacro ("Add land texture record");
|
||||||
undoStack.push (new CSMWorld::CloneCommand (ltexTable, mBrushTexture, mBrushTexturePlugin, CSMWorld::UniversalId::Type_LandTexture));
|
undoStack.push (new CSMWorld::CloneCommand (ltexTable, brushTexture, newBrushTextureId, CSMWorld::UniversalId::Type_LandTexture));
|
||||||
undoStack.endMacro();
|
undoStack.endMacro();
|
||||||
mBrushTexture = mBrushTexturePlugin;
|
|
||||||
emit passTextureId(mBrushTexture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
|
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
|
||||||
{
|
{
|
||||||
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
|
mBrushTextureLabel = "Selected texture: " + newBrushTextureId + " ";
|
||||||
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
|
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel) + landtexturesCollection.getData(index, landTextureFilename).value<QString>());
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
|
newBrushTextureId = "";
|
||||||
mBrushTextureLabel = "No selected texture or invalid texture";
|
mBrushTextureLabel = "No selected texture or invalid texture";
|
||||||
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
emit passBrushShape(mBrushShape); // update icon
|
mBrushTexture = newBrushTextureId;
|
||||||
|
|
||||||
|
emit passTextureId(mBrushTexture);
|
||||||
|
emit passBrushShape(mBrushShape); // updates the icon tooltip
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)
|
void CSVWidget::TextureBrushWindow::setBrushSize(int brushSize)
|
||||||
|
|
|
@ -81,7 +81,7 @@ add_openmw_dir (mwclass
|
||||||
add_openmw_dir (mwmechanics
|
add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
mechanicsmanagerimp stat creaturestats magiceffects movement actorutil
|
||||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
|
||||||
aicast aiescort aiface aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||||
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
|
||||||
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype
|
character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype
|
||||||
)
|
)
|
||||||
|
|
|
@ -215,6 +215,7 @@ bool OMW::Engine::frame(float frametime)
|
||||||
{
|
{
|
||||||
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||||
mEnvironment.getWorld()->advanceTime(hours, true);
|
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||||
|
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
||||||
|
|
|
@ -73,8 +73,6 @@ namespace MWBase
|
||||||
/// \param paused In game type does not currently advance (this usually means some GUI
|
/// \param paused In game type does not currently advance (this usually means some GUI
|
||||||
/// component is up).
|
/// component is up).
|
||||||
|
|
||||||
virtual void advanceTime (float duration) = 0;
|
|
||||||
|
|
||||||
virtual void setPlayerName (const std::string& name) = 0;
|
virtual void setPlayerName (const std::string& name) = 0;
|
||||||
///< Set player name.
|
///< Set player name.
|
||||||
|
|
||||||
|
|
|
@ -776,6 +776,7 @@ namespace MWBase
|
||||||
virtual bool isPlayerInJail() const = 0;
|
virtual bool isPlayerInJail() const = 0;
|
||||||
|
|
||||||
virtual void rest(double hours) = 0;
|
virtual void rest(double hours) = 0;
|
||||||
|
virtual void rechargeItems(double duration, bool activeOnly) = 0;
|
||||||
|
|
||||||
virtual void setPlayerTraveling(bool traveling) = 0;
|
virtual void setPlayerTraveling(bool traveling) = 0;
|
||||||
virtual bool isPlayerTraveling() const = 0;
|
virtual bool isPlayerTraveling() const = 0;
|
||||||
|
|
|
@ -25,12 +25,12 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
|
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
if (hasProfit(mActor))
|
if (hasProfit(mActor))
|
||||||
modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);
|
modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count);
|
||||||
|
|
||||||
return InventoryItemModel::copyItem(item, count, setNewOwner);
|
return InventoryItemModel::copyItem(item, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
|
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace MWGui
|
||||||
public:
|
public:
|
||||||
CompanionItemModel (const MWWorld::Ptr& actor);
|
CompanionItemModel (const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner);
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count);
|
||||||
virtual void removeItem (const ItemStack& item, size_t count);
|
virtual void removeItem (const ItemStack& item, size_t count);
|
||||||
|
|
||||||
bool hasProfit(const MWWorld::Ptr& actor);
|
bool hasProfit(const MWWorld::Ptr& actor);
|
||||||
|
|
|
@ -91,7 +91,7 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
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))
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace MWGui
|
||||||
virtual ModelIndex getIndex (ItemStack item);
|
virtual ModelIndex getIndex (ItemStack item);
|
||||||
virtual size_t getItemCount();
|
virtual size_t getItemCount();
|
||||||
|
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count);
|
||||||
virtual void removeItem (const ItemStack& item, size_t count);
|
virtual void removeItem (const ItemStack& item, size_t count);
|
||||||
|
|
||||||
virtual void update();
|
virtual void update();
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace MWGui
|
||||||
public:
|
public:
|
||||||
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
|
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
|
||||||
virtual ~WorldItemModel() {}
|
virtual ~WorldItemModel() {}
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
@ -61,8 +61,7 @@ namespace MWGui
|
||||||
dropped = world->placeObject(item.mBase, mLeft, mTop, count);
|
dropped = world->placeObject(item.mBase, mLeft, mTop, count);
|
||||||
else
|
else
|
||||||
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
|
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
|
||||||
if (setNewOwner)
|
dropped.getCellRef().setOwner("");
|
||||||
dropped.getCellRef().setOwner("");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
|
|
@ -46,11 +46,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
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!");
|
||||||
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
|
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
|
void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
|
||||||
|
@ -88,7 +88,7 @@ MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, I
|
||||||
if (item.mFlags & ItemStack::Flag_Bound)
|
if (item.mFlags & ItemStack::Flag_Bound)
|
||||||
return MWWorld::Ptr();
|
return MWWorld::Ptr();
|
||||||
|
|
||||||
MWWorld::Ptr ret = otherModel->copyItem(item, count, false);
|
MWWorld::Ptr ret = otherModel->copyItem(item, count);
|
||||||
removeItem(item, count);
|
removeItem(item, count);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace MWGui
|
||||||
|
|
||||||
virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
|
virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
|
||||||
|
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count);
|
||||||
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.
|
/// Move items from this model to \a otherModel.
|
||||||
|
|
|
@ -540,7 +540,11 @@ namespace MWGui
|
||||||
|
|
||||||
if (canEquip.first == 0)
|
if (canEquip.first == 0)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
/// If PCSkipEquip is set, set OnPCEquip to 1 and don't message anything
|
||||||
|
if (!script.empty() && ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)
|
||||||
|
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
|
||||||
|
else
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||||
updateItemView();
|
updateItemView();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,9 +116,9 @@ namespace MWGui
|
||||||
return mSourceModel->allowedToUseItems();
|
return mSourceModel->allowedToUseItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count)
|
||||||
{
|
{
|
||||||
return mSourceModel->copyItem (item, count, setNewOwner);
|
return mSourceModel->copyItem (item, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyItemModel::removeItem (const ItemStack& item, size_t count)
|
void ProxyItemModel::removeItem (const ItemStack& item, size_t count)
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace MWGui
|
||||||
|
|
||||||
/// @param setNewOwner If true, set the copied item's owner to the actor we are copying to,
|
/// @param setNewOwner If true, set the copied item's owner to the actor we are copying to,
|
||||||
/// otherwise reset owner to ""
|
/// otherwise reset owner to ""
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count) = 0;
|
||||||
virtual void removeItem (const ItemStack& item, size_t count) = 0;
|
virtual void removeItem (const ItemStack& item, size_t count) = 0;
|
||||||
|
|
||||||
/// Is the player allowed to use items from this item model? (default true)
|
/// Is the player allowed to use items from this item model? (default true)
|
||||||
|
@ -97,7 +97,7 @@ namespace MWGui
|
||||||
virtual bool onDropItem(const MWWorld::Ptr &item, int count);
|
virtual bool onDropItem(const MWWorld::Ptr &item, int count);
|
||||||
virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
|
virtual bool onTakeItem(const MWWorld::Ptr &item, int count);
|
||||||
|
|
||||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count);
|
||||||
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);
|
||||||
|
|
||||||
|
|
|
@ -7,19 +7,6 @@
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
/*
|
|
||||||
Start of tes3mp addition
|
|
||||||
|
|
||||||
Include additional headers for multiplayer purposes
|
|
||||||
*/
|
|
||||||
#include "../mwmp/Main.hpp"
|
|
||||||
#include "../mwmp/Networking.hpp"
|
|
||||||
#include "../mwmp/LocalPlayer.hpp"
|
|
||||||
#include "../mwmp/MechanicsHelper.hpp"
|
|
||||||
/*
|
|
||||||
End of tes3mp addition
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -30,6 +17,7 @@
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "itemwidget.hpp"
|
#include "itemwidget.hpp"
|
||||||
#include "itemchargeview.hpp"
|
#include "itemchargeview.hpp"
|
||||||
|
@ -143,79 +131,9 @@ void Recharge::onItemCancel()
|
||||||
void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
|
void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
|
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
|
||||||
|
if (!MWMechanics::rechargeItem(item, gem))
|
||||||
if (!gem.getRefData().getCount())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
|
||||||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
|
||||||
|
|
||||||
float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();
|
|
||||||
if (luckTerm < 1|| luckTerm > 10)
|
|
||||||
luckTerm = 1;
|
|
||||||
|
|
||||||
float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
|
||||||
|
|
||||||
if (intelligenceTerm > 20)
|
|
||||||
intelligenceTerm = 20;
|
|
||||||
if (intelligenceTerm < 1)
|
|
||||||
intelligenceTerm = 1;
|
|
||||||
|
|
||||||
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
|
|
||||||
int roll = Misc::Rng::roll0to99();
|
|
||||||
if (roll < x)
|
|
||||||
{
|
|
||||||
std::string soul = gem.getCellRef().getSoul();
|
|
||||||
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);
|
|
||||||
|
|
||||||
float restored = creature->mData.mSoul * (roll / x);
|
|
||||||
|
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
|
||||||
item.getClass().getEnchantment(item));
|
|
||||||
|
|
||||||
/*
|
|
||||||
Start of tes3mp change (minor)
|
|
||||||
|
|
||||||
Send PlayerInventory packets that replace the original item with the new one
|
|
||||||
*/
|
|
||||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
|
||||||
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
|
|
||||||
|
|
||||||
item.getCellRef().setEnchantmentCharge(
|
|
||||||
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
|
||||||
|
|
||||||
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
|
|
||||||
|
|
||||||
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
|
||||||
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
|
||||||
/*
|
|
||||||
End of tes3mp change (minor)
|
|
||||||
*/
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
|
|
||||||
|
|
||||||
player.getClass().getContainerStore(player).restack(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
|
|
||||||
}
|
|
||||||
|
|
||||||
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
|
|
||||||
gem.getContainerStore()->remove(gem, 1, player);
|
|
||||||
|
|
||||||
if (gem.getRefData().getCount() == 0)
|
|
||||||
{
|
|
||||||
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
|
|
||||||
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
|
||||||
|
|
||||||
// special case: readd Azura's Star
|
|
||||||
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
|
||||||
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateView();
|
updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -652,19 +652,22 @@ namespace MWGui
|
||||||
std::string ret;
|
std::string ret;
|
||||||
ret += getMiscString(cellref.getOwner(), "Owner");
|
ret += getMiscString(cellref.getOwner(), "Owner");
|
||||||
const std::string factionId = cellref.getFaction();
|
const std::string factionId = cellref.getFaction();
|
||||||
ret += getMiscString(factionId, "Faction");
|
if (!factionId.empty())
|
||||||
if (!factionId.empty() && cellref.getFactionRank() >= 0)
|
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Faction *fact = store.get<ESM::Faction>().search(factionId);
|
const ESM::Faction *fact = store.get<ESM::Faction>().search(factionId);
|
||||||
if (fact != nullptr)
|
if (fact != nullptr)
|
||||||
{
|
{
|
||||||
int rank = cellref.getFactionRank();
|
ret += getMiscString(fact->mName.empty() ? factionId : fact->mName, "Owner Faction");
|
||||||
const std::string rankName = fact->mRanks[rank];
|
if (cellref.getFactionRank() >= 0)
|
||||||
if (rankName.empty())
|
{
|
||||||
ret += getValueString(cellref.getFactionRank(), "Rank");
|
int rank = cellref.getFactionRank();
|
||||||
else
|
const std::string rankName = fact->mRanks[rank];
|
||||||
ret += getMiscString(rankName, "Rank");
|
if (rankName.empty())
|
||||||
|
ret += getValueString(cellref.getFactionRank(), "Rank");
|
||||||
|
else
|
||||||
|
ret += getMiscString(rankName, "Rank");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "../mwrender/vismask.hpp"
|
#include "../mwrender/vismask.hpp"
|
||||||
|
|
||||||
#include "spellcasting.hpp"
|
#include "spellcasting.hpp"
|
||||||
|
#include "steering.hpp"
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
|
@ -158,6 +159,9 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
static const int GREETING_SHOULD_START = 4; //how many updates should pass before NPC can greet player
|
||||||
|
static const int GREETING_SHOULD_END = 10;
|
||||||
|
|
||||||
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -325,10 +329,31 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);
|
bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);
|
||||||
|
|
||||||
store.remove(itemId, 1, actor, true);
|
store.remove(itemId, 1, actor);
|
||||||
|
|
||||||
if (actor != MWMechanics::getPlayer())
|
if (actor != MWMechanics::getPlayer())
|
||||||
|
{
|
||||||
|
// Equip a replacement
|
||||||
|
if (!wasEquipped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string type = currentItem->getTypeName();
|
||||||
|
if (type != typeid(ESM::Weapon).name() && type != typeid(ESM::Armor).name() && type != typeid(ESM::Clothing).name())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!actor.getClass().hasInventoryStore(actor) || !actor.getClass().getInventoryStore(actor).canActorAutoEquip(actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
|
return;
|
||||||
|
|
||||||
|
actor.getClass().getInventoryStore(actor).autoEquip(actor);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||||
std::string prevItemId = player.getPreviousItem(itemId);
|
std::string prevItemId = player.getPreviousItem(itemId);
|
||||||
|
@ -428,6 +453,113 @@ namespace MWMechanics
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
|
||||||
|
{
|
||||||
|
if (!actor.getClass().isActor() || actor == getPlayer())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||||
|
if (hello == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MWWorld::Ptr player = getPlayer();
|
||||||
|
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f dir = playerPos - actorPos;
|
||||||
|
|
||||||
|
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||||
|
int packageId = seq.getTypeId();
|
||||||
|
|
||||||
|
if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
|
||||||
|
{
|
||||||
|
stats.setTurningToPlayer(false);
|
||||||
|
stats.setGreetingTimer(0);
|
||||||
|
stats.setGreetingState(Greet_None);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.isTurningToPlayer())
|
||||||
|
{
|
||||||
|
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||||
|
// epsilon... TODO: a proper fix might be in either the physics or the
|
||||||
|
// animation subsystem
|
||||||
|
if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
|
||||||
|
{
|
||||||
|
stats.setTurningToPlayer(false);
|
||||||
|
// An original engine launches an endless idle2 when an actor greets player.
|
||||||
|
playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Play a random voice greeting if the player gets too close
|
||||||
|
float helloDistance = static_cast<float>(hello);
|
||||||
|
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||||
|
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
||||||
|
|
||||||
|
helloDistance *= iGreetDistanceMultiplier;
|
||||||
|
|
||||||
|
int greetingTimer = stats.getGreetingTimer();
|
||||||
|
GreetingState greetingState = stats.getGreetingState();
|
||||||
|
if (greetingState == Greet_None)
|
||||||
|
{
|
||||||
|
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
||||||
|
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
||||||
|
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||||
|
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||||
|
greetingTimer++;
|
||||||
|
|
||||||
|
if (greetingTimer >= GREETING_SHOULD_START)
|
||||||
|
{
|
||||||
|
greetingState = Greet_InProgress;
|
||||||
|
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||||
|
greetingTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (greetingState == Greet_InProgress)
|
||||||
|
{
|
||||||
|
greetingTimer++;
|
||||||
|
|
||||||
|
turnActorToFacePlayer(actor, dir);
|
||||||
|
|
||||||
|
if (greetingTimer >= GREETING_SHOULD_END)
|
||||||
|
{
|
||||||
|
greetingState = Greet_Done;
|
||||||
|
greetingTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (greetingState == Greet_Done)
|
||||||
|
{
|
||||||
|
float resetDist = 2 * helloDistance;
|
||||||
|
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
||||||
|
greetingState = Greet_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.setGreetingTimer(greetingTimer);
|
||||||
|
stats.setGreetingState(greetingState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir)
|
||||||
|
{
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||||
|
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||||
|
|
||||||
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
if (!stats.isTurningToPlayer())
|
||||||
|
{
|
||||||
|
stats.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
|
||||||
|
stats.setTurningToPlayer(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
|
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
|
||||||
{
|
{
|
||||||
// No combat for totally static creatures
|
// No combat for totally static creatures
|
||||||
|
@ -599,7 +731,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
MagicEffects now = creatureStats.getSpells().getMagicEffects();
|
MagicEffects now = creatureStats.getSpells().getMagicEffects();
|
||||||
|
|
||||||
if (creature.getClass().isNpc())
|
if (creature.getClass().hasInventoryStore(creature))
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature);
|
MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature);
|
||||||
now += store.getMagicEffects();
|
now += store.getMagicEffects();
|
||||||
|
@ -1161,7 +1293,7 @@ namespace MWMechanics
|
||||||
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||||
|
|
||||||
// If we have a torch and can equip it, then equip it now.
|
// If we have a torch and can equip it, then equip it now.
|
||||||
if (heldIter == inventoryStore.end())
|
if (heldIter == inventoryStore.end() && inventoryStore.canActorAutoEquip(ptr))
|
||||||
{
|
{
|
||||||
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
|
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
|
||||||
}
|
}
|
||||||
|
@ -1528,11 +1660,13 @@ namespace MWMechanics
|
||||||
static float timerUpdateAITargets = 0;
|
static float timerUpdateAITargets = 0;
|
||||||
static float timerUpdateHeadTrack = 0;
|
static float timerUpdateHeadTrack = 0;
|
||||||
static float timerUpdateEquippedLight = 0;
|
static float timerUpdateEquippedLight = 0;
|
||||||
|
static float timerUpdateHello = 0;
|
||||||
const float updateEquippedLightInterval = 1.0f;
|
const float updateEquippedLightInterval = 1.0f;
|
||||||
|
|
||||||
// target lists get updated once every 1.0 sec
|
// target lists get updated once every 1.0 sec
|
||||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||||
|
if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
|
||||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||||
|
|
||||||
|
@ -1729,6 +1863,7 @@ namespace MWMechanics
|
||||||
if (isConscious(iter->first))
|
if (isConscious(iter->first))
|
||||||
{
|
{
|
||||||
stats.getAiSequence().execute(iter->first, *ctrl, duration);
|
stats.getAiSequence().execute(iter->first, *ctrl, duration);
|
||||||
|
updateGreetingState(iter->first, timerUpdateHello > 0);
|
||||||
playIdleDialogue(iter->first);
|
playIdleDialogue(iter->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1754,6 +1889,7 @@ namespace MWMechanics
|
||||||
timerUpdateAITargets += duration;
|
timerUpdateAITargets += duration;
|
||||||
timerUpdateHeadTrack += duration;
|
timerUpdateHeadTrack += duration;
|
||||||
timerUpdateEquippedLight += duration;
|
timerUpdateEquippedLight += duration;
|
||||||
|
timerUpdateHello += duration;
|
||||||
mTimerDisposeSummonsCorpses += duration;
|
mTimerDisposeSummonsCorpses += duration;
|
||||||
|
|
||||||
// Animation/movement update
|
// Animation/movement update
|
||||||
|
@ -1923,6 +2059,8 @@ namespace MWMechanics
|
||||||
// Make sure spell effects are removed
|
// Make sure spell effects are removed
|
||||||
purgeSpellEffects(stats.getActorId());
|
purgeSpellEffects(stats.getActorId());
|
||||||
|
|
||||||
|
calculateCreatureStatModifiers(iter->first, 0);
|
||||||
|
|
||||||
if( iter->first == getPlayer())
|
if( iter->first == getPlayer())
|
||||||
{
|
{
|
||||||
//player's death animation is over
|
//player's death animation is over
|
||||||
|
|
|
@ -121,6 +121,8 @@ namespace MWMechanics
|
||||||
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
|
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
|
||||||
|
|
||||||
void playIdleDialogue(const MWWorld::Ptr& actor);
|
void playIdleDialogue(const MWWorld::Ptr& actor);
|
||||||
|
void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly);
|
||||||
|
void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir);
|
||||||
|
|
||||||
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
||||||
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
||||||
|
|
|
@ -43,11 +43,16 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||||
{
|
{
|
||||||
|
auto& stats = actor.getClass().getCreatureStats(actor);
|
||||||
|
|
||||||
|
if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress)
|
||||||
|
return false;
|
||||||
|
|
||||||
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
const osg::Vec3f targetPos(mX, mY, mZ);
|
const osg::Vec3f targetPos(mX, mY, mZ);
|
||||||
|
|
||||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
stats.setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
stats.setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
if (!isWithinMaxRange(targetPos, actorPos))
|
if (!isWithinMaxRange(targetPos, actorPos))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "creaturestats.hpp"
|
#include "creaturestats.hpp"
|
||||||
#include "steering.hpp"
|
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
#include "coordinateconverter.hpp"
|
#include "coordinateconverter.hpp"
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
|
@ -26,8 +25,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
static const int COUNT_BEFORE_RESET = 10;
|
static const int COUNT_BEFORE_RESET = 10;
|
||||||
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
||||||
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
|
|
||||||
static const int GREETING_SHOULD_END = 10;
|
|
||||||
|
|
||||||
// to prevent overcrowding
|
// to prevent overcrowding
|
||||||
static const int DESTINATION_TOLERANCE = 64;
|
static const int DESTINATION_TOLERANCE = 64;
|
||||||
|
@ -176,6 +173,17 @@ namespace MWMechanics
|
||||||
storage.setState(AiWanderStorage::Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GreetingState greetingState = cStats.getGreetingState();
|
||||||
|
if (greetingState == Greet_InProgress)
|
||||||
|
{
|
||||||
|
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||||
|
{
|
||||||
|
stopWalking(actor, storage, false);
|
||||||
|
mObstacleCheck.clear();
|
||||||
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
doPerFrameActionsForState(actor, duration, storage);
|
doPerFrameActionsForState(actor, duration, storage);
|
||||||
|
|
||||||
float& lastReaction = storage.mReaction;
|
float& lastReaction = storage.mReaction;
|
||||||
|
@ -245,13 +253,7 @@ namespace MWMechanics
|
||||||
if(mDistance && cellChange)
|
if(mDistance && cellChange)
|
||||||
mDistance = 0;
|
mDistance = 0;
|
||||||
|
|
||||||
// Allow interrupting a walking actor to trigger a greeting
|
|
||||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||||
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
|
||||||
{
|
|
||||||
playGreetingIfPlayerGetsTooClose(actor, storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
||||||
{
|
{
|
||||||
// Construct a new path if there isn't one
|
// Construct a new path if there isn't one
|
||||||
|
@ -416,19 +418,9 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool& rotate = storage.mTurnActorGivingGreetingToFacePlayer;
|
|
||||||
if (rotate)
|
|
||||||
{
|
|
||||||
// Reduce the turning animation glitch by using a *HUGE* value of
|
|
||||||
// epsilon... TODO: a proper fix might be in either the physics or the
|
|
||||||
// animation subsystem
|
|
||||||
if (zTurn(actor, storage.mTargetAngleRadians, osg::DegreesToRadians(5.f)))
|
|
||||||
rotate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if idle animation finished
|
// Check if idle animation finished
|
||||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState();
|
||||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||||
{
|
{
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
storage.setState(AiWanderStorage::Wander_Walking);
|
storage.setState(AiWanderStorage::Wander_Walking);
|
||||||
|
@ -517,74 +509,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
|
||||||
{
|
|
||||||
// Play a random voice greeting if the player gets too close
|
|
||||||
int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
|
|
||||||
float helloDistance = static_cast<float>(hello);
|
|
||||||
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
|
||||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
|
||||||
|
|
||||||
helloDistance *= iGreetDistanceMultiplier;
|
|
||||||
|
|
||||||
MWWorld::Ptr player = getPlayer();
|
|
||||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
|
||||||
|
|
||||||
int& greetingTimer = storage.mGreetingTimer;
|
|
||||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
|
||||||
if (greetingState == AiWanderStorage::Greet_None)
|
|
||||||
{
|
|
||||||
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
|
||||||
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
|
||||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
|
||||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
|
||||||
greetingTimer++;
|
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_START)
|
|
||||||
{
|
|
||||||
greetingState = AiWanderStorage::Greet_InProgress;
|
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
|
||||||
greetingTimer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (greetingState == AiWanderStorage::Greet_InProgress)
|
|
||||||
{
|
|
||||||
greetingTimer++;
|
|
||||||
|
|
||||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
|
||||||
{
|
|
||||||
stopWalking(actor, storage, false);
|
|
||||||
mObstacleCheck.clear();
|
|
||||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
|
||||||
}
|
|
||||||
|
|
||||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
|
||||||
|
|
||||||
if (greetingTimer >= GREETING_SHOULD_END)
|
|
||||||
{
|
|
||||||
greetingState = AiWanderStorage::Greet_Done;
|
|
||||||
greetingTimer = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (greetingState == AiWanderStorage::Greet_Done)
|
|
||||||
{
|
|
||||||
float resetDist = 2 * helloDistance;
|
|
||||||
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
|
||||||
greetingState = AiWanderStorage::Greet_None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AiWander::turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage)
|
|
||||||
{
|
|
||||||
osg::Vec3f dir = playerPosition - actorPosition;
|
|
||||||
|
|
||||||
float faceAngleRadians = std::atan2(dir.x(), dir.y());
|
|
||||||
storage.mTargetAngleRadians = faceAngleRadians;
|
|
||||||
storage.mTurnActorGivingGreetingToFacePlayer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,21 +25,8 @@ namespace MWMechanics
|
||||||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||||
struct AiWanderStorage : AiTemporaryBase
|
struct AiWanderStorage : AiTemporaryBase
|
||||||
{
|
{
|
||||||
// the z rotation angle to reach
|
|
||||||
// when mTurnActorGivingGreetingToFacePlayer is true
|
|
||||||
float mTargetAngleRadians;
|
|
||||||
bool mTurnActorGivingGreetingToFacePlayer;
|
|
||||||
float mReaction; // update some actions infrequently
|
float mReaction; // update some actions infrequently
|
||||||
|
|
||||||
enum GreetingState
|
|
||||||
{
|
|
||||||
Greet_None,
|
|
||||||
Greet_InProgress,
|
|
||||||
Greet_Done
|
|
||||||
};
|
|
||||||
GreetingState mSaidGreeting;
|
|
||||||
int mGreetingTimer;
|
|
||||||
|
|
||||||
const MWWorld::CellStore* mCell; // for detecting cell change
|
const MWWorld::CellStore* mCell; // for detecting cell change
|
||||||
|
|
||||||
// AiWander states
|
// AiWander states
|
||||||
|
@ -72,11 +59,7 @@ namespace MWMechanics
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage():
|
||||||
mTargetAngleRadians(0),
|
|
||||||
mTurnActorGivingGreetingToFacePlayer(false),
|
|
||||||
mReaction(0),
|
mReaction(0),
|
||||||
mSaidGreeting(Greet_None),
|
|
||||||
mGreetingTimer(0),
|
|
||||||
mCell(nullptr),
|
mCell(nullptr),
|
||||||
mState(Wander_ChooseAction),
|
mState(Wander_ChooseAction),
|
||||||
mIsWanderingManually(false),
|
mIsWanderingManually(false),
|
||||||
|
@ -136,7 +119,6 @@ namespace MWMechanics
|
||||||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||||
short unsigned getRandomIdle();
|
short unsigned getRandomIdle();
|
||||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||||
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
|
||||||
void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
|
|
|
@ -34,12 +34,53 @@ namespace MWMechanics
|
||||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||||
mDeathAnimation(-1), mTimeOfDeath(), mLevel (0)
|
mDeathAnimation(-1), mTimeOfDeath(), mGreetingState(Greet_None),
|
||||||
|
mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0)
|
||||||
{
|
{
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mAiSettings[i] = 0;
|
mAiSettings[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MWMechanics::CreatureStats::getGreetingTimer() const
|
||||||
|
{
|
||||||
|
return mGreetingTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setGreetingTimer(int timer)
|
||||||
|
{
|
||||||
|
mGreetingTimer = timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MWMechanics::CreatureStats::getAngleToPlayer() const
|
||||||
|
{
|
||||||
|
return mTargetAngleRadians;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setAngleToPlayer(float angle)
|
||||||
|
{
|
||||||
|
mTargetAngleRadians = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
GreetingState MWMechanics::CreatureStats::getGreetingState() const
|
||||||
|
{
|
||||||
|
return mGreetingState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setGreetingState(GreetingState state)
|
||||||
|
{
|
||||||
|
mGreetingState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWMechanics::CreatureStats::isTurningToPlayer() const
|
||||||
|
{
|
||||||
|
return mIsTurningToPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWMechanics::CreatureStats::setTurningToPlayer(bool turning)
|
||||||
|
{
|
||||||
|
mIsTurningToPlayer = turning;
|
||||||
|
}
|
||||||
|
|
||||||
const AiSequence& CreatureStats::getAiSequence() const
|
const AiSequence& CreatureStats::getAiSequence() const
|
||||||
{
|
{
|
||||||
return mAiSequence;
|
return mAiSequence;
|
||||||
|
|
|
@ -19,6 +19,13 @@ namespace ESM
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
enum GreetingState
|
||||||
|
{
|
||||||
|
Greet_None,
|
||||||
|
Greet_InProgress,
|
||||||
|
Greet_Done
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Common creature stats
|
/// \brief Common creature stats
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
@ -70,6 +77,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
MWWorld::TimeStamp mTimeOfDeath;
|
MWWorld::TimeStamp mTimeOfDeath;
|
||||||
|
|
||||||
|
GreetingState mGreetingState;
|
||||||
|
int mGreetingTimer;
|
||||||
|
float mTargetAngleRadians;
|
||||||
|
bool mIsTurningToPlayer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||||
private:
|
private:
|
||||||
|
@ -85,6 +97,18 @@ namespace MWMechanics
|
||||||
public:
|
public:
|
||||||
CreatureStats();
|
CreatureStats();
|
||||||
|
|
||||||
|
int getGreetingTimer() const;
|
||||||
|
void setGreetingTimer(int timer);
|
||||||
|
|
||||||
|
float getAngleToPlayer() const;
|
||||||
|
void setAngleToPlayer(float angle);
|
||||||
|
|
||||||
|
GreetingState getGreetingState() const;
|
||||||
|
void setGreetingState(GreetingState state);
|
||||||
|
|
||||||
|
bool isTurningToPlayer() const;
|
||||||
|
void setTurningToPlayer(bool turning);
|
||||||
|
|
||||||
DrawState_ getDrawState() const;
|
DrawState_ getDrawState() const;
|
||||||
void setDrawState(DrawState_ state);
|
void setDrawState(DrawState_ state);
|
||||||
|
|
||||||
|
|
|
@ -300,16 +300,6 @@ namespace MWMechanics
|
||||||
mWatched = ptr;
|
mWatched = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::advanceTime (float duration)
|
|
||||||
{
|
|
||||||
// Uses ingame time, but scaled to real time
|
|
||||||
const float timeScaleFactor = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
|
||||||
if (timeScaleFactor != 0.0f)
|
|
||||||
duration /= timeScaleFactor;
|
|
||||||
MWWorld::Ptr player = getPlayer();
|
|
||||||
player.getClass().getInventoryStore(player).rechargeItems(duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MechanicsManager::update(float duration, bool paused)
|
void MechanicsManager::update(float duration, bool paused)
|
||||||
{
|
{
|
||||||
if(!mWatched.isEmpty())
|
if(!mWatched.isEmpty())
|
||||||
|
|
|
@ -78,8 +78,6 @@ namespace MWMechanics
|
||||||
/// \param paused In game type does not currently advance (this usually means some GUI
|
/// \param paused In game type does not currently advance (this usually means some GUI
|
||||||
/// component is up).
|
/// component is up).
|
||||||
|
|
||||||
virtual void advanceTime (float duration) override;
|
|
||||||
|
|
||||||
virtual void setPlayerName (const std::string& name) override;
|
virtual void setPlayerName (const std::string& name) override;
|
||||||
///< Set player name.
|
///< Set player name.
|
||||||
|
|
||||||
|
|
124
apps/openmw/mwmechanics/recharge.cpp
Normal file
124
apps/openmw/mwmechanics/recharge.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include "recharge.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
Include additional headers for multiplayer purposes
|
||||||
|
*/
|
||||||
|
#include "../mwmp/Main.hpp"
|
||||||
|
#include "../mwmp/Networking.hpp"
|
||||||
|
#include "../mwmp/LocalPlayer.hpp"
|
||||||
|
#include "../mwmp/MechanicsHelper.hpp"
|
||||||
|
/*
|
||||||
|
End of tes3mp addition
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/containerstore.hpp"
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include "creaturestats.hpp"
|
||||||
|
#include "actorutil.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration)
|
||||||
|
{
|
||||||
|
float charge = item.getCellRef().getEnchantmentCharge();
|
||||||
|
if (charge == -1 || charge == maxCharge)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||||
|
"fMagicItemRechargePerSecond")->mValue.getFloat();
|
||||||
|
|
||||||
|
item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem)
|
||||||
|
{
|
||||||
|
if (!gem.getRefData().getCount())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||||
|
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||||
|
|
||||||
|
float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||||
|
if (luckTerm < 1 || luckTerm > 10)
|
||||||
|
luckTerm = 1;
|
||||||
|
|
||||||
|
float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||||
|
|
||||||
|
if (intelligenceTerm > 20)
|
||||||
|
intelligenceTerm = 20;
|
||||||
|
if (intelligenceTerm < 1)
|
||||||
|
intelligenceTerm = 1;
|
||||||
|
|
||||||
|
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
|
||||||
|
int roll = Misc::Rng::roll0to99();
|
||||||
|
if (roll < x)
|
||||||
|
{
|
||||||
|
std::string soul = gem.getCellRef().getSoul();
|
||||||
|
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);
|
||||||
|
|
||||||
|
float restored = creature->mData.mSoul * (roll / x);
|
||||||
|
|
||||||
|
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
||||||
|
item.getClass().getEnchantment(item));
|
||||||
|
item.getCellRef().setEnchantmentCharge(
|
||||||
|
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
Start of tes3mp change (minor)
|
||||||
|
|
||||||
|
Send PlayerInventory packets that replace the original item with the new one
|
||||||
|
*/
|
||||||
|
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||||
|
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
|
||||||
|
|
||||||
|
item.getCellRef().setEnchantmentCharge(
|
||||||
|
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||||
|
|
||||||
|
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
|
||||||
|
|
||||||
|
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
||||||
|
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
||||||
|
/*
|
||||||
|
End of tes3mp change (minor)
|
||||||
|
*/
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
|
||||||
|
|
||||||
|
player.getClass().getContainerStore(player).restack(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
|
||||||
|
gem.getContainerStore()->remove(gem, 1, player);
|
||||||
|
|
||||||
|
if (gem.getRefData().getCount() == 0)
|
||||||
|
{
|
||||||
|
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
|
||||||
|
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
||||||
|
|
||||||
|
// special case: readd Azura's Star
|
||||||
|
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
||||||
|
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
apps/openmw/mwmechanics/recharge.hpp
Normal file
15
apps/openmw/mwmechanics/recharge.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef MWMECHANICS_RECHARGE_H
|
||||||
|
#define MWMECHANICS_RECHARGE_H
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWMechanics
|
||||||
|
{
|
||||||
|
|
||||||
|
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration);
|
||||||
|
|
||||||
|
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -943,7 +943,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
|
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
|
||||||
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
|
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
|
||||||
isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ranged);
|
isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo);
|
||||||
}
|
}
|
||||||
int type = enchantment->mData.mType;
|
int type = enchantment->mData.mType;
|
||||||
|
|
||||||
|
|
|
@ -133,9 +133,10 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update summon effects
|
// Update summon effects
|
||||||
|
bool casterDead = creatureStats.isDead();
|
||||||
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||||
{
|
{
|
||||||
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
|
bool found = !casterDead && mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
// Effect has ended
|
// Effect has ended
|
||||||
|
|
|
@ -184,7 +184,7 @@ void ObjectList::editContainers(MWWorld::CellStore* cellStore)
|
||||||
if (!containerItem.soul.empty())
|
if (!containerItem.soul.empty())
|
||||||
newPtr.getCellRef().setSoul(containerItem.soul);
|
newPtr.getCellRef().setSoul(containerItem.soul);
|
||||||
|
|
||||||
containerStore.add(newPtr, containerItem.count, ownerPtr, true);
|
containerStore.add(newPtr, containerItem.count, ownerPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (action == BaseObjectList::REMOVE && containerItem.actionCount > 0)
|
else if (action == BaseObjectList::REMOVE && containerItem.actionCount > 0)
|
||||||
|
|
|
@ -87,17 +87,17 @@ namespace
|
||||||
{
|
{
|
||||||
if (mRendered)
|
if (mRendered)
|
||||||
{
|
{
|
||||||
node->setNodeMask(0);
|
if (mParent->copyResult(static_cast<osg::Camera*>(node), nv->getTraversalNumber()))
|
||||||
|
{
|
||||||
|
node->setNodeMask(0);
|
||||||
|
mParent->markForRemoval(static_cast<osg::Camera*>(node));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
|
|
||||||
if (!mRendered)
|
mRendered = true;
|
||||||
{
|
|
||||||
mRendered = true;
|
|
||||||
mParent->markForRemoval(static_cast<osg::Camera*>(node));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -361,7 +361,7 @@ namespace MWRender
|
||||||
imageDest.mImage = image;
|
imageDest.mImage = image;
|
||||||
imageDest.mX = x;
|
imageDest.mX = x;
|
||||||
imageDest.mY = y;
|
imageDest.mY = y;
|
||||||
mPendingImageDest.push_back(imageDest);
|
mPendingImageDest[camera] = imageDest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a quad rendering the updated texture
|
// Create a quad rendering the updated texture
|
||||||
|
@ -614,35 +614,21 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalMap::markForRemoval(osg::Camera *camera)
|
bool GlobalMap::copyResult(osg::Camera *camera, unsigned int frame)
|
||||||
{
|
{
|
||||||
CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera);
|
ImageDestMap::iterator it = mPendingImageDest.find(camera);
|
||||||
if (found == mActiveCameras.end())
|
if (it == mPendingImageDest.end())
|
||||||
|
return true;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera";
|
ImageDest& imageDest = it->second;
|
||||||
return;
|
if (imageDest.mFrameDone == 0) imageDest.mFrameDone = frame+2; // wait an extra frame to ensure the draw thread has completed its frame.
|
||||||
}
|
if (imageDest.mFrameDone > frame)
|
||||||
mActiveCameras.erase(found);
|
|
||||||
mCamerasPendingRemoval.push_back(camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalMap::cleanupCameras()
|
|
||||||
{
|
|
||||||
for (auto& camera : mCamerasPendingRemoval)
|
|
||||||
removeCamera(camera);
|
|
||||||
|
|
||||||
mCamerasPendingRemoval.clear();
|
|
||||||
|
|
||||||
for (ImageDestVector::iterator it = mPendingImageDest.begin(); it != mPendingImageDest.end();)
|
|
||||||
{
|
|
||||||
ImageDest& imageDest = *it;
|
|
||||||
if (--imageDest.mFramesUntilDone > 0)
|
|
||||||
{
|
{
|
||||||
++it;
|
++it;
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureLoaded();
|
|
||||||
mOverlayImage->copySubImage(imageDest.mX, imageDest.mY, 0, imageDest.mImage);
|
mOverlayImage->copySubImage(imageDest.mX, imageDest.mY, 0, imageDest.mImage);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -673,7 +659,7 @@ namespace MWRender
|
||||||
if (!readerwriter)
|
if (!readerwriter)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: Can't write temporary map image, no '" << "png" << "' readerwriter found" << std::endl;
|
std::cerr << "Error: Can't write temporary map image, no '" << "png" << "' readerwriter found" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream ostream;
|
std::ostringstream ostream;
|
||||||
|
@ -697,9 +683,30 @@ namespace MWRender
|
||||||
*/
|
*/
|
||||||
|
|
||||||
it = mPendingImageDest.erase(it);
|
it = mPendingImageDest.erase(it);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalMap::markForRemoval(osg::Camera *camera)
|
||||||
|
{
|
||||||
|
CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera);
|
||||||
|
if (found == mActiveCameras.end())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mActiveCameras.erase(found);
|
||||||
|
mCamerasPendingRemoval.push_back(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalMap::cleanupCameras()
|
||||||
|
{
|
||||||
|
for (auto& camera : mCamerasPendingRemoval)
|
||||||
|
removeCamera(camera);
|
||||||
|
|
||||||
|
mCamerasPendingRemoval.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void GlobalMap::removeCamera(osg::Camera *cam)
|
void GlobalMap::removeCamera(osg::Camera *cam)
|
||||||
{
|
{
|
||||||
cam->removeChildren(0, cam->getNumChildren());
|
cam->removeChildren(0, cam->getNumChildren());
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
@ -60,6 +61,8 @@ namespace MWRender
|
||||||
|
|
||||||
void removeCamera(osg::Camera* cam);
|
void removeCamera(osg::Camera* cam);
|
||||||
|
|
||||||
|
bool copyResult(osg::Camera* cam, unsigned int frame);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
|
||||||
|
@ -106,18 +109,18 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
ImageDest()
|
ImageDest()
|
||||||
: mX(0), mY(0)
|
: mX(0), mY(0)
|
||||||
, mFramesUntilDone(3) // wait an extra frame to ensure the draw thread has completed its frame.
|
, mFrameDone(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> mImage;
|
osg::ref_ptr<osg::Image> mImage;
|
||||||
int mX, mY;
|
int mX, mY;
|
||||||
int mFramesUntilDone;
|
unsigned int mFrameDone;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<ImageDest> ImageDestVector;
|
typedef std::map<osg::ref_ptr<osg::Camera>, ImageDest> ImageDestMap;
|
||||||
|
|
||||||
ImageDestVector mPendingImageDest;
|
ImageDestMap mPendingImageDest;
|
||||||
|
|
||||||
std::vector< std::pair<int,int> > mExploredCells;
|
std::vector< std::pair<int,int> > mExploredCells;
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
#include <osg/PolygonOffset>
|
#include <osg/PolygonOffset>
|
||||||
#include <osg/observer_ptr>
|
#include <osg/observer_ptr>
|
||||||
|
|
||||||
|
#include <osgParticle/BoxPlacer>
|
||||||
|
#include <osgParticle/ModularEmitter>
|
||||||
#include <osgParticle/ParticleSystem>
|
#include <osgParticle/ParticleSystem>
|
||||||
#include <osgParticle/ParticleSystemUpdater>
|
#include <osgParticle/ParticleSystemUpdater>
|
||||||
#include <osgParticle/ModularEmitter>
|
|
||||||
#include <osgParticle/BoxPlacer>
|
|
||||||
#include <osgParticle/ConstantRateCounter>
|
#include <osgParticle/ConstantRateCounter>
|
||||||
#include <osgParticle/RadialShooter>
|
#include <osgParticle/RadialShooter>
|
||||||
|
|
||||||
|
@ -1117,7 +1117,11 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
||||||
, mRemainingTransitionTime(0.0f)
|
, mRemainingTransitionTime(0.0f)
|
||||||
, mRainEnabled(false)
|
, mRainEnabled(false)
|
||||||
, mRainSpeed(0)
|
, mRainSpeed(0)
|
||||||
, mRainFrequency(1)
|
, mRainDiameter(0)
|
||||||
|
, mRainMinHeight(0)
|
||||||
|
, mRainMaxHeight(0)
|
||||||
|
, mRainEntranceSpeed(1)
|
||||||
|
, mRainMaxRaindrops(0)
|
||||||
, mWindSpeed(0.f)
|
, mWindSpeed(0.f)
|
||||||
, mEnabled(true)
|
, mEnabled(true)
|
||||||
, mSunEnabled(true)
|
, mSunEnabled(true)
|
||||||
|
@ -1458,7 +1462,7 @@ void SkyManager::createRain()
|
||||||
mRainNode = new osg::Group;
|
mRainNode = new osg::Group;
|
||||||
|
|
||||||
mRainParticleSystem = new osgParticle::ParticleSystem;
|
mRainParticleSystem = new osgParticle::ParticleSystem;
|
||||||
osg::Vec3 rainRange = osg::Vec3(600,600,600);
|
osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);
|
||||||
|
|
||||||
mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
|
mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
|
||||||
mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0));
|
mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0));
|
||||||
|
@ -1485,14 +1489,19 @@ void SkyManager::createRain()
|
||||||
emitter->setParticleSystem(mRainParticleSystem);
|
emitter->setParticleSystem(mRainParticleSystem);
|
||||||
|
|
||||||
osg::ref_ptr<osgParticle::BoxPlacer> placer (new osgParticle::BoxPlacer);
|
osg::ref_ptr<osgParticle::BoxPlacer> placer (new osgParticle::BoxPlacer);
|
||||||
placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); // Rain_Diameter
|
placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);
|
||||||
placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);
|
placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);
|
||||||
placer->setZRange(300, 300);
|
placer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);
|
||||||
emitter->setPlacer(placer);
|
emitter->setPlacer(placer);
|
||||||
|
mPlacer = placer;
|
||||||
|
|
||||||
|
// FIXME: vanilla engine does not use a particle system to handle rain, it uses a NIF-file with 20 raindrops in it.
|
||||||
|
// It spawns the (maxRaindrops-getParticleSystem()->numParticles())*dt/rainEntranceSpeed batches every frame (near 1-2).
|
||||||
|
// Since the rain is a regular geometry, it produces water ripples, also in theory it can be removed if collides with something.
|
||||||
osg::ref_ptr<RainCounter> counter (new RainCounter);
|
osg::ref_ptr<RainCounter> counter (new RainCounter);
|
||||||
counter->setNumberOfParticlesPerSecondToCreate(600.0);
|
counter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);
|
||||||
emitter->setCounter(counter);
|
emitter->setCounter(counter);
|
||||||
|
mCounter = counter;
|
||||||
|
|
||||||
osg::ref_ptr<RainShooter> shooter (new RainShooter);
|
osg::ref_ptr<RainShooter> shooter (new RainShooter);
|
||||||
mRainShooter = shooter;
|
mRainShooter = shooter;
|
||||||
|
@ -1525,6 +1534,8 @@ void SkyManager::destroyRain()
|
||||||
|
|
||||||
mRootNode->removeChild(mRainNode);
|
mRootNode->removeChild(mRainNode);
|
||||||
mRainNode = nullptr;
|
mRainNode = nullptr;
|
||||||
|
mPlacer = nullptr;
|
||||||
|
mCounter = nullptr;
|
||||||
mRainParticleSystem = nullptr;
|
mRainParticleSystem = nullptr;
|
||||||
mRainShooter = nullptr;
|
mRainShooter = nullptr;
|
||||||
mRainFader = nullptr;
|
mRainFader = nullptr;
|
||||||
|
@ -1625,10 +1636,17 @@ void SkyManager::updateRainParameters()
|
||||||
{
|
{
|
||||||
if (mRainShooter)
|
if (mRainShooter)
|
||||||
{
|
{
|
||||||
float windFactor = mWindSpeed/3.f;
|
float angle = -std::atan2(1, 50.f/mWindSpeed);
|
||||||
float angle = windFactor * osg::PI/4;
|
mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle)));
|
||||||
mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed * windFactor, -mRainSpeed));
|
|
||||||
mRainShooter->setAngle(angle);
|
mRainShooter->setAngle(angle);
|
||||||
|
|
||||||
|
osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);
|
||||||
|
|
||||||
|
mPlacer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);
|
||||||
|
mPlacer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);
|
||||||
|
mPlacer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);
|
||||||
|
|
||||||
|
mCounter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1645,6 +1663,14 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
||||||
{
|
{
|
||||||
if (!mCreated) return;
|
if (!mCreated) return;
|
||||||
|
|
||||||
|
mRainEntranceSpeed = weather.mRainEntranceSpeed;
|
||||||
|
mRainMaxRaindrops = weather.mRainMaxRaindrops;
|
||||||
|
mRainDiameter = weather.mRainDiameter;
|
||||||
|
mRainMinHeight = weather.mRainMinHeight;
|
||||||
|
mRainMaxHeight = weather.mRainMaxHeight;
|
||||||
|
mRainSpeed = weather.mRainSpeed;
|
||||||
|
mWindSpeed = weather.mWindSpeed;
|
||||||
|
|
||||||
if (mRainEffect != weather.mRainEffect)
|
if (mRainEffect != weather.mRainEffect)
|
||||||
{
|
{
|
||||||
mRainEffect = weather.mRainEffect;
|
mRainEffect = weather.mRainEffect;
|
||||||
|
@ -1658,9 +1684,6 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mRainFrequency = weather.mRainFrequency;
|
|
||||||
mRainSpeed = weather.mRainSpeed;
|
|
||||||
mWindSpeed = weather.mWindSpeed;
|
|
||||||
updateRainParameters();
|
updateRainParameters();
|
||||||
|
|
||||||
mIsStorm = weather.mIsStorm;
|
mIsStorm = weather.mIsStorm;
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace osg
|
||||||
namespace osgParticle
|
namespace osgParticle
|
||||||
{
|
{
|
||||||
class ParticleSystem;
|
class ParticleSystem;
|
||||||
|
class BoxPlacer;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
|
@ -39,6 +40,7 @@ namespace MWRender
|
||||||
class CloudUpdater;
|
class CloudUpdater;
|
||||||
class Sun;
|
class Sun;
|
||||||
class Moon;
|
class Moon;
|
||||||
|
class RainCounter;
|
||||||
class RainShooter;
|
class RainShooter;
|
||||||
class RainFader;
|
class RainFader;
|
||||||
class AlphaFader;
|
class AlphaFader;
|
||||||
|
@ -68,6 +70,8 @@ namespace MWRender
|
||||||
float mDLFogOffset;
|
float mDLFogOffset;
|
||||||
|
|
||||||
float mWindSpeed;
|
float mWindSpeed;
|
||||||
|
float mCurrentWindSpeed;
|
||||||
|
float mNextWindSpeed;
|
||||||
|
|
||||||
float mCloudSpeed;
|
float mCloudSpeed;
|
||||||
|
|
||||||
|
@ -85,8 +89,12 @@ namespace MWRender
|
||||||
std::string mRainEffect;
|
std::string mRainEffect;
|
||||||
float mEffectFade;
|
float mEffectFade;
|
||||||
|
|
||||||
|
float mRainDiameter;
|
||||||
|
float mRainMinHeight;
|
||||||
|
float mRainMaxHeight;
|
||||||
float mRainSpeed;
|
float mRainSpeed;
|
||||||
float mRainFrequency;
|
float mRainEntranceSpeed;
|
||||||
|
int mRainMaxRaindrops;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MoonState
|
struct MoonState
|
||||||
|
@ -216,6 +224,8 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> mRainNode;
|
osg::ref_ptr<osg::Group> mRainNode;
|
||||||
osg::ref_ptr<osgParticle::ParticleSystem> mRainParticleSystem;
|
osg::ref_ptr<osgParticle::ParticleSystem> mRainParticleSystem;
|
||||||
|
osg::ref_ptr<osgParticle::BoxPlacer> mPlacer;
|
||||||
|
osg::ref_ptr<RainCounter> mCounter;
|
||||||
osg::ref_ptr<RainShooter> mRainShooter;
|
osg::ref_ptr<RainShooter> mRainShooter;
|
||||||
osg::ref_ptr<RainFader> mRainFader;
|
osg::ref_ptr<RainFader> mRainFader;
|
||||||
|
|
||||||
|
@ -249,7 +259,11 @@ namespace MWRender
|
||||||
bool mRainEnabled;
|
bool mRainEnabled;
|
||||||
std::string mRainEffect;
|
std::string mRainEffect;
|
||||||
float mRainSpeed;
|
float mRainSpeed;
|
||||||
float mRainFrequency;
|
float mRainDiameter;
|
||||||
|
float mRainMinHeight;
|
||||||
|
float mRainMaxHeight;
|
||||||
|
float mRainEntranceSpeed;
|
||||||
|
int mRainMaxRaindrops;
|
||||||
float mWindSpeed;
|
float mWindSpeed;
|
||||||
|
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
|
|
|
@ -527,7 +527,7 @@ namespace MWScript
|
||||||
|
|
||||||
MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();
|
MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();
|
||||||
effects += stats.getActiveSpells().getMagicEffects();
|
effects += stats.getActiveSpells().getMagicEffects();
|
||||||
if (ptr.getClass().isNpc())
|
if (ptr.getClass().hasInventoryStore(ptr))
|
||||||
{
|
{
|
||||||
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
|
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
|
||||||
effects += store.getMagicEffects();
|
effects += store.getMagicEffects();
|
||||||
|
|
|
@ -184,6 +184,19 @@ void MWWorld::Cells::rest (double hours)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWWorld::Cells::recharge (float duration)
|
||||||
|
{
|
||||||
|
for (auto &interior : mInteriors)
|
||||||
|
{
|
||||||
|
interior.second.recharge(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &exterior : mExteriors)
|
||||||
|
{
|
||||||
|
exterior.second.recharge(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
|
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
|
||||||
{
|
{
|
||||||
if (id.mPaged)
|
if (id.mPaged)
|
||||||
|
|
|
@ -72,7 +72,9 @@ namespace MWWorld
|
||||||
|
|
||||||
/// @note name must be lower case
|
/// @note name must be lower case
|
||||||
Ptr getPtr (const std::string& name);
|
Ptr getPtr (const std::string& name);
|
||||||
|
|
||||||
void rest (double hours);
|
void rest (double hours);
|
||||||
|
void recharge (float duration);
|
||||||
|
|
||||||
/// Get all Ptrs referencing \a name in exterior cells
|
/// Get all Ptrs referencing \a name in exterior cells
|
||||||
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
#include "esmstore.hpp"
|
#include "esmstore.hpp"
|
||||||
|
@ -385,6 +386,7 @@ namespace MWWorld
|
||||||
void CellStore::updateMergedRefs()
|
void CellStore::updateMergedRefs()
|
||||||
{
|
{
|
||||||
mMergedRefs.clear();
|
mMergedRefs.clear();
|
||||||
|
mRechargingItemsUpToDate = false;
|
||||||
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
||||||
forEachInternal(visitor);
|
forEachInternal(visitor);
|
||||||
visitor.merge();
|
visitor.merge();
|
||||||
|
@ -419,7 +421,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)
|
CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)
|
||||||
: mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0)
|
: mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false)
|
||||||
{
|
{
|
||||||
mWaterLevel = cell->mWater;
|
mWaterLevel = cell->mWater;
|
||||||
}
|
}
|
||||||
|
@ -1178,6 +1180,42 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CellStore::recharge(float duration)
|
||||||
|
{
|
||||||
|
if (duration <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mState == State_Loaded)
|
||||||
|
{
|
||||||
|
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rechargeItems(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CellStore::respawn()
|
void CellStore::respawn()
|
||||||
{
|
{
|
||||||
if (mState == State_Loaded)
|
if (mState == State_Loaded)
|
||||||
|
@ -1213,4 +1251,73 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWWorld::CellStore::rechargeItems(float duration)
|
||||||
|
{
|
||||||
|
if (!mRechargingItemsUpToDate)
|
||||||
|
{
|
||||||
|
updateRechargingItems();
|
||||||
|
mRechargingItemsUpToDate = true;
|
||||||
|
}
|
||||||
|
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
||||||
|
{
|
||||||
|
MWMechanics::rechargeItem(it->first, it->second, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWWorld::CellStore::updateRechargingItems()
|
||||||
|
{
|
||||||
|
mRechargingItems.clear();
|
||||||
|
|
||||||
|
for (CellRefList<ESM::Weapon>::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
checkItem(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CellRefList<ESM::Armor>::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
checkItem(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CellRefList<ESM::Clothing>::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
checkItem(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (CellRefList<ESM::Book>::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr = getCurrentPtr(&*it);
|
||||||
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||||
|
{
|
||||||
|
checkItem(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWWorld::CellStore::checkItem(Ptr ptr)
|
||||||
|
{
|
||||||
|
if (ptr.getClass().getEnchantment(ptr).empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string enchantmentId = ptr.getClass().getEnchantment(ptr);
|
||||||
|
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
|
||||||
|
if (!enchantment)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << ptr.getCellRef().getRefId();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
||||||
|
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||||
|
mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,16 @@ namespace MWWorld
|
||||||
/// Repopulate mMergedRefs.
|
/// Repopulate mMergedRefs.
|
||||||
void updateMergedRefs();
|
void updateMergedRefs();
|
||||||
|
|
||||||
|
// (item, max charge)
|
||||||
|
typedef std::vector<std::pair<LiveCellRefBase*, float> > TRechargingItems;
|
||||||
|
TRechargingItems mRechargingItems;
|
||||||
|
|
||||||
|
bool mRechargingItemsUpToDate;
|
||||||
|
|
||||||
|
void updateRechargingItems();
|
||||||
|
void rechargeItems(float duration);
|
||||||
|
void checkItem(Ptr ptr);
|
||||||
|
|
||||||
// helper function for forEachInternal
|
// helper function for forEachInternal
|
||||||
template<class Visitor, class List>
|
template<class Visitor, class List>
|
||||||
bool forEachImp (Visitor& visitor, List& list)
|
bool forEachImp (Visitor& visitor, List& list)
|
||||||
|
@ -184,6 +194,7 @@ namespace MWWorld
|
||||||
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo);
|
||||||
|
|
||||||
void rest(double hours);
|
void rest(double hours);
|
||||||
|
void recharge(float duration);
|
||||||
|
|
||||||
/// Make a copy of the given object and insert it into this cell.
|
/// Make a copy of the given object and insert it into this cell.
|
||||||
/// @note If you get a linker error here, this means the given type can not be inserted into a cell.
|
/// @note If you get a linker error here, this means the given type can not be inserted into a cell.
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/levelledlist.hpp"
|
#include "../mwmechanics/levelledlist.hpp"
|
||||||
#include "../mwmechanics/actorutil.hpp"
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
#include "../mwmechanics/recharge.hpp"
|
||||||
|
|
||||||
#include "manualref.hpp"
|
#include "manualref.hpp"
|
||||||
#include "refdata.hpp"
|
#include "refdata.hpp"
|
||||||
|
@ -126,7 +127,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
|
||||||
|
|
||||||
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
|
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
|
||||||
|
|
||||||
MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {}
|
MWWorld::ContainerStore::ContainerStore()
|
||||||
|
: mListener(nullptr)
|
||||||
|
, mRechargingItemsUpToDate(false)
|
||||||
|
, mCachedWeight (0)
|
||||||
|
, mWeightUpToDate (false) {}
|
||||||
|
|
||||||
MWWorld::ContainerStore::~ContainerStore() {}
|
MWWorld::ContainerStore::~ContainerStore() {}
|
||||||
|
|
||||||
|
@ -272,7 +277,6 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr1 != ptr2 // an item never stacks onto itself
|
return ptr1 != ptr2 // an item never stacks onto itself
|
||||||
&& ptr1.getCellRef().getOwner() == ptr2.getCellRef().getOwner()
|
|
||||||
&& ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul()
|
&& ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul()
|
||||||
|
|
||||||
&& ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2)
|
&& ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2)
|
||||||
|
@ -290,30 +294,14 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
|
||||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
|
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
|
||||||
{
|
{
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);
|
||||||
return add(ref.getPtr(), count, actorPtr, true);
|
return add(ref.getPtr(), count, actorPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)
|
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr)
|
||||||
{
|
{
|
||||||
Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||||
|
|
||||||
MWWorld::ContainerStoreIterator it = end();
|
MWWorld::ContainerStoreIterator it = addImp(itemPtr, count);
|
||||||
|
|
||||||
// HACK: Set owner on the original item, then reset it after we have copied it
|
|
||||||
// If we set the owner on the copied item, it would not stack correctly...
|
|
||||||
std::string oldOwner = itemPtr.getCellRef().getOwner();
|
|
||||||
if (!setOwner || actorPtr == MWMechanics::getPlayer()) // No point in setting owner to the player - NPCs will not respect this anyway
|
|
||||||
{
|
|
||||||
itemPtr.getCellRef().setOwner("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId());
|
|
||||||
}
|
|
||||||
|
|
||||||
it = addImp(itemPtr, count);
|
|
||||||
|
|
||||||
itemPtr.getCellRef().setOwner(oldOwner);
|
|
||||||
|
|
||||||
// The copy of the original item we just made
|
// The copy of the original item we just made
|
||||||
MWWorld::Ptr item = *it;
|
MWWorld::Ptr item = *it;
|
||||||
|
@ -354,7 +342,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
||||||
pos.pos[2] = 0;
|
pos.pos[2] = 0;
|
||||||
item.getCellRef().setPosition(pos);
|
item.getCellRef().setPosition(pos);
|
||||||
|
|
||||||
// reset ownership stuff, owner was already handled above
|
// We do not need to store owners for items in container stores - we do not use it anyway.
|
||||||
|
item.getCellRef().setOwner("");
|
||||||
item.getCellRef().resetGlobalVariable();
|
item.getCellRef().resetGlobalVariable();
|
||||||
item.getCellRef().setFaction("");
|
item.getCellRef().setFaction("");
|
||||||
item.getCellRef().setFactionRank(-1);
|
item.getCellRef().setFactionRank(-1);
|
||||||
|
@ -472,6 +461,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWWorld::ContainerStore::rechargeItems(float duration)
|
||||||
|
{
|
||||||
|
if (!mRechargingItemsUpToDate)
|
||||||
|
{
|
||||||
|
updateRechargingItems();
|
||||||
|
mRechargingItemsUpToDate = true;
|
||||||
|
}
|
||||||
|
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
||||||
|
{
|
||||||
|
if (!MWMechanics::rechargeItem(*it->first, it->second, duration))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// attempt to restack when fully recharged
|
||||||
|
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
|
||||||
|
it->first = restack(*it->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MWWorld::ContainerStore::updateRechargingItems()
|
||||||
|
{
|
||||||
|
mRechargingItems.clear();
|
||||||
|
for (ContainerStoreIterator it = begin(); it != end(); ++it)
|
||||||
|
{
|
||||||
|
const std::string& enchantmentId = it->getClass().getEnchantment(*it);
|
||||||
|
if (!enchantmentId.empty())
|
||||||
|
{
|
||||||
|
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
|
||||||
|
if (!enchantment)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
||||||
|
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||||
|
mRechargingItems.emplace_back(it, static_cast<float>(enchantment->mData.mCharge));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
|
||||||
{
|
{
|
||||||
int toRemove = count;
|
int toRemove = count;
|
||||||
|
@ -725,6 +754,7 @@ void MWWorld::ContainerStore::clear()
|
||||||
void MWWorld::ContainerStore::flagAsModified()
|
void MWWorld::ContainerStore::flagAsModified()
|
||||||
{
|
{
|
||||||
mWeightUpToDate = false;
|
mWeightUpToDate = false;
|
||||||
|
mRechargingItemsUpToDate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float MWWorld::ContainerStore::getWeight() const
|
float MWWorld::ContainerStore::getWeight() const
|
||||||
|
|
|
@ -71,6 +71,12 @@ namespace MWWorld
|
||||||
protected:
|
protected:
|
||||||
ContainerStoreListener* mListener;
|
ContainerStoreListener* mListener;
|
||||||
|
|
||||||
|
// (item, max charge)
|
||||||
|
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
|
||||||
|
TRechargingItems mRechargingItems;
|
||||||
|
|
||||||
|
bool mRechargingItemsUpToDate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MWWorld::CellRefList<ESM::Potion> potions;
|
MWWorld::CellRefList<ESM::Potion> potions;
|
||||||
|
@ -108,6 +114,7 @@ namespace MWWorld
|
||||||
ESM::InventoryState& inventory, int& index,
|
ESM::InventoryState& inventory, int& index,
|
||||||
bool equipable = false) const;
|
bool equipable = false) const;
|
||||||
|
|
||||||
|
void updateRechargingItems();
|
||||||
|
|
||||||
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
||||||
|
|
||||||
|
@ -131,7 +138,7 @@ namespace MWWorld
|
||||||
|
|
||||||
bool hasVisibleItems() const;
|
bool hasVisibleItems() const;
|
||||||
|
|
||||||
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false);
|
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr);
|
||||||
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
||||||
///
|
///
|
||||||
/// \note The item pointed to is not required to exist beyond this function call.
|
/// \note The item pointed to is not required to exist beyond this function call.
|
||||||
|
@ -156,6 +163,9 @@ namespace MWWorld
|
||||||
///
|
///
|
||||||
/// @return the number of items actually removed
|
/// @return the number of items actually removed
|
||||||
|
|
||||||
|
void rechargeItems (float duration);
|
||||||
|
///< Restore charge on enchanted items. Note this should only be done for the player.
|
||||||
|
|
||||||
ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1);
|
ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1);
|
||||||
///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count).
|
///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count).
|
||||||
///
|
///
|
||||||
|
|
|
@ -211,7 +211,7 @@ void ESMStore::validate()
|
||||||
const ESM::MagicEffect* mgef = mMagicEffects.search(iter->mEffectID);
|
const ESM::MagicEffect* mgef = mMagicEffects.search(iter->mEffectID);
|
||||||
if (!mgef)
|
if (!mgef)
|
||||||
{
|
{
|
||||||
Log(Debug::Verbose) << "Spell '" << spell.mId << "' has an an invalid effect (index " << iter->mEffectID << ") present, dropping it.";
|
Log(Debug::Verbose) << "Spell '" << spell.mId << "' has an invalid effect (index " << iter->mEffectID << ") present. Dropping the effect.";
|
||||||
iter = spell.mEffects.mList.erase(iter);
|
iter = spell.mEffects.mList.erase(iter);
|
||||||
changed = true;
|
changed = true;
|
||||||
continue;
|
continue;
|
||||||
|
@ -223,7 +223,7 @@ void ESMStore::validate()
|
||||||
{
|
{
|
||||||
iter->mAttribute = -1;
|
iter->mAttribute = -1;
|
||||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||||
" effect of spell '" << spell.mId << "' has an attribute argument present, dropping it.";
|
" effect of spell '" << spell.mId << "' has an attribute argument present. Dropping the argument.";
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ void ESMStore::validate()
|
||||||
{
|
{
|
||||||
iter->mSkill = -1;
|
iter->mSkill = -1;
|
||||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||||
" effect of spell '" << spell.mId << "' has a skill argument present, dropping it.";
|
" effect of spell '" << spell.mId << "' has a skill argument present. Dropping the argument.";
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ void ESMStore::validate()
|
||||||
iter->mSkill = -1;
|
iter->mSkill = -1;
|
||||||
iter->mAttribute = -1;
|
iter->mAttribute = -1;
|
||||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||||
" effect of spell '" << spell.mId << "' has argument(s) present, dropping them.";
|
" effect of spell '" << spell.mId << "' has argument(s) present. Dropping the argument(s).";
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,6 @@ MWWorld::InventoryStore::InventoryStore()
|
||||||
, mUpdatesEnabled (true)
|
, mUpdatesEnabled (true)
|
||||||
, mFirstAutoEquip(true)
|
, mFirstAutoEquip(true)
|
||||||
, mSelectedEnchantItem(end())
|
, mSelectedEnchantItem(end())
|
||||||
, mRechargingItemsUpToDate(false)
|
|
||||||
{
|
{
|
||||||
initSlots (mSlots);
|
initSlots (mSlots);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +126,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
||||||
, mFirstAutoEquip(store.mFirstAutoEquip)
|
, mFirstAutoEquip(store.mFirstAutoEquip)
|
||||||
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
||||||
, mSelectedEnchantItem(end())
|
, mSelectedEnchantItem(end())
|
||||||
, mRechargingItemsUpToDate(false)
|
|
||||||
{
|
{
|
||||||
copySlots (store);
|
copySlots (store);
|
||||||
}
|
}
|
||||||
|
@ -146,11 +144,11 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)
|
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr)
|
||||||
{
|
{
|
||||||
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner);
|
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr);
|
||||||
|
|
||||||
// Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves
|
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
|
||||||
if (actorPtr != MWMechanics::getPlayer()
|
if (actorPtr != MWMechanics::getPlayer()
|
||||||
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
|
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
|
||||||
{
|
{
|
||||||
|
@ -223,22 +221,29 @@ MWWorld::ConstContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
|
||||||
return findSlot (slot);
|
return findSlot (slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item)
|
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor)
|
||||||
{
|
{
|
||||||
if (!Settings::Manager::getBool("prevent merchant equipping", "Game"))
|
// Treat player as non-trader indifferently from service flags.
|
||||||
|
if (actor == MWMechanics::getPlayer())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Only autoEquip if we are the original owner of the item.
|
static const bool prevent = Settings::Manager::getBool("prevent merchant equipping", "Game");
|
||||||
// This stops merchants from auto equipping anything you sell to them.
|
if (!prevent)
|
||||||
// ...unless this is a companion, he should always equip items given to him.
|
return true;
|
||||||
if (!Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), actor.getCellRef().getRefId()) &&
|
|
||||||
(actor.getClass().getScript(actor).empty() ||
|
// Corpses can be dressed up by the player as desired.
|
||||||
!actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
|
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||||
&& !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired
|
return true;
|
||||||
)
|
|
||||||
{
|
// Companions can autoequip items.
|
||||||
return false;
|
if (!actor.getClass().getScript(actor).empty() &&
|
||||||
}
|
actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// If the actor is trader, he can auto-equip items only during initial auto-equipping
|
||||||
|
int services = actor.getClass().getServices(actor);
|
||||||
|
if (services & ESM::NPC::AllItems)
|
||||||
|
return mFirstAutoEquip;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -352,9 +357,6 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
|
||||||
|
|
||||||
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)
|
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)
|
||||||
{
|
{
|
||||||
if (!canActorAutoEquip(actor, *iter))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;
|
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;
|
||||||
|
|
||||||
if (MWMechanics::getWeaponType(esmWeapon->mData.mType)->mWeaponClass == ESM::WeaponType::Ammo)
|
if (MWMechanics::getWeaponType(esmWeapon->mData.mType)->mWeaponClass == ESM::WeaponType::Ammo)
|
||||||
|
@ -456,9 +458,6 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots&
|
||||||
{
|
{
|
||||||
Ptr test = *iter;
|
Ptr test = *iter;
|
||||||
|
|
||||||
if (!canActorAutoEquip(actor, test))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch(test.getClass().canBeEquipped (test, actor).first)
|
switch(test.getClass().canBeEquipped (test, actor).first)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -590,6 +589,9 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (!canActorAutoEquip(actor))
|
||||||
|
return;
|
||||||
|
|
||||||
TSlots slots_;
|
TSlots slots_;
|
||||||
initSlots (slots_);
|
initSlots (slots_);
|
||||||
|
|
||||||
|
@ -746,12 +748,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
|
||||||
mFirstAutoEquip = false;
|
mFirstAutoEquip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::flagAsModified()
|
|
||||||
{
|
|
||||||
ContainerStore::flagAsModified();
|
|
||||||
mRechargingItemsUpToDate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const
|
bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const
|
||||||
{
|
{
|
||||||
bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2);
|
bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2);
|
||||||
|
@ -994,57 +990,6 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::InventoryStore::updateRechargingItems()
|
|
||||||
{
|
|
||||||
mRechargingItems.clear();
|
|
||||||
for (ContainerStoreIterator it = begin(); it != end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->getClass().getEnchantment(*it) != "")
|
|
||||||
{
|
|
||||||
std::string enchantmentId = it->getClass().getEnchantment(*it);
|
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(
|
|
||||||
enchantmentId);
|
|
||||||
if (!enchantment)
|
|
||||||
{
|
|
||||||
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
|
||||||
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
||||||
mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MWWorld::InventoryStore::rechargeItems(float duration)
|
|
||||||
{
|
|
||||||
if (!mRechargingItemsUpToDate)
|
|
||||||
{
|
|
||||||
updateRechargingItems();
|
|
||||||
mRechargingItemsUpToDate = true;
|
|
||||||
}
|
|
||||||
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->first->getCellRef().getEnchantmentCharge() == -1
|
|
||||||
|| it->first->getCellRef().getEnchantmentCharge() == it->second)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
|
||||||
"fMagicItemRechargePerSecond")->mValue.getFloat();
|
|
||||||
|
|
||||||
if (it->first->getCellRef().getEnchantmentCharge() <= it->second)
|
|
||||||
{
|
|
||||||
it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration,
|
|
||||||
it->second));
|
|
||||||
|
|
||||||
// attempt to restack when fully recharged
|
|
||||||
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
|
|
||||||
it->first = restack(*it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MWWorld::InventoryStore::purgeEffect(short effectId)
|
void MWWorld::InventoryStore::purgeEffect(short effectId)
|
||||||
{
|
{
|
||||||
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)
|
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)
|
||||||
|
|
|
@ -101,25 +101,17 @@ namespace MWWorld
|
||||||
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
||||||
ContainerStoreIterator mSelectedEnchantItem;
|
ContainerStoreIterator mSelectedEnchantItem;
|
||||||
|
|
||||||
// (item, max charge)
|
|
||||||
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
|
|
||||||
TRechargingItems mRechargingItems;
|
|
||||||
|
|
||||||
bool mRechargingItemsUpToDate;
|
|
||||||
|
|
||||||
void copySlots (const InventoryStore& store);
|
void copySlots (const InventoryStore& store);
|
||||||
|
|
||||||
void initSlots (TSlots& slots_);
|
void initSlots (TSlots& slots_);
|
||||||
|
|
||||||
void updateMagicEffects(const Ptr& actor);
|
void updateMagicEffects(const Ptr& actor);
|
||||||
void updateRechargingItems();
|
|
||||||
|
|
||||||
void fireEquipmentChangedEvent(const Ptr& actor);
|
void fireEquipmentChangedEvent(const Ptr& actor);
|
||||||
|
|
||||||
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
||||||
virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);
|
virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);
|
||||||
|
|
||||||
bool canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item);
|
|
||||||
ContainerStoreIterator findSlot (int slot) const;
|
ContainerStoreIterator findSlot (int slot) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -132,7 +124,7 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual InventoryStore* clone() { return new InventoryStore(*this); }
|
virtual InventoryStore* clone() { return new InventoryStore(*this); }
|
||||||
|
|
||||||
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false);
|
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr);
|
||||||
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
|
||||||
/// Auto-equip items if specific conditions are fulfilled (see the implementation).
|
/// Auto-equip items if specific conditions are fulfilled (see the implementation).
|
||||||
///
|
///
|
||||||
|
@ -168,13 +160,11 @@ namespace MWWorld
|
||||||
void autoEquip (const MWWorld::Ptr& actor);
|
void autoEquip (const MWWorld::Ptr& actor);
|
||||||
///< Auto equip items according to stats and item value.
|
///< Auto equip items according to stats and item value.
|
||||||
|
|
||||||
|
bool canActorAutoEquip(const MWWorld::Ptr& actor);
|
||||||
|
|
||||||
const MWMechanics::MagicEffects& getMagicEffects() const;
|
const MWMechanics::MagicEffects& getMagicEffects() const;
|
||||||
///< Return magic effects from worn items.
|
///< Return magic effects from worn items.
|
||||||
|
|
||||||
virtual void flagAsModified();
|
|
||||||
///< \attention This function is internal to the world model and should not be called from
|
|
||||||
/// outside.
|
|
||||||
|
|
||||||
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
||||||
///< @return true if the two specified objects can stack with each other
|
///< @return true if the two specified objects can stack with each other
|
||||||
|
|
||||||
|
@ -216,9 +206,6 @@ namespace MWWorld
|
||||||
|
|
||||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
||||||
|
|
||||||
void rechargeItems (float duration);
|
|
||||||
///< Restore charge on enchanted items. Note this should only be done for the player.
|
|
||||||
|
|
||||||
void purgeEffect (short effectId);
|
void purgeEffect (short effectId);
|
||||||
///< Remove a magic effect
|
///< Remove a magic effect
|
||||||
|
|
||||||
|
|
|
@ -160,7 +160,12 @@ Weather::Weather(const std::string& name,
|
||||||
, mGlareView(Fallback::Map::getFloat("Weather_" + name + "_Glare_View"))
|
, mGlareView(Fallback::Map::getFloat("Weather_" + name + "_Glare_View"))
|
||||||
, mIsStorm(mWindSpeed > stormWindSpeed)
|
, mIsStorm(mWindSpeed > stormWindSpeed)
|
||||||
, mRainSpeed(rainSpeed)
|
, mRainSpeed(rainSpeed)
|
||||||
, mRainFrequency(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed"))
|
, mRainEntranceSpeed(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed"))
|
||||||
|
, mRainMaxRaindrops(Fallback::Map::getFloat("Weather_" + name + "_Max_Raindrops"))
|
||||||
|
, mRainDiameter(Fallback::Map::getFloat("Weather_" + name + "_Rain_Diameter"))
|
||||||
|
, mRainThreshold(Fallback::Map::getFloat("Weather_" + name + "_Rain_Threshold"))
|
||||||
|
, mRainMinHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Min"))
|
||||||
|
, mRainMaxHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Max"))
|
||||||
, mParticleEffect(particleEffect)
|
, mParticleEffect(particleEffect)
|
||||||
, mRainEffect(Fallback::Map::getBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "")
|
, mRainEffect(Fallback::Map::getBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "")
|
||||||
, mTransitionDelta(Fallback::Map::getFloat("Weather_" + name + "_Transition_Delta"))
|
, mTransitionDelta(Fallback::Map::getFloat("Weather_" + name + "_Transition_Delta"))
|
||||||
|
@ -191,15 +196,6 @@ Weather::Weather(const std::string& name,
|
||||||
|
|
||||||
if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None"))
|
if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None"))
|
||||||
mAmbientLoopSoundID.clear();
|
mAmbientLoopSoundID.clear();
|
||||||
|
|
||||||
/*
|
|
||||||
Unhandled:
|
|
||||||
Rain Diameter=600 ?
|
|
||||||
Rain Height Min=200 ?
|
|
||||||
Rain Height Max=700 ?
|
|
||||||
Rain Threshold=0.6 ?
|
|
||||||
Max Raindrops=650 ?
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Weather::transitionDelta() const
|
float Weather::transitionDelta() const
|
||||||
|
@ -551,6 +547,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::E
|
||||||
, mMasser("Masser")
|
, mMasser("Masser")
|
||||||
, mSecunda("Secunda")
|
, mSecunda("Secunda")
|
||||||
, mWindSpeed(0.f)
|
, mWindSpeed(0.f)
|
||||||
|
, mCurrentWindSpeed(0.f)
|
||||||
|
, mNextWindSpeed(0.f)
|
||||||
, mIsStorm(false)
|
, mIsStorm(false)
|
||||||
, mPrecipitation(false)
|
, mPrecipitation(false)
|
||||||
, mStormDirection(0,1,0)
|
, mStormDirection(0,1,0)
|
||||||
|
@ -747,6 +745,40 @@ void WeatherManager::setRegionWeather(const std::string& region, const int curre
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
osg::Vec3f WeatherManager::calculateStormDirection()
|
||||||
|
{
|
||||||
|
osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());
|
||||||
|
playerPos.z() = 0;
|
||||||
|
osg::Vec3f redMountainPos (25000, 70000, 0);
|
||||||
|
osg::Vec3f stormDirection = (playerPos - redMountainPos);
|
||||||
|
stormDirection.normalize();
|
||||||
|
|
||||||
|
return stormDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
|
||||||
|
{
|
||||||
|
float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f);
|
||||||
|
if (currentSpeed == 0.f)
|
||||||
|
currentSpeed = targetSpeed;
|
||||||
|
|
||||||
|
float multiplier = mWeatherSettings[weatherId].mRainEffect.empty() ? 1.f : 0.5f;
|
||||||
|
float updatedSpeed = (Misc::Rng::rollClosedProbability() - 0.5f) * multiplier * targetSpeed + currentSpeed;
|
||||||
|
|
||||||
|
if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed)
|
||||||
|
{
|
||||||
|
currentSpeed = updatedSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take in account direction to the Red Mountain, when needed
|
||||||
|
if (weatherId == 6 || weatherId == 7)
|
||||||
|
{
|
||||||
|
currentSpeed = (calculateStormDirection() * currentSpeed).length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
|
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
|
||||||
{
|
{
|
||||||
MWWorld::ConstPtr player = MWMechanics::getPlayer();
|
MWWorld::ConstPtr player = MWMechanics::getPlayer();
|
||||||
|
@ -779,12 +811,21 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
|
||||||
{
|
{
|
||||||
mRendering.setSkyEnabled(false);
|
mRendering.setSkyEnabled(false);
|
||||||
stopSounds();
|
stopSounds();
|
||||||
|
mWindSpeed = 0.f;
|
||||||
|
mCurrentWindSpeed = 0.f;
|
||||||
|
mNextWindSpeed = 0.f;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateWeatherResult(time.getHour(), duration, paused);
|
calculateWeatherResult(time.getHour(), duration, paused);
|
||||||
|
|
||||||
mWindSpeed = mResult.mWindSpeed;
|
if (!paused)
|
||||||
|
{
|
||||||
|
mWindSpeed = mResult.mWindSpeed;
|
||||||
|
mCurrentWindSpeed = mResult.mCurrentWindSpeed;
|
||||||
|
mNextWindSpeed = mResult.mNextWindSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
mIsStorm = mResult.mIsStorm;
|
mIsStorm = mResult.mIsStorm;
|
||||||
|
|
||||||
// For some reason Ash Storm is not considered as a precipitation weather in game
|
// For some reason Ash Storm is not considered as a precipitation weather in game
|
||||||
|
@ -793,11 +834,7 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
|
||||||
|
|
||||||
if (mIsStorm)
|
if (mIsStorm)
|
||||||
{
|
{
|
||||||
osg::Vec3f playerPos (player.getRefData().getPosition().asVec3());
|
mStormDirection = calculateStormDirection();
|
||||||
playerPos.z() = 0;
|
|
||||||
osg::Vec3f redMountainPos (25000, 70000, 0);
|
|
||||||
mStormDirection = (playerPos - redMountainPos);
|
|
||||||
mStormDirection.normalize();
|
|
||||||
mRendering.getSkyManager()->setStormDirection(mStormDirection);
|
mRendering.getSkyManager()->setStormDirection(mStormDirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,7 +1357,9 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
|
||||||
|
|
||||||
mResult.mCloudTexture = current.mCloudTexture;
|
mResult.mCloudTexture = current.mCloudTexture;
|
||||||
mResult.mCloudBlendFactor = 0;
|
mResult.mCloudBlendFactor = 0;
|
||||||
mResult.mWindSpeed = current.mWindSpeed;
|
mResult.mNextWindSpeed = 0;
|
||||||
|
mResult.mWindSpeed = mResult.mCurrentWindSpeed = calculateWindSpeed(weatherID, mWindSpeed);
|
||||||
|
|
||||||
mResult.mCloudSpeed = current.mCloudSpeed;
|
mResult.mCloudSpeed = current.mCloudSpeed;
|
||||||
mResult.mGlareView = current.mGlareView;
|
mResult.mGlareView = current.mGlareView;
|
||||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||||
|
@ -1330,7 +1369,11 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
|
||||||
mResult.mIsStorm = current.mIsStorm;
|
mResult.mIsStorm = current.mIsStorm;
|
||||||
|
|
||||||
mResult.mRainSpeed = current.mRainSpeed;
|
mResult.mRainSpeed = current.mRainSpeed;
|
||||||
mResult.mRainFrequency = current.mRainFrequency;
|
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
|
||||||
|
mResult.mRainDiameter = current.mRainDiameter;
|
||||||
|
mResult.mRainMinHeight = current.mRainMinHeight;
|
||||||
|
mResult.mRainMaxHeight = current.mRainMaxHeight;
|
||||||
|
mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;
|
||||||
|
|
||||||
mResult.mParticleEffect = current.mParticleEffect;
|
mResult.mParticleEffect = current.mParticleEffect;
|
||||||
mResult.mRainEffect = current.mRainEffect;
|
mResult.mRainEffect = current.mRainEffect;
|
||||||
|
@ -1404,23 +1447,35 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
||||||
mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);
|
mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);
|
||||||
mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor);
|
mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor);
|
||||||
mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor);
|
mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor);
|
||||||
mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor);
|
|
||||||
|
mResult.mCurrentWindSpeed = calculateWindSpeed(mCurrentWeather, mCurrentWindSpeed);
|
||||||
|
mResult.mNextWindSpeed = calculateWindSpeed(mNextWeather, mNextWindSpeed);
|
||||||
|
|
||||||
|
mResult.mWindSpeed = lerp(mResult.mCurrentWindSpeed, mResult.mNextWindSpeed, factor);
|
||||||
mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);
|
mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);
|
||||||
mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);
|
mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);
|
||||||
mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);
|
mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);
|
||||||
|
|
||||||
mResult.mNight = current.mNight;
|
mResult.mNight = current.mNight;
|
||||||
|
|
||||||
if(factor < 0.5)
|
float threshold = mWeatherSettings[mNextWeather].mRainThreshold;
|
||||||
|
if (threshold <= 0)
|
||||||
|
threshold = 0.5f;
|
||||||
|
|
||||||
|
if(factor < threshold)
|
||||||
{
|
{
|
||||||
mResult.mIsStorm = current.mIsStorm;
|
mResult.mIsStorm = current.mIsStorm;
|
||||||
mResult.mParticleEffect = current.mParticleEffect;
|
mResult.mParticleEffect = current.mParticleEffect;
|
||||||
mResult.mRainEffect = current.mRainEffect;
|
mResult.mRainEffect = current.mRainEffect;
|
||||||
mResult.mRainSpeed = current.mRainSpeed;
|
mResult.mRainSpeed = current.mRainSpeed;
|
||||||
mResult.mRainFrequency = current.mRainFrequency;
|
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
|
||||||
mResult.mAmbientSoundVolume = 1-(factor*2);
|
mResult.mAmbientSoundVolume = 1 - factor / threshold;
|
||||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||||
|
mResult.mRainDiameter = current.mRainDiameter;
|
||||||
|
mResult.mRainMinHeight = current.mRainMinHeight;
|
||||||
|
mResult.mRainMaxHeight = current.mRainMaxHeight;
|
||||||
|
mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1428,10 +1483,15 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
||||||
mResult.mParticleEffect = other.mParticleEffect;
|
mResult.mParticleEffect = other.mParticleEffect;
|
||||||
mResult.mRainEffect = other.mRainEffect;
|
mResult.mRainEffect = other.mRainEffect;
|
||||||
mResult.mRainSpeed = other.mRainSpeed;
|
mResult.mRainSpeed = other.mRainSpeed;
|
||||||
mResult.mRainFrequency = other.mRainFrequency;
|
mResult.mRainEntranceSpeed = other.mRainEntranceSpeed;
|
||||||
mResult.mAmbientSoundVolume = 2*(factor-0.5f);
|
mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold);
|
||||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||||
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
||||||
|
|
||||||
|
mResult.mRainDiameter = other.mRainDiameter;
|
||||||
|
mResult.mRainMinHeight = other.mRainMinHeight;
|
||||||
|
mResult.mRainMaxHeight = other.mRainMaxHeight;
|
||||||
|
mResult.mRainMaxRaindrops = other.mRainMaxRaindrops;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,20 @@ namespace MWWorld
|
||||||
float mRainSpeed;
|
float mRainSpeed;
|
||||||
|
|
||||||
// How often does a new rain mesh spawn?
|
// How often does a new rain mesh spawn?
|
||||||
float mRainFrequency;
|
float mRainEntranceSpeed;
|
||||||
|
|
||||||
|
// Maximum count of rain particles
|
||||||
|
int mRainMaxRaindrops;
|
||||||
|
|
||||||
|
// Radius of rain effect
|
||||||
|
float mRainDiameter;
|
||||||
|
|
||||||
|
// Transition threshold to spawn rain
|
||||||
|
float mRainThreshold;
|
||||||
|
|
||||||
|
// Height of rain particles spawn
|
||||||
|
float mRainMinHeight;
|
||||||
|
float mRainMaxHeight;
|
||||||
|
|
||||||
std::string mParticleEffect;
|
std::string mParticleEffect;
|
||||||
|
|
||||||
|
@ -370,6 +383,8 @@ namespace MWWorld
|
||||||
MoonModel mSecunda;
|
MoonModel mSecunda;
|
||||||
|
|
||||||
float mWindSpeed;
|
float mWindSpeed;
|
||||||
|
float mCurrentWindSpeed;
|
||||||
|
float mNextWindSpeed;
|
||||||
bool mIsStorm;
|
bool mIsStorm;
|
||||||
bool mPrecipitation;
|
bool mPrecipitation;
|
||||||
osg::Vec3f mStormDirection;
|
osg::Vec3f mStormDirection;
|
||||||
|
@ -411,6 +426,7 @@ namespace MWWorld
|
||||||
bool updateWeatherRegion(const std::string& playerRegion);
|
bool updateWeatherRegion(const std::string& playerRegion);
|
||||||
void updateWeatherTransitions(const float elapsedRealSeconds);
|
void updateWeatherTransitions(const float elapsedRealSeconds);
|
||||||
void forceWeather(const int weatherID);
|
void forceWeather(const int weatherID);
|
||||||
|
osg::Vec3f calculateStormDirection();
|
||||||
|
|
||||||
bool inTransition();
|
bool inTransition();
|
||||||
void addWeatherTransition(const int weatherID);
|
void addWeatherTransition(const int weatherID);
|
||||||
|
@ -418,6 +434,7 @@ namespace MWWorld
|
||||||
void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused);
|
void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused);
|
||||||
void calculateResult(const int weatherID, const float gameHour);
|
void calculateResult(const int weatherID, const float gameHour);
|
||||||
void calculateTransitionResult(const float factor, const float gameHour);
|
void calculateTransitionResult(const float factor, const float gameHour);
|
||||||
|
float calculateWindSpeed(int weatherId, float currentSpeed);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -971,7 +971,17 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::advanceTime (double hours, bool incremental)
|
void World::advanceTime (double hours, bool incremental)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast<float>(hours * 3600));
|
if (!incremental)
|
||||||
|
{
|
||||||
|
// When we fast-forward time, we should recharge magic items
|
||||||
|
// in all loaded cells, using game world time
|
||||||
|
float duration = hours * 3600;
|
||||||
|
const float timeScaleFactor = getTimeScaleFactor();
|
||||||
|
if (timeScaleFactor != 0.0f)
|
||||||
|
duration /= timeScaleFactor;
|
||||||
|
|
||||||
|
rechargeItems(duration, false);
|
||||||
|
}
|
||||||
|
|
||||||
mWeatherManager->advanceTime (hours, incremental);
|
mWeatherManager->advanceTime (hours, incremental);
|
||||||
|
|
||||||
|
@ -3762,7 +3772,6 @@ namespace MWWorld
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
closestMarker = marker;
|
closestMarker = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return closestMarker;
|
return closestMarker;
|
||||||
|
@ -3773,6 +3782,22 @@ namespace MWWorld
|
||||||
mCells.rest(hours);
|
mCells.rest(hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::rechargeItems(double duration, bool activeOnly)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = getPlayerPtr();
|
||||||
|
player.getClass().getInventoryStore(player).rechargeItems(duration);
|
||||||
|
|
||||||
|
if (activeOnly)
|
||||||
|
{
|
||||||
|
for (auto &cell : mWorldScene->getActiveCells())
|
||||||
|
{
|
||||||
|
cell->recharge(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mCells.recharge(duration);
|
||||||
|
}
|
||||||
|
|
||||||
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
|
||||||
const std::string& id)
|
const std::string& id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -752,6 +752,7 @@ namespace MWWorld
|
||||||
///< check if the player is allowed to rest
|
///< check if the player is allowed to rest
|
||||||
|
|
||||||
void rest(double hours) override;
|
void rest(double hours) override;
|
||||||
|
void rechargeItems(double duration, bool activeOnly) override;
|
||||||
|
|
||||||
/// \todo Probably shouldn't be here
|
/// \todo Probably shouldn't be here
|
||||||
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;
|
||||||
|
|
Loading…
Reference in a new issue