mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-03 16:45:34 +00:00
merge master, resolve conflicts
This commit is contained in:
commit
aa5647b45e
127 changed files with 2166 additions and 801 deletions
|
@ -594,6 +594,7 @@ if (WIN32)
|
|||
4706 # Assignment in conditional expression
|
||||
4738 # Storing 32-bit float result in memory, possible loss of performance
|
||||
4986 # Undocumented warning that occurs in the crtdbg.h file
|
||||
4987 # nonstandard extension used (triggered by setjmp.h)
|
||||
4996 # Function was declared deprecated
|
||||
|
||||
# cause by ogre extensivly
|
||||
|
@ -610,7 +611,9 @@ if (WIN32)
|
|||
4305 # Truncating value (double to float, for example)
|
||||
4309 # Variable overflow, trying to store 128 in a signed char for example
|
||||
4355 # Using 'this' in member initialization list
|
||||
4505 # Unreferenced local function has been removed
|
||||
4701 # Potentially uninitialized local variable used
|
||||
4702 # Unreachable code
|
||||
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
||||
)
|
||||
|
||||
|
@ -618,19 +621,31 @@ if (WIN32)
|
|||
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||
endforeach(d)
|
||||
|
||||
set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
|
||||
set(SHINY_WARNINGS "${WARNINGS} /wd4245")
|
||||
set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${SHINY_WARNINGS})
|
||||
# there's an unreferenced local variable in the ogre platform, suppress it
|
||||
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
|
||||
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${SHINY_OGRE_WARNINGS})
|
||||
set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
set_target_properties(oics PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
if (BUILD_LAUNCHER)
|
||||
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_LAUNCHER)
|
||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
if (BUILD_BSATOOL)
|
||||
if (BUILD_BSATOOL)
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_BSATOOL)
|
||||
endif (BUILD_BSATOOL)
|
||||
if (BUILD_ESMTOOL)
|
||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_ESMTOOL)
|
||||
if (BUILD_OPENCS)
|
||||
set_target_properties(opencs PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_OPENCS)
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
set_target_properties(mwiniimport PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||
endif (BUILD_MWINIIMPORTER)
|
||||
endif(MSVC)
|
||||
|
||||
# Same for MinGW
|
||||
|
|
|
@ -111,13 +111,17 @@ void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::Univers
|
|||
{
|
||||
std::vector<std::pair<std::string, std::vector<std::string> > > filterSource;
|
||||
|
||||
for (std::vector<CSMWorld::UniversalId>::iterator it = types.begin(); it != types.end(); ++it)
|
||||
for (std::vector<CSMWorld::UniversalId>::iterator it(types.begin()); it != types.end(); ++it)
|
||||
{
|
||||
std::pair<std::string, std::vector<std::string> > pair( //splited long line
|
||||
std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType()))));
|
||||
std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType()))));
|
||||
|
||||
filterSource.push_back(pair);
|
||||
if(!pair.second.empty())
|
||||
{
|
||||
filterSource.push_back(pair);
|
||||
}
|
||||
}
|
||||
|
||||
mFilterBox->createFilterRequest(filterSource, action);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type)
|
|||
std::vector<std::string> enums =
|
||||
CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType);
|
||||
|
||||
if (type<0 && type>=enums.size())
|
||||
if (static_cast<size_t>(type) >= enums.size())
|
||||
throw std::logic_error ("Unsupported variable type");
|
||||
|
||||
mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str())));
|
||||
|
|
|
@ -67,7 +67,7 @@ add_openmw_dir (mwclass
|
|||
|
||||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow
|
||||
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
|
||||
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
|
||||
disease pickpocket levelledlist combat steering obstacle
|
||||
)
|
||||
|
|
|
@ -200,6 +200,9 @@ namespace MWBase
|
|||
virtual MWWorld::Ptr searchPtrViaHandle (const std::string& handle) = 0;
|
||||
///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found
|
||||
|
||||
virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0;
|
||||
///< Search is limited to the active cells.
|
||||
|
||||
/// \todo enable reference in the OGRE scene
|
||||
virtual void enable (const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
|
@ -390,10 +393,10 @@ namespace MWBase
|
|||
virtual void setupPlayer() = 0;
|
||||
virtual void renderPlayer() = 0;
|
||||
|
||||
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0;
|
||||
///< if activated, should this door be opened or closed?
|
||||
/// open or close a non-teleport door (depending on current state)
|
||||
virtual void activateDoor(const MWWorld::Ptr& door) = 0;
|
||||
///< activate (open or close) an non-teleport door
|
||||
/// open or close a non-teleport door as specified
|
||||
virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0;
|
||||
|
||||
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
|
||||
virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object
|
||||
|
|
|
@ -164,7 +164,11 @@ namespace MWClass
|
|||
{
|
||||
const std::string model = getModel(ptr);
|
||||
if(!model.empty())
|
||||
{
|
||||
physics.addActor(ptr);
|
||||
if (getCreatureStats(ptr).isDead())
|
||||
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
|
||||
}
|
||||
MWBase::Environment::get().getMechanicsManager()->add(ptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "creaturelevlist.hpp"
|
||||
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/creaturelevliststate.hpp>
|
||||
|
||||
#include "../mwmechanics/levelledlist.hpp"
|
||||
|
||||
|
@ -11,7 +12,9 @@ namespace
|
|||
{
|
||||
struct CreatureLevListCustomData : public MWWorld::CustomData
|
||||
{
|
||||
// TODO: save the creature we spawned here
|
||||
// actorId of the creature we spawned
|
||||
int mSpawnActorId;
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
};
|
||||
|
||||
|
@ -38,6 +41,25 @@ namespace MWClass
|
|||
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const
|
||||
{
|
||||
ensureCustomData(ptr);
|
||||
|
||||
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
|
||||
if (customData.mSpawnActorId != -1)
|
||||
return; // TODO: handle respawning
|
||||
|
||||
|
||||
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
|
||||
ptr.get<ESM::CreatureLevList>();
|
||||
|
||||
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
|
||||
|
||||
if (!id.empty())
|
||||
{
|
||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||
MWWorld::ManualRef ref(store, id);
|
||||
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
|
||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
|
||||
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const
|
||||
|
@ -45,22 +67,29 @@ namespace MWClass
|
|||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
|
||||
ptr.get<ESM::CreatureLevList>();
|
||||
|
||||
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
|
||||
|
||||
if (!id.empty())
|
||||
{
|
||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||
MWWorld::ManualRef ref(store, id);
|
||||
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
|
||||
// TODO: hold on to this for respawn purposes later
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
|
||||
}
|
||||
data->mSpawnActorId = -1;
|
||||
|
||||
ptr.getRefData().setCustomData(data.release());
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
|
||||
const
|
||||
{
|
||||
const ESM::CreatureLevListState& state2 = dynamic_cast<const ESM::CreatureLevListState&> (state);
|
||||
|
||||
ensureCustomData(ptr);
|
||||
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
|
||||
customData.mSpawnActorId = state2.mSpawnActorId;
|
||||
}
|
||||
|
||||
void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
|
||||
const
|
||||
{
|
||||
ESM::CreatureLevListState& state2 = dynamic_cast<ESM::CreatureLevListState&> (state);
|
||||
|
||||
ensureCustomData(ptr);
|
||||
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
|
||||
state2.mSpawnActorId = customData.mSpawnActorId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,14 @@ namespace MWClass
|
|||
|
||||
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
|
||||
///< Add reference into a cell for rendering
|
||||
|
||||
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
|
||||
const;
|
||||
///< Read additional state from \a state into \a ptr.
|
||||
|
||||
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
|
||||
const;
|
||||
///< Write additional state from \a ptr into \a state.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "door.hpp"
|
||||
|
||||
#include <components/esm/loaddoor.hpp>
|
||||
#include <components/esm/doorstate.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -17,12 +18,28 @@
|
|||
#include "../mwworld/physicssystem.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/actiontrap.hpp"
|
||||
#include "../mwworld/customdata.hpp"
|
||||
|
||||
#include "../mwgui/tooltips.hpp"
|
||||
|
||||
#include "../mwrender/objects.hpp"
|
||||
#include "../mwrender/renderinginterface.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct DoorCustomData : public MWWorld::CustomData
|
||||
{
|
||||
int mDoorState; // 0 = nothing, 1 = opening, 2 = closing
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
};
|
||||
|
||||
MWWorld::CustomData *DoorCustomData::clone() const
|
||||
{
|
||||
return new DoorCustomData (*this);
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWClass
|
||||
{
|
||||
void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
|
||||
|
@ -38,6 +55,14 @@ namespace MWClass
|
|||
const std::string model = getModel(ptr);
|
||||
if(!model.empty())
|
||||
physics.addObject(ptr);
|
||||
|
||||
// Resume the door's opening/closing animation if it wasn't finished
|
||||
ensureCustomData(ptr);
|
||||
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
|
||||
if (customData.mDoorState > 0)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Door::getModel(const MWWorld::Ptr &ptr) const
|
||||
|
@ -125,7 +150,14 @@ namespace MWClass
|
|||
{
|
||||
// animated door
|
||||
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionDoor(ptr));
|
||||
if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr))
|
||||
int doorstate = getDoorState(ptr);
|
||||
bool opening = true;
|
||||
if (doorstate == 1)
|
||||
opening = false;
|
||||
if (doorstate == 0 && ptr.getRefData().getLocalRotation().rot[2] != 0)
|
||||
opening = false;
|
||||
|
||||
if (opening)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr,
|
||||
closeSound, 0.5);
|
||||
|
@ -262,4 +294,48 @@ namespace MWClass
|
|||
|
||||
return MWWorld::Ptr(&cell.get<ESM::Door>().insert(*ref), &cell);
|
||||
}
|
||||
|
||||
void Door::ensureCustomData(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<DoorCustomData> data(new DoorCustomData);
|
||||
|
||||
data->mDoorState = 0;
|
||||
ptr.getRefData().setCustomData(data.release());
|
||||
}
|
||||
}
|
||||
|
||||
int Door::getDoorState (const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
ensureCustomData(ptr);
|
||||
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
|
||||
return customData.mDoorState;
|
||||
}
|
||||
|
||||
void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const
|
||||
{
|
||||
ensureCustomData(ptr);
|
||||
DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData());
|
||||
customData.mDoorState = state;
|
||||
}
|
||||
|
||||
void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const
|
||||
{
|
||||
ensureCustomData(ptr);
|
||||
DoorCustomData& customData = dynamic_cast<DoorCustomData&>(*ptr.getRefData().getCustomData());
|
||||
|
||||
const ESM::DoorState& state2 = dynamic_cast<const ESM::DoorState&>(state);
|
||||
customData.mDoorState = state2.mDoorState;
|
||||
}
|
||||
|
||||
void Door::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const
|
||||
{
|
||||
ensureCustomData(ptr);
|
||||
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
|
||||
|
||||
ESM::DoorState& state2 = dynamic_cast<ESM::DoorState&>(state);
|
||||
state2.mDoorState = customData.mDoorState;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace MWClass
|
|||
{
|
||||
class Door : public MWWorld::Class
|
||||
{
|
||||
void ensureCustomData (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual MWWorld::Ptr
|
||||
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
|
||||
|
||||
|
@ -48,7 +50,20 @@ namespace MWClass
|
|||
static void registerSelf();
|
||||
|
||||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
private:
|
||||
|
||||
/// 0 = nothing, 1 = opening, 2 = closing
|
||||
virtual int getDoorState (const MWWorld::Ptr &ptr) const;
|
||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
|
||||
|
||||
|
||||
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
|
||||
const;
|
||||
///< Read additional state from \a state into \a ptr.
|
||||
|
||||
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
|
||||
const;
|
||||
///< Write additional state from \a ptr into \a state.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -357,9 +357,6 @@ namespace MWClass
|
|||
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "",
|
||||
MWBase::Environment::get().getWorld()->getStore());
|
||||
|
||||
// Relates to NPC gold reset delay
|
||||
data->mNpcStats.setTradeTime(MWWorld::TimeStamp(0.0, 0));
|
||||
|
||||
data->mNpcStats.setGoldPool(gold);
|
||||
|
||||
// store
|
||||
|
@ -391,6 +388,8 @@ namespace MWClass
|
|||
{
|
||||
physics.addActor(ptr);
|
||||
MWBase::Environment::get().getMechanicsManager()->add(ptr);
|
||||
if (getCreatureStats(ptr).isDead())
|
||||
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
|
||||
}
|
||||
|
||||
bool Npc::isPersistent(const MWWorld::Ptr &actor) const
|
||||
|
@ -621,7 +620,8 @@ namespace MWClass
|
|||
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||
|
||||
// Attacking peaceful NPCs is a crime
|
||||
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
|
||||
// anything below 80 is considered peaceful (see Actors::updateActor)
|
||||
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
|
||||
|
||||
getCreatureStats(ptr).setAttacked(true);
|
||||
|
|
|
@ -695,6 +695,8 @@ protected:
|
|||
typedef TypesetBookImpl::Section Section;
|
||||
typedef TypesetBookImpl::Line Line;
|
||||
typedef TypesetBookImpl::Run Run;
|
||||
bool mIsPageReset;
|
||||
size_t mPage;
|
||||
|
||||
struct TextFormat : ISubWidget
|
||||
{
|
||||
|
@ -745,6 +747,23 @@ protected:
|
|||
void destroyDrawItem() {};
|
||||
};
|
||||
|
||||
void resetPage()
|
||||
{
|
||||
mIsPageReset = true;
|
||||
mPage = 0;
|
||||
}
|
||||
|
||||
void setPage(size_t page)
|
||||
{
|
||||
mIsPageReset = false;
|
||||
mPage = page;
|
||||
}
|
||||
|
||||
bool isPageDifferent(size_t page)
|
||||
{
|
||||
return mIsPageReset || (mPage != page);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
typedef TypesetBookImpl::StyleImpl Style;
|
||||
|
@ -760,14 +779,13 @@ public:
|
|||
|
||||
|
||||
boost::shared_ptr <TypesetBookImpl> mBook;
|
||||
size_t mPage;
|
||||
|
||||
MyGUI::ILayerNode* mNode;
|
||||
ActiveTextFormats mActiveTextFormats;
|
||||
|
||||
PageDisplay ()
|
||||
{
|
||||
mPage = -1;
|
||||
resetPage ();
|
||||
mViewTop = 0;
|
||||
mViewBottom = 0;
|
||||
mFocusItem = NULL;
|
||||
|
@ -902,7 +920,7 @@ public:
|
|||
createActiveFormats (newBook);
|
||||
|
||||
mBook = newBook;
|
||||
mPage = newPage;
|
||||
setPage (newPage);
|
||||
|
||||
if (newPage < mBook->mPages.size ())
|
||||
{
|
||||
|
@ -918,19 +936,19 @@ public:
|
|||
else
|
||||
{
|
||||
mBook.reset ();
|
||||
mPage = -1;
|
||||
resetPage ();
|
||||
mViewTop = 0;
|
||||
mViewBottom = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mBook && mPage != newPage)
|
||||
if (mBook && isPageDifferent (newPage))
|
||||
{
|
||||
if (mNode != NULL)
|
||||
for (ActiveTextFormats::iterator i = mActiveTextFormats.begin (); i != mActiveTextFormats.end (); ++i)
|
||||
mNode->outOfDate(i->second->mRenderItem);
|
||||
|
||||
mPage = newPage;
|
||||
setPage (newPage);
|
||||
|
||||
if (newPage < mBook->mPages.size ())
|
||||
{
|
||||
|
|
|
@ -10,23 +10,23 @@ namespace MWGui
|
|||
{
|
||||
}
|
||||
|
||||
void CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
|
||||
MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false)
|
||||
{
|
||||
if (mActor.getClass().isNpc())
|
||||
{
|
||||
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
||||
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
|
||||
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
|
||||
stats.modifyProfit(item.mBase.getClass().getValue(item.mBase) * count);
|
||||
}
|
||||
|
||||
InventoryItemModel::copyItem(item, count, setNewOwner);
|
||||
return InventoryItemModel::copyItem(item, count, setNewOwner);
|
||||
}
|
||||
|
||||
void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
{
|
||||
if (mActor.getClass().isNpc())
|
||||
{
|
||||
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
||||
stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
|
||||
MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor);
|
||||
stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count);
|
||||
}
|
||||
|
||||
InventoryItemModel::removeItem(item, count);
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace MWGui
|
|||
public:
|
||||
CompanionItemModel (const MWWorld::Ptr& actor);
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner);
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MWGui
|
|||
ItemView* mItemView;
|
||||
SortFilterItemModel* mSortModel;
|
||||
CompanionItemModel* mModel;
|
||||
size_t mSelectedItem;
|
||||
int mSelectedItem;
|
||||
|
||||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace MWGui
|
|||
MWGui::ItemView* mItemView;
|
||||
SortFilterItemModel* mSortModel;
|
||||
ItemModel* mModel;
|
||||
size_t mSelectedItem;
|
||||
int mSelectedItem;
|
||||
|
||||
MyGUI::Button* mDisposeCorpseButton;
|
||||
MyGUI::Button* mTakeButton;
|
||||
|
|
|
@ -71,12 +71,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1];
|
||||
if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source))
|
||||
throw std::runtime_error("Item to copy needs to be from a different container!");
|
||||
source.getClass().getContainerStore(source).add(item.mBase, count, source);
|
||||
return *source.getClass().getContainerStore(source).add(item.mBase, count, source);
|
||||
}
|
||||
|
||||
void ContainerItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWGui
|
|||
virtual ModelIndex getIndex (ItemStack item);
|
||||
virtual size_t getItemCount();
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
|
||||
virtual void update();
|
||||
|
|
|
@ -511,7 +511,7 @@ namespace MWGui
|
|||
|
||||
void DialogueWindow::onScrollbarMoved(MyGUI::ScrollBar *sender, size_t pos)
|
||||
{
|
||||
mHistory->setPosition(0,-pos);
|
||||
mHistory->setPosition(0, pos * -1);
|
||||
}
|
||||
|
||||
void DialogueWindow::addResponse(const std::string &text, const std::string &title)
|
||||
|
|
|
@ -233,6 +233,9 @@ namespace MWGui
|
|||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(
|
||||
MWBase::Environment::get().getWorld()->getPlayerPtr());
|
||||
|
||||
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
MyGUI::IntPoint cursorPosition = MyGUI::InputManager::getInstance().getMousePosition();
|
||||
float mouseX = cursorPosition.left / float(viewSize.width);
|
||||
|
|
|
@ -40,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor))
|
||||
throw std::runtime_error("Item to copy needs to be from a different container!");
|
||||
mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
|
||||
return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner);
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,16 +59,20 @@ void InventoryItemModel::removeItem (const ItemStack& item, size_t count)
|
|||
throw std::runtime_error("Not enough items in the stack to remove");
|
||||
}
|
||||
|
||||
void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
{
|
||||
bool setNewOwner = false;
|
||||
|
||||
// Are you dead? Then you wont need that anymore
|
||||
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead())
|
||||
if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()
|
||||
// Make sure that the item is actually owned by the dead actor
|
||||
// Prevents a potential exploit for resetting the owner of any item, by placing the item in a corpse
|
||||
&& Misc::StringUtils::ciEqual(item.mBase.getCellRef().mOwner, mActor.getCellRef().mRefID))
|
||||
setNewOwner = true;
|
||||
|
||||
otherModel->copyItem(item, count, setNewOwner);
|
||||
MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner);
|
||||
removeItem(item, count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InventoryItemModel::update()
|
||||
|
|
|
@ -15,11 +15,11 @@ namespace MWGui
|
|||
virtual ModelIndex getIndex (ItemStack item);
|
||||
virtual size_t getItemCount();
|
||||
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
|
||||
/// Move items from this model to \a otherModel.
|
||||
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
|
||||
virtual void update();
|
||||
|
||||
|
|
|
@ -408,14 +408,8 @@ namespace MWGui
|
|||
|
||||
if (mDragAndDrop->mSourceModel != mTradeModel)
|
||||
{
|
||||
// add item to the player's inventory
|
||||
MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator it = invStore.begin();
|
||||
|
||||
it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr);
|
||||
|
||||
mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount);
|
||||
ptr = *it;
|
||||
// Move item to the player's inventory
|
||||
ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel);
|
||||
}
|
||||
useItem(ptr);
|
||||
}
|
||||
|
@ -541,9 +535,11 @@ namespace MWGui
|
|||
|
||||
int count = object.getRefData().getCount();
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(player);
|
||||
|
||||
// add to player inventory
|
||||
// can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player);
|
||||
// remove from world
|
||||
MWBase::Environment::get().getWorld()->deleteObject (object);
|
||||
|
|
|
@ -52,7 +52,7 @@ namespace MWGui
|
|||
DragAndDrop* mDragAndDrop;
|
||||
|
||||
bool mPreviewDirty;
|
||||
size_t mSelectedItem;
|
||||
int mSelectedItem;
|
||||
|
||||
MWWorld::Ptr mPtr;
|
||||
|
||||
|
|
|
@ -71,10 +71,11 @@ namespace MWGui
|
|||
{
|
||||
}
|
||||
|
||||
void ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
MWWorld::Ptr ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel)
|
||||
{
|
||||
otherModel->copyItem(item, count);
|
||||
MWWorld::Ptr ret = otherModel->copyItem(item, count);
|
||||
removeItem(item, count);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -83,10 +84,9 @@ namespace MWGui
|
|||
delete mSourceModel;
|
||||
}
|
||||
|
||||
void ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner)
|
||||
{
|
||||
// no need to use mapToSource since itemIndex refers to an index in the sourceModel
|
||||
mSourceModel->copyItem (item, count, setNewOwner);
|
||||
return mSourceModel->copyItem (item, count, setNewOwner);
|
||||
}
|
||||
|
||||
void ProxyItemModel::removeItem (const ItemStack& item, size_t count)
|
||||
|
|
|
@ -56,10 +56,10 @@ namespace MWGui
|
|||
virtual void update() = 0;
|
||||
|
||||
/// Move items from this model to \a otherModel.
|
||||
virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel);
|
||||
|
||||
/// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner?
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0;
|
||||
virtual void removeItem (const ItemStack& item, size_t count) = 0;
|
||||
|
||||
private:
|
||||
|
@ -73,7 +73,7 @@ namespace MWGui
|
|||
{
|
||||
public:
|
||||
virtual ~ProxyItemModel();
|
||||
virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false);
|
||||
virtual void removeItem (const ItemStack& item, size_t count);
|
||||
virtual ModelIndex getIndex (ItemStack item);
|
||||
|
||||
|
|
|
@ -465,7 +465,18 @@ namespace MWGui
|
|||
|
||||
void MapWindow::cellExplored(int x, int y)
|
||||
{
|
||||
mGlobalMapRender->exploreCell(x,y);
|
||||
mQueuedToExplore.push_back(std::make_pair(x,y));
|
||||
}
|
||||
|
||||
void MapWindow::onFrame(float dt)
|
||||
{
|
||||
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
|
||||
{
|
||||
mGlobalMapRender->exploreCell(it->first, it->second);
|
||||
}
|
||||
mQueuedToExplore.clear();
|
||||
|
||||
NoDrop::onFrame(dt);
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
|
|
|
@ -97,14 +97,17 @@ namespace MWGui
|
|||
|
||||
void renderGlobalMap(Loading::Listener* loadingListener);
|
||||
|
||||
void addVisitedLocation(const std::string& name, int x, int y); // adds the marker to the global map
|
||||
// adds the marker to the global map
|
||||
void addVisitedLocation(const std::string& name, int x, int y);
|
||||
|
||||
// reveals this cell's map on the global map
|
||||
void cellExplored(int x, int y);
|
||||
|
||||
void setGlobalMapPlayerPosition (float worldX, float worldY);
|
||||
|
||||
virtual void open();
|
||||
|
||||
void onFrame(float dt) { NoDrop::onFrame(dt); }
|
||||
void onFrame(float dt);
|
||||
|
||||
/// Clear all savegame-specific data
|
||||
void clear();
|
||||
|
@ -132,6 +135,10 @@ namespace MWGui
|
|||
typedef std::pair<int, int> CellId;
|
||||
std::vector<CellId> mMarkers;
|
||||
|
||||
// Cells that should be explored in the next frame (i.e. their map revealed on the global map)
|
||||
// We can't do this immediately, because the map update is not immediate either (see mNeedMapUpdate in scene.cpp)
|
||||
std::vector<CellId> mQueuedToExplore;
|
||||
|
||||
MyGUI::Button* mEventBoxGlobal;
|
||||
MyGUI::Button* mEventBoxLocal;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWGui
|
|||
{
|
||||
|
||||
void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
|
||||
const std::string& sourceName, const std::string& casterHandle,
|
||||
const std::string& sourceName, int casterActorId,
|
||||
float magnitude, float remainingTime)
|
||||
{
|
||||
MagicEffectInfo newEffectSource;
|
||||
|
|
|
@ -42,8 +42,10 @@ namespace MWGui
|
|||
|
||||
std::map <int, std::vector<MagicEffectInfo> > mEffectSources;
|
||||
|
||||
virtual ~EffectSourceVisitor() {}
|
||||
|
||||
virtual void visit (MWMechanics::EffectKey key,
|
||||
const std::string& sourceName, const std::string& casterHandle,
|
||||
const std::string& sourceName, int casterActorId,
|
||||
float magnitude, float remainingTime = -1);
|
||||
};
|
||||
|
||||
|
|
|
@ -1326,9 +1326,6 @@ namespace MWGui
|
|||
|
||||
void WindowManager::updatePlayer()
|
||||
{
|
||||
unsetSelectedSpell();
|
||||
unsetSelectedWeapon();
|
||||
|
||||
mInventoryWindow->updatePlayer();
|
||||
}
|
||||
|
||||
|
@ -1425,6 +1422,14 @@ namespace MWGui
|
|||
|
||||
mQuickKeysMenu->write(writer);
|
||||
progress.increaseProgress();
|
||||
|
||||
if (!mSelectedSpell.empty())
|
||||
{
|
||||
writer.startRecord(ESM::REC_ASPL);
|
||||
writer.writeHNString("ID__", mSelectedSpell);
|
||||
writer.endRecord(ESM::REC_ASPL);
|
||||
progress.increaseProgress();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::readRecord(ESM::ESMReader &reader, int32_t type)
|
||||
|
@ -1433,12 +1438,18 @@ namespace MWGui
|
|||
mMap->readRecord(reader, type);
|
||||
else if (type == ESM::REC_KEYS)
|
||||
mQuickKeysMenu->readRecord(reader, type);
|
||||
else if (type == ESM::REC_ASPL)
|
||||
{
|
||||
reader.getSubNameIs("ID__");
|
||||
mSelectedSpell = reader.getHString();
|
||||
}
|
||||
}
|
||||
|
||||
int WindowManager::countSavedGameRecords() const
|
||||
{
|
||||
return 1 // Global map
|
||||
+ 1; // QuickKeysMenu
|
||||
+ 1 // QuickKeysMenu
|
||||
+ (!mSelectedSpell.empty() ? 1 : 0);
|
||||
}
|
||||
|
||||
bool WindowManager::isSavingAllowed() const
|
||||
|
|
|
@ -53,9 +53,9 @@ namespace MWMechanics
|
|||
{
|
||||
const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
|
||||
|
||||
const std::vector<Effect>& effects = iter->second.mEffects;
|
||||
const std::vector<ActiveEffect>& effects = iter->second.mEffects;
|
||||
|
||||
for (std::vector<Effect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
||||
for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
||||
{
|
||||
int duration = effectIt->mDuration;
|
||||
MWWorld::TimeStamp end = start;
|
||||
|
@ -63,7 +63,7 @@ namespace MWMechanics
|
|||
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
|
||||
|
||||
if (end>now)
|
||||
mEffects.add(effectIt->mKey, MWMechanics::EffectParam(effectIt->mMagnitude));
|
||||
mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,11 +91,11 @@ namespace MWMechanics
|
|||
|
||||
double ActiveSpells::timeToExpire (const TIterator& iterator) const
|
||||
{
|
||||
const std::vector<Effect>& effects = iterator->second.mEffects;
|
||||
const std::vector<ActiveEffect>& effects = iterator->second.mEffects;
|
||||
|
||||
int duration = 0;
|
||||
|
||||
for (std::vector<Effect>::const_iterator iter (effects.begin());
|
||||
for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());
|
||||
iter!=effects.end(); ++iter)
|
||||
{
|
||||
if (iter->mDuration > duration)
|
||||
|
@ -132,8 +132,8 @@ namespace MWMechanics
|
|||
return mSpells;
|
||||
}
|
||||
|
||||
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects,
|
||||
const std::string &displayName, const std::string& casterHandle)
|
||||
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<ActiveEffect> effects,
|
||||
const std::string &displayName, int casterActorId)
|
||||
{
|
||||
bool exists = false;
|
||||
for (TContainer::const_iterator it = begin(); it != end(); ++it)
|
||||
|
@ -146,7 +146,7 @@ namespace MWMechanics
|
|||
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||
params.mEffects = effects;
|
||||
params.mDisplayName = displayName;
|
||||
params.mCasterHandle = casterHandle;
|
||||
params.mCasterActorId = casterActorId;
|
||||
|
||||
if (!exists || stack)
|
||||
mSpells.insert (std::make_pair(id, params));
|
||||
|
@ -168,7 +168,7 @@ namespace MWMechanics
|
|||
{
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
for (std::vector<Effect>::const_iterator effectIt = it->second.mEffects.begin();
|
||||
for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
|
||||
effectIt != it->second.mEffects.end(); ++effectIt)
|
||||
{
|
||||
std::string name = it->second.mDisplayName;
|
||||
|
@ -178,7 +178,7 @@ namespace MWMechanics
|
|||
float magnitude = effectIt->mMagnitude;
|
||||
|
||||
if (magnitude)
|
||||
visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime);
|
||||
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,10 +200,10 @@ namespace MWMechanics
|
|||
{
|
||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||
{
|
||||
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
|
||||
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
|
||||
effectIt != it->second.mEffects.end();)
|
||||
{
|
||||
if (effectIt->mKey.mId == effectId)
|
||||
if (effectIt->mEffectId == effectId)
|
||||
effectIt = it->second.mEffects.erase(effectIt);
|
||||
else
|
||||
++effectIt;
|
||||
|
@ -212,16 +212,16 @@ namespace MWMechanics
|
|||
mSpellsChanged = true;
|
||||
}
|
||||
|
||||
void ActiveSpells::purge(const std::string &actorHandle)
|
||||
void ActiveSpells::purge(int casterActorId)
|
||||
{
|
||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||
{
|
||||
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
|
||||
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
|
||||
effectIt != it->second.mEffects.end();)
|
||||
{
|
||||
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mKey.mId);
|
||||
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectId);
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked
|
||||
&& it->second.mCasterHandle == actorHandle)
|
||||
&& it->second.mCasterActorId == casterActorId)
|
||||
effectIt = it->second.mEffects.erase(effectIt);
|
||||
else
|
||||
++effectIt;
|
||||
|
@ -229,4 +229,41 @@ namespace MWMechanics
|
|||
}
|
||||
mSpellsChanged = true;
|
||||
}
|
||||
|
||||
void ActiveSpells::clear()
|
||||
{
|
||||
mSpells.clear();
|
||||
mSpellsChanged = true;
|
||||
}
|
||||
|
||||
void ActiveSpells::writeState(ESM::ActiveSpells &state) const
|
||||
{
|
||||
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||
{
|
||||
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
|
||||
ESM::ActiveSpells::ActiveSpellParams params;
|
||||
params.mEffects = it->second.mEffects;
|
||||
params.mCasterActorId = it->second.mCasterActorId;
|
||||
params.mDisplayName = it->second.mDisplayName;
|
||||
params.mTimeStamp = it->second.mTimeStamp.toEsm();
|
||||
|
||||
state.mSpells.insert (std::make_pair(it->first, params));
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveSpells::readState(const ESM::ActiveSpells &state)
|
||||
{
|
||||
for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)
|
||||
{
|
||||
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
|
||||
ActiveSpellParams params;
|
||||
params.mEffects = it->second.mEffects;
|
||||
params.mCasterActorId = it->second.mCasterActorId;
|
||||
params.mDisplayName = it->second.mDisplayName;
|
||||
params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp);
|
||||
|
||||
mSpells.insert (std::make_pair(it->first, params));
|
||||
mSpellsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "magiceffects.hpp"
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
#include <components/esm/activespells.hpp>
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -21,29 +22,24 @@ namespace MWMechanics
|
|||
{
|
||||
public:
|
||||
|
||||
// Parameters of an effect concerning lasting effects.
|
||||
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
|
||||
// It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.
|
||||
struct Effect
|
||||
{
|
||||
float mMagnitude;
|
||||
EffectKey mKey;
|
||||
float mDuration;
|
||||
};
|
||||
typedef ESM::ActiveEffect ActiveEffect;
|
||||
|
||||
struct ActiveSpellParams
|
||||
{
|
||||
std::vector<Effect> mEffects;
|
||||
std::vector<ActiveEffect> mEffects;
|
||||
MWWorld::TimeStamp mTimeStamp;
|
||||
std::string mDisplayName;
|
||||
|
||||
// Handle to the caster that that inflicted this spell on us
|
||||
std::string mCasterHandle;
|
||||
// The caster that inflicted this spell on us
|
||||
int mCasterActorId;
|
||||
};
|
||||
|
||||
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
void readState (const ESM::ActiveSpells& state);
|
||||
void writeState (ESM::ActiveSpells& state) const;
|
||||
|
||||
private:
|
||||
|
||||
mutable TContainer mSpells;
|
||||
|
@ -76,10 +72,9 @@ namespace MWMechanics
|
|||
/// \param stack If false, the spell is not added if one with the same ID exists already.
|
||||
/// \param effects
|
||||
/// \param displayName Name for display in magic menu.
|
||||
/// \param casterHandle
|
||||
///
|
||||
void addSpell (const std::string& id, bool stack, std::vector<Effect> effects,
|
||||
const std::string& displayName, const std::string& casterHandle);
|
||||
void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,
|
||||
const std::string& displayName, int casterActorId);
|
||||
|
||||
/// Removes the active effects from this spell/potion/.. with \a id
|
||||
void removeEffects (const std::string& id);
|
||||
|
@ -90,8 +85,11 @@ namespace MWMechanics
|
|||
/// Remove all active effects, if roll succeeds (for each effect)
|
||||
void purgeAll (float chance);
|
||||
|
||||
/// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle
|
||||
void purge (const std::string& actorHandle);
|
||||
/// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
|
||||
void purge (int casterActorId);
|
||||
|
||||
/// Remove all spells
|
||||
void clear();
|
||||
|
||||
bool isSpellActive (std::string id) const;
|
||||
///< case insensitive
|
||||
|
|
|
@ -38,9 +38,12 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a
|
|||
{
|
||||
if (bound)
|
||||
{
|
||||
MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
|
||||
MWWorld::ActionEquip action(newPtr);
|
||||
action.execute(actor);
|
||||
if (actor.getClass().getContainerStore(actor).count(item) == 0)
|
||||
{
|
||||
MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor);
|
||||
MWWorld::ActionEquip action(newPtr);
|
||||
action.execute(actor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -116,7 +119,7 @@ namespace MWMechanics
|
|||
: mCreature(trappedCreature) {}
|
||||
|
||||
virtual void visit (MWMechanics::EffectKey key,
|
||||
const std::string& sourceName, const std::string& casterHandle,
|
||||
const std::string& sourceName, int casterActorId,
|
||||
float magnitude, float remainingTime = -1)
|
||||
{
|
||||
if (key.mId != ESM::MagicEffect::Soultrap)
|
||||
|
@ -126,7 +129,7 @@ namespace MWMechanics
|
|||
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
|
||||
MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle);
|
||||
MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId);
|
||||
if (caster.isEmpty() || !caster.getClass().isActor())
|
||||
return;
|
||||
|
||||
|
@ -545,28 +548,54 @@ namespace MWMechanics
|
|||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
|
||||
ref.getPtr().getCellRef().mPos = ipos;
|
||||
|
||||
// TODO: Add AI to follow player and fight for him
|
||||
AiFollow package(ptr.getRefData().getHandle());
|
||||
MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr);
|
||||
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
|
||||
|
||||
// Make the summoned creature follow its master and help in fights
|
||||
AiFollow package(ptr);
|
||||
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
|
||||
int creatureActorId = summonedCreatureStats.getActorId();
|
||||
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
||||
|
||||
// TODO: VFX_SummonStart, VFX_SummonEnd
|
||||
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
|
||||
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string handle = creatureStats.mSummonedCreatures[it->first];
|
||||
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
|
||||
// plays though, which is a rather lame exploit in vanilla.
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle);
|
||||
// Summon lifetime has expired. Try to delete the creature.
|
||||
int actorId = creatureStats.mSummonedCreatures[it->first];
|
||||
creatureStats.mSummonedCreatures.erase(it->first);
|
||||
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
|
||||
// plays though, which is a rather lame exploit in vanilla.
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||
creatureStats.mSummonedCreatures.erase(it->first);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We didn't find the creature. It's probably in an inactive cell.
|
||||
// Add to graveyard so we can delete it when the cell becomes active.
|
||||
creatureStats.mSummonGraveyard.push_back(actorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<int>::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); )
|
||||
{
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
it = creatureStats.mSummonGraveyard.erase(it);
|
||||
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr)
|
||||
|
@ -792,7 +821,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void Actors::addActor (const MWWorld::Ptr& ptr)
|
||||
void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately)
|
||||
{
|
||||
// erase previous death events since we are currently only tracking them while in an active cell
|
||||
MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied();
|
||||
|
@ -801,6 +830,8 @@ namespace MWMechanics
|
|||
|
||||
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
|
||||
if (updateImmediately)
|
||||
mActors[ptr]->update(0);
|
||||
}
|
||||
|
||||
void Actors::removeActor (const MWWorld::Ptr& ptr)
|
||||
|
@ -886,9 +917,8 @@ namespace MWMechanics
|
|||
if (MWBase::Environment::get().getMechanicsManager()->isAIActive())
|
||||
{
|
||||
// make guards and creatures fight each other
|
||||
if (timerUpdateAITargets == 0 && !iter->first.getClass().isNpc() && !listGuards.empty())
|
||||
if (timerUpdateAITargets == 0 && iter->first.getTypeName() == typeid(ESM::Creature).name() && !listGuards.empty())
|
||||
{
|
||||
//findNthClosest
|
||||
sBasePoint = Ogre::Vector3(iter->first.getRefData().getPosition().pos);
|
||||
listGuards.sort(comparePtrDist); // try to engage combat starting from the nearest creature
|
||||
|
||||
|
@ -972,21 +1002,17 @@ namespace MWMechanics
|
|||
continue;
|
||||
}
|
||||
|
||||
// Make sure spell effects with CasterLinked flag are removed
|
||||
// TODO: would be nice not to do this all the time...
|
||||
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
|
||||
{
|
||||
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
|
||||
spells.purge(iter->first.getRefData().getHandle());
|
||||
}
|
||||
|
||||
// FIXME: see http://bugs.openmw.org/issues/869
|
||||
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
|
||||
|
||||
if (iter->second->kill())
|
||||
{
|
||||
++mDeathCount[cls.getId(iter->first)];
|
||||
|
||||
// Make sure spell effects with CasterLinked flag are removed
|
||||
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
|
||||
{
|
||||
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
|
||||
spells.purge(stats.getActorId());
|
||||
}
|
||||
|
||||
// Apply soultrap
|
||||
if (iter->first.getTypeName() == typeid(ESM::Creature).name())
|
||||
{
|
||||
|
@ -997,8 +1023,11 @@ namespace MWMechanics
|
|||
// Reset magic effects and recalculate derived effects
|
||||
// One case where we need this is to make sure bound items are removed upon death
|
||||
stats.setMagicEffects(MWMechanics::MagicEffects());
|
||||
stats.getActiveSpells().clear();
|
||||
calculateCreatureStatModifiers(iter->first, 0);
|
||||
|
||||
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);
|
||||
|
||||
if (cls.isEssential(iter->first))
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWMechanics
|
|||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||
void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); }
|
||||
|
||||
void addActor (const MWWorld::Ptr& ptr);
|
||||
void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false);
|
||||
///< Register an actor for stats management
|
||||
///
|
||||
/// \note Dead actors are ignored.
|
||||
|
|
|
@ -20,82 +20,25 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
|
|||
}
|
||||
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
Movement &movement = actor.getClass().getMovementSettings(actor);
|
||||
const ESM::Cell *cell = actor.getCell()->getCell();
|
||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow
|
||||
|
||||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
|
||||
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
|
||||
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(target == MWWorld::Ptr())
|
||||
return true; //Target doesn't exist
|
||||
|
||||
MWWorld::Ptr target = world->searchPtr(mObjectId,false);
|
||||
if(target == MWWorld::Ptr()) return true;
|
||||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
ESM::Position targetPos = target.getRefData().getPosition();
|
||||
|
||||
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
|
||||
if(!mPathFinder.isPathConstructed() || cellChange)
|
||||
{
|
||||
mCellX = cell->mData.mX;
|
||||
mCellY = cell->mData.mY;
|
||||
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = targetPos.pos[0];
|
||||
dest.mY = targetPos.pos[1];
|
||||
dest.mZ = targetPos.pos[2];
|
||||
|
||||
ESM::Pathgrid::Point start;
|
||||
start.mX = pos.pos[0];
|
||||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true);
|
||||
}
|
||||
|
||||
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
|
||||
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
|
||||
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200)
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
MWWorld::Ptr target = world->getPtr(mObjectId,false);
|
||||
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 200) { //Stop when you get close
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false);
|
||||
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
MWWorld::Ptr target = world->getPtr(mObjectId,false);
|
||||
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
|
||||
return true;
|
||||
else {
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
|
||||
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||
zTurn(actor, Ogre::Degree(zAngle));
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
||||
movement.mPosition[1] = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,20 +8,20 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
/// \brief Causes actor to walk to activatable object and activate it
|
||||
/** Will activate when close to object **/
|
||||
class AiActivate : public AiPackage
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
/** \param objectId Reference to object to activate **/
|
||||
AiActivate(const std::string &objectId);
|
||||
virtual AiActivate *clone() const;
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
virtual int getTypeId() const;
|
||||
|
||||
private:
|
||||
std::string mObjectId;
|
||||
|
||||
PathFinder mPathFinder;
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
};
|
||||
|
|
90
apps/openmw/mwmechanics/aiavoiddoor.cpp
Normal file
90
apps/openmw/mwmechanics/aiavoiddoor.cpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include "aiavoiddoor.hpp"
|
||||
#include <iostream>
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "mechanicsmanagerimp.hpp"
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::Ptr& doorPtr)
|
||||
: AiPackage(), mDoorPtr(doorPtr), mDuration(1), mAdjAngle(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
if(mDuration == 1) //If it just started, get the actor position as the stuck detection thing
|
||||
mLastPos = pos;
|
||||
|
||||
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];
|
||||
int 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 = 1.57079632679f; //pi/2
|
||||
else if (mAdjAngle == 1.57079632679f)
|
||||
mAdjAngle = -1.57079632679;
|
||||
else
|
||||
mAdjAngle = 0;
|
||||
mDuration = 1; //reset timer
|
||||
}
|
||||
else //Not stuck
|
||||
return true; // We have tried backing up for more than one second, we've probably cleared it
|
||||
}
|
||||
|
||||
if (!mDoorPtr.getClass().getDoorState(mDoorPtr))
|
||||
return true; //Door is no longer opening
|
||||
|
||||
ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door
|
||||
float x = pos.pos[0] - tPos.pos[0];
|
||||
float y = pos.pos[1] - tPos.pos[1];
|
||||
float dirToDoor = std::atan2(x,y) + pos.rot[2] + mAdjAngle; //Calculates the direction to the door, relative to the direction of the NPC
|
||||
// For example, if the NPC is directly facing the door this will be pi/2
|
||||
|
||||
// Make actor move away from the door
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = -1 * std::sin(dirToDoor); //I knew I'd use trig someday
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = -1 * std::cos(dirToDoor);
|
||||
|
||||
//Make all nearby actors also avoid the door
|
||||
std::vector<MWWorld::Ptr> actors;
|
||||
MWBase::Environment::get().getMechanicsManager()->getActorsInRange(Ogre::Vector3(pos.pos[0],pos.pos[1],pos.pos[2]),100,actors);
|
||||
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); it++) {
|
||||
if(*it != MWBase::Environment::get().getWorld()->getPlayerPtr()) { //Not the player
|
||||
MWMechanics::AiSequence& seq = MWWorld::Class::get(*it).getCreatureStats(*it).getAiSequence();
|
||||
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) { //Only add it once
|
||||
seq.stack(MWMechanics::AiAvoidDoor(mDoorPtr),*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MWMechanics::AiAvoidDoor::getAvoidedDoor()
|
||||
{
|
||||
return mDoorPtr.getCellRef().mRefID;
|
||||
}
|
||||
|
||||
MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const
|
||||
{
|
||||
return new AiAvoidDoor(*this);
|
||||
}
|
||||
|
||||
int MWMechanics::AiAvoidDoor::getTypeId() const
|
||||
{
|
||||
return TypeIdAvoidDoor;
|
||||
}
|
||||
|
38
apps/openmw/mwmechanics/aiavoiddoor.hpp
Normal file
38
apps/openmw/mwmechanics/aiavoiddoor.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
|
||||
#define GAME_MWMECHANICS_AIAVOIDDOOR_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include <string>
|
||||
#include "pathfinding.hpp"
|
||||
#include <components/esm/defs.hpp>
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief AiPackage to have an actor avoid an opening door
|
||||
/** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it
|
||||
**/
|
||||
class AiAvoidDoor : public AiPackage
|
||||
{
|
||||
public:
|
||||
/// Avoid door until the door is fully open
|
||||
AiAvoidDoor(const MWWorld::Ptr& doorPtr);
|
||||
|
||||
virtual AiAvoidDoor *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
/// Returns the door being avoided
|
||||
std::string getAvoidedDoor();
|
||||
|
||||
private:
|
||||
float mDuration;
|
||||
MWWorld::Ptr mDoorPtr;
|
||||
ESM::Position mLastPos;
|
||||
float mAdjAngle;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
@ -81,7 +81,7 @@ namespace MWMechanics
|
|||
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
|
||||
|
||||
AiCombat::AiCombat(const MWWorld::Ptr& actor) :
|
||||
mTarget(actor),
|
||||
mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()),
|
||||
mTimerAttack(0),
|
||||
mTimerReact(0),
|
||||
mTimerCombatMove(0),
|
||||
|
@ -149,8 +149,11 @@ namespace MWMechanics
|
|||
bool AiCombat::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
//General description
|
||||
if(actor.getClass().getCreatureStats(actor).isDead()
|
||||
|| mTarget.getClass().getCreatureStats(mTarget).isDead() )
|
||||
if(actor.getClass().getCreatureStats(actor).isDead()) return true;
|
||||
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
|
||||
if(target.getClass().getCreatureStats(target).isDead())
|
||||
return true;
|
||||
|
||||
//Update every frame
|
||||
|
@ -322,7 +325,7 @@ namespace MWMechanics
|
|||
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
Ogre::Vector3 vActorPos(pos.pos);
|
||||
Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos;
|
||||
|
||||
bool isStuck = false;
|
||||
|
@ -393,7 +396,7 @@ namespace MWMechanics
|
|||
else // remote pathfinding
|
||||
{
|
||||
bool preferShortcut = false;
|
||||
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
|
||||
bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target);
|
||||
|
||||
if(mReadyToAttack) isStuck = false;
|
||||
|
||||
|
@ -429,7 +432,7 @@ namespace MWMechanics
|
|||
|
||||
mFollowTarget = false;
|
||||
|
||||
buildNewPath(actor); //may fail to build a path, check before use
|
||||
buildNewPath(actor, target); //may fail to build a path, check before use
|
||||
|
||||
//delete visited path node
|
||||
mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
|
@ -473,9 +476,9 @@ namespace MWMechanics
|
|||
//less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing)
|
||||
//then start attacking
|
||||
float speed1 = actorCls.getSpeed(actor);
|
||||
float speed2 = mTarget.getClass().getSpeed(mTarget);
|
||||
if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0
|
||||
&& mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0)
|
||||
float speed2 = target.getClass().getSpeed(target);
|
||||
if(target.getClass().getMovementSettings(target).mPosition[0] == 0
|
||||
&& target.getClass().getMovementSettings(target).mPosition[1] == 0)
|
||||
speed2 = 0;
|
||||
|
||||
float s1 = distToTarget - weapRange;
|
||||
|
@ -567,9 +570,9 @@ namespace MWMechanics
|
|||
return false;
|
||||
}
|
||||
|
||||
void AiCombat::buildNewPath(const MWWorld::Ptr& actor)
|
||||
void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target)
|
||||
{
|
||||
Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos);
|
||||
|
||||
float dist;
|
||||
|
||||
|
@ -624,9 +627,9 @@ namespace MWMechanics
|
|||
return 1;
|
||||
}
|
||||
|
||||
const MWWorld::Ptr &AiCombat::getTarget() const
|
||||
MWWorld::Ptr AiCombat::getTarget() const
|
||||
{
|
||||
return mTarget;
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,21 +14,24 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief Causes the actor to fight another actor
|
||||
class AiCombat : public AiPackage
|
||||
{
|
||||
public:
|
||||
///Constructor
|
||||
/** \param actor Actor to fight **/
|
||||
AiCombat(const MWWorld::Ptr& actor);
|
||||
|
||||
virtual AiCombat *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
virtual unsigned int getPriority() const;
|
||||
|
||||
const MWWorld::Ptr &getTarget() const;
|
||||
///Returns target ID
|
||||
MWWorld::Ptr getTarget() const;
|
||||
|
||||
private:
|
||||
PathFinder mPathFinder;
|
||||
|
@ -50,7 +53,7 @@ namespace MWMechanics
|
|||
|
||||
ESM::Position mLastPos;
|
||||
MWMechanics::Movement mMovement;
|
||||
MWWorld::Ptr mTarget;
|
||||
int mTargetActorId;
|
||||
|
||||
const MWWorld::CellStore* mCell;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
@ -60,7 +63,7 @@ namespace MWMechanics
|
|||
MWWorld::CellRefList<ESM::Door>::List::iterator mDoorIter;
|
||||
MWWorld::CellRefList<ESM::Door>& mDoors;
|
||||
|
||||
void buildNewPath(const MWWorld::Ptr& actor);
|
||||
void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/timestamp.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
|
||||
|
@ -19,8 +21,8 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
|
||||
: mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration)
|
||||
AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z)
|
||||
: mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration)
|
||||
, mCellX(std::numeric_limits<int>::max())
|
||||
, mCellY(std::numeric_limits<int>::max())
|
||||
{
|
||||
|
@ -38,8 +40,8 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z)
|
||||
: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
|
||||
AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z)
|
||||
: mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration)
|
||||
, mCellX(std::numeric_limits<int>::max())
|
||||
, mCellY(std::numeric_limits<int>::max())
|
||||
{
|
||||
|
@ -75,59 +77,14 @@ namespace MWMechanics
|
|||
return true;
|
||||
}
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
bool cellChange = actor.getCell()->getCell()->mData.mX != mCellX || actor.getCell()->getCell()->mData.mY != mCellY;
|
||||
|
||||
if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = PathFinder::sgn(actor.getCell()->getCell()->mData.mX - player.getCell()->getCell()->mData.mX);
|
||||
// Check if actor is near the border of an inactive cell. If so, pause walking.
|
||||
if(sideX * (pos.pos[0] - actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE /
|
||||
2.0 - 200))
|
||||
{
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = PathFinder::sgn(actor.getCell()->getCell()->mData.mY - player.getCell()->getCell()->mData.mY);
|
||||
// Check if actor is near the border of an inactive cell. If so, pause walking.
|
||||
if(sideY*(pos.pos[1] - actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE /
|
||||
2.0 - 200))
|
||||
{
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!mPathFinder.isPathConstructed() || cellChange)
|
||||
{
|
||||
mCellX = actor.getCell()->getCell()->mData.mX;
|
||||
mCellY = actor.getCell()->getCell()->mData.mY;
|
||||
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = mX;
|
||||
dest.mY = mY;
|
||||
dest.mZ = mZ;
|
||||
|
||||
ESM::Pathgrid::Point start;
|
||||
start.mX = pos.pos[0];
|
||||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true);
|
||||
}
|
||||
|
||||
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
|
||||
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId);
|
||||
if (follower.isEmpty())
|
||||
{
|
||||
// The follower disappeared
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
|
||||
const float* const leaderPos = actor.getRefData().getPosition().pos;
|
||||
const float* const followerPos = follower.getRefData().getPosition().pos;
|
||||
double differenceBetween[3];
|
||||
|
@ -141,9 +98,8 @@ namespace MWMechanics
|
|||
|
||||
if(distanceBetweenResult <= mMaxDist * mMaxDist)
|
||||
{
|
||||
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||
zTurn(actor, Ogre::Degree(zAngle));
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
||||
if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete
|
||||
return true;
|
||||
mMaxDist = 470;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -8,23 +8,27 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief AI Package to have an NPC lead the player to a specific point
|
||||
class AiEscort : public AiPackage
|
||||
{
|
||||
public:
|
||||
AiEscort(const std::string &actorId,int duration, float x, float y, float z);
|
||||
///< \implement AiEscort
|
||||
AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z);
|
||||
///< \implement AiEscortCell
|
||||
/// Implementation of AiEscort
|
||||
/** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time
|
||||
\implement AiEscort **/
|
||||
AiEscort(const MWWorld::Ptr& actor,int duration, float x, float y, float z);
|
||||
/// Implementation of AiEscortCell
|
||||
/** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time
|
||||
\implement AiEscortCell **/
|
||||
AiEscort(const MWWorld::Ptr& actor,const std::string &cellId,int duration, float x, float y, float z);
|
||||
|
||||
virtual AiEscort *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
private:
|
||||
std::string mActorId;
|
||||
int mActorId;
|
||||
std::string mCellId;
|
||||
float mX;
|
||||
float mY;
|
||||
|
|
|
@ -11,30 +11,29 @@
|
|||
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
|
||||
MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(""), AiPackage()
|
||||
{
|
||||
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
}
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), mTimer(0), mStuckTimer(0)
|
||||
MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,const std::string &cellId,float duration, float x, float y, float z)
|
||||
: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(cellId), AiPackage()
|
||||
{
|
||||
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
}
|
||||
|
||||
MWMechanics::AiFollow::AiFollow(const std::string &actorId)
|
||||
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), mTimer(0), mStuckTimer(0)
|
||||
MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor)
|
||||
: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage()
|
||||
{
|
||||
mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
}
|
||||
|
||||
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
|
||||
|
||||
if(target == MWWorld::Ptr()) return true; //Target doesn't exist
|
||||
|
||||
mTimer = mTimer + duration; //Update timer
|
||||
mStuckTimer = mStuckTimer + duration; //Update stuck timer
|
||||
mTotalTime = mTotalTime + duration; //Update total time following
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
|
||||
if(!mAlwaysFollow) //Update if you only follow for a bit
|
||||
|
@ -60,74 +59,18 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
|
|||
}
|
||||
|
||||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = target.getRefData().getPosition().pos[0];
|
||||
dest.mY = target.getRefData().getPosition().pos[1];
|
||||
dest.mZ = target.getRefData().getPosition().pos[2];
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
//Current position, for pathfilding stuff
|
||||
ESM::Pathgrid::Point start;
|
||||
start.mX = pos.pos[0];
|
||||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
//Build the path to get to the destination
|
||||
if(mPathFinder.getPath().empty())
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true);
|
||||
|
||||
//***********************
|
||||
// Checks if you can't get to the end position at all
|
||||
//***********************
|
||||
if(mTimer > 0.25)
|
||||
{
|
||||
if(!mPathFinder.getPath().empty()) //Path has points in it
|
||||
{
|
||||
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
|
||||
|
||||
if((dest.mX - lastPos.mX)*(dest.mX - lastPos.mX)
|
||||
+(dest.mY - lastPos.mY)*(dest.mY - lastPos.mY)
|
||||
+(dest.mZ - lastPos.mZ)*(dest.mZ - lastPos.mZ)
|
||||
> 100*100) //End of the path is far from the destination
|
||||
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
|
||||
}
|
||||
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
//************************
|
||||
// Checks if you aren't moving; you're stuck
|
||||
//************************
|
||||
if(mStuckTimer>0.5) //Checks every half of a second
|
||||
{
|
||||
if((mStuckPos.pos[0] - pos.pos[0])*(mStuckPos.pos[0] - pos.pos[0])
|
||||
+(mStuckPos.pos[1] - pos.pos[1])*(mStuckPos.pos[1] - pos.pos[1])
|
||||
+(mStuckPos.pos[2] - pos.pos[2])*(mStuckPos.pos[2] - pos.pos[2]) < 100) //NPC is stuck
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true);
|
||||
|
||||
mStuckTimer = 0;
|
||||
mStuckPos = pos;
|
||||
}
|
||||
|
||||
//Checks if the path isn't over, turn tomards the direction that you're going
|
||||
if(!mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]))
|
||||
{
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
}
|
||||
|
||||
if((dest.mX - pos.pos[0])*(dest.mX - pos.pos[0])+(dest.mY - pos.pos[1])*(dest.mY - pos.pos[1])+(dest.mZ - pos.pos[2])*(dest.mZ - pos.pos[2])
|
||||
< 100*100)
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
else
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
else {
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
|
||||
//Check if you're far away
|
||||
if((dest.mX - start.mX)*(dest.mX - start.mX)
|
||||
+(dest.mY - start.mY)*(dest.mY - start.mY)
|
||||
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) > 1000*1000)
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000)
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
|
||||
else if((dest.mX - start.mX)*(dest.mX - start.mX) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
|
||||
+(dest.mY - start.mY)*(dest.mY - start.mY)
|
||||
+(dest.mZ - start.mZ)*(dest.mZ - start.mZ) < 800*800)
|
||||
else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 800) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk
|
||||
|
||||
return false;
|
||||
|
@ -135,7 +78,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration)
|
|||
|
||||
std::string MWMechanics::AiFollow::getFollowedActor()
|
||||
{
|
||||
return mActorId;
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow
|
||||
return target.getCellRef().mRefID;
|
||||
}
|
||||
|
||||
MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const
|
||||
|
|
|
@ -1,43 +1,45 @@
|
|||
#ifndef GAME_MWMECHANICS_AIFALLOW_H
|
||||
#define GAME_MWMECHANICS_AIFALLOW_H
|
||||
#ifndef GAME_MWMECHANICS_AIFOLLOW_H
|
||||
#define GAME_MWMECHANICS_AIFOLLOW_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include <string>
|
||||
#include "pathfinding.hpp"
|
||||
#include "../../../components/esm/defs.hpp"
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
/// \brief AiPackage for an actor to follow another actor/the PC
|
||||
/** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely
|
||||
**/
|
||||
class AiFollow : public AiPackage
|
||||
{
|
||||
public:
|
||||
AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z);
|
||||
AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z);
|
||||
AiFollow(const std::string &ActorId);
|
||||
/// Follow Actor for duration or until you arrive at a world position
|
||||
AiFollow(const MWWorld::Ptr& actor,float duration, float X, float Y, float Z);
|
||||
/// Follow Actor for duration or until you arrive at a position in a cell
|
||||
AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z);
|
||||
/// Follow Actor indefinitively
|
||||
AiFollow(const MWWorld::Ptr& actor);
|
||||
|
||||
virtual AiFollow *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
/// Returns the actor being followed
|
||||
std::string getFollowedActor();
|
||||
|
||||
private:
|
||||
bool mAlwaysFollow; //this will make the actor always follow, thus ignoring mDuration and mX,mY,mZ (used for summoned creatures).
|
||||
/// This will make the actor always follow.
|
||||
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
|
||||
bool mAlwaysFollow;
|
||||
float mDuration;
|
||||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
std::string mActorId;
|
||||
int mActorId; // The actor we should follow
|
||||
std::string mCellId;
|
||||
|
||||
float mTimer;
|
||||
float mStuckTimer;
|
||||
float mTotalTime;
|
||||
|
||||
ESM::Position mStuckPos;
|
||||
|
||||
PathFinder mPathFinder;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,131 @@
|
|||
|
||||
#include "aipackage.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "movement.hpp"
|
||||
#include "../mwworld/action.hpp"
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiPackage::~AiPackage() {}
|
||||
|
||||
MWMechanics::AiPackage::AiPackage() : mLastDoorChecked(MWWorld::Ptr()), mTimer(.26), mStuckTimer(0) { //mTimer starts at .26 to force initial pathbuild
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration)
|
||||
{
|
||||
//Update various Timers
|
||||
mTimer += duration; //Update timer
|
||||
mStuckTimer += duration; //Update stuck timer
|
||||
mTotalTime += duration; //Update total time following
|
||||
|
||||
|
||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
|
||||
/// Stops the actor when it gets too close to a unloaded cell
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
const ESM::Cell *cell = actor.getCell()->getCell();
|
||||
Movement &movement = actor.getClass().getMovementSettings(actor);
|
||||
|
||||
//Ensure pursuer doesn't leave loaded cells
|
||||
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Start position
|
||||
ESM::Pathgrid::Point start = pos.pos;
|
||||
|
||||
//***********************
|
||||
/// Checks if you can't get to the end position at all, adds end position to end of path
|
||||
/// Rebuilds path every quarter of a second, in case the target has moved
|
||||
//***********************
|
||||
if(mTimer > 0.25)
|
||||
{
|
||||
if(distance(mPrevDest, dest) > 10) { //Only rebuild path if it's moved
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved
|
||||
mPrevDest = dest;
|
||||
}
|
||||
|
||||
if(!mPathFinder.getPath().empty()) //Path has points in it
|
||||
{
|
||||
ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path
|
||||
|
||||
if(distance(dest, lastPos) > 100) //End of the path is far from the destination
|
||||
mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go
|
||||
}
|
||||
|
||||
mTimer = 0;
|
||||
}
|
||||
|
||||
//************************
|
||||
/// Checks if you aren't moving; attempts to unstick you
|
||||
//************************
|
||||
if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) //Path finished?
|
||||
return true;
|
||||
else if(mStuckTimer>0.5) //Every half second see if we need to take action to avoid something
|
||||
{
|
||||
/// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason
|
||||
//if(mObstacleCheck.check(actor, duration)) {
|
||||
if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < 10 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care
|
||||
// first check if we're walking into a door
|
||||
MWWorld::Ptr door = getNearbyDoor(actor);
|
||||
if(door != MWWorld::Ptr()) // NOTE: checks interior cells only
|
||||
{
|
||||
if(door.getCellRef().mTrap.empty() && mLastDoorChecked != door) { //Open the door if untrapped
|
||||
door.getClass().activate(door, actor).get()->execute(actor);
|
||||
mLastDoorChecked = door;
|
||||
}
|
||||
}
|
||||
else // probably walking into another NPC
|
||||
{
|
||||
// TODO: diagonal should have same animation as walk forward
|
||||
// but doesn't seem to do that?
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f;
|
||||
// change the angle a bit, too
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
||||
}
|
||||
}
|
||||
else { //Not stuck, so reset things
|
||||
mStuckTimer = 0;
|
||||
mStuckPos = pos;
|
||||
mLastDoorChecked = MWWorld::Ptr(); //Resets it, in case he gets stuck behind the door again
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward
|
||||
}
|
||||
}
|
||||
else {
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time
|
||||
}
|
||||
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#ifndef GAME_MWMECHANICS_AIPACKAGE_H
|
||||
#define GAME_MWMECHANICS_AIPACKAGE_H
|
||||
|
||||
#include "pathfinding.hpp"
|
||||
#include <components/esm/defs.hpp>
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "obstacle.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
|
@ -12,6 +18,7 @@ namespace MWMechanics
|
|||
class AiPackage
|
||||
{
|
||||
public:
|
||||
///Enumerates the various AITypes availible.
|
||||
enum TypeId {
|
||||
TypeIdNone = -1,
|
||||
TypeIdWander = 0,
|
||||
|
@ -20,21 +27,47 @@ namespace MWMechanics
|
|||
TypeIdFollow = 3,
|
||||
TypeIdActivate = 4,
|
||||
TypeIdCombat = 5,
|
||||
TypeIdPursue = 6
|
||||
TypeIdPursue = 6,
|
||||
TypeIdAvoidDoor = 7
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
AiPackage();
|
||||
|
||||
///Default Deconstructor
|
||||
virtual ~AiPackage();
|
||||
|
||||
///Clones the package
|
||||
virtual AiPackage *clone() const = 0;
|
||||
|
||||
/// Updates and runs the package (Should run every frame)
|
||||
/// \return Package completed?
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration) = 0;
|
||||
///< \return Package completed?
|
||||
|
||||
/// Returns the TypeID of the AiPackage
|
||||
/// \see enum TypeId
|
||||
virtual int getTypeId() const = 0;
|
||||
///< @see enum TypeId
|
||||
|
||||
/// Higher number is higher priority (0 being the lowest)
|
||||
virtual unsigned int getPriority() const {return 0;}
|
||||
///< higher number is higher priority (0 beeing the lowest)
|
||||
|
||||
protected:
|
||||
/// Causes the actor to attempt to walk to the specified location
|
||||
/** \return If the actor has arrived at his destination **/
|
||||
bool pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration);
|
||||
|
||||
PathFinder mPathFinder;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
||||
float mDoorCheckDuration;
|
||||
float mTimer;
|
||||
float mStuckTimer;
|
||||
float mTotalTime;
|
||||
|
||||
MWWorld::Ptr mLastDoorChecked; //Used to ensure we don't try to CONSTANTLY open a door
|
||||
|
||||
ESM::Position mStuckPos;
|
||||
ESM::Pathgrid::Point mPrevDest;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,97 +6,55 @@
|
|||
#include "../mwworld/action.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "steering.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "movement.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
|
||||
MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr target)
|
||||
: mTarget(target)
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
AiPursue::AiPursue(const MWWorld::Ptr& actor)
|
||||
: mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId())
|
||||
{
|
||||
}
|
||||
MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const
|
||||
AiPursue *MWMechanics::AiPursue::clone() const
|
||||
{
|
||||
return new AiPursue(*this);
|
||||
}
|
||||
bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
bool AiPursue::execute (const MWWorld::Ptr& actor, float duration)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
ESM::Position pos = actor.getRefData().getPosition();
|
||||
Movement &movement = actor.getClass().getMovementSettings(actor);
|
||||
const ESM::Cell *cell = actor.getCell()->getCell();
|
||||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); //The target to follow
|
||||
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
if(target == MWWorld::Ptr())
|
||||
return true; //Target doesn't exist
|
||||
|
||||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
|
||||
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY);
|
||||
//check if actor is near the border of an inactive cell. If so, stop walking.
|
||||
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
|
||||
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//Set the target desition from the actor
|
||||
ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos;
|
||||
|
||||
// Big TODO: Sync this with current AiFollow. Move common code to a shared base class or helpers (applies to all AI packages, way too much duplicated code)
|
||||
|
||||
ESM::Position targetPos = mTarget.getRefData().getPosition();
|
||||
|
||||
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
|
||||
if(!mPathFinder.isPathConstructed() || cellChange || mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
|
||||
{
|
||||
mCellX = cell->mData.mX;
|
||||
mCellY = cell->mData.mY;
|
||||
|
||||
ESM::Pathgrid::Point dest;
|
||||
dest.mX = targetPos.pos[0];
|
||||
dest.mY = targetPos.pos[1];
|
||||
dest.mZ = targetPos.pos[2];
|
||||
|
||||
ESM::Pathgrid::Point start;
|
||||
start.mX = pos.pos[0];
|
||||
start.mY = pos.pos[1];
|
||||
start.mZ = pos.pos[2];
|
||||
|
||||
mPathFinder.buildPath(start, dest, actor.getCell(), true);
|
||||
}
|
||||
|
||||
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
|
||||
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
|
||||
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 100*100)
|
||||
{
|
||||
movement.mPosition[1] = 0;
|
||||
MWWorld::Class::get(mTarget).activate(mTarget,actor).get()->execute(actor);
|
||||
if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
target.getClass().activate(target,actor).get()->execute(actor); //Arrest player
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
pathTo(actor, dest, duration); //Go to the destination
|
||||
}
|
||||
|
||||
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||
zTurn(actor, Ogre::Degree(zAngle));
|
||||
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
|
||||
movement.mPosition[1] = 1;
|
||||
actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int MWMechanics::AiPursue::getTypeId() const
|
||||
int AiPursue::getTypeId() const
|
||||
{
|
||||
return TypeIdPursue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr MWMechanics::AiPursue::getTarget() const
|
||||
MWWorld::Ptr AiPursue::getTarget() const
|
||||
{
|
||||
return mTarget;
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
}
|
||||
|
||||
} // namespace MWMechanics
|
||||
|
|
|
@ -9,23 +9,26 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
/// \brief Makes the actor very closely follow the actor
|
||||
/** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them.
|
||||
Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the
|
||||
path is completed). **/
|
||||
class AiPursue : public AiPackage
|
||||
{
|
||||
public:
|
||||
AiPursue(const MWWorld::Ptr target);
|
||||
///Constructor
|
||||
/** \param actor Actor to pursue **/
|
||||
AiPursue(const MWWorld::Ptr& actor);
|
||||
|
||||
virtual AiPursue *clone() const;
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
virtual int getTypeId() const;
|
||||
|
||||
virtual MWWorld::Ptr getTarget() const;
|
||||
MWWorld::Ptr getTarget() const;
|
||||
|
||||
private:
|
||||
|
||||
MWWorld::Ptr mTarget;
|
||||
|
||||
PathFinder mPathFinder;
|
||||
int mTargetActorId; // The actor to pursue
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
};
|
||||
|
|
|
@ -17,21 +17,24 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
void MWMechanics::AiSequence::copy (const AiSequence& sequence)
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
void AiSequence::copy (const AiSequence& sequence)
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
|
||||
iter!=sequence.mPackages.end(); ++iter)
|
||||
mPackages.push_back ((*iter)->clone());
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {}
|
||||
AiSequence::AiSequence() : mDone (false), mLastAiPackage(-1) {}
|
||||
|
||||
MWMechanics::AiSequence::AiSequence (const AiSequence& sequence) : mDone (false)
|
||||
AiSequence::AiSequence (const AiSequence& sequence) : mDone (false)
|
||||
{
|
||||
copy (sequence);
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& sequence)
|
||||
AiSequence& AiSequence::operator= (const AiSequence& sequence)
|
||||
{
|
||||
if (this!=&sequence)
|
||||
{
|
||||
|
@ -43,12 +46,12 @@ MWMechanics::AiSequence& MWMechanics::AiSequence::operator= (const AiSequence& s
|
|||
return *this;
|
||||
}
|
||||
|
||||
MWMechanics::AiSequence::~AiSequence()
|
||||
AiSequence::~AiSequence()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
int MWMechanics::AiSequence::getTypeId() const
|
||||
int AiSequence::getTypeId() const
|
||||
{
|
||||
if (mPackages.empty())
|
||||
return -1;
|
||||
|
@ -56,17 +59,19 @@ int MWMechanics::AiSequence::getTypeId() const
|
|||
return mPackages.front()->getTypeId();
|
||||
}
|
||||
|
||||
bool MWMechanics::AiSequence::getCombatTarget(std::string &targetActorId) const
|
||||
bool AiSequence::getCombatTarget(std::string &targetActorId) const
|
||||
{
|
||||
if (getTypeId() != AiPackage::TypeIdCombat)
|
||||
return false;
|
||||
const AiCombat *combat = static_cast<const AiCombat *>(mPackages.front());
|
||||
targetActorId = combat->getTarget().getRefData().getHandle();
|
||||
|
||||
MWWorld::Ptr target = combat->getTarget();
|
||||
targetActorId = target.getClass().getCreatureStats(target).getActorId();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MWMechanics::AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const
|
||||
bool AiSequence::canAddTarget(const ESM::Position& actorPos, float distToTarget) const
|
||||
{
|
||||
bool firstCombatFound = false;
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
@ -78,7 +83,7 @@ bool MWMechanics::AiSequence::canAddTarget(const ESM::Position& actorPos, float
|
|||
firstCombatFound = true;
|
||||
|
||||
const AiCombat *combat = static_cast<const AiCombat *>(*it);
|
||||
if (combat->getTarget() != player) return false; // only 1 non-player target allowed
|
||||
if (combat->getTarget() != player ) return false; // only 1 non-player target allowed
|
||||
else
|
||||
{
|
||||
// add new target only if current target (player) is farther
|
||||
|
@ -93,7 +98,7 @@ bool MWMechanics::AiSequence::canAddTarget(const ESM::Position& actorPos, float
|
|||
return true;
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stopCombat()
|
||||
void AiSequence::stopCombat()
|
||||
{
|
||||
while (getTypeId() == AiPackage::TypeIdCombat)
|
||||
{
|
||||
|
@ -102,7 +107,7 @@ void MWMechanics::AiSequence::stopCombat()
|
|||
}
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stopPursuit()
|
||||
void AiSequence::stopPursuit()
|
||||
{
|
||||
while (getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
|
@ -111,12 +116,12 @@ void MWMechanics::AiSequence::stopPursuit()
|
|||
}
|
||||
}
|
||||
|
||||
bool MWMechanics::AiSequence::isPackageDone() const
|
||||
bool AiSequence::isPackageDone() const
|
||||
{
|
||||
return mDone;
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
void AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
||||
{
|
||||
if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
|
@ -176,7 +181,7 @@ void MWMechanics::AiSequence::execute (const MWWorld::Ptr& actor,float duration)
|
|||
}
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::clear()
|
||||
void AiSequence::clear()
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
delete *iter;
|
||||
|
@ -184,7 +189,7 @@ void MWMechanics::AiSequence::clear()
|
|||
mPackages.clear();
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||
{
|
||||
if (package.getTypeId() == AiPackage::TypeIdCombat || package.getTypeId() == AiPackage::TypeIdPursue)
|
||||
{
|
||||
|
@ -214,12 +219,12 @@ void MWMechanics::AiSequence::stack (const AiPackage& package, const MWWorld::Pt
|
|||
mPackages.push_front (package.clone());
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::queue (const AiPackage& package)
|
||||
void AiSequence::queue (const AiPackage& package)
|
||||
{
|
||||
mPackages.push_back (package.clone());
|
||||
}
|
||||
|
||||
MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
{
|
||||
if(mPackages.empty())
|
||||
throw std::runtime_error(std::string("No AI Package!"));
|
||||
|
@ -227,7 +232,7 @@ MWMechanics::AiPackage* MWMechanics::AiSequence::getActivePackage()
|
|||
return mPackages.front();
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
||||
void AiSequence::fill(const ESM::AIPackageList &list)
|
||||
{
|
||||
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
||||
{
|
||||
|
@ -243,7 +248,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
|||
else if (it->mType == ESM::AI_Escort)
|
||||
{
|
||||
ESM::AITarget data = it->mTarget;
|
||||
package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false);
|
||||
package = new MWMechanics::AiEscort(target, data.mDuration, data.mX, data.mY, data.mZ);
|
||||
}
|
||||
else if (it->mType == ESM::AI_Travel)
|
||||
{
|
||||
|
@ -258,8 +264,11 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
|||
else //if (it->mType == ESM::AI_Follow)
|
||||
{
|
||||
ESM::AITarget data = it->mTarget;
|
||||
package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false);
|
||||
package = new MWMechanics::AiFollow(target, data.mDuration, data.mX, data.mY, data.mZ);
|
||||
}
|
||||
mPackages.push_back(package);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MWMechanics
|
||||
|
|
|
@ -15,67 +15,80 @@ namespace MWMechanics
|
|||
class AiPackage;
|
||||
|
||||
/// \brief Sequence of AI-packages for a single actor
|
||||
/** The top-most AI package is run each frame. When completed, it is removed from the stack. **/
|
||||
class AiSequence
|
||||
{
|
||||
///AiPackages to run though
|
||||
std::list<AiPackage *> mPackages;
|
||||
|
||||
///Finished with top AIPackage, set for one frame
|
||||
bool mDone;
|
||||
|
||||
///Copy AiSequence
|
||||
void copy (const AiSequence& sequence);
|
||||
|
||||
// The type of AI package that ran last
|
||||
/// The type of AI package that ran last
|
||||
int mLastAiPackage;
|
||||
|
||||
public:
|
||||
|
||||
///Default constructor
|
||||
AiSequence();
|
||||
|
||||
/// Copy Constructor
|
||||
AiSequence (const AiSequence& sequence);
|
||||
|
||||
/// Assignment operator
|
||||
AiSequence& operator= (const AiSequence& sequence);
|
||||
|
||||
virtual ~AiSequence();
|
||||
|
||||
/// Returns currently executing AiPackage type
|
||||
/** \see enum AiPackage::TypeId **/
|
||||
int getTypeId() const;
|
||||
///< @see enum AiPackage::TypeId
|
||||
|
||||
/// Get the typeid of the Ai package that ran last
|
||||
/** NOT the currently "active" Ai package that will be run in the next frame.
|
||||
This difference is important when an Ai package has just finished and been removed.
|
||||
\see enum AiPackage::TypeId **/
|
||||
int getLastRunTypeId() const { return mLastAiPackage; }
|
||||
///< Get the typeid of the Ai package that ran last, NOT the currently "active" Ai package that will be run in the next frame.
|
||||
/// This difference is important when an Ai package has just finished and been removed.
|
||||
|
||||
/// Return true and assign target if combat package is currently active, return false otherwise
|
||||
bool getCombatTarget (std::string &targetActorId) const;
|
||||
///< Return true and assign target if combat package is currently
|
||||
/// active, return false otherwise
|
||||
|
||||
bool canAddTarget(const ESM::Position& actorPos, float distToTarget) const;
|
||||
///< Function assumes that actor can have only 1 target apart player
|
||||
|
||||
/// Removes all combat packages until first non-combat or stack empty.
|
||||
void stopCombat();
|
||||
///< Removes all combat packages until first non-combat or stack empty.
|
||||
|
||||
void stopPursuit();
|
||||
///< Removes all pursue packages until first non-pursue or stack empty.
|
||||
|
||||
/// Has a package been completed during the last update?
|
||||
bool isPackageDone() const;
|
||||
///< Has a package been completed during the last update?
|
||||
|
||||
/// Removes all pursue packages until first non-pursue or stack empty.
|
||||
void stopPursuit();
|
||||
|
||||
/// Execute current package, switching if needed.
|
||||
void execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< Execute package.
|
||||
|
||||
/// Remove all packages.
|
||||
void clear();
|
||||
///< Remove all packages.
|
||||
|
||||
///< Add \a package to the front of the sequence
|
||||
/** Suspends current package
|
||||
@param actor The actor that owns this AiSequence **/
|
||||
void stack (const AiPackage& package, const MWWorld::Ptr& actor);
|
||||
///< Add \a package to the front of the sequence (suspends current package)
|
||||
/// @param actor The actor that owns this AiSequence
|
||||
|
||||
/// Add \a package to the end of the sequence
|
||||
/** Executed after all other packages have been completed **/
|
||||
void queue (const AiPackage& package);
|
||||
///< Add \a package to the end of the sequence (executed after all other packages have been
|
||||
/// completed)
|
||||
|
||||
/// Return the current active package.
|
||||
/** If there is no active package, it will throw an exception **/
|
||||
AiPackage* getActivePackage();
|
||||
///< return the current active package. If there is no active package, throw an exeption
|
||||
|
||||
/// Fills the AiSequence with packages
|
||||
/** Typically used for loading from the ESM
|
||||
\see ESM::AIPackageList **/
|
||||
void fill (const ESM::AIPackageList& list);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,14 +7,15 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief Causes the AI to travel to the specified point
|
||||
class AiTravel : public AiPackage
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
AiTravel(float x, float y, float z);
|
||||
virtual AiTravel *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
|
|
|
@ -14,20 +14,27 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief Causes the Actor to wander within a specified range
|
||||
class AiWander : public AiPackage
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
/** \param distance Max distance the ACtor will wander
|
||||
\param duration Time, in hours, that this package will be preformed
|
||||
\param timeOfDay Start time of the package, if it has a duration. Currently unimplemented
|
||||
\param idle Chances of each idle to play (9 in total)
|
||||
\param repeat Repeat wander or not **/
|
||||
AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat);
|
||||
virtual AiPackage *clone() const;
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
///< \return Package completed?
|
||||
virtual int getTypeId() const;
|
||||
///< 0: Wander
|
||||
|
||||
virtual AiPackage *clone() const;
|
||||
|
||||
virtual bool execute (const MWWorld::Ptr& actor,float duration);
|
||||
|
||||
virtual int getTypeId() const;
|
||||
|
||||
/// Set the position to return to for a stationary (non-wandering) actor
|
||||
/** In case another AI package moved the actor elsewhere **/
|
||||
void setReturnPosition (const Ogre::Vector3& position);
|
||||
///< Set the position to return to for a stationary (non-wandering) actor, in case
|
||||
/// another AI package moved the actor elsewhere
|
||||
|
||||
private:
|
||||
void stopWalking(const MWWorld::Ptr& actor);
|
||||
|
|
|
@ -431,6 +431,11 @@ void CharacterController::playRandomDeath(float startpoint)
|
|||
mDeathState = static_cast<CharacterState>(CharState_Death1 + (selected-1));
|
||||
}
|
||||
|
||||
// For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually.
|
||||
mMovementState = CharState_None;
|
||||
mAnimation->disable(mCurrentMovement);
|
||||
mCurrentMovement = "";
|
||||
|
||||
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
|
||||
false, 1.0f, "start", "stop", startpoint, 0);
|
||||
}
|
||||
|
@ -1372,9 +1377,9 @@ bool CharacterController::kill()
|
|||
{
|
||||
if( isDead() )
|
||||
{
|
||||
//player's death animation is over
|
||||
if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) )
|
||||
{
|
||||
//player's death animation is over
|
||||
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -202,7 +202,9 @@ public:
|
|||
void skipAnim();
|
||||
bool isAnimPlaying(const std::string &groupName);
|
||||
|
||||
/// @return false if the character has already been killed before
|
||||
bool kill();
|
||||
|
||||
void resurrect();
|
||||
bool isDead() const
|
||||
{ return mDeathState != CharState_None; }
|
||||
|
|
|
@ -146,8 +146,8 @@ namespace MWMechanics
|
|||
|| weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Magical))
|
||||
damage *= multiplier;
|
||||
|
||||
if (weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver
|
||||
& actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||
if ((weapon.get<ESM::Weapon>()->mBase->mData.mFlags & ESM::Weapon::Silver)
|
||||
&& actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||
damage *= MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWereWolfSilverWeaponDamageMult")->getFloat();
|
||||
|
||||
if (damage == 0 && attacker.getRefData().getHandle() == "player")
|
||||
|
|
|
@ -11,14 +11,18 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
int CreatureStats::sActorId = 0;
|
||||
|
||||
CreatureStats::CreatureStats()
|
||||
: mLevel (0), mDead (false), mDied (false), mFriendlyHits (0),
|
||||
mTalkedTo (false), mAlarmed (false),
|
||||
mAttacked (false), mHostile (false),
|
||||
mAttackingOrSpell(false),
|
||||
mIsWerewolf(false),
|
||||
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
|
||||
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f)
|
||||
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
|
||||
mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
|
||||
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f),
|
||||
mTradeTime(0,0), mGoldPool(0), mActorId(-1)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i] = 0;
|
||||
|
@ -348,20 +352,6 @@ namespace MWMechanics
|
|||
return mLastHitObject;
|
||||
}
|
||||
|
||||
bool CreatureStats::canUsePower(const std::string &power) const
|
||||
{
|
||||
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
|
||||
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void CreatureStats::usePower(const std::string &power)
|
||||
{
|
||||
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||
}
|
||||
|
||||
void CreatureStats::addToFallHeight(float height)
|
||||
{
|
||||
mFallHeight += height;
|
||||
|
@ -481,20 +471,79 @@ namespace MWMechanics
|
|||
|
||||
void CreatureStats::writeState (ESM::CreatureStats& state) const
|
||||
{
|
||||
for (int i=0; i<8; ++i)
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
mAttributes[i].writeState (state.mAttributes[i]);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
mDynamic[i].writeState (state.mDynamic[i]);
|
||||
|
||||
state.mTradeTime = mTradeTime.toEsm();
|
||||
state.mGoldPool = mGoldPool;
|
||||
|
||||
state.mDead = mDead;
|
||||
state.mDied = mDied;
|
||||
state.mFriendlyHits = mFriendlyHits;
|
||||
state.mTalkedTo = mTalkedTo;
|
||||
state.mAlarmed = mAlarmed;
|
||||
state.mAttacked = mAttacked;
|
||||
state.mHostile = mHostile;
|
||||
state.mAttackingOrSpell = mAttackingOrSpell;
|
||||
// TODO: rewrite. does this really need 3 separate bools?
|
||||
state.mKnockdown = mKnockdown;
|
||||
state.mKnockdownOneFrame = mKnockdownOneFrame;
|
||||
state.mKnockdownOverOneFrame = mKnockdownOverOneFrame;
|
||||
state.mHitRecovery = mHitRecovery;
|
||||
state.mBlock = mBlock;
|
||||
state.mMovementFlags = mMovementFlags;
|
||||
state.mAttackStrength = mAttackStrength;
|
||||
state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)
|
||||
state.mLastHitObject = mLastHitObject;
|
||||
state.mRecalcDynamicStats = mRecalcDynamicStats;
|
||||
state.mDrawState = mDrawState;
|
||||
state.mLevel = mLevel;
|
||||
state.mActorId = mActorId;
|
||||
|
||||
mSpells.writeState(state.mSpells);
|
||||
mActiveSpells.writeState(state.mActiveSpells);
|
||||
}
|
||||
|
||||
void CreatureStats::readState (const ESM::CreatureStats& state)
|
||||
{
|
||||
for (int i=0; i<8; ++i)
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
mAttributes[i].readState (state.mAttributes[i]);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
mDynamic[i].readState (state.mDynamic[i]);
|
||||
|
||||
mTradeTime = MWWorld::TimeStamp(state.mTradeTime);
|
||||
mGoldPool = state.mGoldPool;
|
||||
mFallHeight = state.mFallHeight;
|
||||
|
||||
mDead = state.mDead;
|
||||
mDied = state.mDied;
|
||||
mFriendlyHits = state.mFriendlyHits;
|
||||
mTalkedTo = state.mTalkedTo;
|
||||
mAlarmed = state.mAlarmed;
|
||||
mAttacked = state.mAttacked;
|
||||
mHostile = state.mHostile;
|
||||
mAttackingOrSpell = state.mAttackingOrSpell;
|
||||
// TODO: rewrite. does this really need 3 separate bools?
|
||||
mKnockdown = state.mKnockdown;
|
||||
mKnockdownOneFrame = state.mKnockdownOneFrame;
|
||||
mKnockdownOverOneFrame = state.mKnockdownOverOneFrame;
|
||||
mHitRecovery = state.mHitRecovery;
|
||||
mBlock = state.mBlock;
|
||||
mMovementFlags = state.mMovementFlags;
|
||||
mAttackStrength = state.mAttackStrength;
|
||||
mFallHeight = state.mFallHeight;
|
||||
mLastHitObject = state.mLastHitObject;
|
||||
mRecalcDynamicStats = state.mRecalcDynamicStats;
|
||||
mDrawState = DrawState_(state.mDrawState);
|
||||
mLevel = state.mLevel;
|
||||
mActorId = state.mActorId;
|
||||
|
||||
mSpells.readState(state.mSpells);
|
||||
mActiveSpells.readState(state.mActiveSpells);
|
||||
}
|
||||
|
||||
// Relates to NPC gold reset delay
|
||||
|
@ -516,4 +565,34 @@ namespace MWMechanics
|
|||
{
|
||||
return mGoldPool;
|
||||
}
|
||||
|
||||
int CreatureStats::getActorId()
|
||||
{
|
||||
if (mActorId==-1)
|
||||
mActorId = sActorId++;
|
||||
|
||||
return mActorId;
|
||||
}
|
||||
|
||||
bool CreatureStats::matchesActorId (int id) const
|
||||
{
|
||||
return mActorId!=-1 && id==mActorId;
|
||||
}
|
||||
|
||||
void CreatureStats::cleanup()
|
||||
{
|
||||
sActorId = 0;
|
||||
}
|
||||
|
||||
void CreatureStats::writeActorIdCounter (ESM::ESMWriter& esm)
|
||||
{
|
||||
esm.startRecord(ESM::REC_ACTC);
|
||||
esm.writeHNT("COUN", sActorId);
|
||||
esm.endRecord(ESM::REC_ACTC);
|
||||
}
|
||||
|
||||
void CreatureStats::readActorIdCounter (ESM::ESMReader& esm)
|
||||
{
|
||||
esm.getHNT(sActorId, "COUN");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace MWMechanics
|
|||
///
|
||||
class CreatureStats
|
||||
{
|
||||
static int sActorId;
|
||||
DrawState_ mDrawState;
|
||||
AttributeValue mAttributes[8];
|
||||
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
|
||||
|
@ -55,15 +56,16 @@ namespace MWMechanics
|
|||
// Do we need to recalculate stats derived from attributes or other factors?
|
||||
bool mRecalcDynamicStats;
|
||||
|
||||
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
|
||||
|
||||
MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay
|
||||
|
||||
int mGoldPool; // the pool of merchant gold not in inventory
|
||||
int mActorId;
|
||||
|
||||
protected:
|
||||
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
|
||||
bool mIsWerewolf;
|
||||
AttributeValue mWerewolfAttributes[8];
|
||||
|
||||
int mLevel;
|
||||
|
||||
public:
|
||||
|
@ -84,9 +86,6 @@ namespace MWMechanics
|
|||
/// @return total fall height
|
||||
float land();
|
||||
|
||||
bool canUsePower (const std::string& power) const;
|
||||
void usePower (const std::string& power);
|
||||
|
||||
const AttributeValue & getAttribute(int index) const;
|
||||
|
||||
const DynamicStat<float> & getHealth() const;
|
||||
|
@ -224,21 +223,39 @@ namespace MWMechanics
|
|||
void setLastHitObject(const std::string &objectid);
|
||||
const std::string &getLastHitObject() const;
|
||||
|
||||
// Note, this is just a cache to avoid checking the whole container store every frame TODO: Put it somewhere else?
|
||||
// Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves.
|
||||
// TODO: Put it somewhere else?
|
||||
std::set<int> mBoundItems;
|
||||
// Same as above
|
||||
std::map<int, std::string> mSummonedCreatures;
|
||||
|
||||
// TODO: store in savegame
|
||||
// TODO: encapsulate?
|
||||
// <ESM::MagicEffect index, actor index>
|
||||
std::map<int, int> mSummonedCreatures;
|
||||
// Contains summoned creatures with an expired lifetime that have not been deleted yet.
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
||||
void writeState (ESM::CreatureStats& state) const;
|
||||
|
||||
void readState (const ESM::CreatureStats& state);
|
||||
|
||||
static void writeActorIdCounter (ESM::ESMWriter& esm);
|
||||
static void readActorIdCounter (ESM::ESMReader& esm);
|
||||
|
||||
// Relates to NPC gold reset delay
|
||||
void setTradeTime(MWWorld::TimeStamp tradeTime);
|
||||
MWWorld::TimeStamp getTradeTime() const;
|
||||
|
||||
void setGoldPool(int pool);
|
||||
int getGoldPool() const;
|
||||
|
||||
int getActorId();
|
||||
///< Will generate an actor ID, if the actor does not have one yet.
|
||||
|
||||
bool matchesActorId (int id) const;
|
||||
///< Check if \a id matches the actor ID of *this (if the actor does not have an ID
|
||||
/// assigned this function will return false).
|
||||
|
||||
static void cleanup();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ namespace MWMechanics
|
|||
/// \note The _ suffix is required to avoid a collision with a Windoze macro. Die, Microsoft! Die!
|
||||
enum DrawState_
|
||||
{
|
||||
DrawState_Weapon = 0,
|
||||
DrawState_Spell = 1,
|
||||
DrawState_Nothing = 2
|
||||
DrawState_Nothing = 0,
|
||||
DrawState_Weapon = 1,
|
||||
DrawState_Spell = 2
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace MWMechanics
|
|||
return getLevelledItem(ref.getPtr().get<ESM::CreatureLevList>()->mBase, failChance);
|
||||
}
|
||||
}
|
||||
catch (std::logic_error& e)
|
||||
catch (std::logic_error&)
|
||||
{
|
||||
// Vanilla doesn't fail on nonexistent items in levelled lists
|
||||
std::cerr << "Warning: ignoring nonexistent item '" << item << "'" << std::endl;
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace MWMechanics
|
|||
struct EffectSourceVisitor
|
||||
{
|
||||
virtual void visit (MWMechanics::EffectKey key,
|
||||
const std::string& sourceName, const std::string& casterHandle,
|
||||
const std::string& sourceName, int casterActorId,
|
||||
float magnitude, float remainingTime = -1) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@ namespace
|
|||
bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim)
|
||||
{
|
||||
const std::string& owner = item.getCellRef().mOwner;
|
||||
bool isOwned = !owner.empty();
|
||||
bool isOwned = !owner.empty() && owner != "player";
|
||||
|
||||
const std::string& faction = item.getCellRef().mFaction;
|
||||
bool isFactionOwned = false;
|
||||
if (!faction.empty())
|
||||
if (!faction.empty() && ptr.getClass().isNpc())
|
||||
{
|
||||
const std::map<std::string, int>& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks();
|
||||
if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end())
|
||||
|
@ -342,7 +342,9 @@ namespace MWMechanics
|
|||
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||
if (enchantItem != inv.end())
|
||||
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||
else if (winMgr->getSelectedSpell() == "")
|
||||
else if (!winMgr->getSelectedSpell().empty())
|
||||
winMgr->setSelectedSpell(winMgr->getSelectedSpell(), int(MWMechanics::getSpellSuccessChance(winMgr->getSelectedSpell(), mWatched)));
|
||||
else
|
||||
winMgr->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
|
@ -383,7 +385,7 @@ namespace MWMechanics
|
|||
// have been made for them. Make sure they're properly updated.
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
mActors.removeActor(ptr);
|
||||
mActors.addActor(ptr);
|
||||
mActors.addActor(ptr, true);
|
||||
}
|
||||
|
||||
mActors.update(duration, paused);
|
||||
|
|
|
@ -432,9 +432,9 @@ float MWMechanics::NpcStats::getTimeToStartDrowning() const
|
|||
{
|
||||
return mTimeToStartDrowning;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::setTimeToStartDrowning(float time)
|
||||
{
|
||||
assert(time>=0 && time<=20);
|
||||
mTimeToStartDrowning=time;
|
||||
}
|
||||
|
||||
|
@ -446,11 +446,16 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
|
|||
|
||||
state.mDisposition = mDisposition;
|
||||
|
||||
for (int i=0; i<27; ++i)
|
||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||
{
|
||||
mSkill[i].writeState (state.mSkills[i].mRegular);
|
||||
mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf);
|
||||
}
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
{
|
||||
mWerewolfAttributes[i].writeState (state.mWerewolfAttributes[i]);
|
||||
}
|
||||
state.mIsWerewolf = mIsWerewolf;
|
||||
|
||||
state.mCrimeId = mCrimeId;
|
||||
|
||||
|
@ -467,10 +472,9 @@ void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const
|
|||
state.mReputation = mReputation;
|
||||
state.mWerewolfKills = mWerewolfKills;
|
||||
state.mProfit = mProfit;
|
||||
state.mAttackStrength = mAttackStrength;
|
||||
state.mLevelProgress = mLevelProgress;
|
||||
|
||||
for (int i=0; i<8; ++i)
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
state.mSkillIncrease[i] = mSkillIncreases[i];
|
||||
|
||||
std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds));
|
||||
|
@ -500,21 +504,26 @@ void MWMechanics::NpcStats::readState (const ESM::NpcStats& state)
|
|||
|
||||
mDisposition = state.mDisposition;
|
||||
|
||||
for (int i=0; i<27; ++i)
|
||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||
{
|
||||
mSkill[i].readState (state.mSkills[i].mRegular);
|
||||
mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf);
|
||||
}
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
{
|
||||
mWerewolfAttributes[i].readState (state.mWerewolfAttributes[i]);
|
||||
}
|
||||
|
||||
mIsWerewolf = state.mIsWerewolf;
|
||||
|
||||
mCrimeId = state.mCrimeId;
|
||||
mBounty = state.mBounty;
|
||||
mReputation = state.mReputation;
|
||||
mWerewolfKills = state.mWerewolfKills;
|
||||
mProfit = state.mProfit;
|
||||
mAttackStrength = state.mAttackStrength;
|
||||
mLevelProgress = state.mLevelProgress;
|
||||
|
||||
for (int i=0; i<8; ++i)
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
mSkillIncreases[i] = state.mSkillIncrease[i];
|
||||
|
||||
for (std::vector<std::string>::const_iterator iter (state.mUsedIds.begin());
|
||||
|
|
|
@ -31,8 +31,8 @@ namespace MWMechanics
|
|||
std::map<std::string, int> mFactionRank;
|
||||
|
||||
int mDisposition;
|
||||
SkillValue mSkill[27];
|
||||
SkillValue mWerewolfSkill[27];
|
||||
SkillValue mSkill[ESM::Skill::Length];
|
||||
SkillValue mWerewolfSkill[ESM::Skill::Length];
|
||||
int mBounty;
|
||||
std::set<std::string> mExpelled;
|
||||
std::map<std::string, int> mFactionReputation;
|
||||
|
@ -40,7 +40,6 @@ namespace MWMechanics
|
|||
int mCrimeId;
|
||||
int mWerewolfKills;
|
||||
int mProfit;
|
||||
float mAttackStrength;
|
||||
|
||||
int mLevelProgress; // 0-10
|
||||
|
||||
|
|
|
@ -19,11 +19,19 @@ namespace MWMechanics
|
|||
// Limitation: there can be false detections, and does not test whether the
|
||||
// actor is facing the door.
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor, float minSqr, bool closed)
|
||||
{
|
||||
if(getNearbyDoor(actor, minSqr, closed)!=MWWorld::Ptr())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr, bool closed)
|
||||
{
|
||||
MWWorld::CellStore *cell = actor.getCell();
|
||||
|
||||
if(cell->getCell()->isExterior())
|
||||
return false; // check interior cells only
|
||||
return MWWorld::Ptr(); // check interior cells only
|
||||
|
||||
// Check all the doors in this cell
|
||||
MWWorld::CellRefList<ESM::Door>& doors = cell->get<ESM::Door>();
|
||||
|
@ -31,14 +39,14 @@ namespace MWMechanics
|
|||
MWWorld::CellRefList<ESM::Door>::List::iterator it = refList.begin();
|
||||
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
|
||||
|
||||
// TODO: How to check whether the actor is facing a door? Below code is for
|
||||
// the player, perhaps it can be adapted.
|
||||
/// TODO: How to check whether the actor is facing a door? Below code is for
|
||||
/// the player, perhaps it can be adapted.
|
||||
//MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||
//if(!ptr.isEmpty())
|
||||
//std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl;
|
||||
|
||||
// TODO: The in-game observation of rot[2] value seems to be the
|
||||
// opposite of the code in World::activateDoor() ::confused::
|
||||
/// TODO: The in-game observation of rot[2] value seems to be the
|
||||
/// opposite of the code in World::activateDoor() ::confused::
|
||||
for (; it != refList.end(); ++it)
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||
|
@ -46,10 +54,10 @@ namespace MWMechanics
|
|||
if((closed && ref.mData.getLocalRotation().rot[2] == 0) ||
|
||||
(!closed && ref.mData.getLocalRotation().rot[2] >= 1))
|
||||
{
|
||||
return true; // found, stop searching
|
||||
return MWWorld::Ptr(&ref, actor.getCell()); // found, stop searching
|
||||
}
|
||||
}
|
||||
return false; // none found
|
||||
return MWWorld::Ptr(); // none found
|
||||
}
|
||||
|
||||
ObstacleCheck::ObstacleCheck():
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#ifndef OPENMW_MECHANICS_OBSTACLE_H
|
||||
#define OPENMW_MECHANICS_OBSTACLE_H
|
||||
|
||||
//#include "../mwbase/world.hpp"
|
||||
//#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
|
@ -8,14 +12,20 @@ namespace MWWorld
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
// NOTE: determined empirically based on in-game behaviour
|
||||
/// NOTE: determined empirically based on in-game behaviour
|
||||
static const float MIN_DIST_TO_DOOR_SQUARED = 128*128;
|
||||
|
||||
// tests actor's proximity to a closed door by default
|
||||
/// tests actor's proximity to a closed door by default
|
||||
bool proximityToDoor(const MWWorld::Ptr& actor,
|
||||
float minSqr = MIN_DIST_TO_DOOR_SQUARED,
|
||||
bool closed = true);
|
||||
|
||||
/// Returns door pointer within range. No guarentee is given as too which one
|
||||
/** \return Pointer to the door, or NULL if none exists **/
|
||||
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor,
|
||||
float minSqr = MIN_DIST_TO_DOOR_SQUARED,
|
||||
bool closed = true);
|
||||
|
||||
class ObstacleCheck
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -11,30 +11,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
|
||||
{
|
||||
x -= point.mX;
|
||||
y -= point.mY;
|
||||
z -= point.mZ;
|
||||
return sqrt(x * x + y * y + 0.1 * z * z);
|
||||
}
|
||||
|
||||
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
|
||||
{
|
||||
x -= point.mX;
|
||||
y -= point.mY;
|
||||
z -= point.mZ;
|
||||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
|
||||
{
|
||||
float x = a.mX - b.mX;
|
||||
float y = a.mY - b.mY;
|
||||
float z = a.mZ - b.mZ;
|
||||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
// Slightly cheaper version for comparisons.
|
||||
// Caller needs to be careful for very short distances (i.e. less than 1)
|
||||
// or when accumuating the results i.e. (a + b)^2 != a^2 + b^2
|
||||
|
@ -114,6 +90,30 @@ namespace
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
float distanceZCorrected(ESM::Pathgrid::Point point, float x, float y, float z)
|
||||
{
|
||||
x -= point.mX;
|
||||
y -= point.mY;
|
||||
z -= point.mZ;
|
||||
return sqrt(x * x + y * y + 0.1 * z * z);
|
||||
}
|
||||
|
||||
float distance(ESM::Pathgrid::Point point, float x, float y, float z)
|
||||
{
|
||||
x -= point.mX;
|
||||
y -= point.mY;
|
||||
z -= point.mZ;
|
||||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b)
|
||||
{
|
||||
float x = a.mX - b.mX;
|
||||
float y = a.mY - b.mY;
|
||||
float z = a.mZ - b.mZ;
|
||||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
PathFinder::PathFinder()
|
||||
: mIsPathConstructed(false),
|
||||
mPathgrid(NULL),
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace MWWorld
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
float distance(ESM::Pathgrid::Point point, float x, float y, float);
|
||||
float distance(ESM::Pathgrid::Point a, ESM::Pathgrid::Point b);
|
||||
class PathFinder
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace MWMechanics
|
|||
float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();
|
||||
|
||||
// This makes spells that are easy to cast harder to resist and vice versa
|
||||
if (spell != NULL && caster.getClass().isActor())
|
||||
if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
|
||||
{
|
||||
float castChance = getSpellSuccessChance(spell, caster);
|
||||
if (castChance > 0)
|
||||
|
@ -189,6 +189,9 @@ namespace MWMechanics
|
|||
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
|
||||
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
|
||||
{
|
||||
if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
|
||||
return;
|
||||
|
||||
// If none of the effects need to apply, we can early-out
|
||||
bool found = false;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
|
||||
|
@ -219,10 +222,12 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
ESM::EffectList reflectedEffects;
|
||||
std::vector<ActiveSpells::Effect> appliedLastingEffects;
|
||||
std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
|
||||
bool firstAppliedEffect = true;
|
||||
bool anyHarmfulEffect = false;
|
||||
|
||||
bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
|
||||
effectIt!=effects.mList.end(); ++effectIt)
|
||||
{
|
||||
|
@ -235,7 +240,7 @@ namespace MWMechanics
|
|||
|
||||
if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate)
|
||||
{
|
||||
if (caster.getRefData().getHandle() == "player")
|
||||
if (castByPlayer)
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
|
||||
continue;
|
||||
}
|
||||
|
@ -246,13 +251,13 @@ namespace MWMechanics
|
|||
effectIt->mEffectID == ESM::MagicEffect::Mark ||
|
||||
effectIt->mEffectID == ESM::MagicEffect::Recall))
|
||||
{
|
||||
if (caster.getRefData().getHandle() == "player")
|
||||
if (castByPlayer)
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// If player is healing someone, show the target's HP bar
|
||||
if (caster.getRefData().getHandle() == "player" && target != caster
|
||||
if (castByPlayer && target != caster
|
||||
&& effectIt->mEffectID == ESM::MagicEffect::RestoreHealth
|
||||
&& target.getClass().isActor())
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(target);
|
||||
|
@ -263,7 +268,7 @@ namespace MWMechanics
|
|||
anyHarmfulEffect = true;
|
||||
|
||||
// If player is attempting to cast a harmful spell, show the target's HP bar
|
||||
if (caster.getRefData().getHandle() == "player" && target != caster)
|
||||
if (castByPlayer && target != caster)
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(target);
|
||||
|
||||
// Try absorbing if it's a spell
|
||||
|
@ -329,8 +334,9 @@ namespace MWMechanics
|
|||
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
|
||||
if (target.getClass().isActor() && hasDuration)
|
||||
{
|
||||
ActiveSpells::Effect effect;
|
||||
effect.mKey = MWMechanics::EffectKey(*effectIt);
|
||||
ActiveSpells::ActiveEffect effect;
|
||||
effect.mEffectId = effectIt->mEffectID;
|
||||
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
|
||||
effect.mDuration = effectIt->mDuration;
|
||||
effect.mMagnitude = magnitude;
|
||||
|
||||
|
@ -338,17 +344,20 @@ namespace MWMechanics
|
|||
|
||||
// For absorb effects, also apply the effect to the caster - but with a negative
|
||||
// magnitude, since we're transfering stats from the target to the caster
|
||||
for (int i=0; i<5; ++i)
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
{
|
||||
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
|
||||
for (int i=0; i<5; ++i)
|
||||
{
|
||||
std::vector<ActiveSpells::Effect> effects;
|
||||
ActiveSpells::Effect effect_ = effect;
|
||||
effect_.mMagnitude *= -1;
|
||||
effects.push_back(effect_);
|
||||
// Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies
|
||||
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
|
||||
effects, mSourceName, target.getRefData().getHandle());
|
||||
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
|
||||
{
|
||||
std::vector<ActiveSpells::ActiveEffect> effects;
|
||||
ActiveSpells::ActiveEffect effect_ = effect;
|
||||
effect_.mMagnitude *= -1;
|
||||
effects.push_back(effect_);
|
||||
// Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies
|
||||
caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
|
||||
effects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,6 +398,7 @@ namespace MWMechanics
|
|||
else
|
||||
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
|
||||
|
||||
// TODO: VFX are no longer active after saving/reloading the game
|
||||
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
|
||||
// Note: in case of non actor, a free effect should be fine as well
|
||||
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
|
||||
|
@ -405,12 +415,17 @@ namespace MWMechanics
|
|||
inflict(caster, target, reflectedEffects, range, true);
|
||||
|
||||
if (!appliedLastingEffects.empty())
|
||||
{
|
||||
int casterActorId = -1;
|
||||
if (caster.getClass().isActor())
|
||||
casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
|
||||
target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
|
||||
mSourceName, caster.getRefData().getHandle());
|
||||
mSourceName, casterActorId);
|
||||
}
|
||||
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster
|
||||
&& target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
|
||||
MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault);
|
||||
// Notify the target actor they've been hit
|
||||
if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor())
|
||||
target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
|
||||
}
|
||||
|
||||
void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude)
|
||||
|
@ -431,7 +446,8 @@ namespace MWMechanics
|
|||
if (target.getCellRef().mLockLevel > 0)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
|
||||
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
|
||||
if (!caster.isEmpty() && caster.getClass().isActor())
|
||||
MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);
|
||||
}
|
||||
target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); //unlocks the door
|
||||
}
|
||||
|
|
|
@ -61,9 +61,12 @@ namespace MWMechanics
|
|||
/// @note Auto detects if spell, ingredient or potion
|
||||
bool cast (const std::string& id);
|
||||
|
||||
/// @note \a target can be any type of object, not just actors.
|
||||
/// @note \a caster can be any type of object, or even an empty object.
|
||||
void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster,
|
||||
const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false);
|
||||
|
||||
/// @note \a caster can be any type of object, or even an empty object.
|
||||
void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude);
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdlib>
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/spellstate.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -30,10 +31,19 @@ namespace MWMechanics
|
|||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
|
||||
|
||||
std::vector<float> random;
|
||||
random.resize(spell->mEffects.mList.size());
|
||||
for (unsigned int i=0; i<random.size();++i)
|
||||
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
std::map<const int, float> random;
|
||||
|
||||
// Determine the random magnitudes (unless this is a castable spell, in which case
|
||||
// they will be determined when the spell is cast)
|
||||
if (spell->mData.mType != ESM::Spell::ST_Power && spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
{
|
||||
for (unsigned int i=0; i<spell->mEffects.mList.size();++i)
|
||||
{
|
||||
if (spell->mEffects.mList[i].mMagnMin != spell->mEffects.mList[i].mMagnMax)
|
||||
random[i] = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
mSpells.insert (std::make_pair (Misc::StringUtils::lowerCase(spellId), random));
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +77,11 @@ namespace MWMechanics
|
|||
int i=0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
|
||||
{
|
||||
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * iter->second[i]);
|
||||
float random = 1.f;
|
||||
if (iter->second.find(i) != iter->second.end())
|
||||
random = iter->second.at(i);
|
||||
|
||||
effects.add (*it, it->mMagnMin + (it->mMagnMax - it->mMagnMin) * random);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
@ -192,9 +206,60 @@ namespace MWMechanics
|
|||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt, ++i)
|
||||
{
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * it->second[i];
|
||||
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude);
|
||||
float random = 1.f;
|
||||
if (it->second.find(i) != it->second.end())
|
||||
random = it->second.at(i);
|
||||
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
|
||||
visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Spells::canUsePower(const std::string &power) const
|
||||
{
|
||||
std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.find(power);
|
||||
if (it == mUsedPowers.end() || it->second + 24 <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Spells::usePower(const std::string &power)
|
||||
{
|
||||
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||
}
|
||||
|
||||
void Spells::readState(const ESM::SpellState &state)
|
||||
{
|
||||
mSpells = state.mSpells;
|
||||
mSelectedSpell = state.mSelectedSpell;
|
||||
|
||||
// Discard spells that are no longer available due to changed content files
|
||||
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(iter->first);
|
||||
if (!spell)
|
||||
{
|
||||
if (iter->first == mSelectedSpell)
|
||||
mSelectedSpell = "";
|
||||
mSpells.erase(iter++);
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
|
||||
// No need to discard spells here (doesn't really matter if non existent ids are kept)
|
||||
for (std::map<std::string, ESM::TimeStamp>::const_iterator it = state.mUsedPowers.begin(); it != state.mUsedPowers.end(); ++it)
|
||||
mUsedPowers[it->first] = MWWorld::TimeStamp(it->second);
|
||||
}
|
||||
|
||||
void Spells::writeState(ESM::SpellState &state) const
|
||||
{
|
||||
state.mSpells = mSpells;
|
||||
state.mSelectedSpell = mSelectedSpell;
|
||||
|
||||
for (std::map<std::string, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
|
||||
state.mUsedPowers[it->first] = it->second.toEsm();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/timestamp.hpp"
|
||||
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
|
||||
struct SpellState;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -22,21 +26,29 @@ namespace MWMechanics
|
|||
/// \brief Spell list
|
||||
///
|
||||
/// This class manages known spells as well as abilities, powers and permanent negative effects like
|
||||
/// diseases.
|
||||
/// diseases. It also keeps track of used powers (which can only be used every 24h).
|
||||
class Spells
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::map<std::string, std::vector<float> > TContainer; // ID, normalised magnitudes
|
||||
|
||||
typedef std::map<std::string, std::map<const int, float> > TContainer; // ID, <effect index, normalised random magnitude>
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
private:
|
||||
|
||||
TContainer mSpells;
|
||||
|
||||
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
|
||||
std::string mSelectedSpell;
|
||||
|
||||
std::map<std::string, MWWorld::TimeStamp> mUsedPowers;
|
||||
|
||||
public:
|
||||
|
||||
bool canUsePower (const std::string& power) const;
|
||||
void usePower (const std::string& power);
|
||||
|
||||
void purgeCommonDisease();
|
||||
void purgeBlightDisease();
|
||||
void purgeCorprusDisease();
|
||||
|
@ -72,6 +84,9 @@ namespace MWMechanics
|
|||
bool hasBlightDisease() const;
|
||||
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
|
||||
|
||||
void readState (const ESM::SpellState& state);
|
||||
void writeState (ESM::SpellState& state) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,10 @@ void LocalMap::requestMap(MWWorld::CellStore* cell, float zMin, float zMax)
|
|||
|
||||
mCameraPosNode->setPosition(Vector3(0,0,0));
|
||||
|
||||
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name);
|
||||
// Note: using force=true for exterior cell maps.
|
||||
// They must be updated even if they were visited before, because the set of surrounding active cells might be different
|
||||
// (and objects in a different cell can "bleed" into another cell's map if they cross the border)
|
||||
render((x+0.5)*sSize, (y+0.5)*sSize, zMin, zMax, sSize, sSize, name, true);
|
||||
|
||||
if (mBuffers.find(name) == mBuffers.end())
|
||||
{
|
||||
|
@ -296,7 +299,8 @@ void LocalMap::requestMap(MWWorld::CellStore* cell,
|
|||
ESM::FogState* fog = cell->getFog();
|
||||
|
||||
// We are using the same bounds and angle as we were using when the textures were originally made. Segments should come out the same.
|
||||
assert (i < int(fog->mFogTextures.size()));
|
||||
if (i >= int(fog->mFogTextures.size()))
|
||||
throw std::runtime_error("fog texture count mismatch");
|
||||
|
||||
ESM::FogTexture& esm = fog->mFogTextures[i];
|
||||
loadFogOfWar(texturePrefix, esm);
|
||||
|
@ -338,8 +342,6 @@ Ogre::TexturePtr LocalMap::createFogOfWarTexture(const std::string &texName)
|
|||
PF_A8R8G8B8,
|
||||
TU_DYNAMIC_WRITE_ONLY);
|
||||
}
|
||||
else
|
||||
tex->unload();
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
@ -351,12 +353,14 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
|
|||
Ogre::Image image;
|
||||
image.load(stream, "tga");
|
||||
|
||||
assert (image.getWidth() == sFogOfWarResolution && image.getHeight() == sFogOfWarResolution);
|
||||
if (image.getWidth() != sFogOfWarResolution || image.getHeight() != sFogOfWarResolution)
|
||||
throw std::runtime_error("fog texture size mismatch");
|
||||
|
||||
std::string texName = texturePrefix + "_fog";
|
||||
|
||||
Ogre::TexturePtr tex = createFogOfWarTexture(texName);
|
||||
|
||||
tex->unload();
|
||||
tex->loadImage(image);
|
||||
|
||||
// create a buffer to use for dynamic operations
|
||||
|
@ -369,7 +373,7 @@ void LocalMap::loadFogOfWar (const std::string& texturePrefix, ESM::FogTexture&
|
|||
|
||||
void LocalMap::render(const float x, const float y,
|
||||
const float zlow, const float zhigh,
|
||||
const float xw, const float yw, const std::string& texture)
|
||||
const float xw, const float yw, const std::string& texture, bool force)
|
||||
{
|
||||
mCellCamera->setFarClipDistance( (zhigh-zlow) + 2000 );
|
||||
mCellCamera->setNearClipDistance(50);
|
||||
|
@ -408,6 +412,11 @@ void LocalMap::render(const float x, const float y,
|
|||
PF_R8G8B8);
|
||||
tex->getBuffer()->blit(mRenderTexture->getBuffer());
|
||||
}
|
||||
else if (force)
|
||||
{
|
||||
mRenderTarget->update();
|
||||
tex->getBuffer()->blit(mRenderTexture->getBuffer());
|
||||
}
|
||||
|
||||
mRenderingManager->enableLights(true);
|
||||
mLight->setVisible(false);
|
||||
|
|
|
@ -106,10 +106,11 @@ namespace MWRender
|
|||
float mAngle;
|
||||
const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle);
|
||||
|
||||
/// @param force Always render, even if we already have a cached map
|
||||
void render(const float x, const float y,
|
||||
const float zlow, const float zhigh,
|
||||
const float xw, const float yw,
|
||||
const std::string& texture);
|
||||
const std::string& texture, bool force=false);
|
||||
|
||||
// Creates a fog of war texture and initializes it to full black
|
||||
void createFogOfWar(const std::string& texturePrefix);
|
||||
|
|
|
@ -35,7 +35,7 @@ OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNod
|
|||
|
||||
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0);
|
||||
}
|
||||
catch (Ogre::Exception& e)
|
||||
catch (Ogre::Exception&)
|
||||
{
|
||||
mSupported = false;
|
||||
}
|
||||
|
|
|
@ -380,6 +380,10 @@ void RenderingManager::update (float duration, bool paused)
|
|||
|
||||
mCamera->update(duration, paused);
|
||||
|
||||
Ogre::SceneNode *node = data.getBaseNode();
|
||||
Ogre::Quaternion orient = node->_getDerivedOrientation();
|
||||
mLocalMap->updatePlayer(playerPos, orient);
|
||||
|
||||
if(paused)
|
||||
return;
|
||||
|
||||
|
@ -393,10 +397,6 @@ void RenderingManager::update (float duration, bool paused)
|
|||
|
||||
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
|
||||
|
||||
Ogre::SceneNode *node = data.getBaseNode();
|
||||
Ogre::Quaternion orient = node->_getDerivedOrientation();
|
||||
|
||||
mLocalMap->updatePlayer(playerPos, orient);
|
||||
|
||||
mWater->updateUnderwater(world->isUnderwater(player.getCell(), cam));
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace MWRender
|
|||
{
|
||||
public:
|
||||
WeaponAnimation() : mPitchFactor(0) {}
|
||||
virtual ~WeaponAnimation() {}
|
||||
|
||||
virtual void attachArrow(MWWorld::Ptr actor);
|
||||
virtual void releaseArrow(MWWorld::Ptr actor);
|
||||
|
|
|
@ -91,6 +91,7 @@ namespace MWScript
|
|||
|
||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
Interpreter::Type_Float duration = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
@ -107,7 +108,7 @@ namespace MWScript
|
|||
// discard additional arguments (reset), because we have no idea what they mean.
|
||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiEscort escortPackage(actorID, duration, x, y, z);
|
||||
MWMechanics::AiEscort escortPackage(actor, duration, x, y, z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||
|
||||
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
|
||||
|
@ -126,6 +127,7 @@ namespace MWScript
|
|||
|
||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
@ -145,7 +147,7 @@ namespace MWScript
|
|||
// discard additional arguments (reset), because we have no idea what they mean.
|
||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiEscort escortPackage(actorID, cellID, duration, x, y, z);
|
||||
MWMechanics::AiEscort escortPackage(actor, cellID, duration, x, y, z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||
|
||||
std::cout << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration
|
||||
|
@ -281,6 +283,7 @@ namespace MWScript
|
|||
|
||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
Interpreter::Type_Float duration = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
@ -297,7 +300,7 @@ namespace MWScript
|
|||
// discard additional arguments (reset), because we have no idea what they mean.
|
||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z);
|
||||
MWMechanics::AiFollow followPackage(actor, duration, x, y ,z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||
|
||||
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
|
||||
|
@ -316,6 +319,7 @@ namespace MWScript
|
|||
|
||||
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
std::string cellID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
@ -335,8 +339,8 @@ namespace MWScript
|
|||
// discard additional arguments (reset), because we have no idea what they mean.
|
||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||
|
||||
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z);
|
||||
MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||
MWMechanics::AiFollow followPackage(actor, cellID, duration, x, y ,z);
|
||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||
std::cout << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration
|
||||
<< std::endl;
|
||||
}
|
||||
|
|
|
@ -295,7 +295,7 @@ namespace MWScript
|
|||
{
|
||||
store = MWBase::Environment::get().getWorld()->getInterior(cellID);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID);
|
||||
if(cell)
|
||||
|
@ -395,7 +395,7 @@ namespace MWScript
|
|||
{
|
||||
store = MWBase::Environment::get().getWorld()->getInterior(cellID);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
const ESM::Cell* cell = MWBase::Environment::get().getWorld()->getExterior(cellID);
|
||||
if(cell)
|
||||
|
|
|
@ -204,7 +204,7 @@ void FFmpeg_Decoder::open(const std::string &fname)
|
|||
|
||||
mFrame = avcodec_alloc_frame();
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
if (mFormatCtx->pb->buffer != NULL)
|
||||
{
|
||||
|
|
|
@ -288,7 +288,7 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode
|
|||
|
||||
mOutput.mActiveSounds.push_back(this);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
alDeleteBuffers(sNumBuffers, mBuffers);
|
||||
alGetError();
|
||||
|
@ -471,7 +471,7 @@ bool OpenAL_SoundStream::process()
|
|||
|
||||
mIsFinished = finished;
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
catch(std::exception&) {
|
||||
std::cout<< "Error updating stream \""<<mDecoder->getName()<<"\"" <<std::endl;
|
||||
mSamplesQueued = 0;
|
||||
mIsFinished = true;
|
||||
|
@ -781,7 +781,7 @@ ALuint OpenAL_Output::getBuffer(const std::string &fname)
|
|||
{
|
||||
decoder->open(fname);
|
||||
}
|
||||
catch(Ogre::FileNotFoundException &e)
|
||||
catch(Ogre::FileNotFoundException&)
|
||||
{
|
||||
std::string::size_type pos = fname.rfind('.');
|
||||
if(pos == std::string::npos)
|
||||
|
@ -859,7 +859,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, f
|
|||
buf = getBuffer(fname);
|
||||
sound.reset(new OpenAL_Sound(*this, src, buf, Ogre::Vector3(0.0f), vol, basevol, pitch, 1.0f, 1000.0f, flags));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
if(buf && alIsBuffer(buf))
|
||||
|
@ -898,7 +898,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre
|
|||
buf = getBuffer(fname);
|
||||
sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
if(buf && alIsBuffer(buf))
|
||||
|
@ -940,7 +940,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, fl
|
|||
{
|
||||
sound.reset(new OpenAL_SoundStream(*this, src, decoder, volume, pitch, flags));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
throw;
|
||||
|
|
|
@ -321,7 +321,7 @@ namespace MWSound
|
|||
sound = mOutput->playSound(file, volume, basevol, pitch, mode|type, offset);
|
||||
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
|
@ -349,7 +349,7 @@ namespace MWSound
|
|||
else
|
||||
mActiveSounds[sound] = std::make_pair(ptr, soundId);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
catch(std::exception&)
|
||||
{
|
||||
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "../mwscript/globalscripts.hpp"
|
||||
|
||||
|
@ -46,6 +47,8 @@ void MWState::StateManager::cleanup (bool force)
|
|||
mState = State_NoGame;
|
||||
mCharacterManager.clearCurrentCharacter();
|
||||
mTimePlayed = 0;
|
||||
|
||||
MWMechanics::CreatureStats::cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,6 +318,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
case ESM::REC_CSTA:
|
||||
case ESM::REC_WTHR:
|
||||
case ESM::REC_DYNA:
|
||||
case ESM::REC_ACTC:
|
||||
|
||||
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
|
||||
break;
|
||||
|
@ -326,6 +330,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
|
||||
case ESM::REC_GMAP:
|
||||
case ESM::REC_KEYS:
|
||||
case ESM::REC_ASPL:
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->readRecord(reader, n.val);
|
||||
break;
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include "class.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
ActionApply::ActionApply (const Ptr& target, const std::string& id)
|
||||
|
@ -11,6 +14,8 @@ namespace MWWorld
|
|||
|
||||
void ActionApply::executeImp (const Ptr& actor)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
||||
|
||||
MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor);
|
||||
}
|
||||
|
||||
|
@ -22,6 +27,8 @@ namespace MWWorld
|
|||
|
||||
void ActionApplyWithSkill::executeImp (const Ptr& actor)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->breakInvisibility(actor);
|
||||
|
||||
if (MWWorld::Class::get (getTarget()).apply (getTarget(), mId, actor) && mUsageType!=-1)
|
||||
MWWorld::Class::get (getTarget()).skillUsageSucceeded (actor, mSkillIndex, mUsageType);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ namespace MWWorld
|
|||
std::list<MWWorld::Ptr> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor);
|
||||
for(std::list<MWWorld::Ptr>::iterator it = followers.begin();it != followers.end();++it)
|
||||
{
|
||||
std::cout << "teleporting someone!" << (*it).getCellRef().mRefID;
|
||||
executeImp(*it);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,14 @@
|
|||
#include <components/esm/npcstate.hpp>
|
||||
#include <components/esm/creaturestate.hpp>
|
||||
#include <components/esm/fogstate.hpp>
|
||||
#include <components/esm/creaturelevliststate.hpp>
|
||||
#include <components/esm/doorstate.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "esmstore.hpp"
|
||||
#include "class.hpp"
|
||||
|
@ -41,6 +45,22 @@ namespace
|
|||
return MWWorld::Ptr();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MWWorld::Ptr searchViaActorId (MWWorld::CellRefList<T>& actorList, int actorId,
|
||||
MWWorld::CellStore *cell)
|
||||
{
|
||||
for (typename MWWorld::CellRefList<T>::List::iterator iter (actorList.mList.begin());
|
||||
iter!=actorList.mList.end(); ++iter)
|
||||
{
|
||||
MWWorld::Ptr actor (&*iter, cell);
|
||||
|
||||
if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0)
|
||||
return actor;
|
||||
}
|
||||
|
||||
return MWWorld::Ptr();
|
||||
}
|
||||
|
||||
template<typename RecordType, typename T>
|
||||
void writeReferenceCollection (ESM::ESMWriter& writer,
|
||||
const MWWorld::CellRefList<T>& collection)
|
||||
|
@ -323,6 +343,17 @@ namespace MWWorld
|
|||
return Ptr();
|
||||
}
|
||||
|
||||
Ptr CellStore::searchViaActorId (int id)
|
||||
{
|
||||
if (Ptr ptr = ::searchViaActorId (mNpcs, id, this))
|
||||
return ptr;
|
||||
|
||||
if (Ptr ptr = ::searchViaActorId (mCreatures, id, this))
|
||||
return ptr;
|
||||
|
||||
return Ptr();
|
||||
}
|
||||
|
||||
float CellStore::getWaterLevel() const
|
||||
{
|
||||
return mWaterLevel;
|
||||
|
@ -561,9 +592,9 @@ namespace MWWorld
|
|||
writeReferenceCollection<ESM::ObjectState> (writer, mClothes);
|
||||
writeReferenceCollection<ESM::ContainerState> (writer, mContainers);
|
||||
writeReferenceCollection<ESM::CreatureState> (writer, mCreatures);
|
||||
writeReferenceCollection<ESM::ObjectState> (writer, mDoors);
|
||||
writeReferenceCollection<ESM::DoorState> (writer, mDoors);
|
||||
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
|
||||
writeReferenceCollection<ESM::ObjectState> (writer, mCreatureLists);
|
||||
writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);
|
||||
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
|
||||
writeReferenceCollection<ESM::LightState> (writer, mLights);
|
||||
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
|
||||
|
@ -629,7 +660,7 @@ namespace MWWorld
|
|||
|
||||
case ESM::REC_DOOR:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap);
|
||||
readReferenceCollection<ESM::DoorState> (reader, mDoors, contentFileMap);
|
||||
break;
|
||||
|
||||
case ESM::REC_INGR:
|
||||
|
@ -639,7 +670,7 @@ namespace MWWorld
|
|||
|
||||
case ESM::REC_LEVC:
|
||||
|
||||
readReferenceCollection<ESM::ObjectState> (reader, mCreatureLists, contentFileMap);
|
||||
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, contentFileMap);
|
||||
break;
|
||||
|
||||
case ESM::REC_LEVI:
|
||||
|
|
|
@ -91,6 +91,9 @@ namespace MWWorld
|
|||
Ptr searchViaHandle (const std::string& handle);
|
||||
///< Will return an empty Ptr if cell is not loaded.
|
||||
|
||||
Ptr searchViaActorId (int id);
|
||||
///< Will return an empty Ptr if cell is not loaded.
|
||||
|
||||
float getWaterLevel() const;
|
||||
|
||||
void setWaterLevel (float level);
|
||||
|
|
|
@ -406,4 +406,14 @@ namespace MWWorld
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int Class::getDoorState (const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
throw std::runtime_error("this is not a door");
|
||||
}
|
||||
|
||||
void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const
|
||||
{
|
||||
throw std::runtime_error("this is not a door");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,11 @@ namespace MWWorld
|
|||
virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const;
|
||||
|
||||
/// 0 = nothing, 1 = opening, 2 = closing
|
||||
virtual int getDoorState (const MWWorld::Ptr &ptr) const;
|
||||
/// This does not actually cause the door to move. Use World::activateDoor instead.
|
||||
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ void ESMStore::setUp()
|
|||
|
||||
if (!mRaces.find (player->mRace) ||
|
||||
!mClasses.find (player->mClass))
|
||||
throw std::runtime_error ("Invalid player record (race or class unavilable");
|
||||
throw std::runtime_error ("Invalid player record (race or class unavailable");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -599,7 +599,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
|
|||
const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i];
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom;
|
||||
magnitude *= params.mMultiplier;
|
||||
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude);
|
||||
visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace MWWorld
|
|||
float mMultiplier;
|
||||
};
|
||||
|
||||
// TODO: store in savegame
|
||||
typedef std::map<std::string, std::vector<EffectParams> > TEffectMagnitudes;
|
||||
TEffectMagnitudes mPermanentMagicEffectMagnitudes;
|
||||
|
||||
|
|
|
@ -635,12 +635,12 @@ namespace MWWorld
|
|||
bool cmode = act->getCollisionMode();
|
||||
if(cmode)
|
||||
{
|
||||
act->enableCollisions(false);
|
||||
act->enableCollisionMode(false);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
act->enableCollisions(true);
|
||||
act->enableCollisionMode(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,17 @@ namespace
|
|||
namespace MWWorld
|
||||
{
|
||||
|
||||
void Scene::update (float duration, bool paused){
|
||||
void Scene::update (float duration, bool paused)
|
||||
{
|
||||
if (mNeedMapUpdate)
|
||||
{
|
||||
// Note: exterior cell maps must be updated, even if they were visited before, because the set of surrounding cells might be different
|
||||
// (and objects in a different cell can "bleed" into another cells map if they cross the border)
|
||||
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
|
||||
mRendering.requestMap(*active);
|
||||
mNeedMapUpdate = false;
|
||||
}
|
||||
|
||||
mRendering.update (duration, paused);
|
||||
}
|
||||
|
||||
|
@ -197,8 +207,9 @@ namespace MWWorld
|
|||
|
||||
mRendering.updateTerrain();
|
||||
|
||||
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
|
||||
mRendering.requestMap(*active);
|
||||
// Delay the map update until scripts have been given a chance to run.
|
||||
// If we don't do this, objects that should be disabled will still appear on the map.
|
||||
mNeedMapUpdate = true;
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
||||
}
|
||||
|
@ -342,7 +353,7 @@ namespace MWWorld
|
|||
|
||||
//We need the ogre renderer and a scene node.
|
||||
Scene::Scene (MWRender::RenderingManager& rendering, PhysicsSystem *physics)
|
||||
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering)
|
||||
: mCurrentCell (0), mCellChanged (false), mPhysics(physics), mRendering(rendering), mNeedMapUpdate(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -496,4 +507,24 @@ namespace MWWorld
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ptr Scene::searchPtrViaHandle (const std::string& handle)
|
||||
{
|
||||
for (CellStoreCollection::const_iterator iter (mActiveCells.begin());
|
||||
iter!=mActiveCells.end(); ++iter)
|
||||
if (Ptr ptr = (*iter)->searchViaHandle (handle))
|
||||
return ptr;
|
||||
|
||||
return Ptr();
|
||||
}
|
||||
|
||||
Ptr Scene::searchPtrViaActorId (int actorId)
|
||||
{
|
||||
for (CellStoreCollection::const_iterator iter (mActiveCells.begin());
|
||||
iter!=mActiveCells.end(); ++iter)
|
||||
if (Ptr ptr = (*iter)->searchViaActorId (actorId))
|
||||
return ptr;
|
||||
|
||||
return Ptr();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ namespace MWWorld
|
|||
PhysicsSystem *mPhysics;
|
||||
MWRender::RenderingManager& mRendering;
|
||||
|
||||
bool mNeedMapUpdate;
|
||||
|
||||
void playerCellChange (CellStore *cell, const ESM::Position& position,
|
||||
bool adjustPlayerPos = true);
|
||||
|
||||
|
@ -102,6 +104,10 @@ namespace MWWorld
|
|||
///< Remove an object from the scene, but not from the world model.
|
||||
|
||||
bool isCellActive(const CellStore &cell);
|
||||
|
||||
Ptr searchPtrViaHandle (const std::string& handle);
|
||||
|
||||
Ptr searchPtrViaActorId (int actorId);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
#include "timestamp.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
TimeStamp::TimeStamp (float hour, int day)
|
||||
|
@ -105,4 +105,18 @@ namespace MWWorld
|
|||
|
||||
return hours + 24*days;
|
||||
}
|
||||
|
||||
ESM::TimeStamp TimeStamp::toEsm() const
|
||||
{
|
||||
ESM::TimeStamp ret;
|
||||
ret.mDay = mDay;
|
||||
ret.mHour = mHour;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TimeStamp::TimeStamp(const ESM::TimeStamp &esm)
|
||||
{
|
||||
mDay = esm.mDay;
|
||||
mHour = esm.mHour;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#ifndef GAME_MWWORLD_TIMESTAMP_H
|
||||
#define GAME_MWWORLD_TIMESTAMP_H
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class TimeStamp;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
/// \brief In-game time stamp
|
||||
|
@ -14,9 +19,12 @@ namespace MWWorld
|
|||
public:
|
||||
|
||||
explicit TimeStamp (float hour = 0, int day = 0);
|
||||
///< \oaram hour [0, 23)
|
||||
///< \param hour [0, 23)
|
||||
/// \param day >=0
|
||||
|
||||
explicit TimeStamp (const ESM::TimeStamp& esm);
|
||||
ESM::TimeStamp toEsm () const;
|
||||
|
||||
float getHour() const;
|
||||
|
||||
int getDay() const;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "../mwmechanics/spellcasting.hpp"
|
||||
#include "../mwmechanics/levelledlist.hpp"
|
||||
#include "../mwmechanics/combat.hpp"
|
||||
#include "../mwmechanics/aiavoiddoor.hpp" //Used to tell actors to avoid doors
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
#include "../mwrender/animation.hpp"
|
||||
|
@ -274,7 +275,8 @@ namespace MWWorld
|
|||
+mStore.countSavedGameRecords()
|
||||
+mGlobalVariables.countSavedGameRecords()
|
||||
+1 // player record
|
||||
+1; // weather record
|
||||
+1 // weather record
|
||||
+1; // actorId counter
|
||||
}
|
||||
|
||||
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
|
@ -287,6 +289,9 @@ namespace MWWorld
|
|||
mRendering->writeFog(cellstore);
|
||||
}
|
||||
|
||||
MWMechanics::CreatureStats::writeActorIdCounter(writer);
|
||||
progress.increaseProgress();
|
||||
|
||||
mCells.write (writer, progress);
|
||||
mStore.write (writer, progress);
|
||||
mGlobalVariables.write (writer, progress);
|
||||
|
@ -297,6 +302,12 @@ namespace MWWorld
|
|||
void World::readRecord (ESM::ESMReader& reader, int32_t type,
|
||||
const std::map<int, int>& contentFileMap)
|
||||
{
|
||||
if (type == ESM::REC_ACTC)
|
||||
{
|
||||
MWMechanics::CreatureStats::readActorIdCounter(reader);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mStore.readRecord (reader, type) &&
|
||||
!mGlobalVariables.readRecord (reader, type) &&
|
||||
!mPlayer->readRecord (reader, type) &&
|
||||
|
@ -546,17 +557,17 @@ namespace MWWorld
|
|||
{
|
||||
if (mPlayer->getPlayer().getRefData().getHandle()==handle)
|
||||
return mPlayer->getPlayer();
|
||||
for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin());
|
||||
iter!=mWorldScene->getActiveCells().end(); ++iter)
|
||||
{
|
||||
CellStore* cellstore = *iter;
|
||||
Ptr ptr = cellstore->searchViaHandle (handle);
|
||||
|
||||
if (!ptr.isEmpty())
|
||||
return ptr;
|
||||
}
|
||||
return mWorldScene->searchPtrViaHandle (handle);
|
||||
}
|
||||
|
||||
return MWWorld::Ptr();
|
||||
Ptr World::searchPtrViaActorId (int actorId)
|
||||
{
|
||||
// The player is not registered in any CellStore so must be checked manually
|
||||
if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId())
|
||||
return getPlayerPtr();
|
||||
// Now search cells
|
||||
return mWorldScene->searchPtrViaActorId (actorId);
|
||||
}
|
||||
|
||||
void World::addContainerScripts(const Ptr& reference, CellStore * cell)
|
||||
|
@ -1195,29 +1206,48 @@ namespace MWWorld
|
|||
while (it != mDoorStates.end())
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode())
|
||||
{
|
||||
// The door is no longer in an active cell, or it was disabled.
|
||||
// Erase from mDoorStates, since we no longer need to move it.
|
||||
// Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates.
|
||||
mDoorStates.erase(it++);
|
||||
}
|
||||
else
|
||||
{
|
||||
float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
float diff = duration * 90;
|
||||
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f);
|
||||
float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second == 1 ? 1 : -1)), 90.f);
|
||||
localRotateObject(it->first, 0, 0, targetRot);
|
||||
|
||||
bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f;
|
||||
|
||||
/// \todo should use convexSweepTest here
|
||||
std::vector<std::string> collisions = mPhysics->getCollisions(it->first);
|
||||
for (std::vector<std::string>::iterator cit = collisions.begin(); cit != collisions.end(); ++cit)
|
||||
{
|
||||
MWWorld::Ptr ptr = getPtrViaHandle(*cit);
|
||||
if (MWWorld::Class::get(ptr).isActor())
|
||||
if (ptr.getClass().isActor())
|
||||
{
|
||||
// we collided with an actor, we need to undo the rotation
|
||||
// Collided with actor, ask actor to try to avoid door
|
||||
if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) {
|
||||
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
|
||||
if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once
|
||||
seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr);
|
||||
}
|
||||
|
||||
// we need to undo the rotation
|
||||
localRotateObject(it->first, 0, 0, oldRot);
|
||||
break;
|
||||
reached = false;
|
||||
//break; //Removed in case multiple actors are touching
|
||||
}
|
||||
}
|
||||
|
||||
if ((targetRot == 90.f && it->second) || targetRot == 0.f)
|
||||
if (reached)
|
||||
{
|
||||
// Mark as non-moving
|
||||
it->first.getClass().setDoorState(it->first, 0);
|
||||
mDoorStates.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
@ -1491,6 +1521,9 @@ namespace MWWorld
|
|||
{
|
||||
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||
|
||||
if (!ref.mData.isEnabled())
|
||||
continue;
|
||||
|
||||
if (ref.mRef.mTeleport)
|
||||
{
|
||||
World::DoorMarker newMarker;
|
||||
|
@ -1832,25 +1865,32 @@ namespace MWWorld
|
|||
|
||||
void World::activateDoor(const MWWorld::Ptr& door)
|
||||
{
|
||||
if (mDoorStates.find(door) != mDoorStates.end())
|
||||
{
|
||||
// if currently opening, then close, if closing, then open
|
||||
mDoorStates[door] = !mDoorStates[door];
|
||||
}
|
||||
else
|
||||
int state = door.getClass().getDoorState(door);
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
if (door.getRefData().getLocalRotation().rot[2] == 0)
|
||||
mDoorStates[door] = 1; // open
|
||||
state = 1; // if closed, then open
|
||||
else
|
||||
mDoorStates[door] = 0; // close
|
||||
state = 2; // if open, then close
|
||||
break;
|
||||
case 2:
|
||||
state = 1; // if closing, then open
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
state = 2; // if opening, then close
|
||||
break;
|
||||
}
|
||||
door.getClass().setDoorState(door, state);
|
||||
mDoorStates[door] = state;
|
||||
}
|
||||
|
||||
bool World::getOpenOrCloseDoor(const Ptr &door)
|
||||
void World::activateDoor(const Ptr &door, bool open)
|
||||
{
|
||||
if (mDoorStates.find(door) != mDoorStates.end())
|
||||
return !mDoorStates[door]; // if currently opening or closing, then do the opposite
|
||||
return door.getRefData().getLocalRotation().rot[2] == 0;
|
||||
int state = open ? 1 : 2;
|
||||
door.getClass().setDoorState(door, state);
|
||||
mDoorStates[door] = state;
|
||||
}
|
||||
|
||||
bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
|
||||
|
@ -1954,7 +1994,7 @@ namespace MWWorld
|
|||
{
|
||||
OEngine::Physic::PhysicActor *physicActor = mPhysEngine->getCharacter(actor.getRefData().getHandle());
|
||||
|
||||
physicActor->enableCollisions(enable);
|
||||
physicActor->enableCollisionBody(enable);
|
||||
}
|
||||
|
||||
bool World::findInteriorPosition(const std::string &name, ESM::Position &pos)
|
||||
|
@ -2161,8 +2201,8 @@ namespace MWWorld
|
|||
// If this is a power, check if it was already used in the last 24h
|
||||
if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
|
||||
{
|
||||
if (stats.canUsePower(spell->mId))
|
||||
stats.usePower(spell->mId);
|
||||
if (stats.getSpells().canUsePower(spell->mId))
|
||||
stats.getSpells().usePower(spell->mId);
|
||||
else
|
||||
{
|
||||
message = "#{sPowerAlreadyUsed}";
|
||||
|
@ -2214,7 +2254,7 @@ namespace MWWorld
|
|||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
|
||||
{
|
||||
ProjectileState state;
|
||||
state.mActorHandle = actor.getRefData().getHandle();
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mBow = bow;
|
||||
state.mVelocity = orient.yAxis() * speed;
|
||||
|
||||
|
@ -2301,7 +2341,7 @@ namespace MWWorld
|
|||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = id;
|
||||
state.mActorHandle = actor.getRefData().getHandle();
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mSpeed = speed;
|
||||
state.mStack = stack;
|
||||
|
||||
|
@ -2365,7 +2405,7 @@ namespace MWWorld
|
|||
if (obstacle == ptr)
|
||||
continue;
|
||||
|
||||
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
|
||||
// Arrow intersects with player immediately after shooting :/
|
||||
if (obstacle == caster)
|
||||
|
@ -2451,7 +2491,7 @@ namespace MWWorld
|
|||
if (obstacle == ptr)
|
||||
continue;
|
||||
|
||||
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
|
@ -2474,7 +2514,7 @@ namespace MWWorld
|
|||
|
||||
if (explode)
|
||||
{
|
||||
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName);
|
||||
|
||||
deleteObject(ptr);
|
||||
|
|
|
@ -88,7 +88,7 @@ namespace MWWorld
|
|||
float mFacedDistance;
|
||||
|
||||
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||
///< only holds doors that are currently moving. 0 means closing, 1 opening
|
||||
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
||||
|
||||
struct MagicBoltState
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ namespace MWWorld
|
|||
std::string mId;
|
||||
|
||||
// Actor who casted this projectile
|
||||
std::string mActorHandle;
|
||||
int mActorId;
|
||||
|
||||
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
|
||||
std::string mSourceName;
|
||||
|
@ -111,7 +111,7 @@ namespace MWWorld
|
|||
struct ProjectileState
|
||||
{
|
||||
// Actor who shot this projectile
|
||||
std::string mActorHandle;
|
||||
int mActorId;
|
||||
|
||||
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
|
||||
|
||||
|
@ -287,6 +287,9 @@ namespace MWWorld
|
|||
virtual Ptr searchPtrViaHandle (const std::string& handle);
|
||||
///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found
|
||||
|
||||
virtual Ptr searchPtrViaActorId (int actorId);
|
||||
///< Search is limited to the active cells.
|
||||
|
||||
virtual void adjustPosition (const Ptr& ptr);
|
||||
///< Adjust position after load to be on ground. Must be called after model load.
|
||||
|
||||
|
@ -493,10 +496,11 @@ namespace MWWorld
|
|||
virtual void setupPlayer();
|
||||
virtual void renderPlayer();
|
||||
|
||||
virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door);
|
||||
///< if activated, should this door be opened or closed?
|
||||
/// open or close a non-teleport door (depending on current state)
|
||||
virtual void activateDoor(const MWWorld::Ptr& door);
|
||||
///< activate (open or close) an non-teleport door
|
||||
|
||||
/// open or close a non-teleport door as specified
|
||||
virtual void activateDoor(const MWWorld::Ptr& door, bool open);
|
||||
|
||||
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object
|
||||
virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object
|
||||
|
|
|
@ -45,7 +45,7 @@ add_component_dir (esm
|
|||
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
|
||||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
||||
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
|
||||
npcstats creaturestats weatherstate quickkeys fogstate
|
||||
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate
|
||||
)
|
||||
|
||||
add_component_dir (misc
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue