1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-21 06:39:42 +00:00

Merge branch 'master' into windspeed

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

View file

@ -2,6 +2,7 @@
------
Bug #1515: Opening console masks dialogue, inventory menu
Bug #1933: Actors can have few stocks of the same item
Bug #2395: Duplicated plugins in the launcher when multiple data directories provide the same plugin
Bug #2969: Scripted items can stack
Bug #2976: Data lines in global openmw.cfg take priority over user openmw.cfg
@ -18,6 +19,7 @@
Bug #3778: [Mod] Improved Thrown Weapon Projectiles - weapons have wrong transformation during throw animation
Bug #3812: Wrong multiline tooltips width when word-wrapping is enabled
Bug #3894: Hostile spell effects not detected/present on first frame of OnPCHitMe
Bug #4077: Enchanted items are not recharged if they are not in the player's inventory
Bug #4202: Open .omwaddon files without needing toopen openmw-cs first
Bug #4240: Ash storm origin coordinates and hand shielding animation behavior are incorrect
Bug #4270: Closing doors while they are obstructed desyncs closing sfx
@ -28,6 +30,7 @@
Bug #4384: Resist Normal Weapons only checks ammunition for ranged weapons
Bug #4411: Reloading a saved game while falling prevents damage in some cases
Bug #4449: Value returned by GetWindSpeed is incorrect
Bug #4456: AiActivate should not be cancelled after target activation
Bug #4540: Rain delay when exiting water
Bug #4600: Crash when no sound output is available or --no-sound is used.
Bug #4639: Black screen after completing first mages guild mission + training
@ -77,6 +80,7 @@
Bug #4888: Global variable stray explicit reference calls break script compilation
Bug #4896: Title screen music doesn't loop
Bug #4902: Using scrollbars in settings causes resolution to change
Bug #4904: Editor: Texture painting with duplicate of a base-version texture
Bug #4911: Editor: QOpenGLContext::swapBuffers() warning with Qt5
Bug #4916: Specular power (shininess) material parameter is ignored when shaders are used.
Bug #4918: Abilities don't play looping VFX when they're initially applied
@ -116,6 +120,7 @@
Bug #5050: Invalid spell effects are not handled gracefully
Bug #5055: Mark, Recall, Intervention magic effect abilities have no effect when added and removed in the same frame
Bug #5056: Calling Cast function on player doesn't equip the spell but casts it
Bug #5059: Modded animation with combined attack keys always does max damage and can double damage
Bug #5060: Magic effect visuals stop when death animation begins instead of when it ends
Bug #5063: Shape named "Tri Shadow" in creature mesh is visible if it isn't hidden
Bug #5067: Ranged attacks on unaware opponents ("critical hits") differ from the vanilla engine
@ -140,6 +145,7 @@
Bug #5124: Arrow remains attached to actor if pulling animation was cancelled
Bug #5126: Swimming creatures without RunForward animations are motionless during combat
Bug #5134: Doors rotation by "Lock" console command is inconsistent
Bug #5136: LegionUniform script: can not access local variables
Bug #5137: Textures with Clamp Mode set to Clamp instead of Wrap are too dark outside the boundaries
Bug #5149: Failing lock pick attempts isn't always a crime
Bug #5155: Lock/unlock behavior differs from vanilla
@ -151,6 +157,8 @@
Bug #5167: Player can select and cast spells before magic menu is enabled
Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change
Bug #5169: Nested levelled items/creatures have significantly higher chance not to spawn
Bug #5175: Random script function returns an integer value
Bug #5177: Editor: Unexplored map tiles get corrupted after a file with terrain is saved
Feature #1774: Handle AvoidNode
Feature #2229: Improve pathfinding AI
Feature #3025: Analogue gamepad movement controls

View file

@ -101,7 +101,9 @@ Editor Bug Fixes:
- Colour fields in interior-cell records now also use the colour picker widget (#4745)
- Cloned, added, or moved instances no longer disappear at load-time (#4748)
- "Clear" function in the content selector no longer tries to execute a "Remove" action on an empty file list (#4757)
- Terrain texture editing for plugins now correctly handles drags from base file (#4904)
- Engine no longer tries to swap buffers of windows which weren't exposed to Qt's window management system (#4911)
- Minimap doesn't get corrupted, when editing new omwgame (#5177)
Miscellaneous:
- Upgraded to FFMPEG3 for media decoding (#4686)

View file

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

View file

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

View file

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

View file

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

View file

@ -133,6 +133,7 @@ bool OMW::Engine::frame(float frametime)
{
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
mEnvironment.getWorld()->advanceTime(hours, true);
mEnvironment.getWorld()->rechargeItems(frametime, true);
}
}
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();

View file

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

View file

@ -584,6 +584,7 @@ namespace MWBase
virtual bool isPlayerInJail() const = 0;
virtual void rest(double hours) = 0;
virtual void rechargeItems(double duration, bool activeOnly) = 0;
virtual void setPlayerTraveling(bool traveling) = 0;
virtual bool isPlayerTraveling() const = 0;

View file

@ -25,12 +25,12 @@ namespace MWGui
{
}
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count)
{
if (hasProfit(mActor))
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)

View file

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

View file

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

View file

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

View file

@ -38,7 +38,7 @@ namespace MWGui
public:
WorldItemModel(float left, float top) : mLeft(left), mTop(top) {}
virtual ~WorldItemModel() {}
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
@ -47,8 +47,7 @@ namespace MWGui
dropped = world->placeObject(item.mBase, mLeft, mTop, count);
else
dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count);
if (setNewOwner)
dropped.getCellRef().setOwner("");
dropped.getCellRef().setOwner("");
return dropped;
}

View file

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

View file

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

View file

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

View file

@ -67,7 +67,7 @@ namespace MWGui
/// @param setNewOwner If true, set the copied item's owner to the actor we are copying to,
/// 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);

View file

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

View file

@ -17,6 +17,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/recharge.hpp"
#include "itemwidget.hpp"
#include "itemchargeview.hpp"
@ -130,62 +131,9 @@ void Recharge::onItemCancel()
void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
{
MWWorld::Ptr gem = *mGemIcon->getUserData<MWWorld::Ptr>();
if (!gem.getRefData().getCount())
if (!MWMechanics::rechargeItem(item, gem))
return;
MWWorld::Ptr player = MWMechanics::getPlayer();
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();
if (luckTerm < 1|| luckTerm > 10)
luckTerm = 1;
float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
if (intelligenceTerm > 20)
intelligenceTerm = 20;
if (intelligenceTerm < 1)
intelligenceTerm = 1;
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
int roll = Misc::Rng::roll0to99();
if (roll < x)
{
std::string soul = gem.getCellRef().getSoul();
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);
float restored = creature->mData.mSoul * (roll / x);
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
item.getClass().getEnchantment(item));
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
player.getClass().getContainerStore(player).restack(item);
}
else
{
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
}
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
gem.getContainerStore()->remove(gem, 1, player);
if (gem.getRefData().getCount() == 0)
{
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
MWBase::Environment::get().getWindowManager()->messageBox(message);
// special case: readd Azura's Star
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
}
updateView();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -287,16 +287,6 @@ namespace MWMechanics
mWatched = ptr;
}
void MechanicsManager::advanceTime (float duration)
{
// Uses ingame time, but scaled to real time
const float timeScaleFactor = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
if (timeScaleFactor != 0.0f)
duration /= timeScaleFactor;
MWWorld::Ptr player = getPlayer();
player.getClass().getInventoryStore(player).rechargeItems(duration);
}
void MechanicsManager::update(float duration, bool paused)
{
if(!mWatched.isEmpty())

View file

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

View file

@ -0,0 +1,92 @@
#include "recharge.hpp"
#include <components/misc/rng.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "creaturestats.hpp"
#include "actorutil.hpp"
namespace MWMechanics
{
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration)
{
float charge = item.getCellRef().getEnchantmentCharge();
if (charge == -1 || charge == maxCharge)
return false;
static const float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fMagicItemRechargePerSecond")->mValue.getFloat();
item.getCellRef().setEnchantmentCharge(std::min(charge + fMagicItemRechargePerSecond * duration, maxCharge));
return true;
}
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem)
{
if (!gem.getRefData().getCount())
return false;
MWWorld::Ptr player = MWMechanics::getPlayer();
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
float luckTerm = 0.1f * stats.getAttribute(ESM::Attribute::Luck).getModified();
if (luckTerm < 1 || luckTerm > 10)
luckTerm = 1;
float intelligenceTerm = 0.2f * stats.getAttribute(ESM::Attribute::Intelligence).getModified();
if (intelligenceTerm > 20)
intelligenceTerm = 20;
if (intelligenceTerm < 1)
intelligenceTerm = 1;
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
int roll = Misc::Rng::roll0to99();
if (roll < x)
{
std::string soul = gem.getCellRef().getSoul();
const ESM::Creature *creature = MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>().find(soul);
float restored = creature->mData.mSoul * (roll / x);
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
item.getClass().getEnchantment(item));
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
player.getClass().getContainerStore(player).restack(item);
}
else
{
MWBase::Environment::get().getWindowManager()->playSound("Enchant Fail");
}
player.getClass().skillUsageSucceeded (player, ESM::Skill::Enchant, 0);
gem.getContainerStore()->remove(gem, 1, player);
if (gem.getRefData().getCount() == 0)
{
std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage51")->mValue.getString();
message = Misc::StringUtils::format(message, gem.getClass().getName(gem));
MWBase::Environment::get().getWindowManager()->messageBox(message);
// special case: readd Azura's Star
if (Misc::StringUtils::ciEqual(gem.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
player.getClass().getContainerStore(player).add("Misc_SoulGem_Azura", 1, player);
}
return true;
}
}

View file

@ -0,0 +1,15 @@
#ifndef MWMECHANICS_RECHARGE_H
#define MWMECHANICS_RECHARGE_H
#include "../mwworld/ptr.hpp"
namespace MWMechanics
{
bool rechargeItem(const MWWorld::Ptr &item, const float maxCharge, const float duration);
bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem);
}
#endif

View file

@ -163,6 +163,19 @@ void MWWorld::Cells::rest (double hours)
}
}
void MWWorld::Cells::recharge (float duration)
{
for (auto &interior : mInteriors)
{
interior.second.recharge(duration);
}
for (auto &exterior : mExteriors)
{
exterior.second.recharge(duration);
}
}
MWWorld::CellStore *MWWorld::Cells::getCell (const ESM::CellId& id)
{
if (id.mPaged)

View file

@ -61,7 +61,9 @@ namespace MWWorld
/// @note name must be lower case
Ptr getPtr (const std::string& name);
void rest (double hours);
void recharge (float duration);
/// Get all Ptrs referencing \a name in exterior cells
/// @note Due to the current implementation of getPtr this only supports one Ptr per cell.

View file

@ -21,6 +21,7 @@
#include "../mwbase/world.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/recharge.hpp"
#include "ptr.hpp"
#include "esmstore.hpp"
@ -328,6 +329,7 @@ namespace MWWorld
void CellStore::updateMergedRefs()
{
mMergedRefs.clear();
mRechargingItemsUpToDate = false;
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
forEachInternal(visitor);
visitor.merge();
@ -345,7 +347,7 @@ namespace MWWorld
}
CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector<ESM::ESMReader>& readerList)
: mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0)
: mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0), mRechargingItemsUpToDate(false)
{
mWaterLevel = cell->mWater;
}
@ -992,6 +994,42 @@ namespace MWWorld
}
}
void CellStore::recharge(float duration)
{
if (duration <= 0)
return;
if (mState == State_Loaded)
{
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
}
}
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
}
}
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0)
{
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
}
}
rechargeItems(duration);
}
}
void CellStore::respawn()
{
if (mState == State_Loaded)
@ -1027,4 +1065,73 @@ namespace MWWorld
}
}
}
void MWWorld::CellStore::rechargeItems(float duration)
{
if (!mRechargingItemsUpToDate)
{
updateRechargingItems();
mRechargingItemsUpToDate = true;
}
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
{
MWMechanics::rechargeItem(it->first, it->second, duration);
}
}
void MWWorld::CellStore::updateRechargingItems()
{
mRechargingItems.clear();
for (CellRefList<ESM::Weapon>::List::iterator it (mWeapons.mList.begin()); it!=mWeapons.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
checkItem(ptr);
}
}
for (CellRefList<ESM::Armor>::List::iterator it (mArmors.mList.begin()); it!=mArmors.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
checkItem(ptr);
}
}
for (CellRefList<ESM::Clothing>::List::iterator it (mClothes.mList.begin()); it!=mClothes.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
checkItem(ptr);
}
}
for (CellRefList<ESM::Book>::List::iterator it (mBooks.mList.begin()); it!=mBooks.mList.end(); ++it)
{
Ptr ptr = getCurrentPtr(&*it);
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
{
checkItem(ptr);
}
}
}
void MWWorld::CellStore::checkItem(Ptr ptr)
{
if (ptr.getClass().getEnchantment(ptr).empty())
return;
std::string enchantmentId = ptr.getClass().getEnchantment(ptr);
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
if (!enchantment)
{
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << ptr.getCellRef().getRefId();
return;
}
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));
}
}

View file

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

View file

@ -13,6 +13,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/levelledlist.hpp"
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/recharge.hpp"
#include "manualref.hpp"
#include "refdata.hpp"
@ -114,7 +115,11 @@ void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
MWWorld::ContainerStore::ContainerStore() : mListener(nullptr), mCachedWeight (0), mWeightUpToDate (false) {}
MWWorld::ContainerStore::ContainerStore()
: mListener(nullptr)
, mRechargingItemsUpToDate(false)
, mCachedWeight (0)
, mWeightUpToDate (false) {}
MWWorld::ContainerStore::~ContainerStore() {}
@ -241,7 +246,6 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
}
return ptr1 != ptr2 // an item never stacks onto itself
&& ptr1.getCellRef().getOwner() == ptr2.getCellRef().getOwner()
&& ptr1.getCellRef().getSoul() == ptr2.getCellRef().getSoul()
&& ptr1.getClass().getRemainingUsageTime(ptr1) == ptr2.getClass().getRemainingUsageTime(ptr2)
@ -259,30 +263,14 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const std::string &id, int count, const Ptr &actorPtr)
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), id, count);
return add(ref.getPtr(), count, actorPtr, true);
return add(ref.getPtr(), count, actorPtr);
}
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)
MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, int count, const Ptr& actorPtr)
{
Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
MWWorld::ContainerStoreIterator it = end();
// HACK: Set owner on the original item, then reset it after we have copied it
// If we set the owner on the copied item, it would not stack correctly...
std::string oldOwner = itemPtr.getCellRef().getOwner();
if (!setOwner || actorPtr == MWMechanics::getPlayer()) // No point in setting owner to the player - NPCs will not respect this anyway
{
itemPtr.getCellRef().setOwner("");
}
else
{
itemPtr.getCellRef().setOwner(actorPtr.getCellRef().getRefId());
}
it = addImp(itemPtr, count);
itemPtr.getCellRef().setOwner(oldOwner);
MWWorld::ContainerStoreIterator it = addImp(itemPtr, count);
// The copy of the original item we just made
MWWorld::Ptr item = *it;
@ -298,7 +286,8 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
pos.pos[2] = 0;
item.getCellRef().setPosition(pos);
// reset ownership stuff, owner was already handled above
// We do not need to store owners for items in container stores - we do not use it anyway.
item.getCellRef().setOwner("");
item.getCellRef().resetGlobalVariable();
item.getCellRef().setFaction("");
item.getCellRef().setFactionRank(-1);
@ -408,6 +397,46 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Cons
return it;
}
void MWWorld::ContainerStore::rechargeItems(float duration)
{
if (!mRechargingItemsUpToDate)
{
updateRechargingItems();
mRechargingItemsUpToDate = true;
}
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
{
if (!MWMechanics::rechargeItem(*it->first, it->second, duration))
continue;
// attempt to restack when fully recharged
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
it->first = restack(*it->first);
}
}
void MWWorld::ContainerStore::updateRechargingItems()
{
mRechargingItems.clear();
for (ContainerStoreIterator it = begin(); it != end(); ++it)
{
const std::string& enchantmentId = it->getClass().getEnchantment(*it);
if (!enchantmentId.empty())
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
if (!enchantment)
{
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId();
continue;
}
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.emplace_back(it, static_cast<float>(enchantment->mData.mCharge));
}
}
}
int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor)
{
int toRemove = count;
@ -477,50 +506,64 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
int count, bool topLevel, const std::string& levItem)
{
if (count == 0) return; //Don't restock with nothing.
try {
try
{
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count);
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
if (ref.getPtr().getClass().getScript(ref.getPtr()).empty())
{
const ESM::ItemLevList* levItemList = ref.getPtr().get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(id, owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
addInitialItemImp(ref.getPtr(), owner, count, topLevel, levItem);
}
else
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(id, levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ref.getPtr().getCellRef().setOwner(owner);
addImp (ref.getPtr(), count);
// Adding just one item per time to make sure there isn't a stack of scripted items
for (int i = 0; i < abs(count); i++)
addInitialItemImp(ref.getPtr(), owner, count < 0 ? -1 : 1, topLevel, levItem);
}
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Warning: MWWorld::ContainerStore::addInitialItem: " << e.what();
}
}
void MWWorld::ContainerStore::addInitialItemImp(const MWWorld::Ptr& ptr, const std::string& owner,
int count, bool topLevel, const std::string& levItem)
{
if (ptr.getTypeName()==typeid (ESM::ItemLevList).name())
{
const ESM::ItemLevList* levItemList = ptr.get<ESM::ItemLevList>()->mBase;
if (topLevel && std::abs(count) > 1 && levItemList->mFlags & ESM::ItemLevList::Each)
{
for (int i=0; i<std::abs(count); ++i)
addInitialItem(ptr.getCellRef().getRefId(), owner, count > 0 ? 1 : -1, true, levItemList->mId);
return;
}
else
{
std::string itemId = MWMechanics::getLevelledItem(ptr.get<ESM::ItemLevList>()->mBase, false);
if (itemId.empty())
return;
addInitialItem(itemId, owner, count, false, levItemList->mId);
}
}
else
{
// A negative count indicates restocking items
// For a restocking levelled item, remember what we spawned so we can delete it later when the merchant restocks
if (!levItem.empty() && count < 0)
{
//If there is no item in map, insert it
std::map<std::pair<std::string, std::string>, int>::iterator itemInMap =
mLevelledItemMap.insert(std::make_pair(std::make_pair(ptr.getCellRef().getRefId(), levItem), 0)).first;
//Update spawned count
itemInMap->second += std::abs(count);
}
count = std::abs(count);
ptr.getCellRef().setOwner(owner);
addImp (ptr, count);
}
}
void MWWorld::ContainerStore::restock (const ESM::InventoryList& items, const MWWorld::Ptr& ptr, const std::string& owner)
@ -619,6 +662,7 @@ void MWWorld::ContainerStore::clear()
void MWWorld::ContainerStore::flagAsModified()
{
mWeightUpToDate = false;
mRechargingItemsUpToDate = false;
}
float MWWorld::ContainerStore::getWeight() const

View file

@ -71,6 +71,12 @@ namespace MWWorld
protected:
ContainerStoreListener* mListener;
// (item, max charge)
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
TRechargingItems mRechargingItems;
bool mRechargingItemsUpToDate;
private:
MWWorld::CellRefList<ESM::Potion> potions;
@ -94,6 +100,7 @@ namespace MWWorld
mutable bool mWeightUpToDate;
ContainerStoreIterator addImp (const Ptr& ptr, int count);
void addInitialItem (const std::string& id, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
void addInitialItemImp (const MWWorld::Ptr& ptr, const std::string& owner, int count, bool topLevel=true, const std::string& levItem = "");
template<typename T>
ContainerStoreIterator getState (CellRefList<T>& collection,
@ -107,6 +114,7 @@ namespace MWWorld
ESM::InventoryState& inventory, int& index,
bool equipable = false) const;
void updateRechargingItems();
virtual void storeEquipmentState (const MWWorld::LiveCellRefBase& ref, int index, ESM::InventoryState& inventory) const;
@ -130,7 +138,7 @@ namespace MWWorld
bool hasVisibleItems() const;
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false);
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
///
/// \note The item pointed to is not required to exist beyond this function call.
@ -155,6 +163,9 @@ namespace MWWorld
///
/// @return the number of items actually removed
void rechargeItems (float duration);
///< Restore charge on enchanted items. Note this should only be done for the player.
ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1);
///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count).
///

View file

@ -101,7 +101,6 @@ MWWorld::InventoryStore::InventoryStore()
, mUpdatesEnabled (true)
, mFirstAutoEquip(true)
, mSelectedEnchantItem(end())
, mRechargingItemsUpToDate(false)
{
initSlots (mSlots);
}
@ -114,7 +113,6 @@ MWWorld::InventoryStore::InventoryStore (const InventoryStore& store)
, mFirstAutoEquip(store.mFirstAutoEquip)
, mPermanentMagicEffectMagnitudes(store.mPermanentMagicEffectMagnitudes)
, mSelectedEnchantItem(end())
, mRechargingItemsUpToDate(false)
{
copySlots (store);
}
@ -133,9 +131,9 @@ MWWorld::InventoryStore& MWWorld::InventoryStore::operator= (const InventoryStor
return *this;
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner)
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, int count, const Ptr& actorPtr)
{
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr, setOwner);
const MWWorld::ContainerStoreIterator& retVal = MWWorld::ContainerStore::add(itemPtr, count, actorPtr);
// Auto-equip items if an armor/clothing or weapon item is added, but not for the player nor werewolves
if (actorPtr != MWMechanics::getPlayer()
@ -709,12 +707,6 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
mFirstAutoEquip = false;
}
void MWWorld::InventoryStore::flagAsModified()
{
ContainerStore::flagAsModified();
mRechargingItemsUpToDate = false;
}
bool MWWorld::InventoryStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) const
{
bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2);
@ -957,57 +949,6 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
}
}
void MWWorld::InventoryStore::updateRechargingItems()
{
mRechargingItems.clear();
for (ContainerStoreIterator it = begin(); it != end(); ++it)
{
if (it->getClass().getEnchantment(*it) != "")
{
std::string enchantmentId = it->getClass().getEnchantment(*it);
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(
enchantmentId);
if (!enchantment)
{
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item " << it->getCellRef().getRefId();
continue;
}
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.push_back(std::make_pair(it, static_cast<float>(enchantment->mData.mCharge)));
}
}
}
void MWWorld::InventoryStore::rechargeItems(float duration)
{
if (!mRechargingItemsUpToDate)
{
updateRechargingItems();
mRechargingItemsUpToDate = true;
}
for (TRechargingItems::iterator it = mRechargingItems.begin(); it != mRechargingItems.end(); ++it)
{
if (it->first->getCellRef().getEnchantmentCharge() == -1
|| it->first->getCellRef().getEnchantmentCharge() == it->second)
continue;
static float fMagicItemRechargePerSecond = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"fMagicItemRechargePerSecond")->mValue.getFloat();
if (it->first->getCellRef().getEnchantmentCharge() <= it->second)
{
it->first->getCellRef().setEnchantmentCharge(std::min (it->first->getCellRef().getEnchantmentCharge() + fMagicItemRechargePerSecond * duration,
it->second));
// attempt to restack when fully recharged
if (it->first->getCellRef().getEnchantmentCharge() == it->second)
it->first = restack(*it->first);
}
}
}
void MWWorld::InventoryStore::purgeEffect(short effectId)
{
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)

View file

@ -101,18 +101,11 @@ namespace MWWorld
// selected magic item (for using enchantments of type "Cast once" or "Cast when used")
ContainerStoreIterator mSelectedEnchantItem;
// (item, max charge)
typedef std::vector<std::pair<ContainerStoreIterator, float> > TRechargingItems;
TRechargingItems mRechargingItems;
bool mRechargingItemsUpToDate;
void copySlots (const InventoryStore& store);
void initSlots (TSlots& slots_);
void updateMagicEffects(const Ptr& actor);
void updateRechargingItems();
void fireEquipmentChangedEvent(const Ptr& actor);
@ -132,7 +125,7 @@ namespace MWWorld
virtual InventoryStore* clone() { return new InventoryStore(*this); }
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr, bool setOwner=false);
virtual ContainerStoreIterator add (const Ptr& itemPtr, int count, const Ptr& actorPtr);
///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed)
/// Auto-equip items if specific conditions are fulfilled (see the implementation).
///
@ -171,10 +164,6 @@ namespace MWWorld
const MWMechanics::MagicEffects& getMagicEffects() const;
///< Return magic effects from worn items.
virtual void flagAsModified();
///< \attention This function is internal to the world model and should not be called from
/// outside.
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
///< @return true if the two specified objects can stack with each other
@ -216,9 +205,6 @@ namespace MWWorld
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
void rechargeItems (float duration);
///< Restore charge on enchanted items. Note this should only be done for the player.
void purgeEffect (short effectId);
///< Remove a magic effect

View file

@ -857,7 +857,17 @@ namespace MWWorld
void World::advanceTime (double hours, bool incremental)
{
MWBase::Environment::get().getMechanicsManager()->advanceTime(static_cast<float>(hours * 3600));
if (!incremental)
{
// When we fast-forward time, we should recharge magic items
// in all loaded cells, using game world time
float duration = hours * 3600;
const float timeScaleFactor = getTimeScaleFactor();
if (timeScaleFactor != 0.0f)
duration /= timeScaleFactor;
rechargeItems(duration, false);
}
mWeatherManager->advanceTime (hours, incremental);
@ -1649,7 +1659,6 @@ namespace MWWorld
}
// we need to undo the rotation
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
reached = false;
}
}
@ -1671,6 +1680,8 @@ namespace MWWorld
if (!closeSound.empty() && MWBase::Environment::get().getSoundManager()->getSoundPlaying(door, closeSound))
MWBase::Environment::get().getSoundManager()->stopSound3D(door, closeSound);
}
rotateObject(door, objPos.rot[0], objPos.rot[1], oldRot);
}
// the rotation order we want to use
@ -3304,7 +3315,6 @@ namespace MWWorld
closestDistance = distance;
closestMarker = marker;
}
}
return closestMarker;
@ -3315,6 +3325,22 @@ namespace MWWorld
mCells.rest(hours);
}
void World::rechargeItems(double duration, bool activeOnly)
{
MWWorld::Ptr player = getPlayerPtr();
player.getClass().getInventoryStore(player).rechargeItems(duration);
if (activeOnly)
{
for (auto &cell : mWorldScene->getActiveCells())
{
cell->recharge(duration);
}
}
else
mCells.recharge(duration);
}
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
const std::string& id)
{

View file

@ -574,6 +574,7 @@ namespace MWWorld
///< check if the player is allowed to rest
void rest(double hours) override;
void rechargeItems(double duration, bool activeOnly) override;
/// \todo Probably shouldn't be here
MWRender::Animation* getAnimation(const MWWorld::Ptr &ptr) override;

View file

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

View file

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

View file

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

View file

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

View file

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