mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 14:56:44 +00:00 
			
		
		
		
	Merge branch 'master' into windspeed
This commit is contained in:
		
						commit
						cdbe58c33a
					
				
					 44 changed files with 536 additions and 318 deletions
				
			
		|  | @ -2,6 +2,7 @@ | |||
| ------ | ||||
| 
 | ||||
|     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 #2969: Scripted items can stack | ||||
|     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 #3812: Wrong multiline tooltips width when word-wrapping is enabled | ||||
|     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 #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect | ||||
|     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 #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 #4540: Rain delay when exiting water | ||||
|     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 | ||||
|  | @ -77,6 +80,7 @@ | |||
|     Bug #4888: Global variable stray explicit reference calls break script compilation | ||||
|     Bug #4896: Title screen music doesn't loop | ||||
|     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 #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 | ||||
|  | @ -116,6 +120,7 @@ | |||
|     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 #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 #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 | ||||
|  | @ -140,6 +145,7 @@ | |||
|     Bug #5124: Arrow remains attached to actor if pulling animation was cancelled | ||||
|     Bug #5126: Swimming creatures without RunForward animations are motionless during combat | ||||
|     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 #5149: Failing lock pick attempts isn't always a crime | ||||
|     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 #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 #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 #2229: Improve pathfinding AI | ||||
|     Feature #3025: Analogue gamepad movement controls | ||||
|  |  | |||
|  | @ -101,7 +101,9 @@ Editor Bug Fixes: | |||
| - 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) | ||||
| - "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) | ||||
| - Minimap doesn't get corrupted, when editing new omwgame (#5177) | ||||
| 
 | ||||
| Miscellaneous: | ||||
| - Upgraded to FFMPEG3 for media decoding (#4686) | ||||
|  |  | |||
|  | @ -41,9 +41,9 @@ | |||
| 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), | ||||
|     mBrushTexture("L0#0"), | ||||
|     mBrushSize(0), | ||||
|     mBrushSize(1), | ||||
|     mBrushShape(0), | ||||
|     mTextureBrushScenetool(0), | ||||
|     mTextureBrushScenetool(nullptr), | ||||
|     mDragMode(InteractionType_None), | ||||
|     mParentNode(parentNode), | ||||
|     mIsEditing(false) | ||||
|  | @ -82,7 +82,7 @@ void CSVRender::TerrainTextureMode::deactivate(CSVWidget::SceneToolbar* toolbar) | |||
|     { | ||||
|         toolbar->removeTool (mTextureBrushScenetool); | ||||
|         delete mTextureBrushScenetool; | ||||
|         mTextureBrushScenetool = 0; | ||||
|         mTextureBrushScenetool = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     if (mTerrainTextureSelection) | ||||
|  |  | |||
|  | @ -51,36 +51,37 @@ namespace CSVRender | |||
|             /// \brief Editmode for terrain texture grid
 | ||||
|             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
 | ||||
|             void primaryEditPressed (const WorldspaceHitResult& hit); | ||||
|             void primaryEditPressed (const WorldspaceHitResult& hit) final; | ||||
| 
 | ||||
|             /// \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 deactivate(CSVWidget::SceneToolbar*); | ||||
|             void activate(CSVWidget::SceneToolbar*) final; | ||||
|             void deactivate(CSVWidget::SceneToolbar*) final; | ||||
| 
 | ||||
|             /// \brief Start texture editing command macro
 | ||||
|             virtual bool primaryEditStartDrag (const QPoint& pos); | ||||
|             bool primaryEditStartDrag (const QPoint& pos) final; | ||||
| 
 | ||||
|             virtual bool secondaryEditStartDrag (const QPoint& pos); | ||||
|             virtual bool primarySelectStartDrag (const QPoint& pos); | ||||
|             virtual bool secondarySelectStartDrag (const QPoint& pos); | ||||
|             bool secondaryEditStartDrag (const QPoint& pos) final; | ||||
|             bool primarySelectStartDrag (const QPoint& pos) final; | ||||
|             bool secondarySelectStartDrag (const QPoint& pos) final; | ||||
| 
 | ||||
|             /// \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
 | ||||
|             virtual void dragCompleted(const QPoint& pos); | ||||
|             void dragCompleted(const QPoint& pos) final; | ||||
| 
 | ||||
|             virtual void dragAborted(); | ||||
|             virtual void dragWheel (int diff, double speedFactor); | ||||
|             virtual void dragMoveEvent (QDragMoveEvent *event); | ||||
|             void dragAborted() final; | ||||
|             void dragWheel (int diff, double speedFactor) final; | ||||
|             void dragMoveEvent (QDragMoveEvent *event) final; | ||||
| 
 | ||||
|         private: | ||||
|             /// \brief Handle brush mechanics, maths regarding worldspace hit etc.
 | ||||
|             void editTerrainTextureGrid (const WorldspaceHitResult& hit); | ||||
| 
 | ||||
|  | @ -100,7 +101,6 @@ namespace CSVRender | |||
|             /// \brief Create new cell and land if needed
 | ||||
|             bool allowLandTextureEditing(std::string textureFileName); | ||||
| 
 | ||||
|         private: | ||||
|             std::string mCellId; | ||||
|             std::string mBrushTexture; | ||||
|             int mBrushSize; | ||||
|  | @ -113,7 +113,6 @@ namespace CSVRender | |||
|             std::unique_ptr<TerrainSelection> mTerrainTextureSelection; | ||||
| 
 | ||||
|             const int cellSize {ESM::Land::REAL_SIZE}; | ||||
|             const int landSize {ESM::Land::LAND_SIZE}; | ||||
|             const int landTextureSize {ESM::Land::LAND_TEXTURE_SIZE}; | ||||
| 
 | ||||
|         signals: | ||||
|  |  | |||
|  | @ -33,19 +33,19 @@ | |||
| 
 | ||||
| 
 | ||||
| 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->setTickInterval(10); | ||||
|     mBrushSizeSlider->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); | ||||
|     mBrushSizeSlider->setSingleStep(1); | ||||
| 
 | ||||
|     mBrushSizeSpinBox = new QSpinBox; | ||||
|     mBrushSizeSpinBox->setRange(1, CSMPrefs::get()["3D Scene Editing"]["texturebrush-maximumsize"].toInt()); | ||||
|     mBrushSizeSpinBox->setSingleStep(1); | ||||
| 
 | ||||
|     mLayoutSliderSize = new QHBoxLayout; | ||||
|     mLayoutSliderSize->addWidget(mBrushSizeSlider); | ||||
|     mLayoutSliderSize->addWidget(mBrushSizeSpinBox); | ||||
| 
 | ||||
|  | @ -58,7 +58,7 @@ CSVWidget::BrushSizeControls::BrushSizeControls(const QString &title, QWidget *p | |||
| CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QWidget *parent) | ||||
|     : QFrame(parent, Qt::Popup), | ||||
|     mBrushShape(0), | ||||
|     mBrushSize(0), | ||||
|     mBrushSize(1), | ||||
|     mBrushTexture("L0#0"), | ||||
|     mDocument(document) | ||||
| { | ||||
|  | @ -142,60 +142,61 @@ void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton * | |||
| 
 | ||||
| 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(); | ||||
| 
 | ||||
|     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
 | ||||
|     if(landtexturesCollection.getData(index, columnModification).value<int>() == 0) | ||||
|     int index = 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&> ( | ||||
|             *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()) | ||||
|         if (rowInBase == -1) | ||||
|         { | ||||
|             int counter=0; | ||||
|             bool freeIndexFound = false; | ||||
|             const int maxCounter = std::numeric_limits<uint16_t>::max() - 1; | ||||
|             do { | ||||
|                 const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1; | ||||
|                 mBrushTexturePlugin = CSMWorld::LandTexture::createUniqueRecordId(0, counter); | ||||
|                 if (landtexturesCollection.searchId(mBrushTexturePlugin) != -1 && landtexturesCollection.getRecord(mBrushTexturePlugin).isDeleted() == 0) counter = (counter + 1) % maxCounter; | ||||
|                 newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(0, counter); | ||||
|                 if (landtexturesCollection.searchId(brushTexture) != -1 && | ||||
|                     landtexturesCollection.getRecord(brushTexture).isDeleted() == 0 && | ||||
|                     landtexturesCollection.searchId(newBrushTextureId) != -1 && | ||||
|                     landtexturesCollection.getRecord(newBrushTextureId).isDeleted() == 0) | ||||
|                         counter = (counter + 1) % maxCounter; | ||||
|                 else freeIndexFound = true; | ||||
|             } while (freeIndexFound == false); | ||||
|             } while (freeIndexFound == false || counter < maxCounter); | ||||
|         } | ||||
| 
 | ||||
|         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(); | ||||
|         mBrushTexture = mBrushTexturePlugin; | ||||
|         emit passTextureId(mBrushTexture); | ||||
|     } | ||||
| 
 | ||||
|     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>()); | ||||
|     } else | ||||
|     { | ||||
|         newBrushTextureId = ""; | ||||
|         mBrushTextureLabel = "No selected texture or invalid texture"; | ||||
|         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) | ||||
|  |  | |||
|  | @ -81,7 +81,7 @@ add_openmw_dir (mwclass | |||
| add_openmw_dir (mwmechanics | ||||
|     mechanicsmanagerimp stat creaturestats magiceffects movement actorutil | ||||
|     drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor 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 | ||||
|     character actors objects aistate coordinateconverter trading weaponpriority spellpriority weapontype | ||||
|     ) | ||||
|  |  | |||
|  | @ -133,6 +133,7 @@ bool OMW::Engine::frame(float frametime) | |||
|             { | ||||
|                 double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; | ||||
|                 mEnvironment.getWorld()->advanceTime(hours, true); | ||||
|                 mEnvironment.getWorld()->rechargeItems(frametime, true); | ||||
|             } | ||||
|         } | ||||
|         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
 | ||||
|             /// component is up).
 | ||||
| 
 | ||||
|             virtual void advanceTime (float duration) = 0; | ||||
| 
 | ||||
|             virtual void setPlayerName (const std::string& name) = 0; | ||||
|             ///< Set player name.
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -584,6 +584,7 @@ namespace MWBase | |||
|             virtual bool isPlayerInJail() const = 0; | ||||
| 
 | ||||
|             virtual void rest(double hours) = 0; | ||||
|             virtual void rechargeItems(double duration, bool activeOnly) = 0; | ||||
| 
 | ||||
|             virtual void setPlayerTraveling(bool traveling) = 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)) | ||||
|             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) | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ namespace MWGui | |||
|     public: | ||||
|         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); | ||||
| 
 | ||||
|         bool hasProfit(const MWWorld::Ptr& actor); | ||||
|  |  | |||
|  | @ -91,7 +91,7 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) | |||
|     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]; | ||||
|     if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ namespace MWGui | |||
|         virtual ModelIndex getIndex (ItemStack item); | ||||
|         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 update(); | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ namespace MWGui | |||
|     public: | ||||
|         WorldItemModel(float left, float top) : mLeft(left), mTop(top) {} | ||||
|         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(); | ||||
| 
 | ||||
|  | @ -47,8 +47,7 @@ namespace MWGui | |||
|                 dropped = world->placeObject(item.mBase, mLeft, mTop, count); | ||||
|             else | ||||
|                 dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); | ||||
|             if (setNewOwner) | ||||
|                 dropped.getCellRef().setOwner(""); | ||||
|             dropped.getCellRef().setOwner(""); | ||||
| 
 | ||||
|             return dropped; | ||||
|         } | ||||
|  |  | |||
|  | @ -46,11 +46,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) | |||
|     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)) | ||||
|         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) | ||||
|  | @ -88,7 +88,7 @@ MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, I | |||
|     if (item.mFlags & ItemStack::Flag_Bound) | ||||
|         return MWWorld::Ptr(); | ||||
| 
 | ||||
|     MWWorld::Ptr ret = otherModel->copyItem(item, count, false); | ||||
|     MWWorld::Ptr ret = otherModel->copyItem(item, count); | ||||
|     removeItem(item, count); | ||||
|     return ret; | ||||
| } | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ namespace MWGui | |||
| 
 | ||||
|         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); | ||||
| 
 | ||||
|         /// Move items from this model to \a otherModel.
 | ||||
|  |  | |||
|  | @ -116,9 +116,9 @@ namespace MWGui | |||
|         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) | ||||
|  |  | |||
|  | @ -67,7 +67,7 @@ namespace MWGui | |||
| 
 | ||||
|         /// @param setNewOwner If true, set the copied item's owner to the actor we are copying 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; | ||||
| 
 | ||||
|         /// 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 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 ModelIndex getIndex (ItemStack item); | ||||
| 
 | ||||
|  |  | |||
|  | @ -98,7 +98,7 @@ namespace MWGui | |||
|         if (pickpocket.finish()) | ||||
|         { | ||||
|             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); | ||||
|             mPickpocketDetected = true; | ||||
|         } | ||||
|  | @ -126,7 +126,7 @@ namespace MWGui | |||
|         if (pickpocket.pick(item, count)) | ||||
|         { | ||||
|             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); | ||||
|             mPickpocketDetected = true; | ||||
|             return false; | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| 
 | ||||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/actorutil.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "itemwidget.hpp" | ||||
| #include "itemchargeview.hpp" | ||||
|  | @ -130,62 +131,9 @@ void Recharge::onItemCancel() | |||
| void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item) | ||||
| { | ||||
|     MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>(); | ||||
| 
 | ||||
|     if (!gem.getRefData().getCount()) | ||||
|     if (!MWMechanics::rechargeItem(item, gem)) | ||||
|         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(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -652,19 +652,22 @@ namespace MWGui | |||
|         std::string ret; | ||||
|         ret += getMiscString(cellref.getOwner(), "Owner"); | ||||
|         const std::string factionId = cellref.getFaction(); | ||||
|         ret += getMiscString(factionId, "Faction"); | ||||
|         if (!factionId.empty() && cellref.getFactionRank() >= 0) | ||||
|         if (!factionId.empty()) | ||||
|         { | ||||
|             const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); | ||||
|             const ESM::Faction *fact = store.get<ESM::Faction>().search(factionId); | ||||
|             if (fact != nullptr) | ||||
|             { | ||||
|                 int rank = cellref.getFactionRank(); | ||||
|                 const std::string rankName = fact->mRanks[rank]; | ||||
|                 if (rankName.empty()) | ||||
|                     ret += getValueString(cellref.getFactionRank(), "Rank"); | ||||
|                 else | ||||
|                     ret += getMiscString(rankName, "Rank"); | ||||
|                 ret += getMiscString(fact->mName.empty() ? factionId : fact->mName, "Owner Faction"); | ||||
|                 if (cellref.getFactionRank() >= 0) | ||||
|                 { | ||||
|                     int rank = cellref.getFactionRank(); | ||||
|                     const std::string rankName = fact->mRanks[rank]; | ||||
|                     if (rankName.empty()) | ||||
|                         ret += getValueString(cellref.getFactionRank(), "Rank"); | ||||
|                     else | ||||
|                         ret += getMiscString(rankName, "Rank"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| 
 | ||||
| #include "creaturestats.hpp" | ||||
| #include "movement.hpp" | ||||
| #include "steering.hpp" | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
|  | @ -33,16 +34,18 @@ namespace MWMechanics | |||
|         if (target == MWWorld::Ptr() || !target.getRefData().getCount() || !target.getRefData().isEnabled()) | ||||
|             return true; | ||||
| 
 | ||||
|         //Set the target destination for the actor
 | ||||
|         const osg::Vec3f dest = target.getRefData().getPosition().asVec3(); | ||||
|         // Turn to target and move to it directly, without pathfinding.
 | ||||
|         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); | ||||
|             return true; | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| #include "aiavoiddoor.hpp" | ||||
| 
 | ||||
| #include <components/misc/rng.hpp> | ||||
| 
 | ||||
| #include "../mwbase/world.hpp" | ||||
| #include "../mwbase/environment.hpp" | ||||
| #include "../mwbase/mechanicsmanager.hpp" | ||||
|  | @ -11,8 +13,10 @@ | |||
| #include "actorutil.hpp" | ||||
| #include "steering.hpp" | ||||
| 
 | ||||
| static const int MAX_DIRECTIONS = 4; | ||||
| 
 | ||||
| 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(); | ||||
|     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
 | ||||
| 
 | ||||
|     if(mDuration < 0) { | ||||
|         float x = pos.pos[0] - mLastPos.pos[0]; | ||||
|         float y = pos.pos[1] - mLastPos.pos[1]; | ||||
|         float z = pos.pos[2] - mLastPos.pos[2]; | ||||
|         float distance = x * x + y * y + z * z; | ||||
|         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; | ||||
|     if (mDuration < 0) | ||||
|     { | ||||
|         if (isStuck(pos.asVec3())) | ||||
|         { | ||||
|             adjustDirection(); | ||||
|             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
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -54,7 +51,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont | |||
|     actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); | ||||
| 
 | ||||
|     // 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; | ||||
|     else | ||||
|         actor.getClass().getMovementSettings(actor).mPosition[1] = 0; | ||||
|  | @ -90,4 +87,17 @@ unsigned int MWMechanics::AiAvoidDoor::getPriority() const | |||
|  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; | ||||
| } | ||||
|  |  | |||
|  | @ -36,8 +36,14 @@ namespace MWMechanics | |||
|         private: | ||||
|             float mDuration; | ||||
|             MWWorld::ConstPtr mDoorPtr; | ||||
|             ESM::Position mLastPos; | ||||
|             float mAdjAngle; | ||||
|             osg::Vec3f mLastPos; | ||||
|             int mDirection; | ||||
| 
 | ||||
|             bool isStuck(const osg::Vec3f& actorPos) const; | ||||
| 
 | ||||
|             void adjustDirection(); | ||||
| 
 | ||||
|             float getAdjustedAngle() const; | ||||
|     }; | ||||
| } | ||||
| #endif | ||||
|  |  | |||
|  | @ -1629,7 +1629,9 @@ bool CharacterController::updateWeaponState(CharacterState& idle) | |||
|         if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack && !isKnockedDown()) | ||||
|         { | ||||
|             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
 | ||||
|                 // (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.
 | ||||
|                 // 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 maxAttackTime = mAnimation->getTextKeyTime(mCurrentWeapon+": "+mAttackType+" "+"max attack"); | ||||
|                 if (mAttackingOrSpell || minAttackTime == maxAttackTime) | ||||
|  |  | |||
|  | @ -287,16 +287,6 @@ namespace MWMechanics | |||
|         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) | ||||
|     { | ||||
|         if(!mWatched.isEmpty()) | ||||
|  |  | |||
|  | @ -78,8 +78,6 @@ namespace MWMechanics | |||
|             /// \param paused In game type does not currently advance (this usually means some GUI
 | ||||
|             /// component is up).
 | ||||
| 
 | ||||
|             virtual void advanceTime (float duration) override; | ||||
| 
 | ||||
|             virtual void setPlayerName (const std::string& name) override; | ||||
|             ///< Set player name.
 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										92
									
								
								apps/openmw/mwmechanics/recharge.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								apps/openmw/mwmechanics/recharge.cpp
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										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 | ||||
|  | @ -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) | ||||
| { | ||||
|     if (id.mPaged) | ||||
|  |  | |||
|  | @ -61,7 +61,9 @@ namespace MWWorld | |||
| 
 | ||||
|             /// @note name must be lower case
 | ||||
|             Ptr getPtr (const std::string& name); | ||||
| 
 | ||||
|             void rest (double hours); | ||||
|             void recharge (float duration); | ||||
| 
 | ||||
|             /// Get all Ptrs referencing \a name in exterior cells
 | ||||
|             /// @note Due to the current implementation of getPtr this only supports one Ptr per cell.
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ | |||
| #include "../mwbase/world.hpp" | ||||
| 
 | ||||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "ptr.hpp" | ||||
| #include "esmstore.hpp" | ||||
|  | @ -328,6 +329,7 @@ namespace MWWorld | |||
|     void CellStore::updateMergedRefs() | ||||
|     { | ||||
|         mMergedRefs.clear(); | ||||
|         mRechargingItemsUpToDate = false; | ||||
|         MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); | ||||
|         forEachInternal(visitor); | ||||
|         visitor.merge(); | ||||
|  | @ -345,7 +347,7 @@ namespace MWWorld | |||
|     } | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
|  | @ -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() | ||||
|     { | ||||
|         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)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -118,6 +118,16 @@ namespace MWWorld | |||
|             /// Repopulate mMergedRefs.
 | ||||
|             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
 | ||||
|             template<class Visitor, class List> | ||||
|             bool forEachImp (Visitor& visitor, List& list) | ||||
|  | @ -184,6 +194,7 @@ namespace MWWorld | |||
|             MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); | ||||
| 
 | ||||
|             void rest(double hours); | ||||
|             void recharge(float duration); | ||||
| 
 | ||||
|             /// 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.
 | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "../mwmechanics/creaturestats.hpp" | ||||
| #include "../mwmechanics/levelledlist.hpp" | ||||
| #include "../mwmechanics/actorutil.hpp" | ||||
| #include "../mwmechanics/recharge.hpp" | ||||
| 
 | ||||
| #include "manualref.hpp" | ||||
| #include "refdata.hpp" | ||||
|  | @ -114,7 +115,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection, | |||
| 
 | ||||
| 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() {} | ||||
| 
 | ||||
|  | @ -241,7 +246,6 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) | |||
|     } | ||||
| 
 | ||||
|     return ptr1 != ptr2 // an item never stacks onto itself
 | ||||
|         && ptr1.getCellRef().getOwner() == ptr2.getCellRef().getOwner() | ||||
|         && ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul() | ||||
| 
 | ||||
|         && 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::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(); | ||||
| 
 | ||||
|     MWWorld::ContainerStoreIterator it = end(); | ||||
| 
 | ||||
|     // 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); | ||||
|     MWWorld::ContainerStoreIterator it = addImp(itemPtr, count); | ||||
| 
 | ||||
|     // The copy of the original item we just made
 | ||||
|     MWWorld::Ptr item = *it; | ||||
|  | @ -298,7 +286,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr | |||
|     pos.pos[2] = 0; | ||||
|     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().setFaction(""); | ||||
|     item.getCellRef().setFactionRank(-1); | ||||
|  | @ -408,6 +397,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons | |||
|     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 toRemove = count; | ||||
|  | @ -477,50 +506,64 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: | |||
|                                               int count, bool topLevel, const std::string& levItem) | ||||
| { | ||||
|     if (count == 0) return; //Don't restock with nothing.
 | ||||
|     try { | ||||
|     try | ||||
|     { | ||||
|         ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); | ||||
| 
 | ||||
|         if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) | ||||
|         if (ref.getPtr().getClass().getScript(ref.getPtr()).empty()) | ||||
|         { | ||||
|             const ESM::ItemLevList* levItemList = ref.getPtr().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(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); | ||||
|             } | ||||
|             addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem); | ||||
|         } | ||||
|         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(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); | ||||
|             // Adding just one item per time to make sure there isn't a stack of scripted items
 | ||||
|             for (int i = 0; i < abs(count); i++) | ||||
|                 addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem); | ||||
|         } | ||||
|     } | ||||
|     catch (const std::exception& e) | ||||
|     { | ||||
|         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) | ||||
|  | @ -619,6 +662,7 @@ void MWWorld::ContainerStore::clear() | |||
| void MWWorld::ContainerStore::flagAsModified() | ||||
| { | ||||
|     mWeightUpToDate = false; | ||||
|     mRechargingItemsUpToDate = false; | ||||
| } | ||||
| 
 | ||||
| float MWWorld::ContainerStore::getWeight() const | ||||
|  |  | |||
|  | @ -71,6 +71,12 @@ namespace MWWorld | |||
|         protected: | ||||
|             ContainerStoreListener* mListener; | ||||
| 
 | ||||
|             // (item, max charge)
 | ||||
|             typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems; | ||||
|             TRechargingItems mRechargingItems; | ||||
| 
 | ||||
|             bool mRechargingItemsUpToDate; | ||||
| 
 | ||||
|         private: | ||||
| 
 | ||||
|             MWWorld::CellRefList<ESM::Potion>            potions; | ||||
|  | @ -94,6 +100,7 @@ namespace MWWorld | |||
|             mutable bool mWeightUpToDate; | ||||
|             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 addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = ""); | ||||
| 
 | ||||
|             template<typename T> | ||||
|             ContainerStoreIterator getState (CellRefList<T>& collection, | ||||
|  | @ -107,6 +114,7 @@ namespace MWWorld | |||
|                 ESM::InventoryState& inventory, int& index, | ||||
|                 bool equipable = false) const; | ||||
| 
 | ||||
|             void updateRechargingItems(); | ||||
| 
 | ||||
|             virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const; | ||||
| 
 | ||||
|  | @ -130,7 +138,7 @@ namespace MWWorld | |||
| 
 | ||||
|             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)
 | ||||
|             ///
 | ||||
|             /// \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
 | ||||
| 
 | ||||
|             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); | ||||
|             ///< 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).
 | ||||
|             ///
 | ||||
|  |  | |||
|  | @ -101,7 +101,6 @@ MWWorld::InventoryStore::InventoryStore() | |||
|  , mUpdatesEnabled (true) | ||||
|  , mFirstAutoEquip(true) | ||||
|  , mSelectedEnchantItem(end()) | ||||
|  , mRechargingItemsUpToDate(false) | ||||
| { | ||||
|     initSlots (mSlots); | ||||
| } | ||||
|  | @ -114,7 +113,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store) | |||
|  , mFirstAutoEquip(store.mFirstAutoEquip) | ||||
|  , mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes) | ||||
|  , mSelectedEnchantItem(end()) | ||||
|  , mRechargingItemsUpToDate(false) | ||||
| { | ||||
|     copySlots (store); | ||||
| } | ||||
|  | @ -133,9 +131,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor | |||
|     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
 | ||||
|     if (actorPtr != MWMechanics::getPlayer() | ||||
|  | @ -709,12 +707,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor) | |||
|     mFirstAutoEquip = false; | ||||
| } | ||||
| 
 | ||||
| void MWWorld::InventoryStore::flagAsModified() | ||||
| { | ||||
|     ContainerStore::flagAsModified(); | ||||
|     mRechargingItemsUpToDate = false; | ||||
| } | ||||
| 
 | ||||
| bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const | ||||
| { | ||||
|     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) | ||||
| { | ||||
|     for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) | ||||
|  |  | |||
|  | @ -101,18 +101,11 @@ namespace MWWorld | |||
|             // selected magic item (for using enchantments of type "Cast once" or "Cast when used")
 | ||||
|             ContainerStoreIterator mSelectedEnchantItem; | ||||
| 
 | ||||
|             // (item, max charge)
 | ||||
|             typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems; | ||||
|             TRechargingItems mRechargingItems; | ||||
| 
 | ||||
|             bool mRechargingItemsUpToDate; | ||||
| 
 | ||||
|             void copySlots (const InventoryStore& store); | ||||
| 
 | ||||
|             void initSlots (TSlots& slots_); | ||||
| 
 | ||||
|             void updateMagicEffects(const Ptr& actor); | ||||
|             void updateRechargingItems(); | ||||
| 
 | ||||
|             void fireEquipmentChangedEvent(const Ptr& actor); | ||||
| 
 | ||||
|  | @ -132,7 +125,7 @@ namespace MWWorld | |||
| 
 | ||||
|             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)
 | ||||
|             /// Auto-equip items if specific conditions are fulfilled (see the implementation).
 | ||||
|             ///
 | ||||
|  | @ -171,10 +164,6 @@ namespace MWWorld | |||
|             const MWMechanics::MagicEffects& getMagicEffects() const; | ||||
|             ///< 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; | ||||
|             ///< @return true if the two specified objects can stack with each other
 | ||||
| 
 | ||||
|  | @ -216,9 +205,6 @@ namespace MWWorld | |||
| 
 | ||||
|             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); | ||||
|             ///< Remove a magic effect
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -857,7 +857,17 @@ namespace MWWorld | |||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|  | @ -1649,7 +1659,6 @@ namespace MWWorld | |||
|                 } | ||||
| 
 | ||||
|                 // we need to undo the rotation
 | ||||
|                 rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot); | ||||
|                 reached = false; | ||||
|             } | ||||
|         } | ||||
|  | @ -1671,6 +1680,8 @@ namespace MWWorld | |||
|                 if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(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
 | ||||
|  | @ -3304,7 +3315,6 @@ namespace MWWorld | |||
|                 closestDistance = distance; | ||||
|                 closestMarker = marker; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return closestMarker; | ||||
|  | @ -3315,6 +3325,22 @@ namespace MWWorld | |||
|         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, | ||||
|                                           const std::string& id) | ||||
|     { | ||||
|  |  | |||
|  | @ -574,6 +574,7 @@ namespace MWWorld | |||
|             ///< check if the player is allowed to rest
 | ||||
| 
 | ||||
|             void rest(double hours) override; | ||||
|             void rechargeItems(double duration, bool activeOnly) override; | ||||
| 
 | ||||
|             /// \todo Probably shouldn't be here
 | ||||
|             MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override; | ||||
|  |  | |||
|  | @ -489,7 +489,7 @@ namespace Compiler | |||
|                 parseArguments ("l", scanner); | ||||
| 
 | ||||
|                 Generator::random (mCode); | ||||
|                 mOperands.push_back ('l'); | ||||
|                 mOperands.push_back ('f'); | ||||
| 
 | ||||
|                 mNextOperand = false; | ||||
|                 return true; | ||||
|  |  | |||
|  | @ -323,7 +323,9 @@ namespace ESM | |||
|     : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), | ||||
|       mContext (land.mContext), mDataTypes (land.mDataTypes), | ||||
|       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) | ||||
|     { | ||||
|  | @ -340,6 +342,7 @@ namespace ESM | |||
|         std::swap (mContext, land.mContext); | ||||
|         std::swap (mDataTypes, land.mDataTypes); | ||||
|         std::swap (mLandData, land.mLandData); | ||||
|         std::swap (mWnam, land.mWnam); | ||||
|     } | ||||
| 
 | ||||
|     const Land::LandData *Land::getLandData (int flags) const | ||||
|  |  | |||
|  | @ -190,9 +190,7 @@ namespace Interpreter | |||
|                     throw std::runtime_error ( | ||||
|                         "random: argument out of range (Don't be so negative!)"); | ||||
| 
 | ||||
|                 Type_Integer value = Misc::Rng::rollDice(limit); // [o, limit)
 | ||||
| 
 | ||||
|                 runtime[0].mInteger = value; | ||||
|                 runtime[0].mFloat = static_cast<Type_Float>(Misc::Rng::rollDice(limit)); // [o, limit)
 | ||||
|             } | ||||
|     }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -210,7 +210,6 @@ public: | |||
| private: | ||||
|     Terrain::Storage* mStorage; | ||||
| 
 | ||||
|     float mLodFactor; | ||||
|     float mMinX, mMaxX, mMinY, mMaxY; | ||||
|     float mMinSize; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| Shadow Settings | ||||
| ############### | ||||
| Shadows Settings | ||||
| ################ | ||||
| 
 | ||||
| Main settings | ||||
| ************* | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue