1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-07 13:20:25 +00:00
This commit is contained in:
Kevin Poitra 2014-05-28 12:29:40 -05:00
commit 251df73407
38 changed files with 358 additions and 123 deletions

View File

@ -387,7 +387,7 @@ std::string magicEffectLabel(int idx)
"sEffectSummonCreature04", "sEffectSummonCreature04",
"sEffectSummonCreature05" "sEffectSummonCreature05"
}; };
if (idx >= 0 && idx <= 143) if (idx >= 0 && idx <= 142)
return magicEffectLabels[idx]; return magicEffectLabels[idx];
else else
return "Invalid"; return "Invalid";
@ -471,7 +471,7 @@ std::string skillLabel(int idx)
"Speechcraft", "Speechcraft",
"Hand-to-hand" "Hand-to-hand"
}; };
if (idx >= 0 && idx <= 27) if (idx >= 0 && idx <= 26)
return skillLabels[idx]; return skillLabels[idx];
else else
return "Invalid"; return "Invalid";
@ -498,7 +498,7 @@ std::string rangeTypeLabel(int idx)
"Touch", "Touch",
"Target" "Target"
}; };
if (idx >= 0 && idx <= 3) if (idx >= 0 && idx <= 2)
return rangeTypeLabels[idx]; return rangeTypeLabels[idx];
else else
return "Invalid"; return "Invalid";

View File

@ -707,9 +707,9 @@ void Record<ESM::Faction>::print()
std::cout << " Faction Reaction: " std::cout << " Faction Reaction: "
<< mData.mData.mRankData[i].mFactReaction << std::endl; << mData.mData.mRankData[i].mFactReaction << std::endl;
} }
std::vector<ESM::Faction::Reaction>::iterator rit; std::map<std::string, int>::iterator rit;
for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); rit++) for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); rit++)
std::cout << " Reaction: " << rit->mReaction << " = " << rit->mFaction << std::endl; std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl;
} }
template<> template<>

View File

@ -68,6 +68,12 @@ namespace MWBase
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
/// Changes faction1's opinion of faction2 by \a diff.
virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0;
/// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0;
}; };
} }

View File

@ -642,6 +642,8 @@ namespace MWDialogue
if (iter->second) if (iter->second)
state.mKnownTopics.push_back (iter->first); state.mKnownTopics.push_back (iter->first);
state.mModFactionReaction = mModFactionReaction;
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
state.save (writer); state.save (writer);
writer.endRecord (ESM::REC_DIAS); writer.endRecord (ESM::REC_DIAS);
@ -661,9 +663,46 @@ namespace MWDialogue
iter!=state.mKnownTopics.end(); ++iter) iter!=state.mKnownTopics.end(); ++iter)
if (store.get<ESM::Dialogue>().search (*iter)) if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (std::make_pair (*iter, true)); mKnownTopics.insert (std::make_pair (*iter, true));
mModFactionReaction = state.mModFactionReaction;
} }
} }
void DialogueManager::modFactionReaction(const std::string &faction1, const std::string &faction2, int diff)
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
// Make sure the factions exist
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
std::map<std::string, int>& map = mModFactionReaction[fact1];
if (map.find(fact2) == map.end())
map[fact2] = 0;
map[fact2] += diff;
}
int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
ModFactionReactionMap::const_iterator map = mModFactionReaction.find(fact1);
int diff = 0;
if (map != mModFactionReaction.end() && map->second.find(fact2) != map->second.end())
diff = map->second.at(fact2);
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
std::map<std::string, int>::const_iterator it = faction->mReactions.begin();
for (; it != faction->mReactions.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->first, fact2))
return it->second + diff;
}
return diff;
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text) std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{ {

View File

@ -24,6 +24,11 @@ namespace MWDialogue
{ {
std::map<std::string, ESM::Dialogue> mDialogueMap; std::map<std::string, ESM::Dialogue> mDialogueMap;
std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows. std::map<std::string, bool> mKnownTopics;// Those are the topics the player knows.
// Modified faction reactions. <Faction1, <Faction2, Difference> >
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
ModFactionReactionMap mModFactionReaction;
std::list<std::string> mActorKnownTopics; std::list<std::string> mActorKnownTopics;
Translation::Storage& mTranslationDataStorage; Translation::Storage& mTranslationDataStorage;
@ -86,6 +91,12 @@ namespace MWDialogue
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type); virtual void readRecord (ESM::ESMReader& reader, int32_t type);
/// Changes faction1's opinion of faction2 by \a diff.
virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff);
/// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const;
}; };

View File

@ -396,16 +396,15 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
int value = 0; int value = 0;
const ESM::Faction& faction =
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find (factionId);
MWMechanics::NpcStats& playerStats = player.getClass().getNpcStats (player); MWMechanics::NpcStats& playerStats = player.getClass().getNpcStats (player);
for (std::vector<ESM::Faction::Reaction>::const_iterator iter (faction.mReactions.begin()); std::map<std::string, int>::const_iterator playerFactionIt = playerStats.getFactionRanks().begin();
iter!=faction.mReactions.end(); ++iter) for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt)
if (playerStats.getFactionRanks().find (iter->mFaction)!=playerStats.getFactionRanks().end()) {
if (low ? iter->mReaction<value : iter->mReaction>value) int reaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(factionId, playerFactionIt->first);
value = iter->mReaction; if (low ? reaction < value : reaction > value)
value = reaction;
}
return value; return value;
} }

View File

@ -492,7 +492,8 @@ namespace MWInput
} }
if (arg.keysym.sym == SDLK_x && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) if (arg.keysym.sym == SDLK_x && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL)))
{ {
std::string text = edit->getTextSelection(); // Discard color codes and other escape characters
std::string text = MyGUI::TextIterator::getOnlyText(edit->getTextSelection());
if (text.length()) if (text.length())
{ {
SDL_SetClipboardText(text.c_str()); SDL_SetClipboardText(text.c_str());
@ -504,7 +505,8 @@ namespace MWInput
{ {
if (arg.keysym.sym == SDLK_c && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) if (arg.keysym.sym == SDLK_c && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL)))
{ {
std::string text = edit->getTextSelection(); // Discard color codes and other escape characters
std::string text = MyGUI::TextIterator::getOnlyText(edit->getTextSelection());
if (text.length()) if (text.length())
SDL_SetClipboardText(text.c_str()); SDL_SetClipboardText(text.c_str());
} }

View File

@ -407,29 +407,25 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
} }
void CharacterController::playRandomDeath(float startpoint) void CharacterController::playDeath(float startpoint, CharacterState death)
{ {
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) switch (death)
{ {
mDeathState = CharState_SwimDeath; case CharState_SwimDeath:
mCurrentDeath = "swimdeath"; mCurrentDeath = "swimdeath";
} break;
else if (mHitState == CharState_KnockDown) case CharState_DeathKnockDown:
{
mDeathState = CharState_DeathKnockDown;
mCurrentDeath = "deathknockdown"; mCurrentDeath = "deathknockdown";
} break;
else if (mHitState == CharState_KnockOut) case CharState_DeathKnockOut:
{
mDeathState = CharState_DeathKnockOut;
mCurrentDeath = "deathknockout"; mCurrentDeath = "deathknockout";
break;
default:
mCurrentDeath = "death" + Ogre::StringConverter::toString(death - CharState_Death1 + 1);
} }
else mDeathState = death;
{
int selected=0; mPtr.getClass().getCreatureStats(mPtr).setDeathAnimation(mDeathState - CharState_Death1);
mCurrentDeath = chooseRandomGroup("death", &selected);
mDeathState = static_cast<CharacterState>(CharState_Death1 + (selected-1));
}
// For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually.
mMovementState = CharState_None; mMovementState = CharState_None;
@ -440,6 +436,29 @@ void CharacterController::playRandomDeath(float startpoint)
false, 1.0f, "start", "stop", startpoint, 0); false, 1.0f, "start", "stop", startpoint, 0);
} }
void CharacterController::playRandomDeath(float startpoint)
{
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath"))
{
mDeathState = CharState_SwimDeath;
}
else if (mHitState == CharState_KnockDown)
{
mDeathState = CharState_DeathKnockDown;
}
else if (mHitState == CharState_KnockOut)
{
mDeathState = CharState_DeathKnockOut;
}
else
{
int selected=0;
chooseRandomGroup("death", &selected);
mDeathState = static_cast<CharacterState>(CharState_Death1 + (selected-1));
}
playDeath(startpoint, mDeathState);
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
, mAnimation(anim) , mAnimation(anim)
@ -497,7 +516,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
{ {
playRandomDeath(1.0f); int deathindex = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation();
playDeath(1.0f, CharacterState(CharState_Death1 + deathindex));
} }
else else
refreshCurrentAnims(mIdleState, mMovementState, true); refreshCurrentAnims(mIdleState, mMovementState, true);

View File

@ -181,6 +181,7 @@ class CharacterController
void updateVisibility(); void updateVisibility();
void playDeath(float startpoint, CharacterState death);
void playRandomDeath(float startpoint = 0.0f); void playRandomDeath(float startpoint = 0.0f);
/// choose a random animation group with \a prefix and numeric suffix /// choose a random animation group with \a prefix and numeric suffix

View File

@ -22,7 +22,8 @@ namespace MWMechanics
mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false),
mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false),
mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f),
mLastRestock(0,0), mGoldPool(0), mActorId(-1) mLastRestock(0,0), mGoldPool(0), mActorId(-1),
mDeathAnimation(0)
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i] = 0; mAiSettings[i] = 0;
@ -498,6 +499,7 @@ namespace MWMechanics
state.mDrawState = mDrawState; state.mDrawState = mDrawState;
state.mLevel = mLevel; state.mLevel = mLevel;
state.mActorId = mActorId; state.mActorId = mActorId;
state.mDeathAnimation = mDeathAnimation;
mSpells.writeState(state.mSpells); mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells); mActiveSpells.writeState(state.mActiveSpells);
@ -537,6 +539,7 @@ namespace MWMechanics
mDrawState = DrawState_(state.mDrawState); mDrawState = DrawState_(state.mDrawState);
mLevel = state.mLevel; mLevel = state.mLevel;
mActorId = state.mActorId; mActorId = state.mActorId;
mDeathAnimation = state.mDeathAnimation;
mSpells.readState(state.mSpells); mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells); mActiveSpells.readState(state.mActiveSpells);
@ -590,4 +593,14 @@ namespace MWMechanics
{ {
esm.getHNT(sActorId, "COUN"); esm.getHNT(sActorId, "COUN");
} }
unsigned char CreatureStats::getDeathAnimation() const
{
return mDeathAnimation;
}
void CreatureStats::setDeathAnimation(unsigned char index)
{
mDeathAnimation = index;
}
} }

View File

@ -64,6 +64,9 @@ namespace MWMechanics
int mActorId; int mActorId;
// The index of the death animation that was played
unsigned char mDeathAnimation;
protected: protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods. // These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
bool mIsWerewolf; bool mIsWerewolf;
@ -250,6 +253,9 @@ namespace MWMechanics
void setGoldPool(int pool); void setGoldPool(int pool);
int getGoldPool() const; int getGoldPool() const;
unsigned char getDeathAnimation() const;
void setDeathAnimation(unsigned char index);
int getActorId(); int getActorId();
///< Will generate an actor ID, if the actor does not have one yet. ///< Will generate an actor ID, if the actor does not have one yet.

View File

@ -498,27 +498,24 @@ namespace MWMechanics
if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end()) if (playerStats.getFactionRanks().find(npcFaction) != playerStats.getFactionRanks().end())
{ {
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.begin(); if (!playerStats.getExpelled(npcFaction))
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.end(); ++it)
{ {
if(Misc::StringUtils::ciEqual(it->mFaction, npcFaction) reaction = playerStats.getFactionReputation(npcFaction);
&& !playerStats.getExpelled(it->mFaction))
reaction = it->mReaction; rank = playerStats.getFactionRanks().find(npcFaction)->second;
} }
rank = playerStats.getFactionRanks().find(npcFaction)->second;
} }
else if (npcFaction != "") else if (!npcFaction.empty())
{ {
for(std::vector<ESM::Faction::Reaction>::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.begin(); std::map<std::string, int>::const_iterator playerFactionIt = playerStats.getFactionRanks().begin();
it != MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(npcFaction)->mReactions.end();++it) for (; playerFactionIt != playerStats.getFactionRanks().end(); ++playerFactionIt)
{ {
if(playerStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerStats.getFactionRanks().end() ) std::string itFaction = playerFactionIt->first;
{
if(it->mReaction < reaction) int itReaction = MWBase::Environment::get().getDialogueManager()->getFactionReaction(npcFaction, itFaction);
reaction = it->mReaction; if (playerFactionIt == playerStats.getFactionRanks().begin() || itReaction < reaction)
} reaction = itReaction;
} }
rank = 0;
} }
else else
{ {

View File

@ -54,17 +54,12 @@ void Actors::insertBegin(const MWWorld::Ptr &ptr)
// Convert MW rotation to a quaternion: // Convert MW rotation to a quaternion:
f = ptr.getCellRef().getPosition().rot; f = ptr.getCellRef().getPosition().rot;
// Rotate around X axis // For rendering purposes, actors should only rotate around the Z axis.
Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // X rotation is used for camera rotation (for the player) and for
// ranged magic / ranged weapon aiming.
// Rotate around Y axis
Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y);
// Rotate around Z axis
Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z);
// Rotates first around z, then y, then x insert->setOrientation(zr);
insert->setOrientation(xr*yr*zr);
ptr.getRefData().setBaseNode(insert); ptr.getRefData().setBaseNode(insert);
} }

View File

@ -775,11 +775,11 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
} }
/* Look in reverse; last-inserted source has priority. */ /* Look in reverse; last-inserted source has priority. */
AnimState state;
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
for(;iter != mAnimSources.rend();++iter) for(;iter != mAnimSources.rend();++iter)
{ {
const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys;
AnimState state;
if(reset(state, textkeys, groupname, start, stop, startpoint)) if(reset(state, textkeys, groupname, start, stop, startpoint))
{ {
state.mSource = *iter; state.mSource = *iter;
@ -821,6 +821,13 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
std::cerr<< "Failed to find animation "<<groupname<<" for "<<mPtr.getCellRef().getRefId() <<std::endl; std::cerr<< "Failed to find animation "<<groupname<<" for "<<mPtr.getCellRef().getRefId() <<std::endl;
resetActiveGroups(); resetActiveGroups();
if (!state.mPlaying && mNonAccumCtrl)
{
// If the animation state is not playing, we need to manually apply the accumulation
// (see updatePosition, which would be called if the animation was playing)
mAccumRoot->setPosition(-mNonAccumCtrl->getTranslation(state.mTime)*mAccumulate);
}
} }
bool Animation::isPlaying(const std::string &groupname) const bool Animation::isPlaying(const std::string &groupname) const

View File

@ -196,6 +196,45 @@ namespace MWScript
} }
}; };
class OpModFactionReaction : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
int modReaction = runtime[0].mInteger;
runtime.pop();
MWBase::Environment::get().getDialogueManager()->modFactionReaction(faction1, faction2, modReaction);
}
};
class OpGetFactionReaction : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
// ignore extra garbage argument
runtime.pop();
runtime.push(MWBase::Environment::get().getDialogueManager()
->getFactionReaction(faction1, faction2));
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
@ -215,6 +254,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputationExplicit, new OpGetReputation<ExplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetReputationExplicit, new OpGetReputation<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction);
} }
} }

View File

@ -390,5 +390,7 @@ op 0x200023e: GetPcInJail
op 0x200023f: GetPcTraveling op 0x200023f: GetPcTraveling
op 0x2000240: onKnockout op 0x2000240: onKnockout
op 0x2000241: onKnockoutExplicit op 0x2000241: onKnockoutExplicit
op 0x2000242: ModFactionReaction
op 0x2000243: GetFactionReaction
opcodes 0x2000242-0x3ffffff unused opcodes 0x2000244-0x3ffffff unused

View File

@ -541,6 +541,9 @@ namespace MWScript
runtime.pop(); runtime.pop();
} }
::Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -572,6 +575,9 @@ namespace MWScript
runtime.pop(); runtime.pop();
} }
::Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -607,6 +613,9 @@ namespace MWScript
runtime.pop(); runtime.pop();
} }
::Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);
if(factionID != "") if(factionID != "")
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
@ -645,6 +654,9 @@ namespace MWScript
} }
} }
::Misc::StringUtils::toLower(factionID); ::Misc::StringUtils::toLower(factionID);
// Make sure this faction exists
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(factionID);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if(factionID!="") if(factionID!="")
{ {

View File

@ -5,7 +5,7 @@
namespace ESM namespace ESM
{ {
class ObjectState; struct ObjectState;
} }
namespace MWWorld namespace MWWorld

View File

@ -33,12 +33,12 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
// Cache parent esX files by tracking their indices in the global list of // Cache parent esX files by tracking their indices in the global list of
// all files/readers used by the engine. This will greaty accelerate // all files/readers used by the engine. This will greaty accelerate
// refnumber mangling, as required for handling moved references. // refnumber mangling, as required for handling moved references.
int index = ~0;
const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles(); const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles();
std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList(); std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();
for (size_t j = 0; j < masters.size(); j++) { for (size_t j = 0; j < masters.size(); j++) {
ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]); ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]);
std::string fname = mast.name; std::string fname = mast.name;
int index = ~0;
for (int i = 0; i < esm.getIndex(); i++) { for (int i = 0; i < esm.getIndex(); i++) {
const std::string &candidate = allPlugins->at(i).getContext().filename; const std::string &candidate = allPlugins->at(i).getContext().filename;
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();

View File

@ -492,7 +492,7 @@ namespace MWWorld
return std::make_pair(true, ray.getPoint(len * test.second)); return std::make_pair(true, ray.getPoint(len * test.second));
} }
std::pair<bool, Ogre::Vector3> PhysicsSystem::castRay(float mouseX, float mouseY) std::pair<bool, Ogre::Vector3> PhysicsSystem::castRay(float mouseX, float mouseY, Ogre::Vector3* normal)
{ {
Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay( Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay(
mouseX, mouseX,
@ -504,7 +504,7 @@ namespace MWWorld
_from = btVector3(from.x, from.y, from.z); _from = btVector3(from.x, from.y, from.z);
_to = btVector3(to.x, to.y, to.z); _to = btVector3(to.x, to.y, to.z);
std::pair<std::string, float> result = mEngine->rayTest(_from, _to); std::pair<std::string, float> result = mEngine->rayTest(_from, _to, true, false, normal);
if (result.first == "") if (result.first == "")
return std::make_pair(false, Ogre::Vector3()); return std::make_pair(false, Ogre::Vector3());

View File

@ -70,8 +70,9 @@ namespace MWWorld
std::pair<bool, Ogre::Vector3> std::pair<bool, Ogre::Vector3>
castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len);
std::pair<bool, Ogre::Vector3> castRay(float mouseX, float mouseY); std::pair<bool, Ogre::Vector3> castRay(float mouseX, float mouseY, Ogre::Vector3* normal = NULL);
///< cast ray from the mouse, return true if it hit something and the first result (in OGRE coordinates) ///< cast ray from the mouse, return true if it hit something and the first result
/// @param normal if non-NULL, the hit normal will be written there (if there is a hit)
OEngine::Physic::PhysicEngine* getEngine(); OEngine::Physic::PhysicEngine* getEngine();

View File

@ -1116,13 +1116,15 @@ namespace MWWorld
while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad)
ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad;
Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z);
Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* if (!ptr.getClass().isActor())
Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)); 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)* worldRotQuat;
Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* Ogre::Quaternion rot(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z);
Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)* if (!ptr.getClass().isActor())
Ogre::Quaternion(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z)); rot = Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)*
Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*rot;
ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot);
mPhysics->rotateObject(ptr); mPhysics->rotateObject(ptr);
@ -1618,25 +1620,39 @@ namespace MWWorld
bool World::canPlaceObject(float cursorX, float cursorY) bool World::canPlaceObject(float cursorX, float cursorY)
{ {
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY); Ogre::Vector3 normal(0,0,0);
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY, &normal);
/// \todo also check if the wanted position is on a flat surface, and not e.g. against a vertical wall! if (result.first)
{
if (!result.first) // check if the wanted position is on a flat surface, and not e.g. against a vertical wall
return (normal.angleBetween(Ogre::Vector3(0.f,0.f,1.f)).valueDegrees() < 30);
}
else
return false; return false;
return true;
} }
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) if (!object.getClass().isActor() && adjustPos)
{ {
// Adjust position so the location we wanted ends up in the middle of the object bounding box
Ogre::Vector3 min, max; Ogre::Vector3 min, max;
if (mPhysics->getObjectAABB(object, min, max)) { if (mPhysics->getObjectAABB(object, min, max)) {
pos.pos[0] -= (min.x + max.x) / 2; Ogre::Quaternion xr(Ogre::Radian(-pos.rot[0]), Ogre::Vector3::UNIT_X);
pos.pos[1] -= (min.y + max.y) / 2; Ogre::Quaternion yr(Ogre::Radian(-pos.rot[1]), Ogre::Vector3::UNIT_Y);
pos.pos[2] -= min.z; Ogre::Quaternion zr(Ogre::Radian(-pos.rot[2]), Ogre::Vector3::UNIT_Z);
Ogre::Vector3 adjust (
(min.x + max.x) / 2,
(min.y + max.y) / 2,
min.z
);
adjust = (xr*yr*zr) * adjust;
pos.pos[0] -= adjust.x;
pos.pos[1] -= adjust.y;
pos.pos[2] -= adjust.z;
} }
} }
@ -2526,7 +2542,7 @@ namespace MWWorld
store.remove(*it, it->getRefData().getCount(), ptr); store.remove(*it, it->getRefData().getCount(), ptr);
} }
} }
closestChest.getClass().unlock(closestChest); closestChest.getClass().lock(closestChest,50);
} }
} }

View File

@ -98,7 +98,7 @@ namespace Compiler
else if (t1=='f' || t2=='f') else if (t1=='f' || t2=='f')
mOperands.push_back ('f'); mOperands.push_back ('f');
else else
std::logic_error ("failed to determine result operand type"); throw std::logic_error ("failed to determine result operand type");
} }
void ExprParser::pop() void ExprParser::pop()
@ -730,7 +730,7 @@ namespace Compiler
} }
int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner, int ExprParser::parseArguments (const std::string& arguments, Scanner& scanner,
std::vector<Interpreter::Type_Code>& code, bool invert) std::vector<Interpreter::Type_Code>& code)
{ {
bool optional = false; bool optional = false;
int optionalCount = 0; int optionalCount = 0;
@ -762,15 +762,10 @@ namespace Compiler
if (*iter!='x') if (*iter!='x')
{ {
if (invert) std::vector<Interpreter::Type_Code> tmp;
{ stringParser.append (tmp);
std::vector<Interpreter::Type_Code> tmp;
stringParser.append (tmp);
stack.push (tmp); stack.push (tmp);
}
else
stringParser.append (code);
if (optional) if (optional)
++optionalCount; ++optionalCount;
@ -795,10 +790,7 @@ namespace Compiler
if (type!=*iter) if (type!=*iter)
Generator::convert (tmp, type, *iter); Generator::convert (tmp, type, *iter);
if (invert) stack.push (tmp);
stack.push (tmp);
else
std::copy (tmp.begin(), tmp.end(), std::back_inserter (code));
if (optional) if (optional)
++optionalCount; ++optionalCount;

View File

@ -96,7 +96,7 @@ namespace Compiler
/// \return Type ('l': integer, 'f': float) /// \return Type ('l': integer, 'f': float)
int parseArguments (const std::string& arguments, Scanner& scanner, int parseArguments (const std::string& arguments, Scanner& scanner,
std::vector<Interpreter::Type_Code>& code, bool invert = false); std::vector<Interpreter::Type_Code>& code);
///< Parse sequence of arguments specified by \a arguments. ///< Parse sequence of arguments specified by \a arguments.
/// \param arguments Uses ScriptArgs typedef /// \param arguments Uses ScriptArgs typedef
/// \see Compiler::ScriptArgs /// \see Compiler::ScriptArgs

View File

@ -179,6 +179,8 @@ namespace Compiler
opcodeGetReputationExplicit); opcodeGetReputationExplicit);
extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction,
opcodeSameFactionExplicit); opcodeSameFactionExplicit);
extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction);
extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction);
} }
} }

View File

@ -166,7 +166,7 @@ namespace Compiler
if (!arguments.empty()) if (!arguments.empty())
{ {
mExprParser.reset(); mExprParser.reset();
mExprParser.parseArguments (arguments, scanner, mCode, true); mExprParser.parseArguments (arguments, scanner, mCode);
} }
mName = name; mName = name;
@ -278,7 +278,7 @@ namespace Compiler
mExplicit.clear(); mExplicit.clear();
} }
int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true); int optionals = mExprParser.parseArguments (argumentType, scanner, mCode);
extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals);
mState = EndState; mState = EndState;
@ -363,14 +363,14 @@ namespace Compiler
case Scanner::K_startscript: case Scanner::K_startscript:
mExprParser.parseArguments ("c", scanner, mCode, true); mExprParser.parseArguments ("c", scanner, mCode);
Generator::startScript (mCode); Generator::startScript (mCode);
mState = EndState; mState = EndState;
return true; return true;
case Scanner::K_stopscript: case Scanner::K_stopscript:
mExprParser.parseArguments ("c", scanner, mCode, true); mExprParser.parseArguments ("c", scanner, mCode);
Generator::stopScript (mCode); Generator::stopScript (mCode);
mState = EndState; mState = EndState;
return true; return true;

View File

@ -152,6 +152,8 @@ namespace Compiler
const int opcodeGetReputationExplicit = 0x20001b2; const int opcodeGetReputationExplicit = 0x20001b2;
const int opcodeSameFaction = 0x20001b5; const int opcodeSameFaction = 0x20001b5;
const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeSameFactionExplicit = 0x20001b6;
const int opcodeModFactionReaction = 0x2000242;
const int opcodeGetFactionReaction = 0x2000243;
} }
namespace Gui namespace Gui

View File

@ -74,6 +74,9 @@ void ESM::CreatureStats::load (ESMReader &esm)
mActorId = -1; mActorId = -1;
esm.getHNOT (mActorId, "ACID"); esm.getHNOT (mActorId, "ACID");
mDeathAnimation = 0;
esm.getHNOT (mDeathAnimation, "DANM");
mSpells.load(esm); mSpells.load(esm);
mActiveSpells.load(esm); mActiveSpells.load(esm);
} }
@ -152,6 +155,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mActorId != -1) if (mActorId != -1)
esm.writeHNT ("ACID", mActorId); esm.writeHNT ("ACID", mActorId);
if (mDeathAnimation)
esm.writeHNT ("DANM", mDeathAnimation);
mSpells.save(esm); mSpells.save(esm);
mActiveSpells.save(esm); mActiveSpells.save(esm);
} }

View File

@ -46,6 +46,7 @@ namespace ESM
std::string mLastHitObject; std::string mLastHitObject;
bool mRecalcDynamicStats; bool mRecalcDynamicStats;
int mDrawState; int mDrawState;
unsigned char mDeathAnimation;
int mLevel; int mLevel;

View File

@ -8,6 +8,20 @@ void ESM::DialogueState::load (ESMReader &esm)
{ {
while (esm.isNextSub ("TOPI")) while (esm.isNextSub ("TOPI"))
mKnownTopics.push_back (esm.getHString()); mKnownTopics.push_back (esm.getHString());
while (esm.isNextSub ("FACT"))
{
std::string faction = esm.getHString();
while (esm.isNextSub ("REAC"))
{
std::string faction2 = esm.getHString();
int reaction;
esm.getHNT(reaction, "INTV");
mModFactionReaction[faction][faction2] = reaction;
}
}
} }
void ESM::DialogueState::save (ESMWriter &esm) const void ESM::DialogueState::save (ESMWriter &esm) const
@ -16,6 +30,18 @@ void ESM::DialogueState::save (ESMWriter &esm) const
iter!=mKnownTopics.end(); ++iter) iter!=mKnownTopics.end(); ++iter)
{ {
esm.writeHNString ("TOPI", *iter); esm.writeHNString ("TOPI", *iter);
} }
}
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mModFactionReaction.begin();
iter != mModFactionReaction.end(); ++iter)
{
esm.writeHNString ("FACT", iter->first);
for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin();
reactIter != iter->second.end(); ++reactIter)
{
esm.writeHNString ("REAC", reactIter->first);
esm.writeHNT ("INTV", reactIter->second);
}
}
}

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
namespace ESM namespace ESM
{ {
@ -15,9 +16,11 @@ namespace ESM
{ {
std::vector<std::string> mKnownTopics; std::vector<std::string> mKnownTopics;
std::map<std::string, std::map<std::string, int> > mModFactionReaction;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;
}; };
} }
#endif #endif

View File

@ -44,10 +44,10 @@ void Faction::load(ESMReader &esm)
// Read faction response values // Read faction response values
while (esm.hasMoreSubs()) while (esm.hasMoreSubs())
{ {
Reaction r; std::string faction = esm.getHNString("ANAM");
r.mFaction = esm.getHNString("ANAM"); int reaction;
esm.getHNT(r.mReaction, "INTV"); esm.getHNT(reaction, "INTV");
mReactions.push_back(r); mReactions[faction] = reaction;
} }
} }
void Faction::save(ESMWriter &esm) const void Faction::save(ESMWriter &esm) const
@ -64,10 +64,10 @@ void Faction::save(ESMWriter &esm) const
esm.writeHNT("FADT", mData, 240); esm.writeHNT("FADT", mData, 240);
for (std::vector<Reaction>::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) for (std::map<std::string, int>::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it)
{ {
esm.writeHNString("ANAM", it->mFaction); esm.writeHNString("ANAM", it->first);
esm.writeHNT("INTV", it->mReaction); esm.writeHNT("INTV", it->second);
} }
} }

View File

@ -2,7 +2,7 @@
#define OPENMW_ESM_FACT_H #define OPENMW_ESM_FACT_H
#include <string> #include <string>
#include <vector> #include <map>
namespace ESM namespace ESM
{ {
@ -53,13 +53,8 @@ struct Faction
FADTstruct mData; FADTstruct mData;
struct Reaction // <Faction ID, Reaction>
{ std::map<std::string, int> mReactions;
std::string mFaction;
int mReaction;
};
std::vector<Reaction> mReactions;
// Name of faction ranks (may be empty for NPC factions) // Name of faction ranks (may be empty for NPC factions)
std::string mRanks[10]; std::string mRanks[10];

View File

@ -155,6 +155,7 @@ struct KeyListT {
static const int sLinearInterpolation = 1; static const int sLinearInterpolation = 1;
static const int sQuadraticInterpolation = 2; static const int sQuadraticInterpolation = 2;
static const int sTBCInterpolation = 3; static const int sTBCInterpolation = 3;
static const int sXYZInterpolation = 4;
int mInterpolationType; int mInterpolationType;
VecType mKeys; VecType mKeys;
@ -199,6 +200,38 @@ struct KeyListT {
key.mContinuity = nif->getFloat(); key.mContinuity = nif->getFloat();
} }
} }
//\FIXME This now reads the correct amount of data in the file, but doesn't actually do anything with it.
else if(mInterpolationType == sXYZInterpolation)
{
if (count != 1)
{
nif->file->fail("count should always be '1' for XYZ_ROTATION_KEY. Retrieved Value: "+Ogre::StringConverter::toString(count));
return;
}
//KeyGroup (see http://niftools.sourceforge.net/doc/nif/NiKeyframeData.html)
//Chomp unknown and possibly unused float
nif->getFloat();
for(size_t i=0;i<3;++i)
{
unsigned int numKeys = nif->getInt();
if(numKeys != 0)
{
int interpolationTypeAgain = nif->getInt();
if( interpolationTypeAgain != sLinearInterpolation)
{
nif->file->fail("XYZ_ROTATION_KEY's KeyGroup keyType must be '1' (Linear Interpolation). Retrieved Value: "+Ogre::StringConverter::toString(interpolationTypeAgain));
return;
}
for(size_t j = 0;j < numKeys;j++)
{
//For now just chomp these
nif->getFloat();
nif->getFloat();
}
}
nif->file->warn("XYZ_ROTATION_KEY read, but not used!");
}
}
else if (mInterpolationType == 0) else if (mInterpolationType == 0)
{ {
if (count != 0) if (count != 0)

View File

@ -17,6 +17,7 @@ Alex McKibben (WeirdSexy)
Alexander Nadeau (wareya) Alexander Nadeau (wareya)
Alexander Olofsson (Ace) Alexander Olofsson (Ace)
Artem Kotsynyak (greye) Artem Kotsynyak (greye)
Arthur Moore (EmperorArthur)
athile athile
Britt Mathis (galdor557) Britt Mathis (galdor557)
BrotherBrick BrotherBrick

View File

@ -206,7 +206,7 @@ namespace sh
std::string getCacheFolder () { return mPlatform->getCacheFolder (); } std::string getCacheFolder () { return mPlatform->getCacheFolder (); }
bool getReadSourceCache() { return mReadSourceCache; } bool getReadSourceCache() { return mReadSourceCache; }
bool getWriteSourceCache() { return mReadSourceCache; } bool getWriteSourceCache() { return mWriteSourceCache; }
public: public:
bool getWriteMicrocodeCache() { return mWriteMicrocodeCache; } // Fixme bool getWriteMicrocodeCache() { return mWriteMicrocodeCache; } // Fixme

View File

@ -677,7 +677,7 @@ namespace Physic
{ {
} }
std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap) std::pair<std::string,float> PhysicEngine::rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly,bool ignoreHeightMap, Ogre::Vector3* normal)
{ {
std::string name = ""; std::string name = "";
float d = -1; float d = -1;
@ -694,7 +694,11 @@ namespace Physic
if (resultCallback1.hasHit()) if (resultCallback1.hasHit())
{ {
name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName; name = static_cast<const RigidBody&>(*resultCallback1.m_collisionObject).mName;
d = resultCallback1.m_closestHitFraction;; d = resultCallback1.m_closestHitFraction;
if (normal)
*normal = Ogre::Vector3(resultCallback1.m_hitNormalWorld.x(),
resultCallback1.m_hitNormalWorld.y(),
resultCallback1.m_hitNormalWorld.z());
} }
return std::pair<std::string,float>(name,d); return std::pair<std::string,float>(name,d);

View File

@ -308,8 +308,10 @@ namespace Physic
/** /**
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
* If \a normal is non-NULL, the hit normal will be written there (if there is a hit)
*/ */
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true,bool ignoreHeightMap = false); std::pair<std::string,float> rayTest(btVector3& from,btVector3& to,bool raycastingObjectOnly = true,
bool ignoreHeightMap = false, Ogre::Vector3* normal = NULL);
/** /**
* Return all objects hit by a ray. * Return all objects hit by a ray.