forked from mirror/openmw-tes3mp
Closes #1092: Implement sleep interruption. Fix levelled list flags for creatures. Change World::copyObjectToCell to search for the correct cell.
This commit is contained in:
parent
69381c49c7
commit
52b9ebff9d
13 changed files with 150 additions and 43 deletions
|
@ -717,16 +717,26 @@ std::string landFlags(int flags)
|
|||
return properties;
|
||||
}
|
||||
|
||||
std::string leveledListFlags(int flags)
|
||||
std::string itemListFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels ";
|
||||
// This flag apparently not present on creature lists...
|
||||
if (flags & ESM::LeveledListBase::Each) properties += "Each ";
|
||||
if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels ";
|
||||
if (flags & ESM::ItemLevList::Each) properties += "Each ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::LeveledListBase::AllLevels|
|
||||
ESM::LeveledListBase::Each));
|
||||
(ESM::ItemLevList::AllLevels|
|
||||
ESM::ItemLevList::Each));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string creatureListFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels ";
|
||||
int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels);
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
|
|
|
@ -50,7 +50,8 @@ std::string cellFlags(int flags);
|
|||
std::string containerFlags(int flags);
|
||||
std::string creatureFlags(int flags);
|
||||
std::string landFlags(int flags);
|
||||
std::string leveledListFlags(int flags);
|
||||
std::string creatureListFlags(int flags);
|
||||
std::string itemListFlags(int flags);
|
||||
std::string lightFlags(int flags);
|
||||
std::string magicEffectFlags(int flags);
|
||||
std::string npcFlags(int flags);
|
||||
|
|
|
@ -834,7 +834,8 @@ template<>
|
|||
void Record<ESM::CreatureLevList>::print()
|
||||
{
|
||||
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
|
||||
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Chance none: " << mData.mChanceNone << std::endl;
|
||||
std::cout << " Number of items: " << mData.mList.size() << std::endl;
|
||||
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
|
||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
|
||||
|
@ -846,11 +847,12 @@ template<>
|
|||
void Record<ESM::ItemLevList>::print()
|
||||
{
|
||||
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
|
||||
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Chance none: " << mData.mChanceNone << std::endl;
|
||||
std::cout << " Number of items: " << mData.mList.size() << std::endl;
|
||||
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
|
||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
|
||||
std::cout << " Inventory: Count: " << iit->mLevel
|
||||
std::cout << " Inventory: Level: " << iit->mLevel
|
||||
<< " Item: " << iit->mId << std::endl;
|
||||
}
|
||||
|
||||
|
|
|
@ -460,6 +460,9 @@ namespace MWBase
|
|||
virtual void confiscateStolenItems(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
virtual void goToJail () = 0;
|
||||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
virtual void spawnRandomCreature(const std::string& creatureList) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace MWGui
|
|||
, mRemainingTime(0.05)
|
||||
, mCurHour(0)
|
||||
, mManualHours(1)
|
||||
, mInterruptAt(-1)
|
||||
{
|
||||
getWidget(mDateTimeText, "DateTimeText");
|
||||
getWidget(mRestText, "RestText");
|
||||
|
@ -156,7 +157,8 @@ namespace MWGui
|
|||
|
||||
void WaitDialog::startWaiting(int hoursToWait)
|
||||
{
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0.2);
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
world->getFader ()->fadeOut(0.2);
|
||||
setVisible(false);
|
||||
mProgressBar.setVisible (true);
|
||||
|
||||
|
@ -164,6 +166,30 @@ namespace MWGui
|
|||
mCurHour = 0;
|
||||
mHours = hoursToWait;
|
||||
|
||||
// FIXME: move this somewhere else?
|
||||
mInterruptAt = -1;
|
||||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
if (mSleeping && player.getCell()->isExterior())
|
||||
{
|
||||
std::string regionstr = player.getCell()->mCell->mRegion;
|
||||
if (!regionstr.empty())
|
||||
{
|
||||
const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr);
|
||||
if (!region->mSleepList.empty())
|
||||
{
|
||||
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat();
|
||||
int x = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested]
|
||||
float y = fSleepRandMod * hoursToWait;
|
||||
if (x > y)
|
||||
{
|
||||
float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat();
|
||||
mInterruptAt = int(fSleepRestMod * hoursToWait);
|
||||
mInterruptCreatureList = region->mSleepList;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRemainingTime = 0.05;
|
||||
mProgressBar.setProgress (0, mHours);
|
||||
}
|
||||
|
@ -206,6 +232,13 @@ namespace MWGui
|
|||
if (!mWaiting)
|
||||
return;
|
||||
|
||||
if (mCurHour == mInterruptAt)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sSleepInterrupt}");
|
||||
MWBase::Environment::get().getWorld()->spawnRandomCreature(mInterruptCreatureList);
|
||||
stopWaiting();
|
||||
}
|
||||
|
||||
mRemainingTime -= dt;
|
||||
|
||||
while (mRemainingTime < 0)
|
||||
|
|
|
@ -51,6 +51,9 @@ namespace MWGui
|
|||
int mManualHours; // stores the hours to rest selected via slider
|
||||
float mRemainingTime;
|
||||
|
||||
int mInterruptAt;
|
||||
std::string mInterruptCreatureList;
|
||||
|
||||
WaitDialogProgressBar mProgressBar;
|
||||
|
||||
void onUntilHealedButtonClicked(MyGUI::Widget* sender);
|
||||
|
|
|
@ -515,7 +515,7 @@ namespace MWMechanics
|
|||
if (magnitude > 0)
|
||||
{
|
||||
ESM::Position ipos = ptr.getRefData().getPosition();
|
||||
Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]);
|
||||
Ogre::Vector3 pos(ipos.pos);
|
||||
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
|
||||
const float distance = 50;
|
||||
pos = pos + distance*rot.yAxis();
|
||||
|
|
|
@ -860,6 +860,7 @@ void CharacterController::update(float duration)
|
|||
bool onground = world->isOnGround(mPtr);
|
||||
bool inwater = world->isSwimming(mPtr);
|
||||
bool isrunning = cls.getStance(mPtr, MWWorld::Class::Run);
|
||||
isrunning = true;
|
||||
bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak);
|
||||
bool flying = world->isFlying(mPtr);
|
||||
Ogre::Vector3 vec = cls.getMovementVector(mPtr);
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace MWMechanics
|
|||
{
|
||||
|
||||
/// @return ID of resulting item, or empty if none
|
||||
std::string getLevelledItem (const ESM::LeveledListBase* levItem, unsigned char failChance=0)
|
||||
inline std::string getLevelledItem (const ESM::LeveledListBase* levItem, bool creature, unsigned char failChance=0)
|
||||
{
|
||||
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
|
||||
|
||||
|
@ -20,29 +20,33 @@ namespace MWMechanics
|
|||
|
||||
failChance += levItem->mChanceNone;
|
||||
|
||||
float random = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
if (random < failChance/100.f)
|
||||
int random = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
|
||||
if (random < failChance)
|
||||
return std::string();
|
||||
|
||||
std::vector<std::string> candidates;
|
||||
int highestLevel = 0;
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if (it->mLevel > highestLevel)
|
||||
if (it->mLevel > highestLevel && it->mLevel <= playerLevel)
|
||||
highestLevel = it->mLevel;
|
||||
}
|
||||
|
||||
// For levelled creatures, the flags are swapped. This file format just makes so much sense.
|
||||
bool allLevels = levItem->mFlags & ESM::ItemLevList::AllLevels;
|
||||
if (creature)
|
||||
allLevels = levItem->mFlags & ESM::CreatureLevList::AllLevels;
|
||||
|
||||
std::pair<int, std::string> highest = std::make_pair(-1, "");
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if (playerLevel >= it->mLevel
|
||||
&& (levItem->mFlags & ESM::LeveledListBase::AllLevels || it->mLevel == highestLevel))
|
||||
&& (allLevels || it->mLevel == highestLevel))
|
||||
{
|
||||
candidates.push_back(it->mId);
|
||||
if (it->mLevel >= highest.first)
|
||||
highest = std::make_pair(it->mLevel, it->mId);
|
||||
}
|
||||
|
||||
}
|
||||
if (candidates.empty())
|
||||
return std::string();
|
||||
|
|
|
@ -321,7 +321,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
}
|
||||
else
|
||||
{
|
||||
std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase);
|
||||
std::string id = MWMechanics::getLevelledItem(ref.getPtr().get<ESM::ItemLevList>()->mBase, false);
|
||||
if (id.empty())
|
||||
return;
|
||||
addInitialItem(id, owner, faction, count, false);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "../mwmechanics/movement.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
#include "../mwmechanics/levelledlist.hpp"
|
||||
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
|
@ -1538,23 +1539,28 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
|
||||
Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos, bool adjustPos)
|
||||
Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos)
|
||||
{
|
||||
/// \todo add searching correct cell for position specified
|
||||
MWWorld::Ptr dropped =
|
||||
MWWorld::Class::get(object).copyToCell(object, cell, pos);
|
||||
|
||||
if (object.getClass().isActor() || adjustPos)
|
||||
{
|
||||
Ogre::Vector3 min, max;
|
||||
if (mPhysics->getObjectAABB(object, min, max)) {
|
||||
float *pos = dropped.getRefData().getPosition().pos;
|
||||
pos[0] -= (min.x + max.x) / 2;
|
||||
pos[1] -= (min.y + max.y) / 2;
|
||||
pos[2] -= min.z;
|
||||
pos.pos[0] -= (min.x + max.x) / 2;
|
||||
pos.pos[1] -= (min.y + max.y) / 2;
|
||||
pos.pos[2] -= min.z;
|
||||
}
|
||||
}
|
||||
|
||||
if (cell.isExterior())
|
||||
{
|
||||
int cellX, cellY;
|
||||
positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY);
|
||||
cell = *mCells.getExterior(cellX, cellY);
|
||||
}
|
||||
|
||||
MWWorld::Ptr dropped =
|
||||
MWWorld::Class::get(object).copyToCell(object, cell, pos);
|
||||
|
||||
if (mWorldScene->isCellActive(cell)) {
|
||||
if (dropped.getRefData().isEnabled()) {
|
||||
mWorldScene->addObjectToScene(dropped);
|
||||
|
@ -2571,4 +2577,37 @@ namespace MWWorld
|
|||
MWBase::Environment::get().getWindowManager()->messageBox(message, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
void World::spawnRandomCreature(const std::string &creatureList)
|
||||
{
|
||||
const ESM::CreatureLevList* list = getStore().get<ESM::CreatureLevList>().find(creatureList);
|
||||
|
||||
int iNumberCreatures = getStore().get<ESM::GameSetting>().find("iNumberCreatures")->getInt();
|
||||
int numCreatures = 1 + std::rand()/ (static_cast<double> (RAND_MAX) + 1) * iNumberCreatures; // [1, iNumberCreatures]
|
||||
|
||||
for (int i=0; i<numCreatures; ++i)
|
||||
{
|
||||
std::string selectedCreature = MWMechanics::getLevelledItem(list, true);
|
||||
if (selectedCreature.empty())
|
||||
return;
|
||||
|
||||
ESM::Position ipos = mPlayer->getPlayer().getRefData().getPosition();
|
||||
Ogre::Vector3 pos(ipos.pos);
|
||||
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
|
||||
const float distance = 50;
|
||||
pos = pos + distance*rot.yAxis();
|
||||
ipos.pos[0] = pos.x;
|
||||
ipos.pos[1] = pos.y;
|
||||
ipos.pos[2] = pos.z;
|
||||
ipos.rot[0] = 0;
|
||||
ipos.rot[1] = 0;
|
||||
ipos.rot[2] = 0;
|
||||
|
||||
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
|
||||
MWWorld::ManualRef ref(getStore(), selectedCreature, 1);
|
||||
ref.getPtr().getCellRef().mPos = ipos;
|
||||
|
||||
safePlaceObject(ref.getPtr(),*cell,ipos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace MWWorld
|
|||
bool moveObjectImp (const Ptr& ptr, float x, float y, float z);
|
||||
///< @return true if the active cell (cell player is in) changed
|
||||
|
||||
Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos, bool adjustPos=true);
|
||||
Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true);
|
||||
|
||||
void updateWindowManager ();
|
||||
void performUpdateSceneQueries ();
|
||||
|
@ -546,6 +546,9 @@ namespace MWWorld
|
|||
virtual void confiscateStolenItems(const MWWorld::Ptr& ptr);
|
||||
|
||||
virtual void goToJail ();
|
||||
|
||||
/// Spawn a random creature from a levelled list next to the player
|
||||
virtual void spawnRandomCreature(const std::string& creatureList);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,20 +20,6 @@ class ESMWriter;
|
|||
|
||||
struct LeveledListBase
|
||||
{
|
||||
enum Flags
|
||||
{
|
||||
|
||||
Each = 0x01, // Select a new item each time this
|
||||
// list is instantiated, instead of
|
||||
// giving several identical items
|
||||
// (used when a container has more
|
||||
// than one instance of one leveled
|
||||
// list.)
|
||||
AllLevels = 0x02 // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
// player.
|
||||
};
|
||||
|
||||
int mFlags;
|
||||
unsigned char mChanceNone; // Chance that none are selected (0-100)
|
||||
std::string mId;
|
||||
|
@ -61,6 +47,14 @@ struct CreatureLevList: LeveledListBase
|
|||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
enum Flags
|
||||
{
|
||||
|
||||
AllLevels = 0x01 // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
// player.
|
||||
};
|
||||
|
||||
CreatureLevList()
|
||||
{
|
||||
mRecName = "CNAM";
|
||||
|
@ -71,6 +65,20 @@ struct ItemLevList: LeveledListBase
|
|||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
enum Flags
|
||||
{
|
||||
|
||||
Each = 0x01, // Select a new item each time this
|
||||
// list is instantiated, instead of
|
||||
// giving several identical items
|
||||
// (used when a container has more
|
||||
// than one instance of one leveled
|
||||
// list.)
|
||||
AllLevels = 0x02 // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
// player.
|
||||
};
|
||||
|
||||
ItemLevList()
|
||||
{
|
||||
mRecName = "INAM";
|
||||
|
|
Loading…
Reference in a new issue