1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-04-01 07:36:44 +00:00

Merge branch 'master' into windspeed

This commit is contained in:
Alexei Dobrohotov 2019-10-11 02:04:30 +03:00 committed by GitHub
commit cdbe58c33a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 536 additions and 318 deletions

View file

@ -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,6 +19,7 @@
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 #4270: Closing doors while they are obstructed desyncs closing sfx Bug #4270: Closing doors while they are obstructed desyncs closing sfx
@ -28,6 +30,7 @@
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 #4449: Value returned by GetWindSpeed is incorrect
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 #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
@ -77,6 +80,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
@ -116,6 +120,7 @@
Bug #5050: Invalid spell effects are not handled gracefully Bug #5050: Invalid spell effects are not handled gracefully
Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame
Bug #5056: Calling Cast function on player doesn't equip the spell but casts it Bug #5056: Calling Cast function on player doesn't equip the spell but casts it
Bug #5059: Modded animation with combined attack keys always does max damage and can double damage
Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends
Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden
Bug #5067: Ranged attacks on unaware opponents ("critical hits") differ from the vanilla engine Bug #5067: Ranged attacks on unaware opponents ("critical hits") differ from the vanilla engine
@ -140,6 +145,7 @@
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled Bug #5124: Arrow remains attached to actor if pulling animation was cancelled
Bug #5126: Swimming creatures without RunForward animations are motionless during combat Bug #5126: Swimming creatures without RunForward animations are motionless during combat
Bug #5134: Doors rotation by "Lock" console command is inconsistent Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5136: LegionUniform script: can not access local variables
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Bug #5149: Failing lock pick attempts isn't always a crime Bug #5149: Failing lock pick attempts isn't always a crime
Bug #5155: Lock/unlock behavior differs from vanilla Bug #5155: Lock/unlock behavior differs from vanilla
@ -151,6 +157,8 @@
Bug #5167: Player can select and cast spells before magic menu is enabled Bug #5167: Player can select and cast spells before magic menu is enabled
Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change
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 #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
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

View file

@ -101,7 +101,9 @@ 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)
Miscellaneous: Miscellaneous:
- Upgraded to FFMPEG3 for media decoding (#4686) - Upgraded to FFMPEG3 for media decoding (#4686)

View file

@ -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)

View file

@ -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:

View file

@ -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)

View file

@ -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
) )

View file

@ -133,6 +133,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();

View file

@ -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.

View file

@ -584,6 +584,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;

View file

@ -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)

View file

@ -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);

View file

@ -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))

View file

@ -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();

View file

@ -38,7 +38,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();
@ -47,8 +47,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("");
return dropped; return dropped;
} }

View file

@ -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;
} }

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -98,7 +98,7 @@ namespace MWGui
if (pickpocket.finish()) if (pickpocket.finish())
{ {
MWBase::Environment::get().getMechanicsManager()->commitCrime( MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true; mPickpocketDetected = true;
} }
@ -126,7 +126,7 @@ namespace MWGui
if (pickpocket.pick(item, count)) if (pickpocket.pick(item, count))
{ {
MWBase::Environment::get().getMechanicsManager()->commitCrime( MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); player, mActor, MWBase::MechanicsManager::OT_Pickpocket, std::string(), 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
mPickpocketDetected = true; mPickpocketDetected = true;
return false; return false;

View file

@ -17,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"
@ -130,62 +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));
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
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();
} }

View file

@ -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");
}
} }
} }

View file

@ -9,6 +9,7 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "movement.hpp" #include "movement.hpp"
#include "steering.hpp"
namespace MWMechanics namespace MWMechanics
{ {
@ -33,16 +34,18 @@ namespace MWMechanics
if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled()) if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled())
return true; return true;
//Set the target destination for the actor // Turn to target and move to it directly, without pathfinding.
const osg::Vec3f dest = target.getRefData().getPosition().asVec3(); const osg::Vec3f targetDir = target.getRefData().getPosition().asVec3() - actor.getRefData().getPosition().asVec3();
if (pathTo(actor, dest, duration, MWBase::Environment::get().getWorld()->getMaxActivationDistance())) //Stop when you get in activation range zTurn(actor, std::atan2(targetDir.x(), targetDir.y()), 0.f);
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
if (MWBase::Environment::get().getWorld()->getMaxActivationDistance() >= targetDir.length())
{ {
// activate when reached // Note: we intentionally do not cancel package after activation here for backward compatibility with original engine.
MWBase::Environment::get().getWorld()->activate(target, actor); MWBase::Environment::get().getWorld()->activate(target, actor);
return true;
} }
return false; return false;
} }

View file

@ -1,5 +1,7 @@
#include "aiavoiddoor.hpp" #include "aiavoiddoor.hpp"
#include <components/misc/rng.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -11,8 +13,10 @@
#include "actorutil.hpp" #include "actorutil.hpp"
#include "steering.hpp" #include "steering.hpp"
static const int MAX_DIRECTIONS = 4;
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr)
: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mLastPos(ESM::Position()), mAdjAngle(0) : AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0)
{ {
} }
@ -22,25 +26,18 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
ESM::Position pos = actor.getRefData().getPosition(); ESM::Position pos = actor.getRefData().getPosition();
if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing
mLastPos = pos; mLastPos = pos.asVec3();
mDuration -= duration; //Update timer mDuration -= duration; //Update timer
if(mDuration < 0) { if (mDuration < 0)
float x = pos.pos[0] - mLastPos.pos[0]; {
float y = pos.pos[1] - mLastPos.pos[1]; if (isStuck(pos.asVec3()))
float z = pos.pos[2] - mLastPos.pos[2]; {
float distance = x * x + y * y + z * z; adjustDirection();
if(distance < 10 * 10) { //Got stuck, didn't move
if(mAdjAngle == 0) //Try going in various directions
mAdjAngle = osg::PI / 2;
else if (mAdjAngle == osg::PI / 2)
mAdjAngle = -osg::PI / 2;
else
mAdjAngle = 0;
mDuration = 1; //reset timer mDuration = 1; //reset timer
} }
else //Not stuck else
return true; // We have tried backing up for more than one second, we've probably cleared it return true; // We have tried backing up for more than one second, we've probably cleared it
} }
@ -54,7 +51,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
// Turn away from the door and move when turn completed // Turn away from the door and move when turn completed
if (zTurn(actor, std::atan2(x,y) + mAdjAngle, osg::DegreesToRadians(5.f))) if (zTurn(actor, std::atan2(x,y) + getAdjustedAngle(), osg::DegreesToRadians(5.f)))
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
else else
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
@ -90,4 +87,17 @@ unsigned int MWMechanics::AiAvoidDoor::getPriority() const
return 2; return 2;
} }
bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{
return (actorPos - mLastPos).length2() < 10 * 10;
}
void MWMechanics::AiAvoidDoor::adjustDirection()
{
mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS);
}
float MWMechanics::AiAvoidDoor::getAdjustedAngle() const
{
return 2 * osg::PI / MAX_DIRECTIONS * mDirection;
}

View file

@ -36,8 +36,14 @@ namespace MWMechanics
private: private:
float mDuration; float mDuration;
MWWorld::ConstPtr mDoorPtr; MWWorld::ConstPtr mDoorPtr;
ESM::Position mLastPos; osg::Vec3f mLastPos;
float mAdjAngle; int mDirection;
bool isStuck(const osg::Vec3f& actorPos) const;
void adjustDirection();
float getAdjustedAngle() const;
}; };
} }
#endif #endif

View file

@ -1629,7 +1629,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown())
{ {
float attackStrength = complete; float attackStrength = complete;
if (!mPtr.getClass().isNpc()) float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (minAttackTime == maxAttackTime)
{ {
// most creatures don't actually have an attack wind-up animation, so use a uniform random value // most creatures don't actually have an attack wind-up animation, so use a uniform random value
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings) // (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
@ -1735,7 +1737,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
// If actor is already stopped preparing attack, do not play the "min attack -> max attack" part. // If actor is already stopped preparing attack, do not play the "min attack -> max attack" part.
// Happens if the player did not hold the attack button. // Happens if the player did not hold the attack button.
// Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be 1. // Note: if the "min attack"->"max attack" is a stub, "play" it anyway. Attack strength will be random.
float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack"); float minAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"min attack");
float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack"); float maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack");
if (mAttackingOrSpell || minAttackTime == maxAttackTime) if (mAttackingOrSpell || minAttackTime == maxAttackTime)

View file

@ -287,16 +287,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())

View file

@ -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.

View file

@ -0,0 +1,92 @@
#include "recharge.hpp"
#include <components/misc/rng.hpp>
#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)));
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;
}
}

View 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

View file

@ -163,6 +163,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)

View file

@ -61,7 +61,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.

View file

@ -21,6 +21,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"
@ -328,6 +329,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();
@ -345,7 +347,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;
} }
@ -992,6 +994,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)
@ -1027,4 +1065,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));
}
} }

View file

@ -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.

View file

@ -13,6 +13,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"
@ -114,7 +115,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() {}
@ -241,7 +246,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)
@ -259,30 +263,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;
@ -298,7 +286,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);
@ -408,6 +397,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;
@ -477,50 +506,64 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
int count, bool topLevel, const std::string& levItem) int count, bool topLevel, const std::string& levItem)
{ {
if (count == 0) return; //Don't restock with nothing. if (count == 0) return; //Don't restock with nothing.
try { try
{
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
{ {
const ESM::ItemLevList* levItemList = ref.getPtr().get<ESM::ItemLevList>()->mBase; addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
} }
else else
{ {
// A negative count indicates restocking items // Adding just one item per time to make sure there isn't a stack of scripted items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks for (int i = 0; i < abs(count); i++)
if (!levItem.empty() && count < 0) addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count);
} }
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what(); Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what();
} }
}
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner,
int count, bool topLevel, const std::string& levItem)
{
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
{
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
}
else
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ptr.getCellRef().setOwner(owner);
addImp (ptr, count);
}
} }
void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner) void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)
@ -619,6 +662,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

View file

@ -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;
@ -94,6 +100,7 @@ namespace MWWorld
mutable bool mWeightUpToDate; mutable bool mWeightUpToDate;
ContainerStoreIterator addImp (const Ptr& ptr, int count); ContainerStoreIterator addImp (const Ptr& ptr, int count);
void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
template<typename T> template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection, ContainerStoreIterator getState (CellRefList<T>& collection,
@ -107,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;
@ -130,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.
@ -155,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).
/// ///

View file

@ -101,7 +101,6 @@ MWWorld::InventoryStore::InventoryStore()
, mUpdatesEnabled (true) , mUpdatesEnabled (true)
, mFirstAutoEquip(true) , mFirstAutoEquip(true)
, mSelectedEnchantItem(end()) , mSelectedEnchantItem(end())
, mRechargingItemsUpToDate(false)
{ {
initSlots (mSlots); initSlots (mSlots);
} }
@ -114,7 +113,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);
} }
@ -133,9 +131,9 @@ 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 or weapon item is added, but not for the player nor werewolves
if (actorPtr != MWMechanics::getPlayer() if (actorPtr != MWMechanics::getPlayer()
@ -709,12 +707,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);
@ -957,57 +949,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)

View file

@ -101,18 +101,11 @@ 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);
@ -132,7 +125,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).
/// ///
@ -171,10 +164,6 @@ namespace MWWorld
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 +205,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

View file

@ -857,7 +857,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);
@ -1649,7 +1659,6 @@ namespace MWWorld
} }
// we need to undo the rotation // we need to undo the rotation
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
reached = false; reached = false;
} }
} }
@ -1671,6 +1680,8 @@ namespace MWWorld
if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound)) if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound); MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
} }
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
} }
// the rotation order we want to use // the rotation order we want to use
@ -3304,7 +3315,6 @@ namespace MWWorld
closestDistance = distance; closestDistance = distance;
closestMarker = marker; closestMarker = marker;
} }
} }
return closestMarker; return closestMarker;
@ -3315,6 +3325,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)
{ {

View file

@ -574,6 +574,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;

View file

@ -489,7 +489,7 @@ namespace Compiler
parseArguments ("l", scanner); parseArguments ("l", scanner);
Generator::random (mCode); Generator::random (mCode);
mOperands.push_back ('l'); mOperands.push_back ('f');
mNextOperand = false; mNextOperand = false;
return true; return true;

View file

@ -323,7 +323,9 @@ namespace ESM
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
mContext (land.mContext), mDataTypes (land.mDataTypes), mContext (land.mContext), mDataTypes (land.mDataTypes),
mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) mLandData (land.mLandData ? new LandData (*land.mLandData) : 0)
{} {
std::copy(land.mWnam, land.mWnam + LAND_GLOBAL_MAP_LOD_SIZE, mWnam);
}
Land& Land::operator= (Land land) Land& Land::operator= (Land land)
{ {
@ -340,6 +342,7 @@ namespace ESM
std::swap (mContext, land.mContext); std::swap (mContext, land.mContext);
std::swap (mDataTypes, land.mDataTypes); std::swap (mDataTypes, land.mDataTypes);
std::swap (mLandData, land.mLandData); std::swap (mLandData, land.mLandData);
std::swap (mWnam, land.mWnam);
} }
const Land::LandData *Land::getLandData (int flags) const const Land::LandData *Land::getLandData (int flags) const

View file

@ -190,9 +190,7 @@ namespace Interpreter
throw std::runtime_error ( throw std::runtime_error (
"random: argument out of range (Don't be so negative!)"); "random: argument out of range (Don't be so negative!)");
Type_Integer value = Misc::Rng::rollDice(limit); // [o, limit) runtime[0].mFloat = static_cast<Type_Float>(Misc::Rng::rollDice(limit)); // [o, limit)
runtime[0].mInteger = value;
} }
}; };

View file

@ -210,7 +210,6 @@ public:
private: private:
Terrain::Storage* mStorage; Terrain::Storage* mStorage;
float mLodFactor;
float mMinX, mMaxX, mMinY, mMaxY; float mMinX, mMaxX, mMinY, mMaxY;
float mMinSize; float mMinSize;

View file

@ -1,5 +1,5 @@
Shadow Settings Shadows Settings
############### ################
Main settings Main settings
************* *************