mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-01 05:45:33 +00:00
Add OpenMW commits up to 20 Oct 2019
# Conflicts: # apps/openmw/mwgui/recharge.cpp # apps/openmw/mwrender/globalmap.cpp # apps/openmw/mwrender/globalmap.hpp # apps/openmw/mwworld/inventorystore.cpp
This commit is contained in:
commit
bde9f7b817
53 changed files with 929 additions and 496 deletions
|
@ -2,6 +2,7 @@
|
|||
------
|
||||
|
||||
Bug #1515: Opening console masks dialogue, inventory menu
|
||||
Bug #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,8 +19,10 @@
|
|||
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 #4262: Rain settings are hardcoded
|
||||
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
|
||||
Bug #4276: Resizing character window differs from vanilla
|
||||
Bug #4329: Removed birthsign abilities are restored after reloading the save
|
||||
|
@ -27,8 +30,10 @@
|
|||
Bug #4383: Bow model obscures crosshair when arrow is drawn
|
||||
Bug #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 #4594: Actors without AI packages don't use Hello dialogue
|
||||
Bug #4600: Crash when no sound output is available or --no-sound is used.
|
||||
Bug #4639: Black screen after completing first mages guild mission + training
|
||||
Bug #4650: Focus is lost after pressing ESC in confirmation dialog inside savegame dialog
|
||||
|
@ -77,6 +82,7 @@
|
|||
Bug #4888: Global variable stray explicit reference calls break script compilation
|
||||
Bug #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
|
||||
|
@ -155,6 +161,8 @@
|
|||
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
|
||||
Bug #5182: OnPCEquip doesn't trigger on skipped beast race attempts to equip something not equippable by beasts
|
||||
Bug #5186: Equipped item enchantments don't affect creatures
|
||||
Feature #1774: Handle AvoidNode
|
||||
Feature #2229: Improve pathfinding AI
|
||||
Feature #3025: Analogue gamepad movement controls
|
||||
|
|
|
@ -101,6 +101,7 @@ Editor Bug Fixes:
|
|||
- Colour fields in interior-cell records now also use the colour picker widget (#4745)
|
||||
- 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)
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -215,6 +215,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.
|
||||
|
||||
|
|
|
@ -776,6 +776,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();
|
||||
|
|
|
@ -52,7 +52,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();
|
||||
|
||||
|
@ -61,8 +61,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("");
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -540,7 +540,11 @@ namespace MWGui
|
|||
|
||||
if (canEquip.first == 0)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
/// If PCSkipEquip is set, set OnPCEquip to 1 and don't message anything
|
||||
if (!script.empty() && ptr.getRefData().getLocals().getIntVar(script, "pcskipequip") == 1)
|
||||
ptr.getRefData().getLocals().setVarByInt(script, "onpcequip", 1);
|
||||
else
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(canEquip.second);
|
||||
updateItemView();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -7,19 +7,6 @@
|
|||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/Networking.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
#include "../mwmp/MechanicsHelper.hpp"
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -30,6 +17,7 @@
|
|||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/recharge.hpp"
|
||||
|
||||
#include "itemwidget.hpp"
|
||||
#include "itemchargeview.hpp"
|
||||
|
@ -143,79 +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));
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
||||
Send PlayerInventory packets that replace the original item with the new one
|
||||
*/
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
item.getCellRef().setEnchantmentCharge(
|
||||
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||
|
||||
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
||||
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
|
||||
|
||||
player.getClass().getContainerStore(player).restack(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
|
||||
}
|
||||
|
||||
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
|
||||
gem.getContainerStore()->remove(gem, 1, player);
|
||||
|
||||
if (gem.getRefData().getCount() == 0)
|
||||
{
|
||||
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
|
||||
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
||||
|
||||
// special case: readd Azura's Star
|
||||
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
||||
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
|
||||
}
|
||||
|
||||
updateView();
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "../mwrender/vismask.hpp"
|
||||
|
||||
#include "spellcasting.hpp"
|
||||
#include "steering.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
|
@ -158,6 +159,9 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
static const int GREETING_SHOULD_START = 4; //how many updates should pass before NPC can greet player
|
||||
static const int GREETING_SHOULD_END = 10;
|
||||
|
||||
class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor
|
||||
{
|
||||
public:
|
||||
|
@ -325,10 +329,31 @@ namespace MWMechanics
|
|||
|
||||
bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);
|
||||
|
||||
store.remove(itemId, 1, actor, true);
|
||||
store.remove(itemId, 1, actor);
|
||||
|
||||
if (actor != MWMechanics::getPlayer())
|
||||
{
|
||||
// Equip a replacement
|
||||
if (!wasEquipped)
|
||||
return;
|
||||
|
||||
std::string type = currentItem->getTypeName();
|
||||
if (type != typeid(ESM::Weapon).name() && type != typeid(ESM::Armor).name() && type != typeid(ESM::Clothing).name())
|
||||
return;
|
||||
|
||||
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||
return;
|
||||
|
||||
if (!actor.getClass().hasInventoryStore(actor) || !actor.getClass().getInventoryStore(actor).canActorAutoEquip(actor))
|
||||
return;
|
||||
|
||||
if (actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||
return;
|
||||
|
||||
actor.getClass().getInventoryStore(actor).autoEquip(actor);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
|
||||
std::string prevItemId = player.getPreviousItem(itemId);
|
||||
|
@ -428,6 +453,113 @@ namespace MWMechanics
|
|||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||
}
|
||||
|
||||
void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly)
|
||||
{
|
||||
if (!actor.getClass().isActor() || actor == getPlayer())
|
||||
return;
|
||||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||
if (hello == 0)
|
||||
return;
|
||||
|
||||
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||
return;
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f dir = playerPos - actorPos;
|
||||
|
||||
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
int packageId = seq.getTypeId();
|
||||
|
||||
if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
|
||||
{
|
||||
stats.setTurningToPlayer(false);
|
||||
stats.setGreetingTimer(0);
|
||||
stats.setGreetingState(Greet_None);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.isTurningToPlayer())
|
||||
{
|
||||
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||
// epsilon... TODO: a proper fix might be in either the physics or the
|
||||
// animation subsystem
|
||||
if (zTurn(actor, stats.getAngleToPlayer(), osg::DegreesToRadians(5.f)))
|
||||
{
|
||||
stats.setTurningToPlayer(false);
|
||||
// An original engine launches an endless idle2 when an actor greets player.
|
||||
playAnimationGroup (actor, "idle2", 0, std::numeric_limits<int>::max(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (turnOnly)
|
||||
return;
|
||||
|
||||
// Play a random voice greeting if the player gets too close
|
||||
float helloDistance = static_cast<float>(hello);
|
||||
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
||||
|
||||
helloDistance *= iGreetDistanceMultiplier;
|
||||
|
||||
int greetingTimer = stats.getGreetingTimer();
|
||||
GreetingState greetingState = stats.getGreetingState();
|
||||
if (greetingState == Greet_None)
|
||||
{
|
||||
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
||||
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||
greetingTimer++;
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_START)
|
||||
{
|
||||
greetingState = Greet_InProgress;
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == Greet_InProgress)
|
||||
{
|
||||
greetingTimer++;
|
||||
|
||||
turnActorToFacePlayer(actor, dir);
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_END)
|
||||
{
|
||||
greetingState = Greet_Done;
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == Greet_Done)
|
||||
{
|
||||
float resetDist = 2 * helloDistance;
|
||||
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
||||
greetingState = Greet_None;
|
||||
}
|
||||
|
||||
stats.setGreetingTimer(greetingTimer);
|
||||
stats.setGreetingState(greetingState);
|
||||
}
|
||||
|
||||
void Actors::turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir)
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
if (!stats.isTurningToPlayer())
|
||||
{
|
||||
stats.setAngleToPlayer(std::atan2(dir.x(), dir.y()));
|
||||
stats.setTurningToPlayer(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::engageCombat (const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer)
|
||||
{
|
||||
// No combat for totally static creatures
|
||||
|
@ -599,7 +731,7 @@ namespace MWMechanics
|
|||
|
||||
MagicEffects now = creatureStats.getSpells().getMagicEffects();
|
||||
|
||||
if (creature.getClass().isNpc())
|
||||
if (creature.getClass().hasInventoryStore(creature))
|
||||
{
|
||||
MWWorld::InventoryStore& store = creature.getClass().getInventoryStore (creature);
|
||||
now += store.getMagicEffects();
|
||||
|
@ -1161,7 +1293,7 @@ namespace MWMechanics
|
|||
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
|
||||
// If we have a torch and can equip it, then equip it now.
|
||||
if (heldIter == inventoryStore.end())
|
||||
if (heldIter == inventoryStore.end() && inventoryStore.canActorAutoEquip(ptr))
|
||||
{
|
||||
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
|
||||
}
|
||||
|
@ -1528,11 +1660,13 @@ namespace MWMechanics
|
|||
static float timerUpdateAITargets = 0;
|
||||
static float timerUpdateHeadTrack = 0;
|
||||
static float timerUpdateEquippedLight = 0;
|
||||
static float timerUpdateHello = 0;
|
||||
const float updateEquippedLightInterval = 1.0f;
|
||||
|
||||
// target lists get updated once every 1.0 sec
|
||||
if (timerUpdateAITargets >= 1.0f) timerUpdateAITargets = 0;
|
||||
if (timerUpdateHeadTrack >= 0.3f) timerUpdateHeadTrack = 0;
|
||||
if (timerUpdateHello >= 0.25f) timerUpdateHello = 0;
|
||||
if (mTimerDisposeSummonsCorpses >= 0.2f) mTimerDisposeSummonsCorpses = 0;
|
||||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||
|
||||
|
@ -1729,6 +1863,7 @@ namespace MWMechanics
|
|||
if (isConscious(iter->first))
|
||||
{
|
||||
stats.getAiSequence().execute(iter->first, *ctrl, duration);
|
||||
updateGreetingState(iter->first, timerUpdateHello > 0);
|
||||
playIdleDialogue(iter->first);
|
||||
}
|
||||
}
|
||||
|
@ -1754,6 +1889,7 @@ namespace MWMechanics
|
|||
timerUpdateAITargets += duration;
|
||||
timerUpdateHeadTrack += duration;
|
||||
timerUpdateEquippedLight += duration;
|
||||
timerUpdateHello += duration;
|
||||
mTimerDisposeSummonsCorpses += duration;
|
||||
|
||||
// Animation/movement update
|
||||
|
@ -1923,6 +2059,8 @@ namespace MWMechanics
|
|||
// Make sure spell effects are removed
|
||||
purgeSpellEffects(stats.getActorId());
|
||||
|
||||
calculateCreatureStatModifiers(iter->first, 0);
|
||||
|
||||
if( iter->first == getPlayer())
|
||||
{
|
||||
//player's death animation is over
|
||||
|
|
|
@ -121,6 +121,8 @@ namespace MWMechanics
|
|||
void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map<const MWWorld::Ptr, const std::set<MWWorld::Ptr> >& cachedAllies, bool againstPlayer);
|
||||
|
||||
void playIdleDialogue(const MWWorld::Ptr& actor);
|
||||
void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly);
|
||||
void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir);
|
||||
|
||||
void updateHeadTracking(const MWWorld::Ptr& actor, const MWWorld::Ptr& targetActor,
|
||||
MWWorld::Ptr& headTrackTarget, float& sqrHeadTrackDistance);
|
||||
|
|
|
@ -43,11 +43,16 @@ namespace MWMechanics
|
|||
|
||||
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
{
|
||||
auto& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
if (stats.isTurningToPlayer() || stats.getGreetingState() == Greet_InProgress)
|
||||
return false;
|
||||
|
||||
const osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||
const osg::Vec3f targetPos(mX, mY, mZ);
|
||||
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
||||
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
||||
stats.setMovementFlag(CreatureStats::Flag_Run, false);
|
||||
stats.setDrawState(DrawState_Nothing);
|
||||
|
||||
if (!isWithinMaxRange(targetPos, actorPos))
|
||||
return false;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "pathgrid.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "coordinateconverter.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
@ -26,8 +25,6 @@ namespace MWMechanics
|
|||
{
|
||||
static const int COUNT_BEFORE_RESET = 10;
|
||||
static const float DOOR_CHECK_INTERVAL = 1.5f;
|
||||
static const int GREETING_SHOULD_START = 4; //how many reaction intervals should pass before NPC can greet player
|
||||
static const int GREETING_SHOULD_END = 10;
|
||||
|
||||
// to prevent overcrowding
|
||||
static const int DESTINATION_TOLERANCE = 64;
|
||||
|
@ -176,6 +173,17 @@ namespace MWMechanics
|
|||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
}
|
||||
|
||||
GreetingState greetingState = cStats.getGreetingState();
|
||||
if (greetingState == Greet_InProgress)
|
||||
{
|
||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||
{
|
||||
stopWalking(actor, storage, false);
|
||||
mObstacleCheck.clear();
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
}
|
||||
|
||||
doPerFrameActionsForState(actor, duration, storage);
|
||||
|
||||
float& lastReaction = storage.mReaction;
|
||||
|
@ -245,13 +253,7 @@ namespace MWMechanics
|
|||
if(mDistance && cellChange)
|
||||
mDistance = 0;
|
||||
|
||||
// Allow interrupting a walking actor to trigger a greeting
|
||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||
if ((wanderState == AiWanderStorage::Wander_IdleNow) || (wanderState == AiWanderStorage::Wander_Walking))
|
||||
{
|
||||
playGreetingIfPlayerGetsTooClose(actor, storage);
|
||||
}
|
||||
|
||||
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
||||
{
|
||||
// Construct a new path if there isn't one
|
||||
|
@ -416,19 +418,9 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
bool& rotate = storage.mTurnActorGivingGreetingToFacePlayer;
|
||||
if (rotate)
|
||||
{
|
||||
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||
// epsilon... TODO: a proper fix might be in either the physics or the
|
||||
// animation subsystem
|
||||
if (zTurn(actor, storage.mTargetAngleRadians, osg::DegreesToRadians(5.f)))
|
||||
rotate = false;
|
||||
}
|
||||
|
||||
// Check if idle animation finished
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == AiWanderStorage::Greet_Done || greetingState == AiWanderStorage::Greet_None))
|
||||
GreetingState greetingState = actor.getClass().getCreatureStats(actor).getGreetingState();
|
||||
if (!checkIdle(actor, storage.mIdleAnimation) && (greetingState == Greet_Done || greetingState == Greet_None))
|
||||
{
|
||||
if (mPathFinder.isPathConstructed())
|
||||
storage.setState(AiWanderStorage::Wander_Walking);
|
||||
|
@ -517,74 +509,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage)
|
||||
{
|
||||
// Play a random voice greeting if the player gets too close
|
||||
int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified();
|
||||
float helloDistance = static_cast<float>(hello);
|
||||
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
||||
|
||||
helloDistance *= iGreetDistanceMultiplier;
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||
|
||||
int& greetingTimer = storage.mGreetingTimer;
|
||||
AiWanderStorage::GreetingState& greetingState = storage.mSaidGreeting;
|
||||
if (greetingState == AiWanderStorage::Greet_None)
|
||||
{
|
||||
if ((playerPos - actorPos).length2() <= helloDistance*helloDistance &&
|
||||
!player.getClass().getCreatureStats(player).isDead() && !actor.getClass().getCreatureStats(actor).isParalyzed()
|
||||
&& MWBase::Environment::get().getWorld()->getLOS(player, actor)
|
||||
&& MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor))
|
||||
greetingTimer++;
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_START)
|
||||
{
|
||||
greetingState = AiWanderStorage::Greet_InProgress;
|
||||
MWBase::Environment::get().getDialogueManager()->say(actor, "hello");
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == AiWanderStorage::Greet_InProgress)
|
||||
{
|
||||
greetingTimer++;
|
||||
|
||||
if (storage.mState == AiWanderStorage::Wander_Walking)
|
||||
{
|
||||
stopWalking(actor, storage, false);
|
||||
mObstacleCheck.clear();
|
||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||
}
|
||||
|
||||
turnActorToFacePlayer(actorPos, playerPos, storage);
|
||||
|
||||
if (greetingTimer >= GREETING_SHOULD_END)
|
||||
{
|
||||
greetingState = AiWanderStorage::Greet_Done;
|
||||
greetingTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (greetingState == AiWanderStorage::Greet_Done)
|
||||
{
|
||||
float resetDist = 2 * helloDistance;
|
||||
if ((playerPos - actorPos).length2() >= resetDist*resetDist)
|
||||
greetingState = AiWanderStorage::Greet_None;
|
||||
}
|
||||
}
|
||||
|
||||
void AiWander::turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage)
|
||||
{
|
||||
osg::Vec3f dir = playerPosition - actorPosition;
|
||||
|
||||
float faceAngleRadians = std::atan2(dir.x(), dir.y());
|
||||
storage.mTargetAngleRadians = faceAngleRadians;
|
||||
storage.mTurnActorGivingGreetingToFacePlayer = true;
|
||||
}
|
||||
|
||||
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
||||
{
|
||||
|
|
|
@ -25,21 +25,8 @@ namespace MWMechanics
|
|||
/// \brief This class holds the variables AiWander needs which are deleted if the package becomes inactive.
|
||||
struct AiWanderStorage : AiTemporaryBase
|
||||
{
|
||||
// the z rotation angle to reach
|
||||
// when mTurnActorGivingGreetingToFacePlayer is true
|
||||
float mTargetAngleRadians;
|
||||
bool mTurnActorGivingGreetingToFacePlayer;
|
||||
float mReaction; // update some actions infrequently
|
||||
|
||||
enum GreetingState
|
||||
{
|
||||
Greet_None,
|
||||
Greet_InProgress,
|
||||
Greet_Done
|
||||
};
|
||||
GreetingState mSaidGreeting;
|
||||
int mGreetingTimer;
|
||||
|
||||
const MWWorld::CellStore* mCell; // for detecting cell change
|
||||
|
||||
// AiWander states
|
||||
|
@ -72,11 +59,7 @@ namespace MWMechanics
|
|||
int mStuckCount;
|
||||
|
||||
AiWanderStorage():
|
||||
mTargetAngleRadians(0),
|
||||
mTurnActorGivingGreetingToFacePlayer(false),
|
||||
mReaction(0),
|
||||
mSaidGreeting(Greet_None),
|
||||
mGreetingTimer(0),
|
||||
mCell(nullptr),
|
||||
mState(Wander_ChooseAction),
|
||||
mIsWanderingManually(false),
|
||||
|
@ -136,7 +119,6 @@ namespace MWMechanics
|
|||
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
|
||||
short unsigned getRandomIdle();
|
||||
void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos);
|
||||
void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||
void evadeObstacles(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||
void turnActorToFacePlayer(const osg::Vec3f& actorPosition, const osg::Vec3f& playerPosition, AiWanderStorage& storage);
|
||||
void doPerFrameActionsForState(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||
|
|
|
@ -34,12 +34,53 @@ namespace MWMechanics
|
|||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||
mDeathAnimation(-1), mTimeOfDeath(), mLevel (0)
|
||||
mDeathAnimation(-1), mTimeOfDeath(), mGreetingState(Greet_None),
|
||||
mGreetingTimer(0), mTargetAngleRadians(0), mIsTurningToPlayer(false), mLevel (0)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i] = 0;
|
||||
}
|
||||
|
||||
int MWMechanics::CreatureStats::getGreetingTimer() const
|
||||
{
|
||||
return mGreetingTimer;
|
||||
}
|
||||
|
||||
void MWMechanics::CreatureStats::setGreetingTimer(int timer)
|
||||
{
|
||||
mGreetingTimer = timer;
|
||||
}
|
||||
|
||||
float MWMechanics::CreatureStats::getAngleToPlayer() const
|
||||
{
|
||||
return mTargetAngleRadians;
|
||||
}
|
||||
|
||||
void MWMechanics::CreatureStats::setAngleToPlayer(float angle)
|
||||
{
|
||||
mTargetAngleRadians = angle;
|
||||
}
|
||||
|
||||
GreetingState MWMechanics::CreatureStats::getGreetingState() const
|
||||
{
|
||||
return mGreetingState;
|
||||
}
|
||||
|
||||
void MWMechanics::CreatureStats::setGreetingState(GreetingState state)
|
||||
{
|
||||
mGreetingState = state;
|
||||
}
|
||||
|
||||
bool MWMechanics::CreatureStats::isTurningToPlayer() const
|
||||
{
|
||||
return mIsTurningToPlayer;
|
||||
}
|
||||
|
||||
void MWMechanics::CreatureStats::setTurningToPlayer(bool turning)
|
||||
{
|
||||
mIsTurningToPlayer = turning;
|
||||
}
|
||||
|
||||
const AiSequence& CreatureStats::getAiSequence() const
|
||||
{
|
||||
return mAiSequence;
|
||||
|
|
|
@ -19,6 +19,13 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
enum GreetingState
|
||||
{
|
||||
Greet_None,
|
||||
Greet_InProgress,
|
||||
Greet_Done
|
||||
};
|
||||
|
||||
/// \brief Common creature stats
|
||||
///
|
||||
///
|
||||
|
@ -70,6 +77,11 @@ namespace MWMechanics
|
|||
|
||||
MWWorld::TimeStamp mTimeOfDeath;
|
||||
|
||||
GreetingState mGreetingState;
|
||||
int mGreetingTimer;
|
||||
float mTargetAngleRadians;
|
||||
bool mIsTurningToPlayer;
|
||||
|
||||
public:
|
||||
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||
private:
|
||||
|
@ -85,6 +97,18 @@ namespace MWMechanics
|
|||
public:
|
||||
CreatureStats();
|
||||
|
||||
int getGreetingTimer() const;
|
||||
void setGreetingTimer(int timer);
|
||||
|
||||
float getAngleToPlayer() const;
|
||||
void setAngleToPlayer(float angle);
|
||||
|
||||
GreetingState getGreetingState() const;
|
||||
void setGreetingState(GreetingState state);
|
||||
|
||||
bool isTurningToPlayer() const;
|
||||
void setTurningToPlayer(bool turning);
|
||||
|
||||
DrawState_ getDrawState() const;
|
||||
void setDrawState(DrawState_ state);
|
||||
|
||||
|
|
|
@ -300,16 +300,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.
|
||||
|
||||
|
|
124
apps/openmw/mwmechanics/recharge.cpp
Normal file
124
apps/openmw/mwmechanics/recharge.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
#include "recharge.hpp"
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/Networking.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
#include "../mwmp/MechanicsHelper.hpp"
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "creaturestats.hpp"
|
||||
#include "actorutil.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration)
|
||||
{
|
||||
float charge = item.getCellRef().getEnchantmentCharge();
|
||||
if (charge == -1 || charge == maxCharge)
|
||||
return false;
|
||||
|
||||
static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fMagicItemRechargePerSecond")->mValue.getFloat();
|
||||
|
||||
item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem)
|
||||
{
|
||||
if (!gem.getRefData().getCount())
|
||||
return false;
|
||||
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||
|
||||
float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
if (luckTerm < 1 || luckTerm > 10)
|
||||
luckTerm = 1;
|
||||
|
||||
float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||
|
||||
if (intelligenceTerm > 20)
|
||||
intelligenceTerm = 20;
|
||||
if (intelligenceTerm < 1)
|
||||
intelligenceTerm = 1;
|
||||
|
||||
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
|
||||
int roll = Misc::Rng::roll0to99();
|
||||
if (roll < x)
|
||||
{
|
||||
std::string soul = gem.getCellRef().getSoul();
|
||||
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);
|
||||
|
||||
float restored = creature->mData.mSoul * (roll / x);
|
||||
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
||||
item.getClass().getEnchantment(item));
|
||||
item.getCellRef().setEnchantmentCharge(
|
||||
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
||||
Send PlayerInventory packets that replace the original item with the new one
|
||||
*/
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
item.getCellRef().setEnchantmentCharge(
|
||||
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||
|
||||
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
||||
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
|
||||
|
||||
player.getClass().getContainerStore(player).restack(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
|
||||
}
|
||||
|
||||
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
|
||||
gem.getContainerStore()->remove(gem, 1, player);
|
||||
|
||||
if (gem.getRefData().getCount() == 0)
|
||||
{
|
||||
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
|
||||
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(message);
|
||||
|
||||
// special case: readd Azura's Star
|
||||
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
||||
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
15
apps/openmw/mwmechanics/recharge.hpp
Normal file
15
apps/openmw/mwmechanics/recharge.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef MWMECHANICS_RECHARGE_H
|
||||
#define MWMECHANICS_RECHARGE_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration);
|
||||
|
||||
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -943,7 +943,7 @@ namespace MWMechanics
|
|||
{
|
||||
int type = item.get<ESM::Weapon>()->mBase->mData.mType;
|
||||
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(type)->mWeaponClass;
|
||||
isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ranged);
|
||||
isProjectile = (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo);
|
||||
}
|
||||
int type = enchantment->mData.mType;
|
||||
|
||||
|
|
|
@ -133,9 +133,10 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
// Update summon effects
|
||||
bool casterDead = creatureStats.isDead();
|
||||
for (std::map<CreatureStats::SummonKey, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
|
||||
{
|
||||
bool found = mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||
bool found = !casterDead && mActiveEffects.find(it->first) != mActiveEffects.end();
|
||||
if (!found)
|
||||
{
|
||||
// Effect has ended
|
||||
|
|
|
@ -184,7 +184,7 @@ void ObjectList::editContainers(MWWorld::CellStore* cellStore)
|
|||
if (!containerItem.soul.empty())
|
||||
newPtr.getCellRef().setSoul(containerItem.soul);
|
||||
|
||||
containerStore.add(newPtr, containerItem.count, ownerPtr, true);
|
||||
containerStore.add(newPtr, containerItem.count, ownerPtr);
|
||||
}
|
||||
|
||||
else if (action == BaseObjectList::REMOVE && containerItem.actionCount > 0)
|
||||
|
|
|
@ -87,17 +87,17 @@ namespace
|
|||
{
|
||||
if (mRendered)
|
||||
{
|
||||
node->setNodeMask(0);
|
||||
if (mParent->copyResult(static_cast<osg::Camera*>(node), nv->getTraversalNumber()))
|
||||
{
|
||||
node->setNodeMask(0);
|
||||
mParent->markForRemoval(static_cast<osg::Camera*>(node));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
traverse(node, nv);
|
||||
|
||||
if (!mRendered)
|
||||
{
|
||||
mRendered = true;
|
||||
mParent->markForRemoval(static_cast<osg::Camera*>(node));
|
||||
}
|
||||
mRendered = true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -361,7 +361,7 @@ namespace MWRender
|
|||
imageDest.mImage = image;
|
||||
imageDest.mX = x;
|
||||
imageDest.mY = y;
|
||||
mPendingImageDest.push_back(imageDest);
|
||||
mPendingImageDest[camera] = imageDest;
|
||||
}
|
||||
|
||||
// Create a quad rendering the updated texture
|
||||
|
@ -614,35 +614,21 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
void GlobalMap::markForRemoval(osg::Camera *camera)
|
||||
bool GlobalMap::copyResult(osg::Camera *camera, unsigned int frame)
|
||||
{
|
||||
CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera);
|
||||
if (found == mActiveCameras.end())
|
||||
ImageDestMap::iterator it = mPendingImageDest.find(camera);
|
||||
if (it == mPendingImageDest.end())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera";
|
||||
return;
|
||||
}
|
||||
mActiveCameras.erase(found);
|
||||
mCamerasPendingRemoval.push_back(camera);
|
||||
}
|
||||
|
||||
void GlobalMap::cleanupCameras()
|
||||
{
|
||||
for (auto& camera : mCamerasPendingRemoval)
|
||||
removeCamera(camera);
|
||||
|
||||
mCamerasPendingRemoval.clear();
|
||||
|
||||
for (ImageDestVector::iterator it = mPendingImageDest.begin(); it != mPendingImageDest.end();)
|
||||
{
|
||||
ImageDest& imageDest = *it;
|
||||
if (--imageDest.mFramesUntilDone > 0)
|
||||
ImageDest& imageDest = it->second;
|
||||
if (imageDest.mFrameDone == 0) imageDest.mFrameDone = frame+2; // wait an extra frame to ensure the draw thread has completed its frame.
|
||||
if (imageDest.mFrameDone > frame)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
ensureLoaded();
|
||||
mOverlayImage->copySubImage(imageDest.mX, imageDest.mY, 0, imageDest.mImage);
|
||||
|
||||
/*
|
||||
|
@ -673,7 +659,7 @@ namespace MWRender
|
|||
if (!readerwriter)
|
||||
{
|
||||
std::cerr << "Error: Can't write temporary map image, no '" << "png" << "' readerwriter found" << std::endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ostringstream ostream;
|
||||
|
@ -697,9 +683,30 @@ namespace MWRender
|
|||
*/
|
||||
|
||||
it = mPendingImageDest.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalMap::markForRemoval(osg::Camera *camera)
|
||||
{
|
||||
CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), camera);
|
||||
if (found == mActiveCameras.end())
|
||||
{
|
||||
Log(Debug::Error) << "Error: GlobalMap trying to remove an inactive camera";
|
||||
return;
|
||||
}
|
||||
mActiveCameras.erase(found);
|
||||
mCamerasPendingRemoval.push_back(camera);
|
||||
}
|
||||
|
||||
void GlobalMap::cleanupCameras()
|
||||
{
|
||||
for (auto& camera : mCamerasPendingRemoval)
|
||||
removeCamera(camera);
|
||||
|
||||
mCamerasPendingRemoval.clear();
|
||||
}
|
||||
|
||||
void GlobalMap::removeCamera(osg::Camera *cam)
|
||||
{
|
||||
cam->removeChildren(0, cam->getNumChildren());
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
|
@ -60,6 +61,8 @@ namespace MWRender
|
|||
|
||||
void removeCamera(osg::Camera* cam);
|
||||
|
||||
bool copyResult(osg::Camera* cam, unsigned int frame);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
|
@ -106,18 +109,18 @@ namespace MWRender
|
|||
{
|
||||
ImageDest()
|
||||
: mX(0), mY(0)
|
||||
, mFramesUntilDone(3) // wait an extra frame to ensure the draw thread has completed its frame.
|
||||
, mFrameDone(0)
|
||||
{
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Image> mImage;
|
||||
int mX, mY;
|
||||
int mFramesUntilDone;
|
||||
unsigned int mFrameDone;
|
||||
};
|
||||
|
||||
typedef std::vector<ImageDest> ImageDestVector;
|
||||
typedef std::map<osg::ref_ptr<osg::Camera>, ImageDest> ImageDestMap;
|
||||
|
||||
ImageDestVector mPendingImageDest;
|
||||
ImageDestMap mPendingImageDest;
|
||||
|
||||
std::vector< std::pair<int,int> > mExploredCells;
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
#include <osg/PolygonOffset>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <osgParticle/BoxPlacer>
|
||||
#include <osgParticle/ModularEmitter>
|
||||
#include <osgParticle/ParticleSystem>
|
||||
#include <osgParticle/ParticleSystemUpdater>
|
||||
#include <osgParticle/ModularEmitter>
|
||||
#include <osgParticle/BoxPlacer>
|
||||
#include <osgParticle/ConstantRateCounter>
|
||||
#include <osgParticle/RadialShooter>
|
||||
|
||||
|
@ -1117,7 +1117,11 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
|||
, mRemainingTransitionTime(0.0f)
|
||||
, mRainEnabled(false)
|
||||
, mRainSpeed(0)
|
||||
, mRainFrequency(1)
|
||||
, mRainDiameter(0)
|
||||
, mRainMinHeight(0)
|
||||
, mRainMaxHeight(0)
|
||||
, mRainEntranceSpeed(1)
|
||||
, mRainMaxRaindrops(0)
|
||||
, mWindSpeed(0.f)
|
||||
, mEnabled(true)
|
||||
, mSunEnabled(true)
|
||||
|
@ -1458,7 +1462,7 @@ void SkyManager::createRain()
|
|||
mRainNode = new osg::Group;
|
||||
|
||||
mRainParticleSystem = new osgParticle::ParticleSystem;
|
||||
osg::Vec3 rainRange = osg::Vec3(600,600,600);
|
||||
osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);
|
||||
|
||||
mRainParticleSystem->setParticleAlignment(osgParticle::ParticleSystem::FIXED);
|
||||
mRainParticleSystem->setAlignVectorX(osg::Vec3f(0.1,0,0));
|
||||
|
@ -1485,14 +1489,19 @@ void SkyManager::createRain()
|
|||
emitter->setParticleSystem(mRainParticleSystem);
|
||||
|
||||
osg::ref_ptr<osgParticle::BoxPlacer> placer (new osgParticle::BoxPlacer);
|
||||
placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2); // Rain_Diameter
|
||||
placer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);
|
||||
placer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);
|
||||
placer->setZRange(300, 300);
|
||||
placer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);
|
||||
emitter->setPlacer(placer);
|
||||
mPlacer = placer;
|
||||
|
||||
// FIXME: vanilla engine does not use a particle system to handle rain, it uses a NIF-file with 20 raindrops in it.
|
||||
// It spawns the (maxRaindrops-getParticleSystem()->numParticles())*dt/rainEntranceSpeed batches every frame (near 1-2).
|
||||
// Since the rain is a regular geometry, it produces water ripples, also in theory it can be removed if collides with something.
|
||||
osg::ref_ptr<RainCounter> counter (new RainCounter);
|
||||
counter->setNumberOfParticlesPerSecondToCreate(600.0);
|
||||
counter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);
|
||||
emitter->setCounter(counter);
|
||||
mCounter = counter;
|
||||
|
||||
osg::ref_ptr<RainShooter> shooter (new RainShooter);
|
||||
mRainShooter = shooter;
|
||||
|
@ -1525,6 +1534,8 @@ void SkyManager::destroyRain()
|
|||
|
||||
mRootNode->removeChild(mRainNode);
|
||||
mRainNode = nullptr;
|
||||
mPlacer = nullptr;
|
||||
mCounter = nullptr;
|
||||
mRainParticleSystem = nullptr;
|
||||
mRainShooter = nullptr;
|
||||
mRainFader = nullptr;
|
||||
|
@ -1625,10 +1636,17 @@ void SkyManager::updateRainParameters()
|
|||
{
|
||||
if (mRainShooter)
|
||||
{
|
||||
float windFactor = mWindSpeed/3.f;
|
||||
float angle = windFactor * osg::PI/4;
|
||||
mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed * windFactor, -mRainSpeed));
|
||||
float angle = -std::atan2(1, 50.f/mWindSpeed);
|
||||
mRainShooter->setVelocity(osg::Vec3f(0, mRainSpeed*std::sin(angle), -mRainSpeed/std::cos(angle)));
|
||||
mRainShooter->setAngle(angle);
|
||||
|
||||
osg::Vec3 rainRange = osg::Vec3(mRainDiameter, mRainDiameter, (mRainMinHeight+mRainMaxHeight)/2.f);
|
||||
|
||||
mPlacer->setXRange(-rainRange.x() / 2, rainRange.x() / 2);
|
||||
mPlacer->setYRange(-rainRange.y() / 2, rainRange.y() / 2);
|
||||
mPlacer->setZRange(-rainRange.z() / 2, rainRange.z() / 2);
|
||||
|
||||
mCounter->setNumberOfParticlesPerSecondToCreate(mRainMaxRaindrops/mRainEntranceSpeed*20);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1645,6 +1663,14 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
|||
{
|
||||
if (!mCreated) return;
|
||||
|
||||
mRainEntranceSpeed = weather.mRainEntranceSpeed;
|
||||
mRainMaxRaindrops = weather.mRainMaxRaindrops;
|
||||
mRainDiameter = weather.mRainDiameter;
|
||||
mRainMinHeight = weather.mRainMinHeight;
|
||||
mRainMaxHeight = weather.mRainMaxHeight;
|
||||
mRainSpeed = weather.mRainSpeed;
|
||||
mWindSpeed = weather.mWindSpeed;
|
||||
|
||||
if (mRainEffect != weather.mRainEffect)
|
||||
{
|
||||
mRainEffect = weather.mRainEffect;
|
||||
|
@ -1658,9 +1684,6 @@ void SkyManager::setWeather(const WeatherResult& weather)
|
|||
}
|
||||
}
|
||||
|
||||
mRainFrequency = weather.mRainFrequency;
|
||||
mRainSpeed = weather.mRainSpeed;
|
||||
mWindSpeed = weather.mWindSpeed;
|
||||
updateRainParameters();
|
||||
|
||||
mIsStorm = weather.mIsStorm;
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace osg
|
|||
namespace osgParticle
|
||||
{
|
||||
class ParticleSystem;
|
||||
class BoxPlacer;
|
||||
}
|
||||
|
||||
namespace Resource
|
||||
|
@ -39,6 +40,7 @@ namespace MWRender
|
|||
class CloudUpdater;
|
||||
class Sun;
|
||||
class Moon;
|
||||
class RainCounter;
|
||||
class RainShooter;
|
||||
class RainFader;
|
||||
class AlphaFader;
|
||||
|
@ -68,6 +70,8 @@ namespace MWRender
|
|||
float mDLFogOffset;
|
||||
|
||||
float mWindSpeed;
|
||||
float mCurrentWindSpeed;
|
||||
float mNextWindSpeed;
|
||||
|
||||
float mCloudSpeed;
|
||||
|
||||
|
@ -85,8 +89,12 @@ namespace MWRender
|
|||
std::string mRainEffect;
|
||||
float mEffectFade;
|
||||
|
||||
float mRainDiameter;
|
||||
float mRainMinHeight;
|
||||
float mRainMaxHeight;
|
||||
float mRainSpeed;
|
||||
float mRainFrequency;
|
||||
float mRainEntranceSpeed;
|
||||
int mRainMaxRaindrops;
|
||||
};
|
||||
|
||||
struct MoonState
|
||||
|
@ -216,6 +224,8 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<osg::Group> mRainNode;
|
||||
osg::ref_ptr<osgParticle::ParticleSystem> mRainParticleSystem;
|
||||
osg::ref_ptr<osgParticle::BoxPlacer> mPlacer;
|
||||
osg::ref_ptr<RainCounter> mCounter;
|
||||
osg::ref_ptr<RainShooter> mRainShooter;
|
||||
osg::ref_ptr<RainFader> mRainFader;
|
||||
|
||||
|
@ -249,7 +259,11 @@ namespace MWRender
|
|||
bool mRainEnabled;
|
||||
std::string mRainEffect;
|
||||
float mRainSpeed;
|
||||
float mRainFrequency;
|
||||
float mRainDiameter;
|
||||
float mRainMinHeight;
|
||||
float mRainMaxHeight;
|
||||
float mRainEntranceSpeed;
|
||||
int mRainMaxRaindrops;
|
||||
float mWindSpeed;
|
||||
|
||||
bool mEnabled;
|
||||
|
|
|
@ -527,7 +527,7 @@ namespace MWScript
|
|||
|
||||
MWMechanics::MagicEffects effects = stats.getSpells().getMagicEffects();
|
||||
effects += stats.getActiveSpells().getMagicEffects();
|
||||
if (ptr.getClass().isNpc())
|
||||
if (ptr.getClass().hasInventoryStore(ptr))
|
||||
{
|
||||
MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr);
|
||||
effects += store.getMagicEffects();
|
||||
|
|
|
@ -184,6 +184,19 @@ void MWWorld::Cells::rest (double hours)
|
|||
}
|
||||
}
|
||||
|
||||
void MWWorld::Cells::recharge (float duration)
|
||||
{
|
||||
for (auto &interior : mInteriors)
|
||||
{
|
||||
interior.second.recharge(duration);
|
||||
}
|
||||
|
||||
for (auto &exterior : mExteriors)
|
||||
{
|
||||
exterior.second.recharge(duration);
|
||||
}
|
||||
}
|
||||
|
||||
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
|
||||
{
|
||||
if (id.mPaged)
|
||||
|
|
|
@ -72,7 +72,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.
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/recharge.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "esmstore.hpp"
|
||||
|
@ -385,6 +386,7 @@ namespace MWWorld
|
|||
void CellStore::updateMergedRefs()
|
||||
{
|
||||
mMergedRefs.clear();
|
||||
mRechargingItemsUpToDate = false;
|
||||
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
||||
forEachInternal(visitor);
|
||||
visitor.merge();
|
||||
|
@ -419,7 +421,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;
|
||||
}
|
||||
|
@ -1178,6 +1180,42 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void CellStore::recharge(float duration)
|
||||
{
|
||||
if (duration <= 0)
|
||||
return;
|
||||
|
||||
if (mState == State_Loaded)
|
||||
{
|
||||
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||
}
|
||||
}
|
||||
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||
}
|
||||
}
|
||||
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
||||
}
|
||||
}
|
||||
|
||||
rechargeItems(duration);
|
||||
}
|
||||
}
|
||||
|
||||
void CellStore::respawn()
|
||||
{
|
||||
if (mState == State_Loaded)
|
||||
|
@ -1213,4 +1251,73 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::CellStore::rechargeItems(float duration)
|
||||
{
|
||||
if (!mRechargingItemsUpToDate)
|
||||
{
|
||||
updateRechargingItems();
|
||||
mRechargingItemsUpToDate = true;
|
||||
}
|
||||
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
||||
{
|
||||
MWMechanics::rechargeItem(it->first, it->second, duration);
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::CellStore::updateRechargingItems()
|
||||
{
|
||||
mRechargingItems.clear();
|
||||
|
||||
for (CellRefList<ESM::Weapon>::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
checkItem(ptr);
|
||||
}
|
||||
}
|
||||
for (CellRefList<ESM::Armor>::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
checkItem(ptr);
|
||||
}
|
||||
}
|
||||
for (CellRefList<ESM::Clothing>::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
checkItem(ptr);
|
||||
}
|
||||
}
|
||||
for (CellRefList<ESM::Book>::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it)
|
||||
{
|
||||
Ptr ptr = getCurrentPtr(&*it);
|
||||
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
||||
{
|
||||
checkItem(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::CellStore::checkItem(Ptr ptr)
|
||||
{
|
||||
if (ptr.getClass().getEnchantment(ptr).empty())
|
||||
return;
|
||||
|
||||
std::string enchantmentId = ptr.getClass().getEnchantment(ptr);
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
|
||||
if (!enchantment)
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << ptr.getCellRef().getRefId();
|
||||
return;
|
||||
}
|
||||
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
||||
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,16 @@ namespace MWWorld
|
|||
/// Repopulate mMergedRefs.
|
||||
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.
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/levelledlist.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
#include "../mwmechanics/recharge.hpp"
|
||||
|
||||
#include "manualref.hpp"
|
||||
#include "refdata.hpp"
|
||||
|
@ -126,7 +127,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() {}
|
||||
|
||||
|
@ -272,7 +277,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)
|
||||
|
@ -290,30 +294,14 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
|
|||
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
|
||||
{
|
||||
MWWorld::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;
|
||||
|
@ -354,7 +342,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);
|
||||
|
@ -472,6 +461,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;
|
||||
|
@ -725,6 +754,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;
|
||||
|
@ -108,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;
|
||||
|
||||
|
@ -131,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.
|
||||
|
@ -156,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).
|
||||
///
|
||||
|
|
|
@ -211,7 +211,7 @@ void ESMStore::validate()
|
|||
const ESM::MagicEffect* mgef = mMagicEffects.search(iter->mEffectID);
|
||||
if (!mgef)
|
||||
{
|
||||
Log(Debug::Verbose) << "Spell '" << spell.mId << "' has an an invalid effect (index " << iter->mEffectID << ") present, dropping it.";
|
||||
Log(Debug::Verbose) << "Spell '" << spell.mId << "' has an invalid effect (index " << iter->mEffectID << ") present. Dropping the effect.";
|
||||
iter = spell.mEffects.mList.erase(iter);
|
||||
changed = true;
|
||||
continue;
|
||||
|
@ -223,7 +223,7 @@ void ESMStore::validate()
|
|||
{
|
||||
iter->mAttribute = -1;
|
||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||
" effect of spell '" << spell.mId << "' has an attribute argument present, dropping it.";
|
||||
" effect of spell '" << spell.mId << "' has an attribute argument present. Dropping the argument.";
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ void ESMStore::validate()
|
|||
{
|
||||
iter->mSkill = -1;
|
||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||
" effect of spell '" << spell.mId << "' has a skill argument present, dropping it.";
|
||||
" effect of spell '" << spell.mId << "' has a skill argument present. Dropping the argument.";
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ void ESMStore::validate()
|
|||
iter->mSkill = -1;
|
||||
iter->mAttribute = -1;
|
||||
Log(Debug::Verbose) << ESM::MagicEffect::effectIdToString(iter->mEffectID) <<
|
||||
" effect of spell '" << spell.mId << "' has argument(s) present, dropping them.";
|
||||
" effect of spell '" << spell.mId << "' has argument(s) present. Dropping the argument(s).";
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,6 @@ MWWorld::InventoryStore::InventoryStore()
|
|||
, mUpdatesEnabled (true)
|
||||
, mFirstAutoEquip(true)
|
||||
, mSelectedEnchantItem(end())
|
||||
, mRechargingItemsUpToDate(false)
|
||||
{
|
||||
initSlots (mSlots);
|
||||
}
|
||||
|
@ -127,7 +126,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
|
|||
, mFirstAutoEquip(store.mFirstAutoEquip)
|
||||
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
|
||||
, mSelectedEnchantItem(end())
|
||||
, mRechargingItemsUpToDate(false)
|
||||
{
|
||||
copySlots (store);
|
||||
}
|
||||
|
@ -146,11 +144,11 @@ 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
|
||||
// Auto-equip items if an armor/clothing item is added, but not for the player nor werewolves
|
||||
if (actorPtr != MWMechanics::getPlayer()
|
||||
&& actorPtr.getClass().isNpc() && !actorPtr.getClass().getNpcStats(actorPtr).isWerewolf())
|
||||
{
|
||||
|
@ -223,22 +221,29 @@ MWWorld::ConstContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)
|
|||
return findSlot (slot);
|
||||
}
|
||||
|
||||
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item)
|
||||
bool MWWorld::InventoryStore::canActorAutoEquip(const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (!Settings::Manager::getBool("prevent merchant equipping", "Game"))
|
||||
// Treat player as non-trader indifferently from service flags.
|
||||
if (actor == MWMechanics::getPlayer())
|
||||
return true;
|
||||
|
||||
// Only autoEquip if we are the original owner of the item.
|
||||
// This stops merchants from auto equipping anything you sell to them.
|
||||
// ...unless this is a companion, he should always equip items given to him.
|
||||
if (!Misc::StringUtils::ciEqual(item.getCellRef().getOwner(), actor.getCellRef().getRefId()) &&
|
||||
(actor.getClass().getScript(actor).empty() ||
|
||||
!actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
|
||||
&& !actor.getClass().getCreatureStats(actor).isDead() // Corpses can be dressed up by the player as desired
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static const bool prevent = Settings::Manager::getBool("prevent merchant equipping", "Game");
|
||||
if (!prevent)
|
||||
return true;
|
||||
|
||||
// Corpses can be dressed up by the player as desired.
|
||||
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||
return true;
|
||||
|
||||
// Companions can autoequip items.
|
||||
if (!actor.getClass().getScript(actor).empty() &&
|
||||
actor.getRefData().getLocals().getIntVar(actor.getClass().getScript(actor), "companion"))
|
||||
return true;
|
||||
|
||||
// If the actor is trader, he can auto-equip items only during initial auto-equipping
|
||||
int services = actor.getClass().getServices(actor);
|
||||
if (services & ESM::NPC::AllItems)
|
||||
return mFirstAutoEquip;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -352,9 +357,6 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
|
|||
|
||||
for (ContainerStoreIterator iter(begin(ContainerStore::Type_Weapon)); iter!=end(); ++iter)
|
||||
{
|
||||
if (!canActorAutoEquip(actor, *iter))
|
||||
continue;
|
||||
|
||||
const ESM::Weapon* esmWeapon = iter->get<ESM::Weapon>()->mBase;
|
||||
|
||||
if (MWMechanics::getWeaponType(esmWeapon->mData.mType)->mWeaponClass == ESM::WeaponType::Ammo)
|
||||
|
@ -456,9 +458,6 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots&
|
|||
{
|
||||
Ptr test = *iter;
|
||||
|
||||
if (!canActorAutoEquip(actor, test))
|
||||
continue;
|
||||
|
||||
switch(test.getClass().canBeEquipped (test, actor).first)
|
||||
{
|
||||
case 0:
|
||||
|
@ -590,6 +589,9 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
if (!canActorAutoEquip(actor))
|
||||
return;
|
||||
|
||||
TSlots slots_;
|
||||
initSlots (slots_);
|
||||
|
||||
|
@ -746,12 +748,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);
|
||||
|
@ -994,57 +990,6 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
|
|||
}
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::updateRechargingItems()
|
||||
{
|
||||
mRechargingItems.clear();
|
||||
for (ContainerStoreIterator it = begin(); it != end(); ++it)
|
||||
{
|
||||
if (it->getClass().getEnchantment(*it) != "")
|
||||
{
|
||||
std::string enchantmentId = it->getClass().getEnchantment(*it);
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(
|
||||
enchantmentId);
|
||||
if (!enchantment)
|
||||
{
|
||||
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
||||
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::rechargeItems(float duration)
|
||||
{
|
||||
if (!mRechargingItemsUpToDate)
|
||||
{
|
||||
updateRechargingItems();
|
||||
mRechargingItemsUpToDate = true;
|
||||
}
|
||||
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
|
||||
{
|
||||
if (it->first->getCellRef().getEnchantmentCharge() == -1
|
||||
|| it->first->getCellRef().getEnchantmentCharge() == it->second)
|
||||
continue;
|
||||
|
||||
static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
|
||||
"fMagicItemRechargePerSecond")->mValue.getFloat();
|
||||
|
||||
if (it->first->getCellRef().getEnchantmentCharge() <= it->second)
|
||||
{
|
||||
it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration,
|
||||
it->second));
|
||||
|
||||
// attempt to restack when fully recharged
|
||||
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
|
||||
it->first = restack(*it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MWWorld::InventoryStore::purgeEffect(short effectId)
|
||||
{
|
||||
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)
|
||||
|
|
|
@ -101,25 +101,17 @@ namespace MWWorld
|
|||
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
|
||||
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);
|
||||
|
||||
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
|
||||
virtual void readEquipmentState (const MWWorld::ContainerStoreIterator& iter, int index, const ESM::InventoryState& inventory);
|
||||
|
||||
bool canActorAutoEquip(const MWWorld::Ptr& actor, const MWWorld::Ptr& item);
|
||||
ContainerStoreIterator findSlot (int slot) const;
|
||||
|
||||
public:
|
||||
|
@ -132,7 +124,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).
|
||||
///
|
||||
|
@ -168,13 +160,11 @@ namespace MWWorld
|
|||
void autoEquip (const MWWorld::Ptr& actor);
|
||||
///< Auto equip items according to stats and item value.
|
||||
|
||||
bool canActorAutoEquip(const MWWorld::Ptr& actor);
|
||||
|
||||
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 +206,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
|
||||
|
||||
|
|
|
@ -160,7 +160,12 @@ Weather::Weather(const std::string& name,
|
|||
, mGlareView(Fallback::Map::getFloat("Weather_" + name + "_Glare_View"))
|
||||
, mIsStorm(mWindSpeed > stormWindSpeed)
|
||||
, mRainSpeed(rainSpeed)
|
||||
, mRainFrequency(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed"))
|
||||
, mRainEntranceSpeed(Fallback::Map::getFloat("Weather_" + name + "_Rain_Entrance_Speed"))
|
||||
, mRainMaxRaindrops(Fallback::Map::getFloat("Weather_" + name + "_Max_Raindrops"))
|
||||
, mRainDiameter(Fallback::Map::getFloat("Weather_" + name + "_Rain_Diameter"))
|
||||
, mRainThreshold(Fallback::Map::getFloat("Weather_" + name + "_Rain_Threshold"))
|
||||
, mRainMinHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Min"))
|
||||
, mRainMaxHeight(Fallback::Map::getFloat("Weather_" + name + "_Rain_Height_Max"))
|
||||
, mParticleEffect(particleEffect)
|
||||
, mRainEffect(Fallback::Map::getBool("Weather_" + name + "_Using_Precip") ? "meshes\\raindrop.nif" : "")
|
||||
, mTransitionDelta(Fallback::Map::getFloat("Weather_" + name + "_Transition_Delta"))
|
||||
|
@ -191,15 +196,6 @@ Weather::Weather(const std::string& name,
|
|||
|
||||
if (Misc::StringUtils::ciEqual(mAmbientLoopSoundID, "None"))
|
||||
mAmbientLoopSoundID.clear();
|
||||
|
||||
/*
|
||||
Unhandled:
|
||||
Rain Diameter=600 ?
|
||||
Rain Height Min=200 ?
|
||||
Rain Height Max=700 ?
|
||||
Rain Threshold=0.6 ?
|
||||
Max Raindrops=650 ?
|
||||
*/
|
||||
}
|
||||
|
||||
float Weather::transitionDelta() const
|
||||
|
@ -551,6 +547,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager& rendering, MWWorld::E
|
|||
, mMasser("Masser")
|
||||
, mSecunda("Secunda")
|
||||
, mWindSpeed(0.f)
|
||||
, mCurrentWindSpeed(0.f)
|
||||
, mNextWindSpeed(0.f)
|
||||
, mIsStorm(false)
|
||||
, mPrecipitation(false)
|
||||
, mStormDirection(0,1,0)
|
||||
|
@ -747,6 +745,40 @@ void WeatherManager::setRegionWeather(const std::string& region, const int curre
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
osg::Vec3f WeatherManager::calculateStormDirection()
|
||||
{
|
||||
osg::Vec3f playerPos (MWMechanics::getPlayer().getRefData().getPosition().asVec3());
|
||||
playerPos.z() = 0;
|
||||
osg::Vec3f redMountainPos (25000, 70000, 0);
|
||||
osg::Vec3f stormDirection = (playerPos - redMountainPos);
|
||||
stormDirection.normalize();
|
||||
|
||||
return stormDirection;
|
||||
}
|
||||
|
||||
float WeatherManager::calculateWindSpeed(int weatherId, float currentSpeed)
|
||||
{
|
||||
float targetSpeed = std::min(8.0f * mWeatherSettings[weatherId].mWindSpeed, 70.f);
|
||||
if (currentSpeed == 0.f)
|
||||
currentSpeed = targetSpeed;
|
||||
|
||||
float multiplier = mWeatherSettings[weatherId].mRainEffect.empty() ? 1.f : 0.5f;
|
||||
float updatedSpeed = (Misc::Rng::rollClosedProbability() - 0.5f) * multiplier * targetSpeed + currentSpeed;
|
||||
|
||||
if (updatedSpeed > 0.5f * targetSpeed && updatedSpeed < 2.f * targetSpeed)
|
||||
{
|
||||
currentSpeed = updatedSpeed;
|
||||
}
|
||||
|
||||
// Take in account direction to the Red Mountain, when needed
|
||||
if (weatherId == 6 || weatherId == 7)
|
||||
{
|
||||
currentSpeed = (calculateStormDirection() * currentSpeed).length();
|
||||
}
|
||||
|
||||
return currentSpeed;
|
||||
}
|
||||
|
||||
void WeatherManager::update(float duration, bool paused, const TimeStamp& time, bool isExterior)
|
||||
{
|
||||
MWWorld::ConstPtr player = MWMechanics::getPlayer();
|
||||
|
@ -779,12 +811,21 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
|
|||
{
|
||||
mRendering.setSkyEnabled(false);
|
||||
stopSounds();
|
||||
mWindSpeed = 0.f;
|
||||
mCurrentWindSpeed = 0.f;
|
||||
mNextWindSpeed = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
calculateWeatherResult(time.getHour(), duration, paused);
|
||||
|
||||
mWindSpeed = mResult.mWindSpeed;
|
||||
if (!paused)
|
||||
{
|
||||
mWindSpeed = mResult.mWindSpeed;
|
||||
mCurrentWindSpeed = mResult.mCurrentWindSpeed;
|
||||
mNextWindSpeed = mResult.mNextWindSpeed;
|
||||
}
|
||||
|
||||
mIsStorm = mResult.mIsStorm;
|
||||
|
||||
// For some reason Ash Storm is not considered as a precipitation weather in game
|
||||
|
@ -793,11 +834,7 @@ void WeatherManager::update(float duration, bool paused, const TimeStamp& time,
|
|||
|
||||
if (mIsStorm)
|
||||
{
|
||||
osg::Vec3f playerPos (player.getRefData().getPosition().asVec3());
|
||||
playerPos.z() = 0;
|
||||
osg::Vec3f redMountainPos (25000, 70000, 0);
|
||||
mStormDirection = (playerPos - redMountainPos);
|
||||
mStormDirection.normalize();
|
||||
mStormDirection = calculateStormDirection();
|
||||
mRendering.getSkyManager()->setStormDirection(mStormDirection);
|
||||
}
|
||||
|
||||
|
@ -1320,7 +1357,9 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
|
|||
|
||||
mResult.mCloudTexture = current.mCloudTexture;
|
||||
mResult.mCloudBlendFactor = 0;
|
||||
mResult.mWindSpeed = current.mWindSpeed;
|
||||
mResult.mNextWindSpeed = 0;
|
||||
mResult.mWindSpeed = mResult.mCurrentWindSpeed = calculateWindSpeed(weatherID, mWindSpeed);
|
||||
|
||||
mResult.mCloudSpeed = current.mCloudSpeed;
|
||||
mResult.mGlareView = current.mGlareView;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
|
@ -1330,7 +1369,11 @@ inline void WeatherManager::calculateResult(const int weatherID, const float gam
|
|||
mResult.mIsStorm = current.mIsStorm;
|
||||
|
||||
mResult.mRainSpeed = current.mRainSpeed;
|
||||
mResult.mRainFrequency = current.mRainFrequency;
|
||||
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
|
||||
mResult.mRainDiameter = current.mRainDiameter;
|
||||
mResult.mRainMinHeight = current.mRainMinHeight;
|
||||
mResult.mRainMaxHeight = current.mRainMaxHeight;
|
||||
mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;
|
||||
|
||||
mResult.mParticleEffect = current.mParticleEffect;
|
||||
mResult.mRainEffect = current.mRainEffect;
|
||||
|
@ -1404,23 +1447,35 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
|||
mResult.mFogDepth = lerp(current.mFogDepth, other.mFogDepth, factor);
|
||||
mResult.mDLFogFactor = lerp(current.mDLFogFactor, other.mDLFogFactor, factor);
|
||||
mResult.mDLFogOffset = lerp(current.mDLFogOffset, other.mDLFogOffset, factor);
|
||||
mResult.mWindSpeed = lerp(current.mWindSpeed, other.mWindSpeed, factor);
|
||||
|
||||
mResult.mCurrentWindSpeed = calculateWindSpeed(mCurrentWeather, mCurrentWindSpeed);
|
||||
mResult.mNextWindSpeed = calculateWindSpeed(mNextWeather, mNextWindSpeed);
|
||||
|
||||
mResult.mWindSpeed = lerp(mResult.mCurrentWindSpeed, mResult.mNextWindSpeed, factor);
|
||||
mResult.mCloudSpeed = lerp(current.mCloudSpeed, other.mCloudSpeed, factor);
|
||||
mResult.mGlareView = lerp(current.mGlareView, other.mGlareView, factor);
|
||||
mResult.mNightFade = lerp(current.mNightFade, other.mNightFade, factor);
|
||||
|
||||
mResult.mNight = current.mNight;
|
||||
|
||||
if(factor < 0.5)
|
||||
float threshold = mWeatherSettings[mNextWeather].mRainThreshold;
|
||||
if (threshold <= 0)
|
||||
threshold = 0.5f;
|
||||
|
||||
if(factor < threshold)
|
||||
{
|
||||
mResult.mIsStorm = current.mIsStorm;
|
||||
mResult.mParticleEffect = current.mParticleEffect;
|
||||
mResult.mRainEffect = current.mRainEffect;
|
||||
mResult.mRainSpeed = current.mRainSpeed;
|
||||
mResult.mRainFrequency = current.mRainFrequency;
|
||||
mResult.mAmbientSoundVolume = 1-(factor*2);
|
||||
mResult.mRainEntranceSpeed = current.mRainEntranceSpeed;
|
||||
mResult.mAmbientSoundVolume = 1 - factor / threshold;
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = current.mAmbientLoopSoundID;
|
||||
mResult.mRainDiameter = current.mRainDiameter;
|
||||
mResult.mRainMinHeight = current.mRainMinHeight;
|
||||
mResult.mRainMaxHeight = current.mRainMaxHeight;
|
||||
mResult.mRainMaxRaindrops = current.mRainMaxRaindrops;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1428,10 +1483,15 @@ inline void WeatherManager::calculateTransitionResult(const float factor, const
|
|||
mResult.mParticleEffect = other.mParticleEffect;
|
||||
mResult.mRainEffect = other.mRainEffect;
|
||||
mResult.mRainSpeed = other.mRainSpeed;
|
||||
mResult.mRainFrequency = other.mRainFrequency;
|
||||
mResult.mAmbientSoundVolume = 2*(factor-0.5f);
|
||||
mResult.mRainEntranceSpeed = other.mRainEntranceSpeed;
|
||||
mResult.mAmbientSoundVolume = (factor - threshold) / (1 - threshold);
|
||||
mResult.mEffectFade = mResult.mAmbientSoundVolume;
|
||||
mResult.mAmbientLoopSoundID = other.mAmbientLoopSoundID;
|
||||
|
||||
mResult.mRainDiameter = other.mRainDiameter;
|
||||
mResult.mRainMinHeight = other.mRainMinHeight;
|
||||
mResult.mRainMaxHeight = other.mRainMaxHeight;
|
||||
mResult.mRainMaxRaindrops = other.mRainMaxRaindrops;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,20 @@ namespace MWWorld
|
|||
float mRainSpeed;
|
||||
|
||||
// How often does a new rain mesh spawn?
|
||||
float mRainFrequency;
|
||||
float mRainEntranceSpeed;
|
||||
|
||||
// Maximum count of rain particles
|
||||
int mRainMaxRaindrops;
|
||||
|
||||
// Radius of rain effect
|
||||
float mRainDiameter;
|
||||
|
||||
// Transition threshold to spawn rain
|
||||
float mRainThreshold;
|
||||
|
||||
// Height of rain particles spawn
|
||||
float mRainMinHeight;
|
||||
float mRainMaxHeight;
|
||||
|
||||
std::string mParticleEffect;
|
||||
|
||||
|
@ -370,6 +383,8 @@ namespace MWWorld
|
|||
MoonModel mSecunda;
|
||||
|
||||
float mWindSpeed;
|
||||
float mCurrentWindSpeed;
|
||||
float mNextWindSpeed;
|
||||
bool mIsStorm;
|
||||
bool mPrecipitation;
|
||||
osg::Vec3f mStormDirection;
|
||||
|
@ -411,6 +426,7 @@ namespace MWWorld
|
|||
bool updateWeatherRegion(const std::string& playerRegion);
|
||||
void updateWeatherTransitions(const float elapsedRealSeconds);
|
||||
void forceWeather(const int weatherID);
|
||||
osg::Vec3f calculateStormDirection();
|
||||
|
||||
bool inTransition();
|
||||
void addWeatherTransition(const int weatherID);
|
||||
|
@ -418,6 +434,7 @@ namespace MWWorld
|
|||
void calculateWeatherResult(const float gameHour, const float elapsedSeconds, const bool isPaused);
|
||||
void calculateResult(const int weatherID, const float gameHour);
|
||||
void calculateTransitionResult(const float factor, const float gameHour);
|
||||
float calculateWindSpeed(int weatherId, float currentSpeed);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -971,7 +971,17 @@ namespace MWWorld
|
|||
|
||||
void World::advanceTime (double hours, bool incremental)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -3762,7 +3772,6 @@ namespace MWWorld
|
|||
closestDistance = distance;
|
||||
closestMarker = marker;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return closestMarker;
|
||||
|
@ -3773,6 +3782,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)
|
||||
{
|
||||
|
|
|
@ -752,6 +752,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;
|
||||
|
|
Loading…
Reference in a new issue