forked from teamnwah/openmw-tes3coop
Merge branch 'master' of https://github.com/zinnschlag/openmw into Editor-Dialog
This commit is contained in:
commit
f3475f7ef5
74 changed files with 1263 additions and 676 deletions
|
@ -73,6 +73,8 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie
|
|||
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
|
||||
option(SDL2_STATIC "Link static build of SDL into the binaries" FALSE)
|
||||
|
||||
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_BSATOOL "build BSA extractor" OFF)
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
|
|
93
OFL.txt
93
OFL.txt
|
@ -1,93 +0,0 @@
|
|||
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@ -15,7 +15,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst effectmanager
|
||||
terrainstorage renderconst effectmanager weaponanimation
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -44,7 +44,7 @@ add_openmw_dir (mwscript
|
|||
locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions
|
||||
guiextensions soundextensions skyextensions statsextensions containerextensions
|
||||
aiextensions controlextensions extensions globalscripts ref dialogueextensions
|
||||
animationextensions transformationextensions consoleextensions userextensions locals
|
||||
animationextensions transformationextensions consoleextensions userextensions
|
||||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
|
|
|
@ -404,7 +404,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
// Create the world
|
||||
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
|
||||
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
|
||||
mActivationDistanceOverride));
|
||||
mActivationDistanceOverride, mCellName));
|
||||
MWBase::Environment::get().getWorld()->setupPlayer();
|
||||
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
|
||||
|
||||
|
@ -440,31 +440,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
mechanics->buildPlayer();
|
||||
window->updatePlayer();
|
||||
|
||||
// load cell
|
||||
ESM::Position pos;
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
if (!mCellName.empty())
|
||||
{
|
||||
if (world->findExteriorPosition(mCellName, pos)) {
|
||||
world->changeToExteriorCell (pos);
|
||||
}
|
||||
else {
|
||||
world->findInteriorPosition(mCellName, pos);
|
||||
world->changeToInteriorCell (mCellName, pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
|
||||
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
|
||||
world->changeToExteriorCell (pos);
|
||||
}
|
||||
|
||||
Ogre::FrameEvent event;
|
||||
event.timeSinceLastEvent = 0;
|
||||
event.timeSinceLastFrame = 0;
|
||||
frameRenderingQueued(event);
|
||||
mOgre->getRoot()->addFrameListener (this);
|
||||
|
||||
// scripts
|
||||
|
@ -502,15 +477,15 @@ void OMW::Engine::go()
|
|||
// Play some good 'ol tunes
|
||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
|
||||
|
||||
if (!mStartupScript.empty())
|
||||
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
|
||||
|
||||
// start in main menu
|
||||
if (!mSkipMenu)
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||
else
|
||||
MWBase::Environment::get().getStateManager()->newGame (true);
|
||||
|
||||
if (!mStartupScript.empty())
|
||||
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
|
||||
|
||||
// Start the main rendering loop
|
||||
while (!mEnvironment.get().getStateManager()->hasQuitRequest())
|
||||
Ogre::Root::getSingleton().renderOneFrame();
|
||||
|
|
|
@ -20,6 +20,9 @@ namespace MWBase
|
|||
|
||||
InputManager() {}
|
||||
|
||||
/// Clear all savegame-specific data
|
||||
virtual void clear() = 0;
|
||||
|
||||
virtual ~InputManager() {}
|
||||
|
||||
virtual void update(float dt, bool loading) = 0;
|
||||
|
|
|
@ -101,7 +101,8 @@ namespace MWBase
|
|||
|
||||
virtual ~World() {}
|
||||
|
||||
virtual void startNewGame() = 0;
|
||||
virtual void startNewGame (bool bypass) = 0;
|
||||
///< \param bypass Bypass regular game start.
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
|
@ -274,7 +275,7 @@ namespace MWBase
|
|||
virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
|
||||
|
||||
virtual void
|
||||
moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore &newCell, float x, float y, float z) = 0;
|
||||
moveObject(const MWWorld::Ptr &ptr, MWWorld::CellStore* newCell, float x, float y, float z) = 0;
|
||||
|
||||
virtual void scaleObject (const MWWorld::Ptr& ptr, float scale) = 0;
|
||||
|
||||
|
@ -282,7 +283,7 @@ namespace MWBase
|
|||
|
||||
virtual void localRotateObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
|
||||
|
||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos) = 0;
|
||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
|
||||
///< place an object in a "safe" location (ie not in the void, etc).
|
||||
|
||||
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
|
||||
|
@ -464,8 +465,10 @@ namespace MWBase
|
|||
|
||||
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
|
||||
|
||||
virtual const std::vector<std::string>& getContentFiles() const = 0;
|
||||
|
||||
|
|
|
@ -28,16 +28,16 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct CustomData : public MWWorld::CustomData
|
||||
struct ContainerCustomData : public MWWorld::CustomData
|
||||
{
|
||||
MWWorld::ContainerStore mContainerStore;
|
||||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
};
|
||||
|
||||
MWWorld::CustomData *CustomData::clone() const
|
||||
MWWorld::CustomData *ContainerCustomData::clone() const
|
||||
{
|
||||
return new CustomData (*this);
|
||||
return new ContainerCustomData (*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CustomData> data (new CustomData);
|
||||
std::auto_ptr<ContainerCustomData> data (new ContainerCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Container> *ref =
|
||||
ptr.get<ESM::Container>();
|
||||
|
@ -174,7 +174,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
|
||||
return dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
|
||||
}
|
||||
|
||||
std::string Container::getScript (const MWWorld::Ptr& ptr) const
|
||||
|
@ -267,7 +267,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||
readState (state2.mInventory);
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
|
||||
writeState (state2.mInventory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/magiceffects.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
#include "../mwmechanics/disease.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -36,7 +37,7 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct CustomData : public MWWorld::CustomData
|
||||
struct CreatureCustomData : public MWWorld::CustomData
|
||||
{
|
||||
MWMechanics::CreatureStats mCreatureStats;
|
||||
MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
|
||||
|
@ -44,13 +45,13 @@ namespace
|
|||
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
|
||||
CustomData() : mContainerStore(0) {}
|
||||
virtual ~CustomData() { delete mContainerStore; }
|
||||
CreatureCustomData() : mContainerStore(0) {}
|
||||
virtual ~CreatureCustomData() { delete mContainerStore; }
|
||||
};
|
||||
|
||||
MWWorld::CustomData *CustomData::clone() const
|
||||
MWWorld::CustomData *CreatureCustomData::clone() const
|
||||
{
|
||||
CustomData* cloned = new CustomData (*this);
|
||||
CreatureCustomData* cloned = new CreatureCustomData (*this);
|
||||
cloned->mContainerStore = mContainerStore->clone();
|
||||
return cloned;
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CustomData> data (new CustomData);
|
||||
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
|
||||
static bool inited = false;
|
||||
if(!inited)
|
||||
|
@ -191,7 +192,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
|
||||
return dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
|
||||
}
|
||||
|
||||
|
||||
|
@ -243,15 +244,7 @@ namespace MWClass
|
|||
|
||||
Ogre::Vector3 hitPosition = result.second;
|
||||
|
||||
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
|
||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||
float hitchance = ref->mBase->mData.mCombat +
|
||||
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
|
||||
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
|
||||
hitchance *= stats.getFatigueTerm();
|
||||
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
|
||||
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
|
||||
hitchance -= otherstats.getEvasion();
|
||||
float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat);
|
||||
|
||||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||
{
|
||||
|
@ -334,6 +327,8 @@ namespace MWClass
|
|||
if (damage > 0)
|
||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
||||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
|
||||
}
|
||||
|
||||
|
@ -461,7 +456,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return *dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
|
||||
return *dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
|
||||
}
|
||||
|
||||
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const
|
||||
|
@ -530,7 +525,7 @@ namespace MWClass
|
|||
float moveSpeed;
|
||||
if(normalizedEncumbrance >= 1.0f)
|
||||
moveSpeed = 0.0f;
|
||||
else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
|
||||
else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
|
||||
world->isLevitationEnabled()))
|
||||
{
|
||||
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
|
||||
|
@ -564,7 +559,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement;
|
||||
return dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()).mMovement;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Creature::getMovementVector (const MWWorld::Ptr& ptr) const
|
||||
|
@ -683,7 +678,15 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.get<ESM::Creature>().insert(*ref), &cell);
|
||||
}
|
||||
|
||||
bool Creature::isFlying(const MWWorld::Ptr &ptr) const
|
||||
bool Creature::isBipedal(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
||||
return ref->mBase->mFlags & ESM::Creature::Bipedal;
|
||||
}
|
||||
|
||||
bool Creature::canFly(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
@ -691,6 +694,22 @@ namespace MWClass
|
|||
return ref->mBase->mFlags & ESM::Creature::Flies;
|
||||
}
|
||||
|
||||
bool Creature::canSwim(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
||||
return ref->mBase->mFlags & ESM::Creature::Swims;
|
||||
}
|
||||
|
||||
bool Creature::canWalk(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
||||
return ref->mBase->mFlags & ESM::Creature::Walks;
|
||||
}
|
||||
|
||||
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
|
||||
{
|
||||
if(name == "left")
|
||||
|
@ -767,7 +786,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
|
||||
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
customData.mContainerStore->readState (state2.mInventory);
|
||||
customData.mCreatureStats.readState (state2.mCreatureStats);
|
||||
|
@ -781,7 +800,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
|
||||
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
customData.mContainerStore->writeState (state2.mInventory);
|
||||
customData.mCreatureStats.writeState (state2.mCreatureStats);
|
||||
|
|
|
@ -124,7 +124,10 @@ namespace MWClass
|
|||
return true;
|
||||
}
|
||||
|
||||
virtual bool isFlying (const MWWorld::Ptr &ptr) const;
|
||||
virtual bool isBipedal (const MWWorld::Ptr &ptr) const;
|
||||
virtual bool canFly (const MWWorld::Ptr &ptr) const;
|
||||
virtual bool canSwim (const MWWorld::Ptr &ptr) const;
|
||||
virtual bool canWalk (const MWWorld::Ptr &ptr) const;
|
||||
|
||||
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
|
||||
|
||||
|
|
|
@ -9,15 +9,15 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct CustomData : public MWWorld::CustomData
|
||||
struct CreatureLevListCustomData : public MWWorld::CustomData
|
||||
{
|
||||
// TODO: save the creature we spawned here
|
||||
virtual MWWorld::CustomData *clone() const;
|
||||
};
|
||||
|
||||
MWWorld::CustomData *CustomData::clone() const
|
||||
MWWorld::CustomData *CreatureLevListCustomData::clone() const
|
||||
{
|
||||
return new CustomData (*this);
|
||||
return new CreatureLevListCustomData (*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CustomData> data (new CustomData);
|
||||
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
|
||||
ptr.get<ESM::CreatureLevList>();
|
||||
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||
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);
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
|
||||
}
|
||||
|
||||
ptr.getRefData().setCustomData(data.release());
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct CustomData : public MWWorld::CustomData
|
||||
struct LightCustomData : public MWWorld::CustomData
|
||||
{
|
||||
float mTime;
|
||||
///< Time remaining
|
||||
|
||||
CustomData(MWWorld::Ptr ptr)
|
||||
LightCustomData(MWWorld::Ptr ptr)
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
|
||||
mTime = ref->mBase->mData.mTime;
|
||||
|
@ -40,7 +40,7 @@ namespace
|
|||
|
||||
virtual MWWorld::CustomData *clone() const
|
||||
{
|
||||
return new CustomData (*this);
|
||||
return new LightCustomData (*this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData(ptr);
|
||||
|
||||
float &timeRemaining = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
float &timeRemaining = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
timeRemaining = duration;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData(ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
return dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
}
|
||||
|
||||
MWWorld::Ptr
|
||||
|
@ -233,7 +233,7 @@ namespace MWClass
|
|||
void Light::ensureCustomData (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
ptr.getRefData().setCustomData(new CustomData(ptr));
|
||||
ptr.getRefData().setCustomData(new LightCustomData(ptr));
|
||||
}
|
||||
|
||||
bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const
|
||||
|
@ -278,7 +278,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
|
||||
dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
|
||||
}
|
||||
|
||||
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
|
||||
|
@ -288,6 +288,6 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
state2.mTime = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
struct CustomData : public MWWorld::CustomData
|
||||
struct NpcCustomData : public MWWorld::CustomData
|
||||
{
|
||||
MWMechanics::NpcStats mNpcStats;
|
||||
MWMechanics::Movement mMovement;
|
||||
|
@ -48,9 +48,9 @@ namespace
|
|||
virtual MWWorld::CustomData *clone() const;
|
||||
};
|
||||
|
||||
MWWorld::CustomData *CustomData::clone() const
|
||||
MWWorld::CustomData *NpcCustomData::clone() const
|
||||
{
|
||||
return new CustomData (*this);
|
||||
return new NpcCustomData (*this);
|
||||
}
|
||||
|
||||
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
|
||||
|
@ -262,7 +262,7 @@ namespace MWClass
|
|||
}
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::auto_ptr<CustomData> data(new CustomData);
|
||||
std::auto_ptr<NpcCustomData> data(new NpcCustomData);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
|
||||
|
@ -436,14 +436,14 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
|
||||
return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
|
||||
}
|
||||
|
||||
MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
|
||||
return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
|
||||
}
|
||||
|
||||
|
||||
|
@ -498,15 +498,7 @@ namespace MWClass
|
|||
if(!weapon.isEmpty())
|
||||
weapskill = get(weapon).getEquipmentSkill(weapon);
|
||||
|
||||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||
float hitchance = stats.getSkill(weapskill).getModified() +
|
||||
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
|
||||
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
|
||||
hitchance *= stats.getFatigueTerm();
|
||||
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
|
||||
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
|
||||
hitchance -= otherstats.getEvasion();
|
||||
float hitchance = MWMechanics::getHitChance(ptr, victim, ptr.getClass().getSkill(ptr, weapskill));
|
||||
|
||||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||
{
|
||||
|
@ -516,6 +508,7 @@ namespace MWClass
|
|||
|
||||
bool healthdmg;
|
||||
float damage = 0.0f;
|
||||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||
if(!weapon.isEmpty())
|
||||
{
|
||||
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
|
||||
|
@ -615,6 +608,8 @@ namespace MWClass
|
|||
if (healthdmg && damage > 0)
|
||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
||||
|
||||
MWMechanics::diseaseContact(victim, ptr);
|
||||
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
|
||||
}
|
||||
|
||||
|
@ -648,9 +643,6 @@ namespace MWClass
|
|||
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
|
||||
}
|
||||
|
||||
if (!attacker.isEmpty())
|
||||
MWMechanics::diseaseContact(ptr, attacker);
|
||||
|
||||
if (damage > 0.0f && !object.isEmpty())
|
||||
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
|
||||
|
||||
|
@ -681,12 +673,7 @@ namespace MWClass
|
|||
else
|
||||
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
|
||||
|
||||
if(object.isEmpty())
|
||||
{
|
||||
if(ishealth)
|
||||
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
|
||||
}
|
||||
else if(ishealth)
|
||||
if(ishealth)
|
||||
{
|
||||
// Hit percentages:
|
||||
// cuirass = 30%
|
||||
|
@ -832,7 +819,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
|
||||
return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
|
||||
}
|
||||
|
||||
MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr)
|
||||
|
@ -840,7 +827,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
|
||||
return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore;
|
||||
}
|
||||
|
||||
std::string Npc::getScript (const MWWorld::Ptr& ptr) const
|
||||
|
@ -854,7 +841,7 @@ namespace MWClass
|
|||
float Npc::getSpeed(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
|
||||
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
|
||||
|
@ -909,7 +896,7 @@ namespace MWClass
|
|||
|
||||
float Npc::getJump(const MWWorld::Ptr &ptr) const
|
||||
{
|
||||
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
|
||||
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() +
|
||||
fJumpEncumbranceMultiplier->getFloat() *
|
||||
|
@ -948,7 +935,7 @@ namespace MWClass
|
|||
if (fallHeight >= fallDistanceMin)
|
||||
{
|
||||
const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
|
||||
const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData());
|
||||
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
|
||||
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
|
||||
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat();
|
||||
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat();
|
||||
|
@ -973,7 +960,7 @@ namespace MWClass
|
|||
{
|
||||
ensureCustomData (ptr);
|
||||
|
||||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mMovement;
|
||||
return dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()).mMovement;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const
|
||||
|
@ -1279,7 +1266,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
|
||||
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
customData.mInventoryStore.readState (state2.mInventory);
|
||||
customData.mNpcStats.readState (state2.mNpcStats);
|
||||
|
@ -1293,7 +1280,7 @@ namespace MWClass
|
|||
|
||||
ensureCustomData (ptr);
|
||||
|
||||
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
|
||||
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
|
||||
|
||||
customData.mInventoryStore.writeState (state2.mInventory);
|
||||
customData.mNpcStats.writeState (state2.mNpcStats);
|
||||
|
|
|
@ -26,16 +26,6 @@ namespace
|
|||
return path;
|
||||
}
|
||||
|
||||
std::string getCountString(const int count)
|
||||
{
|
||||
if (count == 1)
|
||||
return "";
|
||||
if (count > 9999)
|
||||
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
|
||||
else
|
||||
return boost::lexical_cast<std::string>(count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
|
@ -226,7 +216,7 @@ namespace MWGui
|
|||
text->setNeedMouseFocus(false);
|
||||
text->setTextShadow(true);
|
||||
text->setTextShadowColour(MyGUI::Colour(0,0,0));
|
||||
text->setCaption(getCountString(ingredient->getUserData<MWWorld::Ptr>()->getRefData().getCount()));
|
||||
text->setCaption(ItemView::getCountString(ingredient->getUserData<MWWorld::Ptr>()->getRefData().getCount()));
|
||||
}
|
||||
|
||||
mItemView->update();
|
||||
|
|
|
@ -23,19 +23,6 @@
|
|||
#include "sortfilteritemmodel.hpp"
|
||||
#include "pickpocketitemmodel.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string getCountString(const int count)
|
||||
{
|
||||
if (count == 1)
|
||||
return "";
|
||||
if (count > 9999)
|
||||
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
|
||||
else
|
||||
return boost::lexical_cast<std::string>(count);
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
@ -79,7 +66,7 @@ namespace MWGui
|
|||
text->setNeedMouseFocus(false);
|
||||
text->setTextShadow(true);
|
||||
text->setTextShadowColour(MyGUI::Colour(0,0,0));
|
||||
text->setCaption(getCountString(count));
|
||||
text->setCaption(ItemView::getCountString(count));
|
||||
|
||||
sourceView->update();
|
||||
|
||||
|
|
|
@ -23,18 +23,7 @@
|
|||
#include "travelwindow.hpp"
|
||||
#include "bookpage.hpp"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
|
||||
{
|
||||
typedef MWGui::BookTypesetter::Utf8Point point;
|
||||
|
||||
point begin = reinterpret_cast <point> (text);
|
||||
|
||||
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
|
||||
}
|
||||
}
|
||||
#include "journalbooks.hpp" // to_utf8_span
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
|
|
@ -604,15 +604,22 @@ namespace MWGui
|
|||
mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());
|
||||
}
|
||||
|
||||
void HUD::updateEnemyHealthBar()
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
|
||||
mEnemyHealth->setProgressRange(100);
|
||||
// Health is usually cast to int before displaying. Actors die whenever they are < 1 health.
|
||||
// Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)
|
||||
mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100);
|
||||
}
|
||||
|
||||
void HUD::update()
|
||||
{
|
||||
mSpellIcons->updateWidgets(mEffectBox, true);
|
||||
|
||||
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
|
||||
mEnemyHealth->setProgressRange(100);
|
||||
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
|
||||
updateEnemyHealthBar();
|
||||
}
|
||||
|
||||
if (mIsDrowning)
|
||||
|
@ -629,9 +636,7 @@ namespace MWGui
|
|||
if (!mEnemyHealth->getVisible())
|
||||
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
|
||||
mEnemyHealth->setVisible(true);
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
|
||||
mEnemyHealth->setProgressRange(100);
|
||||
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
|
||||
updateEnemyHealthBar();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef OPENMW_GAME_MWGUI_HUD_H
|
||||
#define OPENMW_GAME_MWGUI_HUD_H
|
||||
|
||||
#include "mapwindow.hpp"
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
|
@ -112,6 +115,10 @@ namespace MWGui
|
|||
void onMagicClicked(MyGUI::Widget* _sender);
|
||||
void onMapClicked(MyGUI::Widget* _sender);
|
||||
|
||||
void updateEnemyHealthBar();
|
||||
|
||||
void updatePositions();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -514,6 +514,9 @@ namespace MWGui
|
|||
|
||||
void InventoryWindow::pickUpObject (MWWorld::Ptr object)
|
||||
{
|
||||
// If the inventory is not yet enabled, don't pick anything up
|
||||
if (!MWBase::Environment::get().getWindowManager()->isAllowed(GW_Inventory))
|
||||
return;
|
||||
// make sure the object is of a type that can be picked up
|
||||
std::string type = object.getTypeName();
|
||||
if ( (type != typeid(ESM::Apparatus).name())
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef OPENMW_GAME_MWGUI_ITEMSELECTION_H
|
||||
#define OPENMW_GAME_MWGUI_ITEMSELECTION_H
|
||||
|
||||
#include "container.hpp"
|
||||
|
||||
namespace MWGui
|
||||
|
@ -32,3 +35,5 @@ namespace MWGui
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,23 +13,19 @@
|
|||
|
||||
#include "itemmodel.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string getCountString(const int count)
|
||||
{
|
||||
if (count == 1)
|
||||
return "";
|
||||
if (count > 9999)
|
||||
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
|
||||
else
|
||||
return boost::lexical_cast<std::string>(count);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
std::string ItemView::getCountString(int count)
|
||||
{
|
||||
if (count == 1)
|
||||
return "";
|
||||
if (count > 9999)
|
||||
return boost::lexical_cast<std::string>(int(count/1000.f)) + "k";
|
||||
else
|
||||
return boost::lexical_cast<std::string>(count);
|
||||
}
|
||||
|
||||
ItemView::ItemView()
|
||||
: mModel(NULL)
|
||||
, mScrollView(NULL)
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace MWGui
|
|||
|
||||
void update();
|
||||
|
||||
static std::string getCountString(int count);
|
||||
|
||||
private:
|
||||
virtual void initialiseOverride();
|
||||
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
|
||||
{
|
||||
typedef MWGui::BookTypesetter::Utf8Point point;
|
||||
|
||||
point begin = reinterpret_cast <point> (text);
|
||||
|
||||
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
|
||||
}
|
||||
|
||||
const MyGUI::Colour linkHot (0.40f, 0.40f, 0.80f);
|
||||
const MyGUI::Colour linkNormal (0.20f, 0.20f, 0.60f);
|
||||
const MyGUI::Colour linkActive (0.50f, 0.50f, 1.00f);
|
||||
|
@ -178,6 +169,15 @@ namespace
|
|||
namespace MWGui
|
||||
{
|
||||
|
||||
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text)
|
||||
{
|
||||
typedef MWGui::BookTypesetter::Utf8Point point;
|
||||
|
||||
point begin = reinterpret_cast <point> (text);
|
||||
|
||||
return MWGui::BookTypesetter::Utf8Span (begin, begin + strlen (text));
|
||||
}
|
||||
|
||||
typedef TypesetBook::Ptr book;
|
||||
|
||||
JournalBooks::JournalBooks (JournalViewModel::Ptr model) :
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
namespace MWGui
|
||||
{
|
||||
MWGui::BookTypesetter::Utf8Span to_utf8_span (char const * text);
|
||||
|
||||
struct JournalBooks
|
||||
{
|
||||
typedef TypesetBook::Ptr Book;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "mainmenu.hpp"
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
@ -20,6 +22,22 @@ namespace MWGui
|
|||
, mButtonBox(0), mWidth (w), mHeight (h)
|
||||
, mSaveGameDialog(NULL)
|
||||
{
|
||||
getWidget(mVersionText, "VersionText");
|
||||
std::stringstream sstream;
|
||||
sstream << "OpenMW version: " << OPENMW_VERSION;
|
||||
|
||||
// adding info about git hash if availible
|
||||
std::string rev = OPENMW_VERSION_COMMITHASH;
|
||||
std::string tag = OPENMW_VERSION_TAGHASH;
|
||||
if (!rev.empty() && !tag.empty())
|
||||
{
|
||||
rev = rev.substr(0,10);
|
||||
sstream << "\nrevision: " << rev;
|
||||
}
|
||||
|
||||
std::string output = sstream.str();
|
||||
mVersionText->setCaption(output);
|
||||
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef OPENMW_GAME_MWGUI_MAINMENU_H
|
||||
#define OPENMW_GAME_MWGUI_MAINMENU_H
|
||||
|
||||
#include <openengine/gui/layout.hpp>
|
||||
|
||||
#include "imagebutton.hpp"
|
||||
|
@ -24,6 +27,7 @@ namespace MWGui
|
|||
private:
|
||||
|
||||
MyGUI::Widget* mButtonBox;
|
||||
MyGUI::TextBox* mVersionText;
|
||||
|
||||
std::map<std::string, MWGui::ImageButton*> mButtons;
|
||||
|
||||
|
@ -35,3 +39,5 @@ namespace MWGui
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,28 +19,8 @@
|
|||
#include "windowmanagerimp.hpp"
|
||||
#include "itemselection.hpp"
|
||||
|
||||
#include "spellwindow.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
|
||||
{
|
||||
int cmp = left.getClass().getName(left).compare(
|
||||
right.getClass().getName(right));
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
bool sortSpells(const std::string& left, const std::string& right)
|
||||
{
|
||||
const MWWorld::Store<ESM::Spell> &spells =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
|
||||
|
||||
const ESM::Spell* a = spells.find(left);
|
||||
const ESM::Spell* b = spells.find(right);
|
||||
|
||||
int cmp = a->mName.compare(b->mName);
|
||||
return cmp < 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
|
|
@ -41,9 +41,13 @@ namespace MWGui
|
|||
getWidget(mPreviewImage, "PreviewImage");
|
||||
|
||||
getWidget(mHeadRotate, "HeadRotate");
|
||||
mHeadRotate->setScrollRange(50);
|
||||
mHeadRotate->setScrollPosition(25);
|
||||
mHeadRotate->setScrollViewPage(10);
|
||||
|
||||
// Mouse wheel step is hardcoded to 50 in MyGUI 3.2 ("FIXME").
|
||||
// Give other steps the same value to accomodate.
|
||||
mHeadRotate->setScrollRange(1000);
|
||||
mHeadRotate->setScrollPosition(500);
|
||||
mHeadRotate->setScrollViewPage(50);
|
||||
mHeadRotate->setScrollPage(50);
|
||||
mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
|
||||
|
||||
// Set up next/previous buttons
|
||||
|
@ -171,9 +175,9 @@ namespace MWGui
|
|||
eventBack();
|
||||
}
|
||||
|
||||
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position)
|
||||
void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position)
|
||||
{
|
||||
float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2;
|
||||
float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2;
|
||||
float diff = angle - mCurrentAngle;
|
||||
mPreview->update (diff);
|
||||
mPreviewDirty = true;
|
||||
|
|
|
@ -18,8 +18,16 @@
|
|||
#include "inventorywindow.hpp"
|
||||
#include "confirmationdialog.hpp"
|
||||
|
||||
namespace
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
|
||||
{
|
||||
int cmp = left.getClass().getName(left).compare(
|
||||
right.getClass().getName(right));
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
bool sortSpells(const std::string& left, const std::string& right)
|
||||
{
|
||||
const MWWorld::Store<ESM::Spell> &spells =
|
||||
|
@ -32,16 +40,6 @@ namespace
|
|||
return cmp < 0;
|
||||
}
|
||||
|
||||
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right)
|
||||
{
|
||||
int cmp = MWWorld::Class::get(left).getName(left).compare(
|
||||
MWWorld::Class::get(right).getName(right));
|
||||
return cmp < 0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
SpellWindow::SpellWindow(DragAndDrop* drag)
|
||||
: WindowPinnableBase("openmw_spell_window.layout")
|
||||
, NoDrop(drag, mMainWidget)
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
#define MWGUI_SPELLWINDOW_H
|
||||
|
||||
#include "windowpinnablebase.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class SpellIcons;
|
||||
|
||||
bool sortItems(const MWWorld::Ptr& left, const MWWorld::Ptr& right);
|
||||
|
||||
bool sortSpells(const std::string& left, const std::string& right);
|
||||
|
||||
class SpellWindow : public WindowPinnableBase, public NoDrop
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -143,6 +143,13 @@ namespace MWInput
|
|||
mControlSwitch["vanitymode"] = true;
|
||||
}
|
||||
|
||||
void InputManager::clear()
|
||||
{
|
||||
// Enable all controls
|
||||
for (std::map<std::string, bool>::iterator it = mControlSwitch.begin(); it != mControlSwitch.end(); ++it)
|
||||
it->second = true;
|
||||
}
|
||||
|
||||
InputManager::~InputManager()
|
||||
{
|
||||
mInputBinder->save (mUserFile);
|
||||
|
@ -578,13 +585,13 @@ namespace MWInput
|
|||
float rot[3];
|
||||
rot[0] = -y;
|
||||
rot[1] = 0.0f;
|
||||
rot[2] = x;
|
||||
rot[2] = -x;
|
||||
|
||||
// Only actually turn player when we're not in vanity mode
|
||||
if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot))
|
||||
{
|
||||
mPlayer->yaw(x);
|
||||
mPlayer->pitch(-y);
|
||||
mPlayer->pitch(y);
|
||||
}
|
||||
|
||||
if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change
|
||||
|
|
|
@ -65,6 +65,9 @@ namespace MWInput
|
|||
|
||||
virtual ~InputManager();
|
||||
|
||||
/// Clear all savegame-specific data
|
||||
virtual void clear();
|
||||
|
||||
virtual void update(float dt, bool loading);
|
||||
|
||||
void setPlayer (MWWorld::Player* player) { mPlayer = player; }
|
||||
|
|
|
@ -210,7 +210,7 @@ namespace MWMechanics
|
|||
&& LOS
|
||||
)
|
||||
{
|
||||
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()));
|
||||
creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()));
|
||||
creatureStats.setHostile(true);
|
||||
}
|
||||
}
|
||||
|
@ -541,7 +541,7 @@ namespace MWMechanics
|
|||
// TODO: Add AI to follow player and fight for him
|
||||
// TODO: VFX_SummonStart, VFX_SummonEnd
|
||||
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first,
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos).getRefData().getHandle()));
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -10,16 +10,6 @@
|
|||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
float sgn(float a)
|
||||
{
|
||||
if(a > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
MWMechanics::AiActivate::AiActivate(const std::string &objectId)
|
||||
: mObjectId(objectId)
|
||||
{
|
||||
|
@ -38,7 +28,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
|
|||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = sgn(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))
|
||||
|
@ -49,7 +39,7 @@ bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
|
|||
}
|
||||
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = sgn(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))
|
||||
|
|
|
@ -11,16 +11,6 @@
|
|||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
float sgn(float a)
|
||||
{
|
||||
if(a > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Test vanilla behavior on passing x0, y0, and z0 with duration of anything including 0.
|
||||
TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z.
|
||||
|
@ -91,7 +81,7 @@ namespace MWMechanics
|
|||
|
||||
if(actor.getCell()->getCell()->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = sgn(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))
|
||||
|
@ -102,7 +92,7 @@ namespace MWMechanics
|
|||
}
|
||||
if(actor.getCell()->getCell()->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = sgn(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))
|
||||
|
|
|
@ -9,16 +9,6 @@
|
|||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
float sgn(float a)
|
||||
{
|
||||
if(a > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
AiTravel::AiTravel(float x, float y, float z)
|
||||
|
@ -43,7 +33,7 @@ namespace MWMechanics
|
|||
MWWorld::Ptr player = world->getPlayerPtr();
|
||||
if(cell->mData.mX != player.getCell()->getCell()->mData.mX)
|
||||
{
|
||||
int sideX = sgn(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))
|
||||
|
@ -54,7 +44,7 @@ namespace MWMechanics
|
|||
}
|
||||
if(cell->mData.mY != player.getCell()->getCell()->mData.mY)
|
||||
{
|
||||
int sideY = sgn(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))
|
||||
|
|
|
@ -15,18 +15,13 @@
|
|||
#include "steering.hpp"
|
||||
#include "movement.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
float sgn(float a)
|
||||
{
|
||||
if(a > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
// NOTE: determined empirically but probably need further tweaking
|
||||
static const int COUNT_BEFORE_STUCK = 20;
|
||||
static const int COUNT_BEFORE_RESET = 200;
|
||||
static const int COUNT_EVADE = 7;
|
||||
|
||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<int>& idle, bool repeat):
|
||||
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
|
||||
, mCellX(std::numeric_limits<int>::max())
|
||||
|
@ -36,6 +31,11 @@ namespace MWMechanics
|
|||
, mX(0)
|
||||
, mY(0)
|
||||
, mZ(0)
|
||||
, mPrevX(0)
|
||||
, mPrevY(0)
|
||||
, mWalkState(State_Norm)
|
||||
, mStuckCount(0)
|
||||
, mEvadeCount(0)
|
||||
, mSaidGreeting(false)
|
||||
{
|
||||
for(unsigned short counter = 0; counter < mIdle.size(); counter++)
|
||||
|
@ -298,9 +298,91 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
{
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
/* 1 n
|
||||
* State_Norm <---> State_CheckStuck --> State_Evade
|
||||
* ^ ^ | ^ | ^ | |
|
||||
* | | | | | | | |
|
||||
* | +---+ +---+ +---+ | m
|
||||
* | any < n < m |
|
||||
* +--------------------------------------------+
|
||||
*/
|
||||
bool samePosition = (abs(pos.pos[0] - mPrevX) < 1) && (abs(pos.pos[1] - mPrevY) < 1);
|
||||
switch(mWalkState)
|
||||
{
|
||||
case State_Norm:
|
||||
{
|
||||
if(!samePosition)
|
||||
break;
|
||||
else
|
||||
mWalkState = State_CheckStuck;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case State_CheckStuck:
|
||||
{
|
||||
if(!samePosition)
|
||||
{
|
||||
mWalkState = State_Norm;
|
||||
// to do this properly need yet another variable, simply don't clear for now
|
||||
//mStuckCount = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// consider stuck only if position unchanges consecutively
|
||||
if((mStuckCount++ % COUNT_BEFORE_STUCK) == 0)
|
||||
mWalkState = State_Evade;
|
||||
// NOTE: mStuckCount is purposely not cleared here
|
||||
else
|
||||
break; // still in the same state, but counter got incremented
|
||||
}
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case State_Evade:
|
||||
{
|
||||
if(mEvadeCount++ < COUNT_EVADE)
|
||||
break;
|
||||
else
|
||||
{
|
||||
mWalkState = State_Norm; // tried to evade, assume all is ok and start again
|
||||
// NOTE: mStuckCount is purposely not cleared here
|
||||
mEvadeCount = 0;
|
||||
}
|
||||
}
|
||||
/* NO DEFAULT CASE */
|
||||
}
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
if(mWalkState == State_Evade)
|
||||
{
|
||||
//std::cout << "Stuck \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
|
||||
|
||||
// diagonal should have same animation as walk forward
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 1;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0.01f;
|
||||
// change the angle a bit, too
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1])));
|
||||
}
|
||||
else
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
|
||||
zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
|
||||
}
|
||||
|
||||
if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset
|
||||
{
|
||||
//std::cout << "Reset \""<<actor.getClass().getName(actor)<<"\"" << std::endl;
|
||||
mWalkState = State_Norm;
|
||||
mStuckCount = 0;
|
||||
|
||||
stopWalking(actor);
|
||||
mMoveNow = false;
|
||||
mWalking = false;
|
||||
mChooseAction = true;
|
||||
}
|
||||
|
||||
// update position
|
||||
ESM::Position updatedPos = actor.getRefData().getPosition();
|
||||
mPrevX = updatedPos.pos[0];
|
||||
mPrevY = updatedPos.pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,21 @@ namespace MWMechanics
|
|||
float mXCell;
|
||||
float mYCell;
|
||||
|
||||
// for checking if we're stuck (but don't check Z axis)
|
||||
float mPrevX;
|
||||
float mPrevY;
|
||||
|
||||
enum WalkState
|
||||
{
|
||||
State_Norm,
|
||||
State_CheckStuck,
|
||||
State_Evade
|
||||
};
|
||||
WalkState mWalkState;
|
||||
|
||||
int mStuckCount;
|
||||
int mEvadeCount;
|
||||
|
||||
bool mStoredAvailableNodes;
|
||||
bool mChooseAction;
|
||||
bool mIdleNow;
|
||||
|
|
|
@ -313,6 +313,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
|
||||
mAnimation->disable(mCurrentMovement);
|
||||
mCurrentMovement = movement;
|
||||
mMovementAnimVelocity = 0.0f;
|
||||
if(!mCurrentMovement.empty())
|
||||
{
|
||||
float vel, speedmult = 1.0f;
|
||||
|
@ -320,7 +321,10 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
bool isrunning = mPtr.getClass().getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run);
|
||||
|
||||
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f)
|
||||
{
|
||||
mMovementAnimVelocity = vel;
|
||||
speedmult = mMovementSpeed / vel;
|
||||
}
|
||||
else if (mMovementState == CharState_TurnLeft || mMovementState == CharState_TurnRight)
|
||||
speedmult = 1.f; // TODO: should get a speed mult depending on the current turning speed
|
||||
else if (mMovementSpeed > 0.0f)
|
||||
|
@ -330,10 +334,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
speedmult = mMovementSpeed / (isrunning ? 222.857f : 154.064f);
|
||||
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
|
||||
speedmult, ((mode!=2)?"start":"loop start"), "stop", 0.0f, ~0ul);
|
||||
|
||||
mMovementAnimVelocity = vel;
|
||||
}
|
||||
else mMovementAnimVelocity = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1194,7 +1195,10 @@ void CharacterController::update(float duration)
|
|||
: (sneak ? CharState_SneakBack
|
||||
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
|
||||
}
|
||||
else if(rot.z != 0.0f && !inwater && !sneak)
|
||||
// Don't play turning animations during attack. It would break positioning of the arrow bone when releasing a shot.
|
||||
// Actually, in vanilla the turning animation is not even played when merely having equipped the weapon,
|
||||
// but I don't think we need to go as far as that.
|
||||
else if(rot.z != 0.0f && !inwater && !sneak && mUpperBodyState < UpperCharState_StartToMinAttack)
|
||||
{
|
||||
if(rot.z > 0.0f)
|
||||
movestate = CharState_TurnRight;
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/movement.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
@ -17,14 +20,30 @@
|
|||
namespace
|
||||
{
|
||||
|
||||
Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 n)
|
||||
Ogre::Radian signedAngle(Ogre::Vector3 v1, Ogre::Vector3 v2, Ogre::Vector3 normal)
|
||||
{
|
||||
return Ogre::Math::ATan2(
|
||||
n.dotProduct( v1.crossProduct(v2) ),
|
||||
normal.dotProduct( v1.crossProduct(v2) ),
|
||||
v1.dotProduct(v2)
|
||||
);
|
||||
}
|
||||
|
||||
void applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const Ogre::Vector3& hitPosition)
|
||||
{
|
||||
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
|
||||
if (!enchantmentName.empty())
|
||||
{
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
||||
enchantmentName);
|
||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||
{
|
||||
MWMechanics::CastSpell cast(attacker, victim);
|
||||
cast.mHitPosition = hitPosition;
|
||||
cast.cast(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -135,4 +154,94 @@ namespace MWMechanics
|
|||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResistsWeapons}");
|
||||
}
|
||||
|
||||
void projectileHit(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, MWWorld::Ptr weapon, const MWWorld::Ptr &projectile,
|
||||
const Ogre::Vector3& hitPosition)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
|
||||
|
||||
const MWWorld::Class &othercls = victim.getClass();
|
||||
if(!othercls.isActor()) // Can't hit non-actors
|
||||
return;
|
||||
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
|
||||
if(otherstats.isDead()) // Can't hit dead actors
|
||||
return;
|
||||
|
||||
if(attacker.getRefData().getHandle() == "player")
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||
|
||||
int weapskill = ESM::Skill::Marksman;
|
||||
if(!weapon.isEmpty())
|
||||
weapskill = weapon.getClass().getEquipmentSkill(weapon);
|
||||
|
||||
float skillValue = attacker.getClass().getSkill(attacker,
|
||||
weapon.getClass().getEquipmentSkill(weapon));
|
||||
|
||||
if((::rand()/(RAND_MAX+1.0)) > getHitChance(attacker, victim, skillValue)/100.0f)
|
||||
{
|
||||
victim.getClass().onHit(victim, 0.0f, false, projectile, attacker, false);
|
||||
return;
|
||||
}
|
||||
|
||||
float damage = 0.0f;
|
||||
|
||||
float fDamageStrengthBase = gmst.find("fDamageStrengthBase")->getFloat();
|
||||
float fDamageStrengthMult = gmst.find("fDamageStrengthMult")->getFloat();
|
||||
|
||||
const unsigned char* attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
damage = attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength()); // Bow/crossbow damage
|
||||
if (weapon != projectile)
|
||||
{
|
||||
// Arrow/bolt damage
|
||||
attack = projectile.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
damage += attack[0] + ((attack[1]-attack[0])*attackerStats.getAttackStrength());
|
||||
}
|
||||
|
||||
damage *= fDamageStrengthBase +
|
||||
(attackerStats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult * 0.1);
|
||||
|
||||
if(attacker.getRefData().getHandle() == "player")
|
||||
attacker.getClass().skillUsageSucceeded(attacker, weapskill, 0);
|
||||
|
||||
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(attacker, victim);
|
||||
if(!detected)
|
||||
{
|
||||
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||
}
|
||||
if (victim.getClass().getCreatureStats(victim).getKnockedDown())
|
||||
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
||||
|
||||
// Apply "On hit" effect of the weapon
|
||||
applyEnchantment(attacker, victim, weapon, hitPosition);
|
||||
if (weapon != projectile)
|
||||
applyEnchantment(attacker, victim, projectile, hitPosition);
|
||||
|
||||
if (damage > 0)
|
||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
||||
|
||||
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->getFloat();
|
||||
if ((::rand()/(RAND_MAX+1.0)) < fProjectileThrownStoreChance/100.f)
|
||||
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
||||
|
||||
victim.getClass().onHit(victim, damage, true, projectile, attacker, true);
|
||||
}
|
||||
|
||||
float getHitChance(const MWWorld::Ptr &attacker, const MWWorld::Ptr &victim, int skillValue)
|
||||
{
|
||||
MWMechanics::CreatureStats &stats = attacker.getClass().getCreatureStats(attacker);
|
||||
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
|
||||
float hitchance = skillValue +
|
||||
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
|
||||
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
|
||||
hitchance *= stats.getFatigueTerm();
|
||||
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
|
||||
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
|
||||
hitchance -= victim.getClass().getCreatureStats(victim).getEvasion();
|
||||
return hitchance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define OPENMW_MECHANICS_COMBAT_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include <OgreVector3.h>
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -11,6 +12,13 @@ bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker
|
|||
|
||||
void resistNormalWeapon (const MWWorld::Ptr& actor, const MWWorld::Ptr& attacker, const MWWorld::Ptr& weapon, float& damage);
|
||||
|
||||
/// @note for a thrown weapon, \a weapon == \a projectile, for bows/crossbows, \a projectile is the arrow/bolt
|
||||
void projectileHit (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, MWWorld::Ptr weapon, const MWWorld::Ptr& projectile,
|
||||
const Ogre::Vector3& hitPosition);
|
||||
|
||||
/// Get the chance (in percent) for \a attacker to successfully hit \a victim with a given weapon skill value
|
||||
float getHitChance (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, int skillValue);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,13 +37,6 @@ namespace
|
|||
return sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
static float sgn(Ogre::Radian a)
|
||||
{
|
||||
if(a.valueRadians() > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int getClosestPoint(const ESM::Pathgrid* grid, float x, float y, float z)
|
||||
{
|
||||
if(!grid || grid->mPoints.empty())
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <components/esm/loadpgrd.hpp>
|
||||
#include <list>
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class CellStore;
|
||||
|
@ -16,6 +18,20 @@ namespace MWMechanics
|
|||
public:
|
||||
PathFinder();
|
||||
|
||||
static float sgn(Ogre::Radian a)
|
||||
{
|
||||
if(a.valueRadians() > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
static float sgn(float a)
|
||||
{
|
||||
if(a > 0)
|
||||
return 1.0;
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
void clearPath();
|
||||
|
||||
void buildPathgridGraph(const ESM::Pathgrid* pathGrid);
|
||||
|
|
|
@ -587,7 +587,7 @@ namespace MWMechanics
|
|||
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(mId, false, enchantment->mEffects, mCaster, mSourceName);
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -666,7 +666,7 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(mId, false, spell->mEffects, mCaster, mSourceName);
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace MWRender
|
|||
if (!mVanity.enabled && !mPreviewMode) {
|
||||
mCamera->getParentNode()->setOrientation(xr);
|
||||
} else {
|
||||
Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z);
|
||||
Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::UNIT_Z);
|
||||
mCamera->getParentNode()->setOrientation(zr * xr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr)
|
|||
|
||||
updateParts();
|
||||
}
|
||||
|
||||
mWeaponAnimationTime = Ogre::SharedPtr<WeaponAnimationTime>(new WeaponAnimationTime(this));
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::showWeapons(bool showWeapon)
|
||||
|
@ -110,6 +112,20 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
|
|||
setRenderProperties(scene, RV_Actors, RQG_Main, RQG_Alpha, 0,
|
||||
!item.getClass().getEnchantment(item).empty(), &glowColor);
|
||||
|
||||
// Crossbows start out with a bolt attached
|
||||
if (slot == MWWorld::InventoryStore::Slot_CarriedRight &&
|
||||
item.getTypeName() == typeid(ESM::Weapon).name() &&
|
||||
item.get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)
|
||||
attachArrow();
|
||||
else
|
||||
mAmmunition.setNull();
|
||||
}
|
||||
else
|
||||
mAmmunition.setNull();
|
||||
|
||||
if(scene->mSkelBase)
|
||||
{
|
||||
Ogre::SkeletonInstance *skel = scene->mSkelBase->getSkeleton();
|
||||
|
@ -133,15 +149,42 @@ void CreatureWeaponAnimation::updatePart(NifOgre::ObjectScenePtr& scene, int slo
|
|||
updateSkeletonInstance(mSkelBase->getSkeleton(), skel);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// type == ESM::PRT_Weapon should get an animation source based on the current offset
|
||||
// of the weapon attack animation (from its beginning, or start marker?)
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(scene->mControllers.begin());
|
||||
for(;ctrl != scene->mControllers.end();ctrl++)
|
||||
{
|
||||
if(ctrl->getSource().isNull())
|
||||
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
|
||||
{
|
||||
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
|
||||
ctrl->setSource(mWeaponAnimationTime);
|
||||
else
|
||||
ctrl->setSource(Ogre::SharedPtr<NullAnimationTime>(new NullAnimationTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
|
||||
|
||||
setRenderProperties(object, RV_Actors, RQG_Main, RQG_Alpha, 0,
|
||||
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::attachArrow()
|
||||
{
|
||||
WeaponAnimation::attachArrow(mPtr);
|
||||
}
|
||||
|
||||
void CreatureWeaponAnimation::releaseArrow()
|
||||
{
|
||||
WeaponAnimation::releaseArrow(mPtr);
|
||||
}
|
||||
|
||||
Ogre::Vector3 CreatureWeaponAnimation::runAnimation(float duration)
|
||||
{
|
||||
Ogre::Vector3 ret = Animation::runAnimation(duration);
|
||||
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], mSkelBase->getSkeleton());
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GAME_RENDER_CREATUREANIMATION_H
|
||||
|
||||
#include "animation.hpp"
|
||||
#include "weaponanimation.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
|
@ -21,7 +22,7 @@ namespace MWRender
|
|||
// For creatures with weapons and shields
|
||||
// Animation is already virtual anyway, so might as well make a separate class.
|
||||
// Most creatures don't need weapons/shields, so this will save some memory.
|
||||
class CreatureWeaponAnimation : public Animation, public MWWorld::InventoryStoreListener
|
||||
class CreatureWeaponAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||
{
|
||||
public:
|
||||
CreatureWeaponAnimation(const MWWorld::Ptr& ptr);
|
||||
|
@ -36,11 +37,29 @@ namespace MWRender
|
|||
|
||||
void updatePart(NifOgre::ObjectScenePtr& scene, int slot);
|
||||
|
||||
virtual void attachArrow();
|
||||
virtual void releaseArrow();
|
||||
|
||||
virtual Ogre::Vector3 runAnimation(float duration);
|
||||
|
||||
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
|
||||
/// to indicate the facing orientation of the character.
|
||||
virtual void setPitchFactor(float factor) { mPitchFactor = factor; }
|
||||
|
||||
virtual void setWeaponGroup(const std::string& group) { mWeaponAnimationTime->setGroup(group); }
|
||||
|
||||
// WeaponAnimation
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() { return mWeapon; }
|
||||
virtual void showWeapon(bool show) { showWeapons(show); }
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
|
||||
|
||||
private:
|
||||
NifOgre::ObjectScenePtr mWeapon;
|
||||
NifOgre::ObjectScenePtr mShield;
|
||||
bool mShowWeapons;
|
||||
bool mShowCarriedLeft;
|
||||
|
||||
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -76,27 +76,6 @@ float HeadAnimationTime::getValue() const
|
|||
return 1;
|
||||
}
|
||||
|
||||
float WeaponAnimationTime::getValue() const
|
||||
{
|
||||
if (mWeaponGroup.empty())
|
||||
return 0;
|
||||
float current = mAnimation->getCurrentTime(mWeaponGroup);
|
||||
if (current == -1)
|
||||
return 0;
|
||||
return current - mStartTime;
|
||||
}
|
||||
|
||||
void WeaponAnimationTime::setGroup(const std::string &group)
|
||||
{
|
||||
mWeaponGroup = group;
|
||||
mStartTime = mAnimation->getStartTime(mWeaponGroup);
|
||||
}
|
||||
|
||||
void WeaponAnimationTime::updateStartTime()
|
||||
{
|
||||
setGroup(mWeaponGroup);
|
||||
}
|
||||
|
||||
static NpcAnimation::PartBoneMap createPartListMap()
|
||||
{
|
||||
NpcAnimation::PartBoneMap result;
|
||||
|
@ -147,8 +126,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
|
|||
mShowCarriedLeft(true),
|
||||
mFirstPersonOffset(0.f, 0.f, 0.f),
|
||||
mAlpha(1.f),
|
||||
mNpcType(Type_Normal),
|
||||
mPitchFactor(0)
|
||||
mNpcType(Type_Normal)
|
||||
{
|
||||
mNpc = mPtr.get<ESM::NPC>()->mBase;
|
||||
|
||||
|
@ -532,20 +510,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
{
|
||||
float pitch = mPtr.getRefData().getPosition().rot[0];
|
||||
Ogre::Node *node = baseinst->getBone("Bip01 Neck");
|
||||
node->pitch(Ogre::Radian(pitch), Ogre::Node::TS_WORLD);
|
||||
node->pitch(Ogre::Radian(-pitch), Ogre::Node::TS_WORLD);
|
||||
|
||||
// This has to be done before this function ends;
|
||||
// updateSkeletonInstance, below, touches the hands.
|
||||
node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD);
|
||||
}
|
||||
else if (mPitchFactor > 0)
|
||||
else
|
||||
{
|
||||
// In third person mode we may still need pitch for ranged weapon targeting
|
||||
float pitch = mPtr.getRefData().getPosition().rot[0] * mPitchFactor;
|
||||
Ogre::Node *node = baseinst->getBone("Bip01 Spine2");
|
||||
node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD);
|
||||
node = baseinst->getBone("Bip01 Spine1");
|
||||
node->pitch(Ogre::Radian(pitch/2), Ogre::Node::TS_WORLD);
|
||||
pitchSkeleton(mPtr.getRefData().getPosition().rot[0], baseinst);
|
||||
}
|
||||
mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame.
|
||||
|
||||
|
@ -695,13 +669,14 @@ void NpcAnimation::showWeapons(bool showWeapon)
|
|||
{
|
||||
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if(weapon != inv.end()) // special case for weapons
|
||||
if(weapon != inv.end())
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(*weapon);
|
||||
std::string mesh = MWWorld::Class::get(*weapon).getModel(*weapon);
|
||||
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
|
||||
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
|
||||
|
||||
// Crossbows start out with a bolt attached
|
||||
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
|
||||
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
|
||||
{
|
||||
|
@ -743,50 +718,24 @@ void NpcAnimation::showCarriedLeft(bool show)
|
|||
removeIndividualPart(ESM::PRT_Shield);
|
||||
}
|
||||
|
||||
void NpcAnimation::configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot)
|
||||
{
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(ptr);
|
||||
setRenderProperties(object, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
|
||||
!ptr.getClass().getEnchantment(ptr).empty(), &glowColor);
|
||||
|
||||
std::for_each(object->mEntities.begin(), object->mEntities.end(), SetObjectGroup(slot));
|
||||
std::for_each(object->mParticles.begin(), object->mParticles.end(), SetObjectGroup(slot));
|
||||
}
|
||||
|
||||
void NpcAnimation::attachArrow()
|
||||
{
|
||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
showWeapons(true);
|
||||
else
|
||||
{
|
||||
NifOgre::ObjectScenePtr weapon = mObjectParts[ESM::PRT_Weapon];
|
||||
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo == inv.end())
|
||||
return;
|
||||
std::string model = ammo->getClass().getModel(*ammo);
|
||||
|
||||
mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", mInsert, model);
|
||||
Ogre::Vector3 glowColor = getEnchantmentColor(*ammo);
|
||||
setRenderProperties(mAmmunition, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha, 0,
|
||||
!ammo->getClass().getEnchantment(*ammo).empty(), &glowColor);
|
||||
|
||||
std::for_each(mAmmunition->mEntities.begin(), mAmmunition->mEntities.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
|
||||
std::for_each(mAmmunition->mParticles.begin(), mAmmunition->mParticles.end(), SetObjectGroup(MWWorld::InventoryStore::Slot_Ammunition));
|
||||
}
|
||||
WeaponAnimation::attachArrow(mPtr);
|
||||
}
|
||||
|
||||
void NpcAnimation::releaseArrow()
|
||||
{
|
||||
// Thrown weapons get detached now
|
||||
MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weapon != inv.end() && weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
{
|
||||
showWeapons(false);
|
||||
inv.remove(*weapon, 1, mPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// With bows and crossbows only the used arrow/bolt gets detached
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo == inv.end())
|
||||
return;
|
||||
inv.remove(*ammo, 1, mPtr);
|
||||
mAmmunition.setNull();
|
||||
}
|
||||
WeaponAnimation::releaseArrow(mPtr);
|
||||
}
|
||||
|
||||
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "weaponanimation.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct NPC;
|
||||
|
@ -25,24 +27,7 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Animation* mAnimation;
|
||||
std::string mWeaponGroup;
|
||||
float mStartTime;
|
||||
public:
|
||||
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
|
||||
void setGroup(const std::string& group);
|
||||
void updateStartTime();
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
class NpcAnimation : public Animation, public MWWorld::InventoryStoreListener
|
||||
class NpcAnimation : public Animation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
||||
{
|
||||
public:
|
||||
virtual void equipmentChanged() { updateParts(); }
|
||||
|
@ -91,7 +76,6 @@ private:
|
|||
Ogre::SharedPtr<WeaponAnimationTime> mWeaponAnimationTime;
|
||||
|
||||
float mAlpha;
|
||||
float mPitchFactor;
|
||||
|
||||
void updateNpcBase();
|
||||
|
||||
|
@ -138,7 +122,10 @@ public:
|
|||
virtual void attachArrow();
|
||||
virtual void releaseArrow();
|
||||
|
||||
NifOgre::ObjectScenePtr mAmmunition;
|
||||
// WeaponAnimation
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() { return mObjectParts[ESM::PRT_Weapon]; }
|
||||
virtual void showWeapon(bool show) { showWeapons(show); }
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot);
|
||||
|
||||
void setViewMode(ViewMode viewMode);
|
||||
|
||||
|
|
|
@ -280,13 +280,12 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr)
|
|||
|
||||
if(ptr.getRefData().getHandle() == mCamera->getHandle() &&
|
||||
!mCamera->isVanityOrPreviewModeEnabled())
|
||||
mCamera->rotateCamera(rot, false);
|
||||
mCamera->rotateCamera(-rot, false);
|
||||
|
||||
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
|
||||
Ogre::Quaternion newo = Ogre::Quaternion(Ogre::Radian(rot.z), Ogre::Vector3::NEGATIVE_UNIT_Z);
|
||||
if(!MWWorld::Class::get(ptr).isActor())
|
||||
newo = Ogre::Quaternion(Ogre::Radian(-rot.x), Ogre::Vector3::UNIT_X) *
|
||||
Ogre::Quaternion(Ogre::Radian(-rot.y), Ogre::Vector3::UNIT_Y) * newo;
|
||||
|
||||
newo = Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::NEGATIVE_UNIT_X) *
|
||||
Ogre::Quaternion(Ogre::Radian(rot.y), Ogre::Vector3::NEGATIVE_UNIT_Y) * newo;
|
||||
ptr.getRefData().getBaseNode()->setOrientation(newo);
|
||||
}
|
||||
|
||||
|
@ -1074,7 +1073,7 @@ float RenderingManager::getCameraDistance() const
|
|||
|
||||
void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale)
|
||||
{
|
||||
mEffectManager->addEffect(model, "", worldPosition, scale);
|
||||
mEffectManager->addEffect(model, texture, worldPosition, scale);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
159
apps/openmw/mwrender/weaponanimation.cpp
Normal file
159
apps/openmw/mwrender/weaponanimation.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include "weaponanimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreBone.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreSkeletonInstance.h>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "animation.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
float WeaponAnimationTime::getValue() const
|
||||
{
|
||||
if (mWeaponGroup.empty())
|
||||
return 0;
|
||||
float current = mAnimation->getCurrentTime(mWeaponGroup);
|
||||
if (current == -1)
|
||||
return 0;
|
||||
return current - mStartTime;
|
||||
}
|
||||
|
||||
void WeaponAnimationTime::setGroup(const std::string &group)
|
||||
{
|
||||
mWeaponGroup = group;
|
||||
mStartTime = mAnimation->getStartTime(mWeaponGroup);
|
||||
}
|
||||
|
||||
void WeaponAnimationTime::updateStartTime()
|
||||
{
|
||||
setGroup(mWeaponGroup);
|
||||
}
|
||||
|
||||
void WeaponAnimation::attachArrow(MWWorld::Ptr actor)
|
||||
{
|
||||
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
||||
MWWorld::ContainerStoreIterator weaponSlot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weaponSlot != inv.end() && weaponSlot->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
showWeapon(true);
|
||||
else
|
||||
{
|
||||
NifOgre::ObjectScenePtr weapon = getWeapon();
|
||||
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo == inv.end())
|
||||
return;
|
||||
std::string model = ammo->getClass().getModel(*ammo);
|
||||
|
||||
mAmmunition = NifOgre::Loader::createObjects(weapon->mSkelBase, "ArrowBone", weapon->mSkelBase->getParentSceneNode(), model);
|
||||
configureAddedObject(mAmmunition, *ammo, MWWorld::InventoryStore::Slot_Ammunition);
|
||||
}
|
||||
}
|
||||
|
||||
void WeaponAnimation::releaseArrow(MWWorld::Ptr actor)
|
||||
{
|
||||
MWWorld::InventoryStore& inv = actor.getClass().getInventoryStore(actor);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if (weapon == inv.end())
|
||||
return;
|
||||
|
||||
// The orientation of the launched projectile. Always the same as the actor orientation, even if the ArrowBone's orientation dictates otherwise.
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// Reduce fatigue
|
||||
// somewhat of a guess, but using the weapon weight makes sense
|
||||
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
|
||||
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
|
||||
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
|
||||
MWMechanics::CreatureStats& attackerStats = actor.getClass().getCreatureStats(actor);
|
||||
MWMechanics::DynamicStat<float> fatigue = attackerStats.getFatigue();
|
||||
const float normalizedEncumbrance = actor.getClass().getEncumbrance(actor) / actor.getClass().getCapacity(actor);
|
||||
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
|
||||
if (!weapon->isEmpty())
|
||||
fatigueLoss += weapon->getClass().getWeight(*weapon) * attackerStats.getAttackStrength() * fWeaponFatigueMult;
|
||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
|
||||
attackerStats.setFatigue(fatigue);
|
||||
|
||||
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanThrown)
|
||||
{
|
||||
// Thrown weapons get detached now
|
||||
NifOgre::ObjectScenePtr objects = getWeapon();
|
||||
|
||||
Ogre::Vector3 launchPos(0,0,0);
|
||||
if (objects->mSkelBase)
|
||||
{
|
||||
launchPos = objects->mSkelBase->getParentNode()->_getDerivedPosition();
|
||||
}
|
||||
else if (objects->mEntities.size())
|
||||
{
|
||||
objects->mEntities[0]->getParentNode()->needUpdate(true);
|
||||
launchPos = objects->mEntities[0]->getParentNode()->_getDerivedPosition();
|
||||
}
|
||||
|
||||
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
|
||||
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
|
||||
float speed = fThrownWeaponMinSpeed + (fThrownWeaponMaxSpeed - fThrownWeaponMinSpeed) *
|
||||
actor.getClass().getCreatureStats(actor).getAttackStrength();
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(actor, *weapon, launchPos, orient, *weapon, speed);
|
||||
|
||||
showWeapon(false);
|
||||
|
||||
inv.remove(*weapon, 1, actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// With bows and crossbows only the used arrow/bolt gets detached
|
||||
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
|
||||
if (ammo == inv.end())
|
||||
return;
|
||||
|
||||
Ogre::Vector3 launchPos(0,0,0);
|
||||
if (mAmmunition->mSkelBase)
|
||||
{
|
||||
launchPos = mAmmunition->mSkelBase->getParentNode()->_getDerivedPosition();
|
||||
}
|
||||
else if (mAmmunition->mEntities.size())
|
||||
{
|
||||
mAmmunition->mEntities[0]->getParentNode()->needUpdate(true);
|
||||
launchPos = mAmmunition->mEntities[0]->getParentNode()->_getDerivedPosition();
|
||||
}
|
||||
|
||||
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
|
||||
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
|
||||
float speed = fProjectileMinSpeed + (fProjectileMaxSpeed - fProjectileMinSpeed) * actor.getClass().getCreatureStats(actor).getAttackStrength();
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchProjectile(actor, *ammo, launchPos, orient, *weapon, speed);
|
||||
|
||||
inv.remove(*ammo, 1, actor);
|
||||
mAmmunition.setNull();
|
||||
}
|
||||
}
|
||||
|
||||
void WeaponAnimation::pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel)
|
||||
{
|
||||
if (mPitchFactor == 0)
|
||||
return;
|
||||
|
||||
float pitch = xrot * mPitchFactor;
|
||||
Ogre::Node *node = skel->getBone("Bip01 Spine2");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
node = skel->getBone("Bip01 Spine1");
|
||||
node->pitch(Ogre::Radian(-pitch/2), Ogre::Node::TS_WORLD);
|
||||
}
|
||||
|
||||
}
|
56
apps/openmw/mwrender/weaponanimation.hpp
Normal file
56
apps/openmw/mwrender/weaponanimation.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef OPENMW_MWRENDER_WEAPONANIMATION_H
|
||||
#define OPENMW_MWRENDER_WEAPONANIMATION_H
|
||||
|
||||
#include <OgreController.h>
|
||||
|
||||
#include <components/nifogre/ogrenifloader.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class Animation;
|
||||
|
||||
class WeaponAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Animation* mAnimation;
|
||||
std::string mWeaponGroup;
|
||||
float mStartTime;
|
||||
public:
|
||||
WeaponAnimationTime(Animation* animation) : mAnimation(animation), mStartTime(0) {}
|
||||
void setGroup(const std::string& group);
|
||||
void updateStartTime();
|
||||
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{ }
|
||||
};
|
||||
|
||||
/// Handles attach & release of projectiles for ranged weapons
|
||||
class WeaponAnimation
|
||||
{
|
||||
public:
|
||||
WeaponAnimation() : mPitchFactor(0) {}
|
||||
|
||||
virtual void attachArrow(MWWorld::Ptr actor);
|
||||
virtual void releaseArrow(MWWorld::Ptr actor);
|
||||
|
||||
protected:
|
||||
NifOgre::ObjectScenePtr mAmmunition;
|
||||
|
||||
virtual NifOgre::ObjectScenePtr getWeapon() = 0;
|
||||
virtual void showWeapon(bool show) = 0;
|
||||
virtual void configureAddedObject(NifOgre::ObjectScenePtr object, MWWorld::Ptr ptr, int slot) = 0;
|
||||
|
||||
/// A relative factor (0-1) that decides if and how much the skeleton should be pitched
|
||||
/// to indicate the facing orientation of the character, for ranged weapon aiming.
|
||||
float mPitchFactor;
|
||||
|
||||
void pitchSkeleton(float xrot, Ogre::SkeletonInstance* skel);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -121,7 +121,7 @@ namespace MWScript
|
|||
|
||||
std::string itemName;
|
||||
for (MWWorld::ContainerStoreIterator iter(store.begin()); iter != store.end(); ++iter)
|
||||
if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item))
|
||||
if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item))
|
||||
itemName = iter->getClass().getName(*iter);
|
||||
|
||||
int numRemoved = store.remove(item, count, ptr);
|
||||
|
@ -165,7 +165,7 @@ namespace MWScript
|
|||
MWWorld::ContainerStoreIterator it = invStore.begin();
|
||||
for (; it != invStore.end(); ++it)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
|
||||
if (::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
|
||||
break;
|
||||
}
|
||||
if (it == invStore.end())
|
||||
|
@ -268,7 +268,7 @@ namespace MWScript
|
|||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator it = invStore.getSlot (slot);
|
||||
if (it != invStore.end() && Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
|
||||
if (it != invStore.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item))
|
||||
{
|
||||
runtime.push(1);
|
||||
return;
|
||||
|
@ -295,7 +295,7 @@ namespace MWScript
|
|||
it != invStore.end(); ++it)
|
||||
{
|
||||
|
||||
if (Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name))
|
||||
if (::Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name))
|
||||
{
|
||||
runtime.push(1);
|
||||
return;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace MWScript
|
|||
void GlobalScripts::addScript (const std::string& name)
|
||||
{
|
||||
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
|
||||
mScripts.find (Misc::StringUtils::lowerCase (name));
|
||||
mScripts.find (::Misc::StringUtils::lowerCase (name));
|
||||
|
||||
if (iter==mScripts.end())
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ namespace MWScript
|
|||
void GlobalScripts::removeScript (const std::string& name)
|
||||
{
|
||||
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
|
||||
mScripts.find (Misc::StringUtils::lowerCase (name));
|
||||
mScripts.find (::Misc::StringUtils::lowerCase (name));
|
||||
|
||||
if (iter!=mScripts.end())
|
||||
iter->second.first = false;
|
||||
|
@ -53,7 +53,7 @@ namespace MWScript
|
|||
bool GlobalScripts::isRunning (const std::string& name) const
|
||||
{
|
||||
std::map<std::string, std::pair<bool, Locals> >::const_iterator iter =
|
||||
mScripts.find (Misc::StringUtils::lowerCase (name));
|
||||
mScripts.find (::Misc::StringUtils::lowerCase (name));
|
||||
|
||||
if (iter==mScripts.end())
|
||||
return false;
|
||||
|
@ -151,7 +151,7 @@ namespace MWScript
|
|||
|
||||
Locals& GlobalScripts::getLocals (const std::string& name)
|
||||
{
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
std::string name2 = ::Misc::StringUtils::lowerCase (name);
|
||||
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
|
||||
mScripts.find (name2);
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ namespace MWScript
|
|||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
std::string cell = (runtime.getStringLiteral (runtime[0].mInteger));
|
||||
Misc::StringUtils::toLower(cell);
|
||||
::Misc::StringUtils::toLower(cell);
|
||||
runtime.pop();
|
||||
|
||||
// "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well."
|
||||
|
@ -126,7 +126,7 @@ namespace MWScript
|
|||
for (; it != cells.extEnd(); ++it)
|
||||
{
|
||||
std::string name = it->mName;
|
||||
Misc::StringUtils::toLower(name);
|
||||
::Misc::StringUtils::toLower(name);
|
||||
if (name.find(cell) != std::string::npos)
|
||||
MWBase::Environment::get().getWindowManager()->addVisitedLocation (
|
||||
it->mName,
|
||||
|
|
|
@ -540,7 +540,7 @@ namespace MWScript
|
|||
factionID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
}
|
||||
Misc::StringUtils::toLower(factionID);
|
||||
::Misc::StringUtils::toLower(factionID);
|
||||
if(factionID != "")
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
@ -569,7 +569,7 @@ namespace MWScript
|
|||
factionID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
}
|
||||
Misc::StringUtils::toLower(factionID);
|
||||
::Misc::StringUtils::toLower(factionID);
|
||||
if(factionID != "")
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
@ -602,7 +602,7 @@ namespace MWScript
|
|||
factionID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
}
|
||||
Misc::StringUtils::toLower(factionID);
|
||||
::Misc::StringUtils::toLower(factionID);
|
||||
if(factionID != "")
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
@ -640,7 +640,7 @@ namespace MWScript
|
|||
factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first;
|
||||
}
|
||||
}
|
||||
Misc::StringUtils::toLower(factionID);
|
||||
::Misc::StringUtils::toLower(factionID);
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if(factionID!="")
|
||||
{
|
||||
|
@ -742,7 +742,7 @@ namespace MWScript
|
|||
if (factionId.empty())
|
||||
throw std::runtime_error ("failed to determine faction");
|
||||
|
||||
Misc::StringUtils::toLower (factionId);
|
||||
::Misc::StringUtils::toLower (factionId);
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
runtime.push (
|
||||
|
@ -778,7 +778,7 @@ namespace MWScript
|
|||
if (factionId.empty())
|
||||
throw std::runtime_error ("failed to determine faction");
|
||||
|
||||
Misc::StringUtils::toLower (factionId);
|
||||
::Misc::StringUtils::toLower (factionId);
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value);
|
||||
|
@ -813,7 +813,7 @@ namespace MWScript
|
|||
if (factionId.empty())
|
||||
throw std::runtime_error ("failed to determine faction");
|
||||
|
||||
Misc::StringUtils::toLower (factionId);
|
||||
::Misc::StringUtils::toLower (factionId);
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId,
|
||||
|
@ -858,11 +858,11 @@ namespace MWScript
|
|||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
std::string race = runtime.getStringLiteral(runtime[0].mInteger);
|
||||
Misc::StringUtils::toLower(race);
|
||||
::Misc::StringUtils::toLower(race);
|
||||
runtime.pop();
|
||||
|
||||
std::string npcRace = ptr.get<ESM::NPC>()->mBase->mRace;
|
||||
Misc::StringUtils::toLower(npcRace);
|
||||
::Misc::StringUtils::toLower(npcRace);
|
||||
|
||||
runtime.push (npcRace == race);
|
||||
}
|
||||
|
@ -906,7 +906,7 @@ namespace MWScript
|
|||
factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first;
|
||||
}
|
||||
}
|
||||
Misc::StringUtils::toLower(factionID);
|
||||
::Misc::StringUtils::toLower(factionID);
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
if(factionID!="")
|
||||
{
|
||||
|
|
|
@ -82,30 +82,24 @@ namespace MWScript
|
|||
Interpreter::Type_Float angle = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
float ax = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees();
|
||||
|
||||
float *objRot = ptr.getRefData().getPosition().rot;
|
||||
|
||||
float lx = Ogre::Radian(objRot[0]).valueDegrees();
|
||||
float ly = Ogre::Radian(objRot[1]).valueDegrees();
|
||||
float lz = Ogre::Radian(objRot[2]).valueDegrees();
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
float az = Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees();
|
||||
|
||||
if (axis == "x")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,angle-lx,ay,az);
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,angle,ay,az);
|
||||
}
|
||||
else if (axis == "y")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,angle-ly,az);
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,angle,az);
|
||||
}
|
||||
else if (axis == "z")
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->localRotateObject(ptr,ax,ay,angle-lz);
|
||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,angle);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid ration axis: " + axis);
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -134,7 +128,7 @@ namespace MWScript
|
|||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees());
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid ration axis: " + axis);
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -152,18 +146,18 @@ namespace MWScript
|
|||
|
||||
if (axis=="x")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[0]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[0]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees());
|
||||
}
|
||||
else if (axis=="y")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[1]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[1]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees());
|
||||
}
|
||||
else if (axis=="z")
|
||||
{
|
||||
runtime.push(Ogre::Radian(ptr.getCellRef().mPos.rot[2]).valueDegrees()+Ogre::Radian(ptr.getRefData().getLocalRotation().rot[2]).valueDegrees());
|
||||
runtime.push(Ogre::Radian(ptr.getRefData().getPosition().rot[2]).valueDegrees());
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid ration axis: " + axis);
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -192,7 +186,7 @@ namespace MWScript
|
|||
runtime.push(ptr.getRefData().getPosition().pos[2]);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error ("invalid rotation axis: " + axis);
|
||||
throw std::runtime_error ("invalid axis: " + axis);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -313,7 +307,7 @@ namespace MWScript
|
|||
}
|
||||
if(store)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr,*store,x,y,z);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z);
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
||||
|
@ -361,7 +355,7 @@ namespace MWScript
|
|||
int cx,cy;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex(x,y,cx,cy);
|
||||
MWBase::Environment::get().getWorld()->moveObject(ptr,
|
||||
*MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
|
||||
MWBase::Environment::get().getWorld()->getExterior(cx,cy),x,y,z);
|
||||
float ax = Ogre::Radian(ptr.getRefData().getPosition().rot[0]).valueDegrees();
|
||||
float ay = Ogre::Radian(ptr.getRefData().getPosition().rot[1]).valueDegrees();
|
||||
if(ptr.getTypeName() == typeid(ESM::NPC).name())//some morrowind oddity
|
||||
|
@ -421,7 +415,7 @@ namespace MWScript
|
|||
pos.rot[2] = zRot;
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
||||
ref.getPtr().getCellRef().mPos = pos;
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos);
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -462,7 +456,7 @@ namespace MWScript
|
|||
pos.rot[2] = zRot;
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
||||
ref.getPtr().getCellRef().mPos = pos;
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,pos);
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -530,7 +524,7 @@ namespace MWScript
|
|||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, count);
|
||||
ref.getPtr().getCellRef().mPos = ipos;
|
||||
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos);
|
||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
namespace MWSound
|
||||
{
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("FFmpeg exception: "+msg); }
|
||||
|
||||
void FFmpeg_Decoder::fail(const std::string &msg)
|
||||
{
|
||||
throw std::runtime_error("FFmpeg exception: "+msg);
|
||||
}
|
||||
|
||||
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
|
||||
{
|
||||
|
|
|
@ -61,6 +61,8 @@ namespace MWSound
|
|||
virtual void rewind();
|
||||
virtual size_t getSampleOffset();
|
||||
|
||||
void fail(const std::string &msg);
|
||||
|
||||
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
|
||||
FFmpeg_Decoder(const FFmpeg_Decoder &rhs);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/scriptmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -39,6 +40,7 @@ void MWState::StateManager::cleanup (bool force)
|
|||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().clear();
|
||||
MWBase::Environment::get().getWorld()->clear();
|
||||
MWBase::Environment::get().getWindowManager()->clear();
|
||||
MWBase::Environment::get().getInputManager()->clear();
|
||||
|
||||
mState = State_NoGame;
|
||||
mCharacterManager.clearCurrentCharacter();
|
||||
|
@ -123,11 +125,10 @@ void MWState::StateManager::newGame (bool bypass)
|
|||
{
|
||||
cleanup();
|
||||
|
||||
MWBase::Environment::get().getWorld()->startNewGame (bypass);
|
||||
|
||||
if (!bypass)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->startNewGame();
|
||||
MWBase::Environment::get().getWindowManager()->setNewGame (true);
|
||||
}
|
||||
else
|
||||
MWBase::Environment::get().getWorld()->setGlobalInt ("chargenstate", -1);
|
||||
|
||||
|
@ -148,7 +149,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
|
||||
MWBase::World& world = *MWBase::Environment::get().getWorld();
|
||||
|
||||
MWWorld::Ptr player = world.getPlayer().getPlayer();
|
||||
MWWorld::Ptr player = world.getPlayerPtr();
|
||||
|
||||
profile.mContentFiles = world.getContentFiles();
|
||||
|
||||
|
@ -300,7 +301,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
|
|||
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
||||
MWBase::Environment::get().getMechanicsManager()->playerLoaded();
|
||||
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
ESM::CellId cellId = ptr.getCell()->getCell()->getCellId();
|
||||
|
||||
|
|
|
@ -41,11 +41,11 @@ namespace MWWorld
|
|||
int cellX;
|
||||
int cellY;
|
||||
world->positionToIndex(mPosition.pos[0],mPosition.pos[1],cellX,cellY);
|
||||
world->moveObject(actor,*world->getExterior(cellX,cellY),
|
||||
world->moveObject(actor,world->getExterior(cellX,cellY),
|
||||
mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
|
||||
}
|
||||
else
|
||||
world->moveObject(actor,*world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
|
||||
world->moveObject(actor,world->getInterior(mCellName),mPosition.pos[0],mPosition.pos[1],mPosition.pos[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,7 +363,22 @@ namespace MWWorld
|
|||
return newPtr;
|
||||
}
|
||||
|
||||
bool Class::isFlying(const Ptr &ptr) const
|
||||
bool Class::isBipedal(const Ptr &ptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::canFly(const Ptr &ptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::canSwim(const Ptr &ptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::canWalk(const Ptr &ptr) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -307,7 +307,10 @@ namespace MWWorld
|
|||
return false;
|
||||
}
|
||||
|
||||
virtual bool isFlying(const MWWorld::Ptr& ptr) const;
|
||||
virtual bool isBipedal(const MWWorld::Ptr& ptr) const;
|
||||
virtual bool canFly(const MWWorld::Ptr& ptr) const;
|
||||
virtual bool canSwim(const MWWorld::Ptr& ptr) const;
|
||||
virtual bool canWalk(const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
|
||||
|
||||
|
|
|
@ -120,11 +120,9 @@ namespace MWWorld
|
|||
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
|
||||
if(!physicActor || !physicActor->getCollisionMode())
|
||||
{
|
||||
// FIXME: This works, but it's inconcsistent with how the rotations are applied elsewhere. Why?
|
||||
return position + (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
movement * time;
|
||||
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X))
|
||||
* movement * time;
|
||||
}
|
||||
|
||||
btCollisionObject *colobj = physicActor->getCollisionBody();
|
||||
|
@ -132,33 +130,52 @@ namespace MWWorld
|
|||
position.z += halfExtents.z;
|
||||
|
||||
waterlevel -= halfExtents.z * 0.5;
|
||||
/*
|
||||
* A 3/4 submerged example
|
||||
*
|
||||
* +---+
|
||||
* | |
|
||||
* | | <- (original waterlevel)
|
||||
* | |
|
||||
* | | <- position <- waterlevel
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* +---+ <- (original position)
|
||||
*/
|
||||
|
||||
OEngine::Physic::ActorTracer tracer;
|
||||
bool wasOnGround = false;
|
||||
bool isOnGround = false;
|
||||
Ogre::Vector3 inertia(0.0f);
|
||||
Ogre::Vector3 velocity;
|
||||
if(position.z < waterlevel || isFlying)
|
||||
|
||||
bool canWalk = ptr.getClass().canWalk(ptr);
|
||||
bool isBipedal = ptr.getClass().isBipedal(ptr);
|
||||
bool isNpc = ptr.getClass().isNpc();
|
||||
|
||||
if(position.z < waterlevel || isFlying) // under water by 3/4 or can fly
|
||||
{
|
||||
velocity = (Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian(-refpos.rot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian( refpos.rot[0]), Ogre::Vector3::UNIT_X)) *
|
||||
movement;
|
||||
// TODO: Shouldn't water have higher drag in calculating velocity?
|
||||
velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)*
|
||||
Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement;
|
||||
}
|
||||
else
|
||||
{
|
||||
velocity = Ogre::Quaternion(Ogre::Radian(-refpos.rot[2]), Ogre::Vector3::UNIT_Z) * movement;
|
||||
velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement;
|
||||
// not in water nor can fly, so need to deal with gravity
|
||||
if(!physicActor->getOnGround())
|
||||
{
|
||||
// If falling, add part of the incoming velocity with the current inertia
|
||||
velocity = velocity*time + physicActor->getInertialForce();
|
||||
velocity = velocity * time + physicActor->getInertialForce();
|
||||
}
|
||||
inertia = velocity;
|
||||
inertia = velocity; // REM velocity is for z axis only in this code block
|
||||
|
||||
if(!(movement.z > 0.0f))
|
||||
{
|
||||
wasOnGround = physicActor->getOnGround();
|
||||
tracer.doTrace(colobj, position, position-Ogre::Vector3(0,0,2), engine);
|
||||
// TODO: Find out if there is a significance with the value 2 used here
|
||||
tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine);
|
||||
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
|
||||
isOnGround = true;
|
||||
}
|
||||
|
@ -167,24 +184,38 @@ namespace MWWorld
|
|||
if(isOnGround)
|
||||
{
|
||||
// if we're on the ground, don't try to fall
|
||||
velocity.z = std::max(0.0f, velocity.z);
|
||||
velocity.z = std::max(0.0f, velocity.z); // NOTE: two different velocity assignments above
|
||||
}
|
||||
|
||||
Ogre::Vector3 newPosition = position;
|
||||
/*
|
||||
* A loop to find newPosition using tracer, if successful different from the starting position.
|
||||
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
|
||||
* The initial velocity was set earlier (see above).
|
||||
*/
|
||||
float remainingTime = time;
|
||||
for(int iterations = 0;iterations < sMaxIterations && remainingTime > 0.01f;++iterations)
|
||||
for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations)
|
||||
{
|
||||
Ogre::Vector3 nextpos = newPosition + velocity*remainingTime;
|
||||
Ogre::Vector3 nextpos = newPosition + velocity * remainingTime;
|
||||
|
||||
if(newPosition.z < waterlevel && !isFlying &&
|
||||
nextpos.z > waterlevel && newPosition.z <= waterlevel)
|
||||
// If not able to fly, walk or bipedal don't allow to move out of water
|
||||
// TODO: this if condition may not work for large creatures or situations
|
||||
// where the creature gets above the waterline for some reason
|
||||
if(newPosition.z < waterlevel && // started 3/4 under water
|
||||
!isFlying && // can't fly
|
||||
!canWalk && // can't walk
|
||||
!isBipedal && // not bipedal (assume bipedals can walk)
|
||||
!isNpc && // FIXME: shouldn't really need this
|
||||
nextpos.z > waterlevel && // but about to go above water
|
||||
newPosition.z <= waterlevel)
|
||||
{
|
||||
const Ogre::Vector3 down(0,0,-1);
|
||||
Ogre::Real movelen = velocity.normalise();
|
||||
Ogre::Vector3 reflectdir = velocity.reflect(down);
|
||||
reflectdir.normalise();
|
||||
velocity = slide(reflectdir, down)*movelen;
|
||||
continue;
|
||||
// NOTE: remainingTime is unchanged before the loop continues
|
||||
continue; // velocity updated, calculate nextpos again
|
||||
}
|
||||
|
||||
// trace to where character would go if there were no obstructions
|
||||
|
@ -193,13 +224,14 @@ namespace MWWorld
|
|||
// check for obstructions
|
||||
if(tracer.mFraction >= 1.0f)
|
||||
{
|
||||
newPosition = tracer.mEndPos;
|
||||
remainingTime *= (1.0f-tracer.mFraction);
|
||||
newPosition = tracer.mEndPos; // ok to move, so set newPosition
|
||||
remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it?
|
||||
break;
|
||||
}
|
||||
|
||||
// We hit something. Try to step up onto it.
|
||||
if(stepMove(colobj, newPosition, velocity, remainingTime, engine))
|
||||
// NOTE: May need to stop slaughterfish step out of the water.
|
||||
if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine))
|
||||
isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity
|
||||
else
|
||||
{
|
||||
|
@ -218,7 +250,7 @@ namespace MWWorld
|
|||
|
||||
if(isOnGround || wasOnGround)
|
||||
{
|
||||
tracer.doTrace(colobj, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+2.0f), engine);
|
||||
tracer.doTrace(colobj, newPosition, newPosition - Ogre::Vector3(0,0,sStepSize+2.0f), engine);
|
||||
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
|
||||
{
|
||||
newPosition.z = tracer.mEndPos.z + 1.0f;
|
||||
|
@ -240,7 +272,7 @@ namespace MWWorld
|
|||
}
|
||||
physicActor->setOnGround(isOnGround);
|
||||
|
||||
newPosition.z -= halfExtents.z;
|
||||
newPosition.z -= halfExtents.z; // remove what was added at the beggining
|
||||
return newPosition;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
#include "../mwmechanics/levelledlist.hpp"
|
||||
|
||||
#include "../mwmechanics/combat.hpp"
|
||||
|
||||
#include "../mwrender/sky.hpp"
|
||||
#include "../mwrender/animation.hpp"
|
||||
|
@ -121,13 +121,15 @@ namespace MWWorld
|
|||
const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& contentFiles,
|
||||
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride)
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
||||
int activationDistanceOverride, const std::string& startCell)
|
||||
: mPlayer (0), mLocalScripts (mStore),
|
||||
mSky (true), mCells (mStore, mEsm),
|
||||
mActivationDistanceOverride (mActivationDistanceOverride),
|
||||
mActivationDistanceOverride (activationDistanceOverride),
|
||||
mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), mLevitationEnabled(true),
|
||||
mFacedDistance(FLT_MAX), mGodMode(false), mContentFiles (contentFiles),
|
||||
mGoToJail(false)
|
||||
mGoToJail(false),
|
||||
mStartCell (startCell)
|
||||
{
|
||||
mPhysics = new PhysicsSystem(renderer);
|
||||
mPhysEngine = mPhysics->getEngine();
|
||||
|
@ -168,7 +170,7 @@ namespace MWWorld
|
|||
mWorldScene = new Scene(*mRendering, mPhysics);
|
||||
}
|
||||
|
||||
void World::startNewGame()
|
||||
void World::startNewGame (bool bypass)
|
||||
{
|
||||
mGoToJail = false;
|
||||
mLevitationEnabled = true;
|
||||
|
@ -181,23 +183,44 @@ namespace MWWorld
|
|||
|
||||
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
||||
|
||||
// FIXME: this will add cell 0,0 as visible on the global map
|
||||
ESM::Position pos;
|
||||
const int cellSize = 8192;
|
||||
pos.pos[0] = cellSize/2;
|
||||
pos.pos[1] = cellSize/2;
|
||||
pos.pos[2] = 0;
|
||||
pos.rot[0] = 0;
|
||||
pos.rot[1] = 0;
|
||||
pos.rot[2] = 0;
|
||||
mWorldScene->changeToExteriorCell(pos);
|
||||
if (bypass && !mStartCell.empty())
|
||||
{
|
||||
ESM::Position pos;
|
||||
|
||||
// FIXME: should be set to 1, but the sound manager won't pause newly started sounds
|
||||
mPlayIntro = 2;
|
||||
if (findExteriorPosition (mStartCell, pos))
|
||||
{
|
||||
changeToExteriorCell (pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
findInteriorPosition (mStartCell, pos);
|
||||
changeToInteriorCell (mStartCell, pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// \todo if !bypass, do not add player location to global map for the duration of one
|
||||
/// frame
|
||||
ESM::Position pos;
|
||||
const int cellSize = 8192;
|
||||
pos.pos[0] = cellSize/2;
|
||||
pos.pos[1] = cellSize/2;
|
||||
pos.pos[2] = 0;
|
||||
pos.rot[0] = 0;
|
||||
pos.rot[1] = 0;
|
||||
pos.rot[2] = 0;
|
||||
mWorldScene->changeToExteriorCell(pos);
|
||||
}
|
||||
|
||||
// set new game mark
|
||||
mGlobalVariables["chargenstate"].setInteger (1);
|
||||
mGlobalVariables["pcrace"].setInteger (3);
|
||||
if (!bypass)
|
||||
{
|
||||
// FIXME: should be set to 1, but the sound manager won't pause newly started sounds
|
||||
mPlayIntro = 2;
|
||||
|
||||
// set new game mark
|
||||
mGlobalVariables["chargenstate"].setInteger (1);
|
||||
mGlobalVariables["pcrace"].setInteger (3);
|
||||
}
|
||||
|
||||
// we don't want old weather to persist on a new game
|
||||
delete mWeatherManager;
|
||||
|
@ -228,6 +251,7 @@ namespace MWWorld
|
|||
|
||||
mCells.clear();
|
||||
|
||||
mMagicBolts.clear();
|
||||
mProjectiles.clear();
|
||||
mDoorStates.clear();
|
||||
|
||||
|
@ -821,7 +845,7 @@ namespace MWWorld
|
|||
const ESM::Position &posdata = ptr.getRefData().getPosition();
|
||||
Ogre::Vector3 pos(posdata.pos);
|
||||
Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X);
|
||||
Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
|
||||
MWRender::Animation *anim = mRendering->getAnimation(ptr);
|
||||
if(anim != NULL)
|
||||
|
@ -858,7 +882,7 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z)
|
||||
void World::moveObject(const Ptr &ptr, CellStore* newCell, float x, float y, float z)
|
||||
{
|
||||
ESM::Position &pos = ptr.getRefData().getPosition();
|
||||
|
||||
|
@ -872,27 +896,27 @@ namespace MWWorld
|
|||
bool isPlayer = ptr == mPlayer->getPlayer();
|
||||
bool haveToMove = isPlayer || mWorldScene->isCellActive(*currCell);
|
||||
|
||||
if (*currCell != newCell)
|
||||
if (currCell != newCell)
|
||||
{
|
||||
removeContainerScripts(ptr);
|
||||
|
||||
if (isPlayer)
|
||||
{
|
||||
if (!newCell.isExterior())
|
||||
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.getCell()->mName), pos);
|
||||
if (!newCell->isExterior())
|
||||
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell->getCell()->mName), pos);
|
||||
else
|
||||
{
|
||||
int cellX = newCell.getCell()->getGridX();
|
||||
int cellY = newCell.getCell()->getGridY();
|
||||
int cellX = newCell->getCell()->getGridX();
|
||||
int cellY = newCell->getCell()->getGridY();
|
||||
mWorldScene->changeCell(cellX, cellY, pos, false);
|
||||
}
|
||||
addContainerScripts (getPlayerPtr(), &newCell);
|
||||
addContainerScripts (getPlayerPtr(), newCell);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*currCell))
|
||||
copyObjectToCell(ptr, newCell, pos);
|
||||
else if (!mWorldScene->isCellActive(newCell))
|
||||
ptr.getClass().copyToCell(ptr, *newCell, pos);
|
||||
else if (!mWorldScene->isCellActive(*newCell))
|
||||
{
|
||||
mWorldScene->removeObjectFromScene(ptr);
|
||||
mLocalScripts.remove(ptr);
|
||||
|
@ -900,7 +924,7 @@ namespace MWWorld
|
|||
haveToMove = false;
|
||||
|
||||
MWWorld::Ptr newPtr = MWWorld::Class::get(ptr)
|
||||
.copyToCell(ptr, newCell);
|
||||
.copyToCell(ptr, *newCell);
|
||||
newPtr.getRefData().setBaseNode(0);
|
||||
|
||||
objectLeftActiveCell(ptr, newPtr);
|
||||
|
@ -908,7 +932,7 @@ namespace MWWorld
|
|||
else
|
||||
{
|
||||
MWWorld::Ptr copy =
|
||||
MWWorld::Class::get(ptr).copyToCell(ptr, newCell, pos);
|
||||
MWWorld::Class::get(ptr).copyToCell(ptr, *newCell, pos);
|
||||
|
||||
mRendering->updateObjectCell(ptr, copy);
|
||||
MWBase::Environment::get().getSoundManager()->updatePtr (ptr, copy);
|
||||
|
@ -923,7 +947,7 @@ namespace MWWorld
|
|||
mLocalScripts.remove(ptr);
|
||||
removeContainerScripts (ptr);
|
||||
mLocalScripts.add(script, copy);
|
||||
addContainerScripts (copy, &newCell);
|
||||
addContainerScripts (copy, newCell);
|
||||
}
|
||||
}
|
||||
ptr.getRefData().setCount(0);
|
||||
|
@ -947,7 +971,7 @@ namespace MWWorld
|
|||
cell = getExterior(cellX, cellY);
|
||||
}
|
||||
|
||||
moveObject(ptr, *cell, x, y, z);
|
||||
moveObject(ptr, cell, x, y, z);
|
||||
|
||||
return cell != ptr.getCell();
|
||||
}
|
||||
|
@ -1041,15 +1065,13 @@ namespace MWWorld
|
|||
while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad)
|
||||
ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad;
|
||||
|
||||
float *worldRot = ptr.getRefData().getPosition().rot;
|
||||
Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z));
|
||||
|
||||
Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(-worldRot[0]), Ogre::Vector3::UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Radian(-worldRot[1]), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian(-worldRot[2]), Ogre::Vector3::UNIT_Z));
|
||||
|
||||
Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-x).valueRadians()), Ogre::Vector3::UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-y).valueRadians()), Ogre::Vector3::UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Radian(Ogre::Degree(-z).valueRadians()), Ogre::Vector3::UNIT_Z));
|
||||
Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)*
|
||||
Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*
|
||||
Ogre::Quaternion(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z));
|
||||
|
||||
ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot);
|
||||
mPhysics->rotateObject(ptr);
|
||||
|
@ -1080,7 +1102,7 @@ namespace MWWorld
|
|||
pos.z = traced.z;
|
||||
}
|
||||
|
||||
moveObject(ptr, *ptr.getCell(), pos.x, pos.y, pos.z);
|
||||
moveObject(ptr, ptr.getCell(), pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
void World::rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust)
|
||||
|
@ -1091,9 +1113,9 @@ namespace MWWorld
|
|||
adjust);
|
||||
}
|
||||
|
||||
MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos)
|
||||
MWWorld::Ptr World::safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos)
|
||||
{
|
||||
return copyObjectToCell(ptr,Cell,pos,false);
|
||||
return copyObjectToCell(ptr,cell,pos,false);
|
||||
}
|
||||
|
||||
void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const
|
||||
|
@ -1127,6 +1149,7 @@ namespace MWWorld
|
|||
{
|
||||
processDoors(duration);
|
||||
|
||||
moveMagicBolts(duration);
|
||||
moveProjectiles(duration);
|
||||
|
||||
const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration);
|
||||
|
@ -1506,15 +1529,7 @@ namespace MWWorld
|
|||
if (!result.first)
|
||||
return false;
|
||||
|
||||
CellStore* cell;
|
||||
if (isCellExterior())
|
||||
{
|
||||
int cellX, cellY;
|
||||
positionToIndex(result.second[0], result.second[1], cellX, cellY);
|
||||
cell = mCells.getExterior(cellX, cellY);
|
||||
}
|
||||
else
|
||||
cell = getPlayerPtr().getCell();
|
||||
CellStore* cell = getPlayerPtr().getCell();
|
||||
|
||||
ESM::Position pos = getPlayerPtr().getRefData().getPosition();
|
||||
pos.pos[0] = result.second[0];
|
||||
|
@ -1527,7 +1542,7 @@ namespace MWWorld
|
|||
// copy the object and set its count
|
||||
int origCount = object.getRefData().getCount();
|
||||
object.getRefData().setCount(amount);
|
||||
Ptr dropped = copyObjectToCell(object, *cell, pos, true);
|
||||
Ptr dropped = copyObjectToCell(object, cell, pos, true);
|
||||
object.getRefData().setCount(origCount);
|
||||
|
||||
// only the player place items in the world, so no need to check actor
|
||||
|
@ -1548,7 +1563,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
|
||||
Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, ESM::Position pos, bool adjustPos)
|
||||
Ptr World::copyObjectToCell(const Ptr &object, CellStore* cell, ESM::Position pos, bool adjustPos)
|
||||
{
|
||||
if (object.getClass().isActor() || adjustPos)
|
||||
{
|
||||
|
@ -1560,17 +1575,17 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
if (cell.isExterior())
|
||||
if (cell->isExterior())
|
||||
{
|
||||
int cellX, cellY;
|
||||
positionToIndex(pos.pos[0], pos.pos[1], cellX, cellY);
|
||||
cell = *mCells.getExterior(cellX, cellY);
|
||||
cell = mCells.getExterior(cellX, cellY);
|
||||
}
|
||||
|
||||
MWWorld::Ptr dropped =
|
||||
MWWorld::Class::get(object).copyToCell(object, cell, pos);
|
||||
MWWorld::Class::get(object).copyToCell(object, *cell, pos);
|
||||
|
||||
if (mWorldScene->isCellActive(cell)) {
|
||||
if (mWorldScene->isCellActive(*cell)) {
|
||||
if (dropped.getRefData().isEnabled()) {
|
||||
mWorldScene->addObjectToScene(dropped);
|
||||
}
|
||||
|
@ -1578,7 +1593,7 @@ namespace MWWorld
|
|||
if (!script.empty()) {
|
||||
mLocalScripts.add(script, dropped);
|
||||
}
|
||||
addContainerScripts(dropped, &cell);
|
||||
addContainerScripts(dropped, cell);
|
||||
}
|
||||
|
||||
return dropped;
|
||||
|
@ -1608,7 +1623,7 @@ namespace MWWorld
|
|||
// copy the object and set its count
|
||||
int origCount = object.getRefData().getCount();
|
||||
object.getRefData().setCount(amount);
|
||||
Ptr dropped = copyObjectToCell(object, *cell, pos);
|
||||
Ptr dropped = copyObjectToCell(object, cell, pos);
|
||||
object.getRefData().setCount(origCount);
|
||||
|
||||
if(actor == mPlayer->getPlayer()) // Only call if dropped by player
|
||||
|
@ -1634,7 +1649,7 @@ namespace MWWorld
|
|||
if (ptr.getClass().getCreatureStats(ptr).isDead())
|
||||
return false;
|
||||
|
||||
if (ptr.getClass().isFlying(ptr))
|
||||
if (ptr.getClass().canFly(ptr))
|
||||
return true;
|
||||
|
||||
const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);
|
||||
|
@ -2143,7 +2158,37 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void World::launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
|
||||
{
|
||||
ProjectileState state;
|
||||
state.mActorHandle = actor.getRefData().getHandle();
|
||||
state.mBow = bow;
|
||||
state.mVelocity = orient.yAxis() * speed;
|
||||
|
||||
MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID);
|
||||
|
||||
ESM::Position pos;
|
||||
pos.pos[0] = worldPos.x;
|
||||
pos.pos[1] = worldPos.y;
|
||||
pos.pos[2] = worldPos.z;
|
||||
|
||||
// Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction.
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
pos.rot[0] = -xr.valueRadians();
|
||||
pos.rot[1] = -yr.valueRadians();
|
||||
pos.rot[2] = -zr.valueRadians();
|
||||
|
||||
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false);
|
||||
ptr.getRefData().setCount(1);
|
||||
|
||||
mProjectiles[ptr] = state;
|
||||
}
|
||||
|
||||
void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName)
|
||||
{
|
||||
std::string projectileModel;
|
||||
|
@ -2185,13 +2230,23 @@ namespace MWWorld
|
|||
pos.pos[0] = actor.getRefData().getPosition().pos[0];
|
||||
pos.pos[1] = actor.getRefData().getPosition().pos[1];
|
||||
pos.pos[2] = actor.getRefData().getPosition().pos[2] + height;
|
||||
pos.rot[0] = actor.getRefData().getPosition().rot[0];
|
||||
pos.rot[1] = actor.getRefData().getPosition().rot[1];
|
||||
pos.rot[2] = actor.getRefData().getPosition().rot[2];
|
||||
ref.getPtr().getCellRef().mPos = pos;
|
||||
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), *actor.getCell(), pos);
|
||||
|
||||
ProjectileState state;
|
||||
// Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction.
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
pos.rot[0] = -xr.valueRadians();
|
||||
pos.rot[1] = -yr.valueRadians();
|
||||
pos.rot[2] = -zr.valueRadians();
|
||||
|
||||
ref.getPtr().getCellRef().mPos = pos;
|
||||
CellStore* cell = actor.getCell();
|
||||
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos);
|
||||
|
||||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = id;
|
||||
state.mActorHandle = actor.getRefData().getHandle();
|
||||
|
@ -2209,7 +2264,7 @@ namespace MWWorld
|
|||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
|
||||
mProjectiles[ptr] = state;
|
||||
mMagicBolts[ptr] = state;
|
||||
}
|
||||
|
||||
void World::moveProjectiles(float duration)
|
||||
|
@ -2226,13 +2281,105 @@ namespace MWWorld
|
|||
|
||||
MWWorld::Ptr ptr = it->first;
|
||||
|
||||
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
||||
// simulating aerodynamics at all
|
||||
it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration;
|
||||
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 newPos = pos + it->second.mVelocity * duration;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity);
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees());
|
||||
|
||||
// Check for impact
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine->rayTest2(from, to);
|
||||
bool hit=false;
|
||||
|
||||
// HACK: query against the shape as well, since the ray does not take the volume into account
|
||||
// really, this should be a convex cast, but the whole physics system needs a rewrite
|
||||
std::vector<std::string> col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle());
|
||||
for (std::vector<std::string>::iterator ci = col2.begin(); ci != col2.end(); ++ci)
|
||||
collisions.push_back(std::make_pair(0.f,*ci));
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
{
|
||||
MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second);
|
||||
if (obstacle == ptr)
|
||||
continue;
|
||||
|
||||
MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle);
|
||||
|
||||
// Arrow intersects with player immediately after shooting :/
|
||||
if (obstacle == caster)
|
||||
continue;
|
||||
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
if (obstacle.isEmpty())
|
||||
{
|
||||
// Terrain
|
||||
}
|
||||
else if (obstacle.getClass().isActor())
|
||||
{
|
||||
MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first);
|
||||
}
|
||||
hit = true;
|
||||
}
|
||||
if (hit)
|
||||
{
|
||||
deleteObject(ptr);
|
||||
mProjectiles.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string handle = ptr.getRefData().getHandle();
|
||||
|
||||
moveObject(ptr, newPos.x, newPos.y, newPos.z);
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
if (!ptr.getRefData().getCount())
|
||||
{
|
||||
moved[handle] = it->second;
|
||||
mProjectiles.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
for (std::map<std::string, ProjectileState>::iterator it = moved.begin(); it != moved.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
|
||||
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
|
||||
continue;
|
||||
mProjectiles[getPtrViaHandle(it->first)] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::moveMagicBolts(float duration)
|
||||
{
|
||||
std::map<std::string, MagicBoltState> moved;
|
||||
for (std::map<MWWorld::Ptr, MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()))
|
||||
{
|
||||
deleteObject(it->first);
|
||||
mMagicBolts.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr ptr = it->first;
|
||||
|
||||
Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
|
||||
|
||||
// TODO: Why -rot.z, but not -rot.x? (note: same issue in MovementSolver::move)
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(-rot.z), Ogre::Vector3::UNIT_Z);
|
||||
orient = orient * Ogre::Quaternion(Ogre::Radian(rot.x), Ogre::Vector3::UNIT_X);
|
||||
|
||||
|
||||
Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation();
|
||||
static float fTargetSpellMaxSpeed = getStore().get<ESM::GameSetting>().find("fTargetSpellMaxSpeed")->getFloat();
|
||||
float speed = fTargetSpellMaxSpeed * it->second.mSpeed;
|
||||
|
||||
|
@ -2279,7 +2426,7 @@ namespace MWWorld
|
|||
explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName);
|
||||
|
||||
deleteObject(ptr);
|
||||
mProjectiles.erase(it++);
|
||||
mMagicBolts.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2291,29 +2438,29 @@ namespace MWWorld
|
|||
if (!ptr.getRefData().getCount())
|
||||
{
|
||||
moved[handle] = it->second;
|
||||
mProjectiles.erase(it++);
|
||||
mMagicBolts.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
for (std::map<std::string, ProjectileState>::iterator it = moved.begin(); it != moved.end(); ++it)
|
||||
for (std::map<std::string, MagicBoltState>::iterator it = moved.begin(); it != moved.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
|
||||
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
|
||||
continue;
|
||||
mProjectiles[getPtrViaHandle(it->first)] = it->second;
|
||||
mMagicBolts[getPtrViaHandle(it->first)] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::objectLeftActiveCell(Ptr object, Ptr movedPtr)
|
||||
{
|
||||
// For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information
|
||||
if (mProjectiles.find(object) != mProjectiles.end())
|
||||
{
|
||||
if (mMagicBolts.find(object) != mMagicBolts.end())
|
||||
deleteObject(movedPtr);
|
||||
if (mProjectiles.find(object) != mProjectiles.end())
|
||||
deleteObject(movedPtr);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string>& World::getContentFiles() const
|
||||
|
@ -2650,7 +2797,7 @@ namespace MWWorld
|
|||
MWWorld::ManualRef ref(getStore(), selectedCreature, 1);
|
||||
ref.getPtr().getCellRef().mPos = ipos;
|
||||
|
||||
safePlaceObject(ref.getPtr(),*cell,ipos);
|
||||
safePlaceObject(ref.getPtr(), cell, ipos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace MWWorld
|
|||
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||
///< only holds doors that are currently moving. 0 means closing, 1 opening
|
||||
|
||||
struct ProjectileState
|
||||
struct MagicBoltState
|
||||
{
|
||||
// Id of spell or enchantment to apply when it hits
|
||||
std::string mId;
|
||||
|
@ -108,7 +108,21 @@ namespace MWWorld
|
|||
bool mStack;
|
||||
};
|
||||
|
||||
struct ProjectileState
|
||||
{
|
||||
// Actor who shot this projectile
|
||||
std::string mActorHandle;
|
||||
|
||||
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
|
||||
|
||||
Ogre::Vector3 mVelocity;
|
||||
};
|
||||
|
||||
std::map<MWWorld::Ptr, MagicBoltState> mMagicBolts;
|
||||
std::map<MWWorld::Ptr, ProjectileState> mProjectiles;
|
||||
|
||||
std::string mStartCell;
|
||||
|
||||
void updateWeather(float duration);
|
||||
int getDaysPerMonth (int month) const;
|
||||
|
||||
|
@ -117,7 +131,7 @@ namespace MWWorld
|
|||
bool moveObjectImp (const Ptr& ptr, float x, float y, float z);
|
||||
///< @return true if the active cell (cell player is in) changed
|
||||
|
||||
Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, ESM::Position pos, bool adjustPos=true);
|
||||
Ptr copyObjectToCell(const Ptr &ptr, CellStore* cell, ESM::Position pos, bool adjustPos=true);
|
||||
|
||||
void updateWindowManager ();
|
||||
void performUpdateSceneQueries ();
|
||||
|
@ -134,6 +148,7 @@ namespace MWWorld
|
|||
void processDoors(float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
||||
void moveMagicBolts(float duration);
|
||||
void moveProjectiles(float duration);
|
||||
|
||||
void doPhysics(float duration);
|
||||
|
@ -167,11 +182,13 @@ namespace MWWorld
|
|||
const Files::Collections& fileCollections,
|
||||
const std::vector<std::string>& contentFiles,
|
||||
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir,
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride);
|
||||
ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap,
|
||||
int activationDistanceOverride, const std::string& startCell);
|
||||
|
||||
virtual ~World();
|
||||
|
||||
virtual void startNewGame();
|
||||
virtual void startNewGame (bool bypass);
|
||||
///< \param bypass Bypass regular game start.
|
||||
|
||||
virtual void clear();
|
||||
|
||||
|
@ -341,7 +358,7 @@ namespace MWWorld
|
|||
virtual void deleteObject (const Ptr& ptr);
|
||||
|
||||
virtual void moveObject (const Ptr& ptr, float x, float y, float z);
|
||||
virtual void moveObject (const Ptr& ptr, CellStore &newCell, float x, float y, float z);
|
||||
virtual void moveObject (const Ptr& ptr, CellStore* newCell, float x, float y, float z);
|
||||
|
||||
virtual void scaleObject (const Ptr& ptr, float scale);
|
||||
|
||||
|
@ -351,7 +368,7 @@ namespace MWWorld
|
|||
|
||||
virtual void localRotateObject (const Ptr& ptr, float x, float y, float z);
|
||||
|
||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos);
|
||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos);
|
||||
///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr.
|
||||
|
||||
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
|
||||
|
@ -552,8 +569,10 @@ namespace MWWorld
|
|||
*/
|
||||
virtual void castSpell (const MWWorld::Ptr& actor);
|
||||
|
||||
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
||||
|
||||
virtual const std::vector<std::string>& getContentFiles() const;
|
||||
|
|
|
@ -1,26 +1,75 @@
|
|||
function(enable_unity_build UB_SUFFIX SOURCE_VARIABLE_NAME)
|
||||
set(files ${SOURCE_VARIABLE_NAME})
|
||||
# Generate a unique filename for the unity build translation unit
|
||||
set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${UB_SUFFIX}.cpp)
|
||||
# Exclude all translation units from compilation
|
||||
set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true)
|
||||
# Open the ub file
|
||||
FILE(WRITE ${unit_build_file} "// Unity Build generated by CMake\n")
|
||||
# Add include statement for each translation unit
|
||||
foreach(source_file ${files} )
|
||||
FILE( APPEND ${unit_build_file} "#include <${source_file}>\n")
|
||||
endforeach(source_file)
|
||||
# Complement list of translation units with the name of ub
|
||||
set(${SOURCE_VARIABLE_NAME} ${${SOURCE_VARIABLE_NAME}} ${unit_build_file} PARENT_SCOPE)
|
||||
endfunction(enable_unity_build)
|
||||
|
||||
|
||||
|
||||
macro (add_openmw_dir dir)
|
||||
set (files)
|
||||
foreach (u ${ARGN})
|
||||
file (GLOB ALL "${dir}/${u}.[ch]pp")
|
||||
foreach (f ${ALL})
|
||||
list (APPEND files "${f}")
|
||||
list (APPEND OPENMW_FILES "${f}")
|
||||
endforeach (f)
|
||||
endforeach (u)
|
||||
source_group ("apps\\openmw\\${dir}" FILES ${files})
|
||||
set (files)
|
||||
set (cppfiles)
|
||||
foreach (u ${ARGN})
|
||||
|
||||
# Add cpp and hpp to OPENMW_FILES
|
||||
file (GLOB ALL "${dir}/${u}.[ch]pp")
|
||||
foreach (f ${ALL})
|
||||
list (APPEND files "${f}")
|
||||
list (APPEND OPENMW_FILES "${f}")
|
||||
endforeach (f)
|
||||
|
||||
# Add cpp to unity build
|
||||
file (GLOB ALL "${dir}/${u}.cpp")
|
||||
foreach (f ${ALL})
|
||||
list (APPEND cppfiles "${f}")
|
||||
endforeach (f)
|
||||
|
||||
endforeach (u)
|
||||
|
||||
if (OPENMW_UNITY_BUILD)
|
||||
enable_unity_build(${dir} "${cppfiles}")
|
||||
list (APPEND OPENMW_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp)
|
||||
endif()
|
||||
|
||||
source_group ("apps\\openmw\\${dir}" FILES ${files})
|
||||
endmacro (add_openmw_dir)
|
||||
|
||||
macro (add_component_dir dir)
|
||||
set (files)
|
||||
foreach (u ${ARGN})
|
||||
file (GLOB ALL "${dir}/${u}.[ch]pp")
|
||||
foreach (f ${ALL})
|
||||
list (APPEND files "${f}")
|
||||
list (APPEND COMPONENT_FILES "${f}")
|
||||
endforeach (f)
|
||||
endforeach (u)
|
||||
source_group ("components\\${dir}" FILES ${files})
|
||||
set (files)
|
||||
set (cppfiles)
|
||||
|
||||
foreach (u ${ARGN})
|
||||
file (GLOB ALL "${dir}/${u}.[ch]pp")
|
||||
|
||||
foreach (f ${ALL})
|
||||
list (APPEND files "${f}")
|
||||
list (APPEND COMPONENT_FILES "${f}")
|
||||
endforeach (f)
|
||||
|
||||
# Add cpp to unity build
|
||||
file (GLOB ALL "${dir}/${u}.cpp")
|
||||
foreach (f ${ALL})
|
||||
list (APPEND cppfiles "${f}")
|
||||
endforeach (f)
|
||||
|
||||
endforeach (u)
|
||||
|
||||
if (OPENMW_UNITY_BUILD)
|
||||
enable_unity_build(${dir} "${cppfiles}")
|
||||
list (APPEND COMPONENT_FILES ${CMAKE_CURRENT_BINARY_DIR}/ub_${dir}.cpp)
|
||||
endif()
|
||||
|
||||
source_group ("components\\${dir}" FILES ${files})
|
||||
endmacro (add_component_dir)
|
||||
|
||||
macro (add_component_qt_dir dir)
|
||||
|
|
|
@ -111,7 +111,7 @@ void BSAFile::readHeader()
|
|||
fail("Directory information larger than entire archive");
|
||||
|
||||
// Read the offset info into a temporary buffer
|
||||
vector<uint32_t> offsets(3*filenum);
|
||||
std::vector<uint32_t> offsets(3*filenum);
|
||||
input.read(reinterpret_cast<char*>(&offsets[0]), 12*filenum);
|
||||
|
||||
// Read the string table
|
||||
|
|
|
@ -18,7 +18,7 @@ bool Compiler::DeclarationParser::parseName (const std::string& name, const Toke
|
|||
{
|
||||
if (mState==State_Name)
|
||||
{
|
||||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
std::string name2 = ::Misc::StringUtils::lowerCase (name);
|
||||
|
||||
char type = mLocals.getType (name2);
|
||||
|
||||
|
@ -80,4 +80,4 @@ bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, S
|
|||
void Compiler::DeclarationParser::reset()
|
||||
{
|
||||
mState = State_Begin;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Interpreter
|
|||
return literalBlock+offset;
|
||||
}
|
||||
|
||||
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context)
|
||||
void Runtime::configure (const Type_Code *code, int codeSize, Context& context)
|
||||
{
|
||||
clear();
|
||||
|
||||
|
|
|
@ -282,9 +282,6 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
size_t wantedLod = 0;
|
||||
float cellWorldSize = mTerrain->getStorage()->getCellWorldSize();
|
||||
|
||||
if (!mTerrain->getDistantLandEnabled() && dist > cellWorldSize)
|
||||
return true;
|
||||
|
||||
if (dist > cellWorldSize*64)
|
||||
wantedLod = 6;
|
||||
else if (dist > cellWorldSize*32)
|
||||
|
@ -357,6 +354,7 @@ bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
|||
|
||||
if (!childrenLoaded)
|
||||
{
|
||||
mChunk->setVisible(true);
|
||||
// Make sure child scene nodes are detached until all children are loaded
|
||||
mSceneNode->removeAllChildren();
|
||||
}
|
||||
|
@ -391,6 +389,8 @@ void QuadTreeNode::load(const LoadResponseData &data)
|
|||
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data);
|
||||
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
|
||||
mChunk->setCastShadows(true);
|
||||
if (!mTerrain->getDistantLandEnabled())
|
||||
mChunk->setRenderingDistance(8192);
|
||||
mSceneNode->attachObject(mChunk);
|
||||
|
||||
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
|
||||
|
@ -437,7 +437,7 @@ void QuadTreeNode::unload(bool recursive)
|
|||
if (recursive && hasChildren())
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mChildren[i]->unload();
|
||||
mChildren[i]->unload(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,12 +82,14 @@ Sandy Carter (bwrsandman) - Arch Linux
|
|||
|
||||
|
||||
Public Relations and Translations:
|
||||
Alex McKibben (WeirdSexy) - Podcaster
|
||||
Artem Kotsynyak (greye) - Russian News Writer
|
||||
Jim Clauwaert (Zedd) - Public Outreach
|
||||
Julien Voisin (jvoisin/ap0) - French News Writer
|
||||
Lukasz Gromanowski (lgro) - English News Writer
|
||||
Mickey Lyle (raevol) - Release Manager
|
||||
Pithorn - Chinese News Writer
|
||||
sir_herrbatka - English/Polish News Writer
|
||||
Alex McKibben (WeirdSexy) - Podcaster
|
||||
sir_herrbatka - Polish News Writer
|
||||
|
||||
|
||||
Website:
|
||||
|
|
|
@ -2,5 +2,11 @@
|
|||
|
||||
<MyGUI type="Layout">
|
||||
<!-- The entire screen -->
|
||||
<Widget type="Widget" layer="Windows" position="0 0 300 300" name="_Main" />
|
||||
<Widget type="Widget" layer="Windows" position="0 0 300 300" name="_Main" >
|
||||
<Widget type="TextBox" skin="SandText" position="0 250 300 50" align="Bottom" name="VersionText">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<Property key="TextShadow" value="true"/>
|
||||
<Property key="TextShadowColour" value="0 0 0"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
|
|
@ -597,6 +597,8 @@ namespace Physic
|
|||
std::vector<std::string> PhysicEngine::getCollisions(const std::string& name)
|
||||
{
|
||||
RigidBody* body = getRigidBody(name);
|
||||
if (!body) // fall back to raycasting body if there is no collision body
|
||||
body = getRigidBody(name, true);
|
||||
ContactTestResultCallback callback;
|
||||
dynamicsWorld->contactTest(body, callback);
|
||||
return callback.mResult;
|
||||
|
|
|
@ -8,7 +8,6 @@ License: GPL (see GPL3.txt for more information)
|
|||
Website: http://www.openmw.org
|
||||
|
||||
Font Licenses:
|
||||
EBGaramond-Regular.ttf: OFL (see OFL.txt for more information)
|
||||
DejaVuLGCSansMono.ttf: custom (see DejaVu Font License.txt for more information)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue