forked from teamnwah/openmw-tes3coop
Merge remote-tracking branch 'kcat/animations' into ref
This commit is contained in:
commit
2c1796cc74
99 changed files with 10193 additions and 9948 deletions
|
@ -15,7 +15,7 @@ include (OpenMWMacros)
|
|||
# Version
|
||||
|
||||
set (OPENMW_VERSION_MAJOR 0)
|
||||
set (OPENMW_VERSION_MINOR 22)
|
||||
set (OPENMW_VERSION_MINOR 23)
|
||||
set (OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||
|
|
|
@ -439,7 +439,7 @@ void Record<ESM::Apparatus>::print()
|
|||
template<>
|
||||
void Record<ESM::BodyPart>::print()
|
||||
{
|
||||
std::cout << " Name: " << mData.mName << std::endl;
|
||||
std::cout << " Race: " << mData.mRace << std::endl;
|
||||
std::cout << " Model: " << mData.mModel << std::endl;
|
||||
std::cout << " Type: " << meshTypeLabel(mData.mData.mType)
|
||||
<< " (" << (int)mData.mData.mType << ")" << std::endl;
|
||||
|
|
|
@ -102,3 +102,9 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
add_definitions (--coverage)
|
||||
target_link_libraries(omwlauncher gcov)
|
||||
endif()
|
||||
|
||||
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(omwlauncher dl Xt)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -96,15 +96,15 @@ bool GameSettings::readFile(QTextStream &stream)
|
|||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
QString key = keyRe.cap(1);
|
||||
QString value = keyRe.cap(2);
|
||||
|
||||
// Don't remove existing data entries
|
||||
if (key != QLatin1String("data"))
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
@ -65,8 +65,8 @@ public:
|
|||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
QString key = keyRe.cap(1);
|
||||
QString value = keyRe.cap(2);
|
||||
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
|
|
|
@ -122,6 +122,12 @@ if (UNIX AND NOT APPLE)
|
|||
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(openmw dl Xt)
|
||||
endif()
|
||||
|
||||
|
||||
if(APPLE)
|
||||
find_library(CARBON_FRAMEWORK Carbon)
|
||||
find_library(COCOA_FRAMEWORK Cocoa)
|
||||
|
|
|
@ -99,6 +99,9 @@ namespace MWBase
|
|||
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
|
||||
///< Perform a persuasion action on NPC
|
||||
|
||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
|
||||
///< Forces an object to refresh its animation state.
|
||||
|
||||
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
|
||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||
/// in the scene should be ignored.
|
||||
|
|
|
@ -49,6 +49,67 @@ namespace
|
|||
{
|
||||
return new CustomData (*this);
|
||||
}
|
||||
|
||||
void autoCalculateAttributes (const ESM::NPC* npc, MWMechanics::CreatureStats& creatureStats)
|
||||
{
|
||||
// race bonus
|
||||
const ESM::Race *race =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npc->mRace);
|
||||
|
||||
bool male = (npc->mFlags & ESM::NPC::Female) == 0;
|
||||
|
||||
int level = creatureStats.getLevel();
|
||||
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
{
|
||||
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i];
|
||||
creatureStats.getAttribute(i).setBase (male ? attribute.mMale : attribute.mFemale);
|
||||
}
|
||||
|
||||
// class bonus
|
||||
const ESM::Class *class_ =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
|
||||
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
int attribute = class_->mData.mAttribute[i];
|
||||
if (attribute>=0 && attribute<8)
|
||||
{
|
||||
creatureStats.getAttribute(attribute).setBase (
|
||||
creatureStats.getAttribute(attribute).getBase() + 10);
|
||||
}
|
||||
}
|
||||
|
||||
// skill bonus
|
||||
for (int attribute=0; attribute<ESM::Attribute::Length; ++attribute)
|
||||
{
|
||||
float modifierSum = 0;
|
||||
|
||||
for (int j=0; j<ESM::Skill::Length; ++j)
|
||||
{
|
||||
const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(j);
|
||||
|
||||
if (skill->mData.mAttribute != attribute)
|
||||
continue;
|
||||
|
||||
// is this a minor or major skill?
|
||||
float add=0.2;
|
||||
for (int k=0; k<5; ++k)
|
||||
{
|
||||
if (class_->mData.mSkills[k][0] == j)
|
||||
add=0.5;
|
||||
}
|
||||
for (int k=0; k<5; ++k)
|
||||
{
|
||||
if (class_->mData.mSkills[k][1] == j)
|
||||
add=1.0;
|
||||
}
|
||||
modifierSum += add;
|
||||
}
|
||||
creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
|
||||
+ static_cast<int>((level-1) * modifierSum+0.5), 100) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWClass
|
||||
|
@ -126,15 +187,14 @@ namespace MWClass
|
|||
}
|
||||
else
|
||||
{
|
||||
for (int i=0; i<8; ++i)
|
||||
data->mCreatureStats.getAttribute (i).set (10);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
data->mCreatureStats.setDynamic (i, 10);
|
||||
|
||||
data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel);
|
||||
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition);
|
||||
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
|
||||
|
||||
autoCalculateAttributes(ref->mBase, data->mCreatureStats);
|
||||
}
|
||||
|
||||
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
|
||||
|
|
|
@ -426,6 +426,7 @@ namespace MWDialogue
|
|||
|
||||
void DialogueManager::questionAnswered (const std::string& answer)
|
||||
{
|
||||
|
||||
if (mChoiceMap.find(answer) != mChoiceMap.end())
|
||||
{
|
||||
mChoice = mChoiceMap[answer];
|
||||
|
@ -442,6 +443,10 @@ namespace MWDialogue
|
|||
std::string text = info->mResponse;
|
||||
parseText (text);
|
||||
|
||||
mChoiceMap.clear();
|
||||
mChoice = -1;
|
||||
mIsInChoice = false;
|
||||
|
||||
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
|
||||
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext));
|
||||
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId);
|
||||
|
@ -449,9 +454,6 @@ namespace MWDialogue
|
|||
mLastDialogue = *info;
|
||||
}
|
||||
}
|
||||
mChoiceMap.clear();
|
||||
mChoice = -1;
|
||||
mIsInChoice = false;
|
||||
}
|
||||
|
||||
updateTopics();
|
||||
|
|
|
@ -73,6 +73,11 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
|
|||
if (iter->second < info.mData.mRank)
|
||||
return false;
|
||||
}
|
||||
else if (info.mData.mRank != -1)
|
||||
{
|
||||
// if there is a rank condition, but the NPC is not in a faction, always fail
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gender
|
||||
if (!isCreature)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace MWWorld
|
||||
namespace MWWorld
|
||||
{
|
||||
struct ESMStore;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace MWDialogue
|
|||
static std::string idFromIndex (const std::string& topic, int index);
|
||||
};
|
||||
|
||||
/// \biref A quest entry with a timestamp.
|
||||
/// \brief A quest entry with a timestamp.
|
||||
struct StampedJournalEntry : public JournalEntry
|
||||
{
|
||||
int mDay;
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
class AlchemyWindow : public WindowBase, public ContainerBase
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -3,245 +3,245 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "widgets.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace Widgets;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, const std::pair<std::string, const ESM::BirthSign*>& right)
|
||||
{
|
||||
return left.second->mName.compare (right.second->mName) < 0;
|
||||
}
|
||||
bool sortBirthSigns(const std::pair<std::string, const ESM::BirthSign*>& left, const std::pair<std::string, const ESM::BirthSign*>& right)
|
||||
{
|
||||
return left.second->mName.compare (right.second->mName) < 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BirthDialog::BirthDialog()
|
||||
: WindowModal("openmw_chargen_birth.layout")
|
||||
namespace MWGui
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
getWidget(mSpellArea, "SpellArea");
|
||||
BirthDialog::BirthDialog()
|
||||
: WindowModal("openmw_chargen_birth.layout")
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
getWidget(mBirthImage, "BirthsignImage");
|
||||
getWidget(mSpellArea, "SpellArea");
|
||||
|
||||
getWidget(mBirthList, "BirthsignList");
|
||||
mBirthList->setScrollVisible(true);
|
||||
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
getWidget(mBirthImage, "BirthsignImage");
|
||||
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
|
||||
getWidget(mBirthList, "BirthsignList");
|
||||
mBirthList->setScrollVisible(true);
|
||||
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
|
||||
|
||||
updateBirths();
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
void BirthDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
|
||||
|
||||
void BirthDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
updateBirths();
|
||||
updateSpells();
|
||||
}
|
||||
updateBirths();
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
|
||||
void BirthDialog::setBirthId(const std::string &birthId)
|
||||
{
|
||||
mCurrentBirthId = birthId;
|
||||
mBirthList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
size_t count = mBirthList->getItemCount();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
void BirthDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
if (boost::iequals(*mBirthList->getItemDataAt<std::string>(i), birthId))
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
|
||||
void BirthDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
updateBirths();
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
|
||||
void BirthDialog::setBirthId(const std::string &birthId)
|
||||
{
|
||||
mCurrentBirthId = birthId;
|
||||
mBirthList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
size_t count = mBirthList->getItemCount();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
mBirthList->setIndexSelected(i);
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void BirthDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index)
|
||||
{
|
||||
if (_index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
|
||||
if (boost::iequals(mCurrentBirthId, *birthId))
|
||||
return;
|
||||
|
||||
mCurrentBirthId = *birthId;
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
// update widget content
|
||||
|
||||
void BirthDialog::updateBirths()
|
||||
{
|
||||
mBirthList->removeAllItems();
|
||||
|
||||
const MWWorld::Store<ESM::BirthSign> &signs =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>();
|
||||
|
||||
// sort by name
|
||||
std::vector < std::pair<std::string, const ESM::BirthSign*> > birthSigns;
|
||||
|
||||
MWWorld::Store<ESM::BirthSign>::iterator it = signs.begin();
|
||||
for (; it != signs.end(); ++it)
|
||||
{
|
||||
birthSigns.push_back(std::make_pair(it->mId, &(*it)));
|
||||
}
|
||||
std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns);
|
||||
|
||||
int index = 0;
|
||||
for (std::vector<std::pair<std::string, const ESM::BirthSign*> >::const_iterator it2 = birthSigns.begin();
|
||||
it2 != birthSigns.end(); ++it2, ++index)
|
||||
{
|
||||
mBirthList->addItem(it2->second->mName, it2->first);
|
||||
if (mCurrentBirthId.empty())
|
||||
{
|
||||
mBirthList->setIndexSelected(index);
|
||||
mCurrentBirthId = it2->first;
|
||||
}
|
||||
else if (boost::iequals(it2->first, mCurrentBirthId))
|
||||
{
|
||||
mBirthList->setIndexSelected(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BirthDialog::updateSpells()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSpellItems.clear();
|
||||
|
||||
if (mCurrentBirthId.empty())
|
||||
return;
|
||||
|
||||
MWSpellPtr spellWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store =
|
||||
MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
const ESM::BirthSign *birth =
|
||||
store.get<ESM::BirthSign>().find(mCurrentBirthId);
|
||||
|
||||
std::string texturePath = std::string("textures\\") + birth->mTexture;
|
||||
fixTexturePath(texturePath);
|
||||
mBirthImage->setImageTexture(texturePath);
|
||||
|
||||
std::vector<std::string> abilities, powers, spells;
|
||||
|
||||
std::vector<std::string>::const_iterator it = birth->mPowers.mList.begin();
|
||||
std::vector<std::string>::const_iterator end = birth->mPowers.mList.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
const std::string &spellId = *it;
|
||||
const ESM::Spell *spell = store.get<ESM::Spell>().search(spellId);
|
||||
if (!spell)
|
||||
continue; // Skip spells which cannot be found
|
||||
ESM::Spell::SpellType type = static_cast<ESM::Spell::SpellType>(spell->mData.mType);
|
||||
if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power)
|
||||
continue; // We only want spell, ability and powers.
|
||||
|
||||
if (type == ESM::Spell::ST_Ability)
|
||||
abilities.push_back(spellId);
|
||||
else if (type == ESM::Spell::ST_Power)
|
||||
powers.push_back(spellId);
|
||||
else if (type == ESM::Spell::ST_Spell)
|
||||
spells.push_back(spellId);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
struct {
|
||||
const std::vector<std::string> &spells;
|
||||
const char *label;
|
||||
}
|
||||
categories[3] = {
|
||||
{abilities, "sBirthsignmenu1"},
|
||||
{powers, "sPowers"},
|
||||
{spells, "sBirthsignmenu2"}
|
||||
};
|
||||
|
||||
for (int category = 0; category < 3; ++category)
|
||||
{
|
||||
if (!categories[category].spells.empty())
|
||||
{
|
||||
MyGUI::TextBox* label = mSpellArea->createWidget<MyGUI::TextBox>("SandBrightText", coord, MyGUI::Align::Default, std::string("Label"));
|
||||
label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, ""));
|
||||
mSpellItems.push_back(label);
|
||||
coord.top += lineHeight;
|
||||
|
||||
std::vector<std::string>::const_iterator end = categories[category].spells.end();
|
||||
for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it)
|
||||
if (boost::iequals(*mBirthList->getItemDataAt<std::string>(i), birthId))
|
||||
{
|
||||
const std::string &spellId = *it;
|
||||
spellWidget = mSpellArea->createWidget<MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast<std::string>(i));
|
||||
spellWidget->setSpellId(spellId);
|
||||
mBirthList->setIndexSelected(i);
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mSpellItems.push_back(spellWidget);
|
||||
coord.top += lineHeight;
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
MyGUI::IntCoord spellCoord = coord;
|
||||
spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template?
|
||||
spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? MWEffectList::EF_Constant : 0);
|
||||
coord.top = spellCoord.top;
|
||||
// widget controls
|
||||
|
||||
++i;
|
||||
void BirthDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index)
|
||||
{
|
||||
if (_index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
|
||||
if (boost::iequals(mCurrentBirthId, *birthId))
|
||||
return;
|
||||
|
||||
mCurrentBirthId = *birthId;
|
||||
updateSpells();
|
||||
}
|
||||
|
||||
// update widget content
|
||||
|
||||
void BirthDialog::updateBirths()
|
||||
{
|
||||
mBirthList->removeAllItems();
|
||||
|
||||
const MWWorld::Store<ESM::BirthSign> &signs =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>();
|
||||
|
||||
// sort by name
|
||||
std::vector < std::pair<std::string, const ESM::BirthSign*> > birthSigns;
|
||||
|
||||
MWWorld::Store<ESM::BirthSign>::iterator it = signs.begin();
|
||||
for (; it != signs.end(); ++it)
|
||||
{
|
||||
birthSigns.push_back(std::make_pair(it->mId, &(*it)));
|
||||
}
|
||||
std::sort(birthSigns.begin(), birthSigns.end(), sortBirthSigns);
|
||||
|
||||
int index = 0;
|
||||
for (std::vector<std::pair<std::string, const ESM::BirthSign*> >::const_iterator it2 = birthSigns.begin();
|
||||
it2 != birthSigns.end(); ++it2, ++index)
|
||||
{
|
||||
mBirthList->addItem(it2->second->mName, it2->first);
|
||||
if (mCurrentBirthId.empty())
|
||||
{
|
||||
mBirthList->setIndexSelected(index);
|
||||
mCurrentBirthId = it2->first;
|
||||
}
|
||||
else if (boost::iequals(it2->first, mCurrentBirthId))
|
||||
{
|
||||
mBirthList->setIndexSelected(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BirthDialog::updateSpells()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSpellItems.begin(); it != mSpellItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSpellItems.clear();
|
||||
|
||||
if (mCurrentBirthId.empty())
|
||||
return;
|
||||
|
||||
Widgets::MWSpellPtr spellWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, mSpellArea->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store =
|
||||
MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
const ESM::BirthSign *birth =
|
||||
store.get<ESM::BirthSign>().find(mCurrentBirthId);
|
||||
|
||||
std::string texturePath = std::string("textures\\") + birth->mTexture;
|
||||
Widgets::fixTexturePath(texturePath);
|
||||
mBirthImage->setImageTexture(texturePath);
|
||||
|
||||
std::vector<std::string> abilities, powers, spells;
|
||||
|
||||
std::vector<std::string>::const_iterator it = birth->mPowers.mList.begin();
|
||||
std::vector<std::string>::const_iterator end = birth->mPowers.mList.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
const std::string &spellId = *it;
|
||||
const ESM::Spell *spell = store.get<ESM::Spell>().search(spellId);
|
||||
if (!spell)
|
||||
continue; // Skip spells which cannot be found
|
||||
ESM::Spell::SpellType type = static_cast<ESM::Spell::SpellType>(spell->mData.mType);
|
||||
if (type != ESM::Spell::ST_Spell && type != ESM::Spell::ST_Ability && type != ESM::Spell::ST_Power)
|
||||
continue; // We only want spell, ability and powers.
|
||||
|
||||
if (type == ESM::Spell::ST_Ability)
|
||||
abilities.push_back(spellId);
|
||||
else if (type == ESM::Spell::ST_Power)
|
||||
powers.push_back(spellId);
|
||||
else if (type == ESM::Spell::ST_Spell)
|
||||
spells.push_back(spellId);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
struct {
|
||||
const std::vector<std::string> &spells;
|
||||
const char *label;
|
||||
}
|
||||
categories[3] = {
|
||||
{abilities, "sBirthsignmenu1"},
|
||||
{powers, "sPowers"},
|
||||
{spells, "sBirthsignmenu2"}
|
||||
};
|
||||
|
||||
for (int category = 0; category < 3; ++category)
|
||||
{
|
||||
if (!categories[category].spells.empty())
|
||||
{
|
||||
MyGUI::TextBox* label = mSpellArea->createWidget<MyGUI::TextBox>("SandBrightText", coord, MyGUI::Align::Default, std::string("Label"));
|
||||
label->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString(categories[category].label, ""));
|
||||
mSpellItems.push_back(label);
|
||||
coord.top += lineHeight;
|
||||
|
||||
std::vector<std::string>::const_iterator end = categories[category].spells.end();
|
||||
for (std::vector<std::string>::const_iterator it = categories[category].spells.begin(); it != end; ++it)
|
||||
{
|
||||
const std::string &spellId = *it;
|
||||
spellWidget = mSpellArea->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("Spell") + boost::lexical_cast<std::string>(i));
|
||||
spellWidget->setSpellId(spellId);
|
||||
|
||||
mSpellItems.push_back(spellWidget);
|
||||
coord.top += lineHeight;
|
||||
|
||||
MyGUI::IntCoord spellCoord = coord;
|
||||
spellCoord.height = 24; // TODO: This should be fetched from the skin somehow, or perhaps a widget in the layout as a template?
|
||||
spellWidget->createEffectWidgets(mSpellItems, mSpellArea, spellCoord, (category == 0) ? Widgets::MWEffectList::EF_Constant : 0);
|
||||
coord.top = spellCoord.top;
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,148 +12,151 @@
|
|||
|
||||
#include "formatting.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
BookWindow::BookWindow ()
|
||||
: WindowBase("openmw_book.layout")
|
||||
, mTakeButtonShow(true)
|
||||
, mTakeButtonAllowed(true)
|
||||
namespace MWGui
|
||||
{
|
||||
getWidget(mCloseButton, "CloseButton");
|
||||
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked);
|
||||
|
||||
getWidget(mTakeButton, "TakeButton");
|
||||
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked);
|
||||
|
||||
getWidget(mNextPageButton, "NextPageBTN");
|
||||
mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked);
|
||||
|
||||
getWidget(mPrevPageButton, "PrevPageBTN");
|
||||
mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked);
|
||||
|
||||
getWidget(mLeftPageNumber, "LeftPageNumber");
|
||||
getWidget(mRightPageNumber, "RightPageNumber");
|
||||
|
||||
getWidget(mLeftPage, "LeftPage");
|
||||
getWidget(mRightPage, "RightPage");
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void BookWindow::clearPages()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it=mPages.begin();
|
||||
it!=mPages.end(); ++it)
|
||||
BookWindow::BookWindow ()
|
||||
: WindowBase("openmw_book.layout")
|
||||
, mTakeButtonShow(true)
|
||||
, mTakeButtonAllowed(true)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mPages.clear();
|
||||
}
|
||||
getWidget(mCloseButton, "CloseButton");
|
||||
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onCloseButtonClicked);
|
||||
|
||||
void BookWindow::open (MWWorld::Ptr book)
|
||||
{
|
||||
mBook = book;
|
||||
getWidget(mTakeButton, "TakeButton");
|
||||
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onTakeButtonClicked);
|
||||
|
||||
clearPages();
|
||||
mCurrentPage = 0;
|
||||
getWidget(mNextPageButton, "NextPageBTN");
|
||||
mNextPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onNextPageButtonClicked);
|
||||
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
||||
getWidget(mPrevPageButton, "PrevPageBTN");
|
||||
mPrevPageButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BookWindow::onPrevPageButtonClicked);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();
|
||||
getWidget(mLeftPageNumber, "LeftPageNumber");
|
||||
getWidget(mRightPageNumber, "RightPageNumber");
|
||||
|
||||
BookTextParser parser;
|
||||
std::vector<std::string> results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height);
|
||||
getWidget(mLeftPage, "LeftPage");
|
||||
getWidget(mRightPage, "RightPage");
|
||||
|
||||
int i=0;
|
||||
for (std::vector<std::string>::iterator it=results.begin();
|
||||
it!=results.end(); ++it)
|
||||
{
|
||||
MyGUI::Widget* parent;
|
||||
if (i%2 == 0)
|
||||
parent = mLeftPage;
|
||||
else
|
||||
parent = mRightPage;
|
||||
|
||||
MyGUI::Widget* pageWidget = parent->createWidgetReal<MyGUI::Widget>("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast<std::string>(i));
|
||||
parser.parse(*it, pageWidget, mLeftPage->getSize().width);
|
||||
mPages.push_back(pageWidget);
|
||||
++i;
|
||||
center();
|
||||
}
|
||||
|
||||
updatePages();
|
||||
|
||||
setTakeButtonShow(true);
|
||||
}
|
||||
|
||||
void BookWindow::setTakeButtonShow(bool show)
|
||||
{
|
||||
mTakeButtonShow = show;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void BookWindow::setInventoryAllowed(bool allowed)
|
||||
{
|
||||
mTakeButtonAllowed = allowed;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
// no 3d sounds because the object could be in a container.
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
|
||||
}
|
||||
|
||||
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
|
||||
MWWorld::ActionTake take(mBook);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
|
||||
}
|
||||
|
||||
void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
if ((mCurrentPage+1)*2 < mPages.size())
|
||||
void BookWindow::clearPages()
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0);
|
||||
|
||||
++mCurrentPage;
|
||||
|
||||
updatePages();
|
||||
}
|
||||
}
|
||||
|
||||
void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
if (mCurrentPage > 0)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0);
|
||||
|
||||
--mCurrentPage;
|
||||
|
||||
updatePages();
|
||||
}
|
||||
}
|
||||
|
||||
void BookWindow::updatePages()
|
||||
{
|
||||
mLeftPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 1) );
|
||||
mRightPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 2) );
|
||||
|
||||
unsigned int i=0;
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mPages.begin();
|
||||
it != mPages.end(); ++it)
|
||||
{
|
||||
if (mCurrentPage*2 == i || mCurrentPage*2+1 == i)
|
||||
(*it)->setVisible(true);
|
||||
else
|
||||
for (std::vector<MyGUI::Widget*>::iterator it=mPages.begin();
|
||||
it!=mPages.end(); ++it)
|
||||
{
|
||||
(*it)->setVisible(false);
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
++i;
|
||||
mPages.clear();
|
||||
}
|
||||
|
||||
void BookWindow::open (MWWorld::Ptr book)
|
||||
{
|
||||
mBook = book;
|
||||
|
||||
clearPages();
|
||||
mCurrentPage = 0;
|
||||
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book> *ref = mBook.get<ESM::Book>();
|
||||
|
||||
BookTextParser parser;
|
||||
std::vector<std::string> results = parser.split(ref->mBase->mText, mLeftPage->getSize().width, mLeftPage->getSize().height);
|
||||
|
||||
int i=0;
|
||||
for (std::vector<std::string>::iterator it=results.begin();
|
||||
it!=results.end(); ++it)
|
||||
{
|
||||
MyGUI::Widget* parent;
|
||||
if (i%2 == 0)
|
||||
parent = mLeftPage;
|
||||
else
|
||||
parent = mRightPage;
|
||||
|
||||
MyGUI::Widget* pageWidget = parent->createWidgetReal<MyGUI::Widget>("", MyGUI::FloatCoord(0.0,0.0,1.0,1.0), MyGUI::Align::Default, "BookPage" + boost::lexical_cast<std::string>(i));
|
||||
parser.parse(*it, pageWidget, mLeftPage->getSize().width);
|
||||
mPages.push_back(pageWidget);
|
||||
++i;
|
||||
}
|
||||
|
||||
updatePages();
|
||||
|
||||
setTakeButtonShow(true);
|
||||
}
|
||||
|
||||
void BookWindow::setTakeButtonShow(bool show)
|
||||
{
|
||||
mTakeButtonShow = show;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void BookWindow::setInventoryAllowed(bool allowed)
|
||||
{
|
||||
mTakeButtonAllowed = allowed;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
// no 3d sounds because the object could be in a container.
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
|
||||
}
|
||||
|
||||
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
|
||||
MWWorld::ActionTake take(mBook);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Book);
|
||||
}
|
||||
|
||||
void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
if ((mCurrentPage+1)*2 < mPages.size())
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book page2", 1.0, 1.0);
|
||||
|
||||
++mCurrentPage;
|
||||
|
||||
updatePages();
|
||||
}
|
||||
}
|
||||
|
||||
void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
if (mCurrentPage > 0)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book page", 1.0, 1.0);
|
||||
|
||||
--mCurrentPage;
|
||||
|
||||
updatePages();
|
||||
}
|
||||
}
|
||||
|
||||
void BookWindow::updatePages()
|
||||
{
|
||||
mLeftPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 1) );
|
||||
mRightPageNumber->setCaption( boost::lexical_cast<std::string>(mCurrentPage*2 + 2) );
|
||||
|
||||
unsigned int i=0;
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mPages.begin();
|
||||
it != mPages.end(); ++it)
|
||||
{
|
||||
if (mCurrentPage*2 == i || mCurrentPage*2+1 == i)
|
||||
(*it)->setVisible(true);
|
||||
else
|
||||
{
|
||||
(*it)->setVisible(false);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "confirmationdialog.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
ConfirmationDialog::ConfirmationDialog() :
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
|
||||
#include "console.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
#include <components/compiler/exception.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwscript/extensions.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -284,16 +278,15 @@ namespace MWGui
|
|||
|
||||
std::string Console::complete( std::string input, std::vector<std::string> &matches )
|
||||
{
|
||||
using namespace std;
|
||||
string output=input;
|
||||
string tmp=input;
|
||||
std::string output = input;
|
||||
std::string tmp = input;
|
||||
bool has_front_quote = false;
|
||||
|
||||
/* Does the input string contain things that don't have to be completed? If yes erase them. */
|
||||
/* Are there quotation marks? */
|
||||
if( tmp.find('"') != string::npos ) {
|
||||
if( tmp.find('"') != std::string::npos ) {
|
||||
int numquotes=0;
|
||||
for(string::iterator it=tmp.begin(); it < tmp.end(); ++it) {
|
||||
for(std::string::iterator it=tmp.begin(); it < tmp.end(); ++it) {
|
||||
if( *it == '"' )
|
||||
numquotes++;
|
||||
}
|
||||
|
@ -305,7 +298,7 @@ namespace MWGui
|
|||
}
|
||||
else {
|
||||
size_t pos;
|
||||
if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) {
|
||||
if( ( ((pos = tmp.rfind(' ')) != std::string::npos ) ) && ( pos > tmp.rfind('"') ) ) {
|
||||
tmp.erase( 0, tmp.rfind(' ')+1);
|
||||
}
|
||||
else {
|
||||
|
@ -317,7 +310,7 @@ namespace MWGui
|
|||
/* No quotation marks. Are there spaces?*/
|
||||
else {
|
||||
size_t rpos;
|
||||
if( (rpos=tmp.rfind(' ')) != string::npos ) {
|
||||
if( (rpos=tmp.rfind(' ')) != std::string::npos ) {
|
||||
if( rpos == 0 ) {
|
||||
tmp.clear();
|
||||
}
|
||||
|
@ -336,7 +329,7 @@ namespace MWGui
|
|||
}
|
||||
|
||||
/* Iterate through the vector. */
|
||||
for(vector<string>::iterator it=mNames.begin(); it < mNames.end();++it) {
|
||||
for(std::vector<std::string>::iterator it=mNames.begin(); it < mNames.end();++it) {
|
||||
bool string_different=false;
|
||||
|
||||
/* Is the string shorter than the input string? If yes skip it. */
|
||||
|
@ -344,7 +337,7 @@ namespace MWGui
|
|||
continue;
|
||||
|
||||
/* Is the beginning of the string different from the input string? If yes skip it. */
|
||||
for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) {
|
||||
for( std::string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) {
|
||||
if( tolower(*iter) != tolower(*iter2) ) {
|
||||
string_different=true;
|
||||
break;
|
||||
|
@ -367,24 +360,24 @@ namespace MWGui
|
|||
/* Only one match. We're done. */
|
||||
if( matches.size() == 1 ) {
|
||||
/* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/
|
||||
if( ( matches.front().find(' ') != string::npos ) ) {
|
||||
if( ( matches.front().find(' ') != std::string::npos ) ) {
|
||||
if( !has_front_quote )
|
||||
output.append(string("\""));
|
||||
return output.append(matches.front() + string("\" "));
|
||||
output.append(std::string("\""));
|
||||
return output.append(matches.front() + std::string("\" "));
|
||||
}
|
||||
else if( has_front_quote ) {
|
||||
return output.append(matches.front() + string("\" "));
|
||||
return output.append(matches.front() + std::string("\" "));
|
||||
}
|
||||
else {
|
||||
return output.append(matches.front() + string(" "));
|
||||
return output.append(matches.front() + std::string(" "));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if all matching strings match further than input. If yes complete to this match. */
|
||||
int i = tmp.length();
|
||||
|
||||
for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) {
|
||||
for(vector<string>::iterator it=matches.begin(); it < matches.end();++it) {
|
||||
for(std::string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) {
|
||||
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end();++it) {
|
||||
if( tolower((*it)[i]) != tolower(*iter) ) {
|
||||
/* Append the longest match to the end of the output string*/
|
||||
output.append(matches.front().substr( 0, i));
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,9 +2,6 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
CountDialog::CountDialog() :
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
#include <MyGUI_PointerManager.h>
|
||||
#include <MyGUI_InputManager.h>
|
||||
#include <MyGUI_RenderManager.h>
|
||||
#include <MyGUI_RotatingSkin.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
|
||||
#include <OgreMath.h>
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
#include "dialogue.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
|
@ -26,521 +19,521 @@
|
|||
#include "inventorywindow.hpp"
|
||||
#include "travelwindow.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace Widgets;
|
||||
|
||||
/**
|
||||
*Copied from the internet.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
|
||||
std::string lower_string(const std::string& str)
|
||||
namespace
|
||||
{
|
||||
std::string lowerCase = Misc::StringUtils::lowerCase (str);
|
||||
|
||||
return lowerCase;
|
||||
}
|
||||
|
||||
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
|
||||
{
|
||||
return lower_string(str).find(lower_string(substr),pos);
|
||||
}
|
||||
|
||||
bool sortByLength (const std::string& left, const std::string& right)
|
||||
{
|
||||
return left.size() > right.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
PersuasionDialog::PersuasionDialog()
|
||||
: WindowModal("openmw_persuasion_dialog.layout")
|
||||
{
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
getWidget(mAdmireButton, "AdmireButton");
|
||||
getWidget(mIntimidateButton, "IntimidateButton");
|
||||
getWidget(mTauntButton, "TauntButton");
|
||||
getWidget(mBribe10Button, "Bribe10Button");
|
||||
getWidget(mBribe100Button, "Bribe100Button");
|
||||
getWidget(mBribe1000Button, "Bribe1000Button");
|
||||
getWidget(mGoldLabel, "GoldLabel");
|
||||
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel);
|
||||
mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
}
|
||||
|
||||
void PersuasionDialog::onCancel(MyGUI::Widget *sender)
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void PersuasionDialog::onPersuade(MyGUI::Widget *sender)
|
||||
{
|
||||
MWBase::MechanicsManager::PersuasionType type;
|
||||
if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire;
|
||||
else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate;
|
||||
else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt;
|
||||
else if (sender == mBribe10Button)
|
||||
std::string lower_string(const std::string& str)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10);
|
||||
type = MWBase::MechanicsManager::PT_Bribe10;
|
||||
}
|
||||
else if (sender == mBribe100Button)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100);
|
||||
type = MWBase::MechanicsManager::PT_Bribe100;
|
||||
}
|
||||
else /*if (sender == mBribe1000Button)*/
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000);
|
||||
type = MWBase::MechanicsManager::PT_Bribe1000;
|
||||
std::string lowerCase = Misc::StringUtils::lowerCase (str);
|
||||
|
||||
return lowerCase;
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getDialogueManager()->persuade(type);
|
||||
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void PersuasionDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
center();
|
||||
|
||||
int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold();
|
||||
|
||||
mBribe10Button->setEnabled (playerGold >= 10);
|
||||
mBribe100Button->setEnabled (playerGold >= 100);
|
||||
mBribe1000Button->setEnabled (playerGold >= 1000);
|
||||
|
||||
mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast<std::string>(playerGold));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
||||
DialogueWindow::DialogueWindow()
|
||||
: WindowBase("openmw_dialogue_window.layout")
|
||||
, mPersuasionDialog()
|
||||
, mEnabled(false)
|
||||
, mServices(0)
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
mPersuasionDialog.setVisible(false);
|
||||
|
||||
//History view
|
||||
getWidget(mHistory, "History");
|
||||
mHistory->setOverflowToTheLeft(true);
|
||||
mHistory->setMaxTextLength(1000000);
|
||||
MyGUI::Widget* eventbox;
|
||||
|
||||
//An EditBox cannot receive mouse click events, so we use an
|
||||
//invisible widget on top of the editbox to receive them
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
|
||||
eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
|
||||
|
||||
//Topics list
|
||||
getWidget(mTopicsList, "TopicsList");
|
||||
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
|
||||
|
||||
MyGUI::Button* byeButton;
|
||||
getWidget(byeButton, "ByeButton");
|
||||
byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
|
||||
|
||||
getWidget(mDispositionBar, "Disposition");
|
||||
getWidget(mDispositionText,"DispositionText");
|
||||
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
|
||||
}
|
||||
|
||||
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
|
||||
if(t == NULL)
|
||||
return;
|
||||
|
||||
const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
|
||||
|
||||
size_t cursorPosition = t->getCursorPosition(lastPressed);
|
||||
MyGUI::UString color = mHistory->getColorAtPos(cursorPosition);
|
||||
|
||||
if (!mEnabled && color == "#572D21")
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
|
||||
if (!mEnabled)
|
||||
return;
|
||||
|
||||
if(color != "#B29154")
|
||||
std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos)
|
||||
{
|
||||
MyGUI::UString key = mHistory->getColorTextAt(cursorPosition);
|
||||
return lower_string(str).find(lower_string(substr),pos);
|
||||
}
|
||||
|
||||
if(color == "#686EBA")
|
||||
bool sortByLength (const std::string& left, const std::string& right)
|
||||
{
|
||||
return left.size() > right.size();
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
PersuasionDialog::PersuasionDialog()
|
||||
: WindowModal("openmw_persuasion_dialog.layout")
|
||||
{
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
getWidget(mAdmireButton, "AdmireButton");
|
||||
getWidget(mIntimidateButton, "IntimidateButton");
|
||||
getWidget(mTauntButton, "TauntButton");
|
||||
getWidget(mBribe10Button, "Bribe10Button");
|
||||
getWidget(mBribe100Button, "Bribe100Button");
|
||||
getWidget(mBribe1000Button, "Bribe1000Button");
|
||||
getWidget(mGoldLabel, "GoldLabel");
|
||||
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onCancel);
|
||||
mAdmireButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mIntimidateButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mTauntButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe10Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe100Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
mBribe1000Button->eventMouseButtonClick += MyGUI::newDelegate(this, &PersuasionDialog::onPersuade);
|
||||
}
|
||||
|
||||
void PersuasionDialog::onCancel(MyGUI::Widget *sender)
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void PersuasionDialog::onPersuade(MyGUI::Widget *sender)
|
||||
{
|
||||
MWBase::MechanicsManager::PersuasionType type;
|
||||
if (sender == mAdmireButton) type = MWBase::MechanicsManager::PT_Admire;
|
||||
else if (sender == mIntimidateButton) type = MWBase::MechanicsManager::PT_Intimidate;
|
||||
else if (sender == mTauntButton) type = MWBase::MechanicsManager::PT_Taunt;
|
||||
else if (sender == mBribe10Button)
|
||||
{
|
||||
std::map<size_t, HyperLink>::iterator i = mHyperLinks.upper_bound(cursorPosition);
|
||||
if( !mHyperLinks.empty() )
|
||||
{
|
||||
--i;
|
||||
|
||||
if( i->first + i->second.mLength > cursorPosition)
|
||||
{
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the link was colored, but it is not in mHyperLinks.
|
||||
// It means that those liunks are not marked with @# and found
|
||||
// by topic name search
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key));
|
||||
}
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-10);
|
||||
type = MWBase::MechanicsManager::PT_Bribe10;
|
||||
}
|
||||
else if (sender == mBribe100Button)
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-100);
|
||||
type = MWBase::MechanicsManager::PT_Bribe100;
|
||||
}
|
||||
else /*if (sender == mBribe1000Button)*/
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->addOrRemoveGold(-1000);
|
||||
type = MWBase::MechanicsManager::PT_Bribe1000;
|
||||
}
|
||||
|
||||
if(color == "#572D21")
|
||||
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
|
||||
}
|
||||
}
|
||||
MWBase::Environment::get().getDialogueManager()->persuade(type);
|
||||
|
||||
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
|
||||
{
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
|
||||
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mHistory->getVScrollPosition() - _rel*0.3 < 0)
|
||||
mHistory->setVScrollPosition(0);
|
||||
else
|
||||
mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3);
|
||||
}
|
||||
|
||||
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
}
|
||||
|
||||
void DialogueWindow::onSelectTopic(const std::string& topic, int id)
|
||||
{
|
||||
if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice())
|
||||
return;
|
||||
|
||||
int separatorPos = 0;
|
||||
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
{
|
||||
if (mTopicsList->getItemNameAt(i) == "")
|
||||
separatorPos = i;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
if (id >= separatorPos)
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic));
|
||||
else
|
||||
void PersuasionDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
center();
|
||||
|
||||
int playerGold = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getPlayerGold();
|
||||
|
||||
mBribe10Button->setEnabled (playerGold >= 10);
|
||||
mBribe100Button->setEnabled (playerGold >= 100);
|
||||
mBribe1000Button->setEnabled (playerGold >= 1000);
|
||||
|
||||
mGoldLabel->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast<std::string>(playerGold));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
||||
DialogueWindow::DialogueWindow()
|
||||
: WindowBase("openmw_dialogue_window.layout")
|
||||
, mPersuasionDialog()
|
||||
, mEnabled(false)
|
||||
, mServices(0)
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
mPersuasionDialog.setVisible(false);
|
||||
|
||||
//History view
|
||||
getWidget(mHistory, "History");
|
||||
mHistory->setOverflowToTheLeft(true);
|
||||
mHistory->setMaxTextLength(1000000);
|
||||
MyGUI::Widget* eventbox;
|
||||
|
||||
//An EditBox cannot receive mouse click events, so we use an
|
||||
//invisible widget on top of the editbox to receive them
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
|
||||
eventbox->eventMouseWheel += MyGUI::newDelegate(this, &DialogueWindow::onMouseWheel);
|
||||
|
||||
//Topics list
|
||||
getWidget(mTopicsList, "TopicsList");
|
||||
mTopicsList->eventItemSelected += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
|
||||
|
||||
MyGUI::Button* byeButton;
|
||||
getWidget(byeButton, "ByeButton");
|
||||
byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
|
||||
|
||||
getWidget(mDispositionBar, "Disposition");
|
||||
getWidget(mDispositionText,"DispositionText");
|
||||
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &DialogueWindow::onWindowResize);
|
||||
}
|
||||
|
||||
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
MyGUI::ISubWidgetText* t = mHistory->getClient()->getSubWidgetText();
|
||||
if(t == NULL)
|
||||
return;
|
||||
|
||||
const MyGUI::IntPoint& lastPressed = MyGUI::InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
|
||||
|
||||
size_t cursorPosition = t->getCursorPosition(lastPressed);
|
||||
MyGUI::UString color = mHistory->getColorAtPos(cursorPosition);
|
||||
|
||||
if (!mEnabled && color == "#572D21")
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
|
||||
if (!mEnabled)
|
||||
return;
|
||||
|
||||
if(color != "#B29154")
|
||||
{
|
||||
MyGUI::UString key = mHistory->getColorTextAt(cursorPosition);
|
||||
|
||||
if(color == "#686EBA")
|
||||
{
|
||||
std::map<size_t, HyperLink>::iterator i = mHyperLinks.upper_bound(cursorPosition);
|
||||
if( !mHyperLinks.empty() )
|
||||
{
|
||||
--i;
|
||||
|
||||
if( i->first + i->second.mLength > cursorPosition)
|
||||
{
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the link was colored, but it is not in mHyperLinks.
|
||||
// It means that those liunks are not marked with @# and found
|
||||
// by topic name search
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key));
|
||||
}
|
||||
}
|
||||
|
||||
if(color == "#572D21")
|
||||
MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key));
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueWindow::onWindowResize(MyGUI::Window* _sender)
|
||||
{
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
|
||||
void DialogueWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mHistory->getVScrollPosition() - _rel*0.3 < 0)
|
||||
mHistory->setVScrollPosition(0);
|
||||
else
|
||||
mHistory->setVScrollPosition(mHistory->getVScrollPosition() - _rel*0.3);
|
||||
}
|
||||
|
||||
void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
|
||||
}
|
||||
|
||||
void DialogueWindow::onSelectTopic(const std::string& topic, int id)
|
||||
{
|
||||
if (!mEnabled || MWBase::Environment::get().getDialogueManager()->isInChoice())
|
||||
return;
|
||||
|
||||
int separatorPos = 0;
|
||||
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
{
|
||||
if (mTopicsList->getItemNameAt(i) == "")
|
||||
separatorPos = i;
|
||||
}
|
||||
|
||||
if (id >= separatorPos)
|
||||
MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(topic));
|
||||
else
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
if (topic == gmst.find("sPersuasion")->getString())
|
||||
{
|
||||
mPersuasionDialog.setVisible(true);
|
||||
}
|
||||
else if (topic == gmst.find("sCompanionShare")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion);
|
||||
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr);
|
||||
}
|
||||
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused())
|
||||
{
|
||||
if (topic == gmst.find("sBarter")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter);
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sSpells")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying);
|
||||
MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sTravel")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel);
|
||||
MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sSpellMakingMenuTitle")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation);
|
||||
MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sEnchanting")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting);
|
||||
MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sServiceTrainingTitle")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training);
|
||||
MWBase::Environment::get().getWindowManager()->startTraining (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sRepair")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair);
|
||||
MWBase::Environment::get().getWindowManager()->startRepair (mPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName)
|
||||
{
|
||||
mEnabled = true;
|
||||
mPtr = actor;
|
||||
mTopicsList->setEnabled(true);
|
||||
setTitle(npcName);
|
||||
|
||||
mTopicsList->clear();
|
||||
mHyperLinks.clear();
|
||||
mHistory->setCaption("");
|
||||
updateOptions();
|
||||
}
|
||||
|
||||
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||
{
|
||||
mTopicsList->clear();
|
||||
|
||||
bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty()
|
||||
&& mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion");
|
||||
|
||||
bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name();
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
if (topic == gmst.find("sPersuasion")->getString())
|
||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
mTopicsList->addItem(gmst.find("sPersuasion")->getString());
|
||||
|
||||
if (mServices & Service_Trade)
|
||||
mTopicsList->addItem(gmst.find("sBarter")->getString());
|
||||
|
||||
if (mServices & Service_BuySpells)
|
||||
mTopicsList->addItem(gmst.find("sSpells")->getString());
|
||||
|
||||
if (mServices & Service_Travel)
|
||||
mTopicsList->addItem(gmst.find("sTravel")->getString());
|
||||
|
||||
if (mServices & Service_CreateSpells)
|
||||
mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString());
|
||||
|
||||
if (mServices & Service_Enchant)
|
||||
mTopicsList->addItem(gmst.find("sEnchanting")->getString());
|
||||
|
||||
if (mServices & Service_Training)
|
||||
mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString());
|
||||
|
||||
if (mServices & Service_Repair)
|
||||
mTopicsList->addItem(gmst.find("sRepair")->getString());
|
||||
|
||||
if (isCompanion)
|
||||
mTopicsList->addItem(gmst.find("sCompanionShare")->getString());
|
||||
|
||||
if (anyService)
|
||||
mTopicsList->addSeparator();
|
||||
|
||||
|
||||
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
|
||||
{
|
||||
mPersuasionDialog.setVisible(true);
|
||||
mTopicsList->addItem(*it);
|
||||
}
|
||||
else if (topic == gmst.find("sCompanionShare")->getString())
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
|
||||
void DialogueWindow::removeKeyword(std::string keyWord)
|
||||
{
|
||||
if(mTopicsList->hasItem(keyWord))
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Companion);
|
||||
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mPtr);
|
||||
mTopicsList->removeItem(keyWord);
|
||||
}
|
||||
else if (!MWBase::Environment::get().getDialogueManager()->checkServiceRefused())
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
|
||||
void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = find_str_ci(str,keyword, pos)) != std::string::npos)
|
||||
{
|
||||
if (topic == gmst.find("sBarter")->getString())
|
||||
// do not add color if this portion of text is already colored.
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Barter);
|
||||
MWBase::Environment::get().getWindowManager()->getTradeWindow()->startTrade(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sSpells")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellBuying);
|
||||
MWBase::Environment::get().getWindowManager()->getSpellBuyingWindow()->startSpellBuying(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sTravel")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Travel);
|
||||
MWBase::Environment::get().getWindowManager()->getTravelWindow()->startTravel(mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sSpellMakingMenuTitle")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_SpellCreation);
|
||||
MWBase::Environment::get().getWindowManager()->startSpellMaking (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sEnchanting")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Enchanting);
|
||||
MWBase::Environment::get().getWindowManager()->startEnchanting (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sServiceTrainingTitle")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Training);
|
||||
MWBase::Environment::get().getWindowManager()->startTraining (mPtr);
|
||||
}
|
||||
else if (topic == gmst.find("sRepair")->getString())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_MerchantRepair);
|
||||
MWBase::Environment::get().getWindowManager()->startRepair (mPtr);
|
||||
MyGUI::TextIterator iterator (str);
|
||||
MyGUI::UString colour;
|
||||
while(iterator.moveNext())
|
||||
{
|
||||
size_t iteratorPos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if (iteratorPos == pos)
|
||||
break;
|
||||
}
|
||||
|
||||
if (colour == color1)
|
||||
return;
|
||||
}
|
||||
|
||||
str.insert(pos,color1);
|
||||
pos += color1.length();
|
||||
pos += keyword.length();
|
||||
str.insert(pos,color2);
|
||||
pos+= color2.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName)
|
||||
{
|
||||
mEnabled = true;
|
||||
mPtr = actor;
|
||||
mTopicsList->setEnabled(true);
|
||||
setTitle(npcName);
|
||||
|
||||
mTopicsList->clear();
|
||||
mHyperLinks.clear();
|
||||
mHistory->setCaption("");
|
||||
updateOptions();
|
||||
}
|
||||
|
||||
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||
{
|
||||
mTopicsList->clear();
|
||||
|
||||
bool isCompanion = !MWWorld::Class::get(mPtr).getScript(mPtr).empty()
|
||||
&& mPtr.getRefData().getLocals().getIntVar(MWWorld::Class::get(mPtr).getScript(mPtr), "companion");
|
||||
|
||||
bool anyService = mServices > 0 || isCompanion || mPtr.getTypeName() == typeid(ESM::NPC).name();
|
||||
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
mTopicsList->addItem(gmst.find("sPersuasion")->getString());
|
||||
|
||||
if (mServices & Service_Trade)
|
||||
mTopicsList->addItem(gmst.find("sBarter")->getString());
|
||||
|
||||
if (mServices & Service_BuySpells)
|
||||
mTopicsList->addItem(gmst.find("sSpells")->getString());
|
||||
|
||||
if (mServices & Service_Travel)
|
||||
mTopicsList->addItem(gmst.find("sTravel")->getString());
|
||||
|
||||
if (mServices & Service_CreateSpells)
|
||||
mTopicsList->addItem(gmst.find("sSpellmakingMenuTitle")->getString());
|
||||
|
||||
if (mServices & Service_Enchant)
|
||||
mTopicsList->addItem(gmst.find("sEnchanting")->getString());
|
||||
|
||||
if (mServices & Service_Training)
|
||||
mTopicsList->addItem(gmst.find("sServiceTrainingTitle")->getString());
|
||||
|
||||
if (mServices & Service_Repair)
|
||||
mTopicsList->addItem(gmst.find("sRepair")->getString());
|
||||
|
||||
if (isCompanion)
|
||||
mTopicsList->addItem(gmst.find("sCompanionShare")->getString());
|
||||
|
||||
if (anyService)
|
||||
mTopicsList->addSeparator();
|
||||
|
||||
|
||||
for(std::list<std::string>::iterator it = keyWords.begin(); it != keyWords.end(); ++it)
|
||||
std::string DialogueWindow::parseText(const std::string& text)
|
||||
{
|
||||
mTopicsList->addItem(*it);
|
||||
}
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored)
|
||||
|
||||
void DialogueWindow::removeKeyword(std::string keyWord)
|
||||
{
|
||||
if(mTopicsList->hasItem(keyWord))
|
||||
{
|
||||
mTopicsList->removeItem(keyWord);
|
||||
}
|
||||
mTopicsList->adjustSize();
|
||||
}
|
||||
std::vector<std::string> topics;
|
||||
|
||||
void addColorInString(std::string& str, const std::string& keyword,std::string color1, std::string color2)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while((pos = find_str_ci(str,keyword, pos)) != std::string::npos)
|
||||
{
|
||||
// do not add color if this portion of text is already colored.
|
||||
bool hasSeparator = false;
|
||||
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
{
|
||||
MyGUI::TextIterator iterator (str);
|
||||
MyGUI::UString colour;
|
||||
while(iterator.moveNext())
|
||||
{
|
||||
size_t iteratorPos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if (iteratorPos == pos)
|
||||
break;
|
||||
}
|
||||
|
||||
if (colour == color1)
|
||||
return;
|
||||
if (mTopicsList->getItemNameAt(i) == "")
|
||||
hasSeparator = true;
|
||||
}
|
||||
|
||||
str.insert(pos,color1);
|
||||
pos += color1.length();
|
||||
pos += keyword.length();
|
||||
str.insert(pos,color2);
|
||||
pos+= color2.length();
|
||||
}
|
||||
}
|
||||
|
||||
std::string DialogueWindow::parseText(const std::string& text)
|
||||
{
|
||||
bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored)
|
||||
|
||||
std::vector<std::string> topics;
|
||||
|
||||
bool hasSeparator = false;
|
||||
for (unsigned int i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
{
|
||||
if (mTopicsList->getItemNameAt(i) == "")
|
||||
hasSeparator = true;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0;i<mTopicsList->getItemCount();i++)
|
||||
{
|
||||
std::string keyWord = mTopicsList->getItemNameAt(i);
|
||||
if (separatorReached || !hasSeparator)
|
||||
topics.push_back(keyWord);
|
||||
else if (keyWord == "")
|
||||
separatorReached = true;
|
||||
}
|
||||
|
||||
// sort by length to make sure longer topics are replaced first
|
||||
std::sort(topics.begin(), topics.end(), sortByLength);
|
||||
|
||||
std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
|
||||
|
||||
size_t historySize = 0;
|
||||
if(mHistory->getClient()->getSubWidgetText() != NULL)
|
||||
{
|
||||
historySize = mHistory->getOnlyText().size();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
size_t hypertextPos = 0;
|
||||
for (size_t i = 0; i < hypertext.size(); ++i)
|
||||
{
|
||||
if (hypertext[i].mLink)
|
||||
for(unsigned int i = 0;i<mTopicsList->getItemCount();i++)
|
||||
{
|
||||
size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText);
|
||||
std::string standardForm = hypertext[i].mText;
|
||||
for(; asterisk_count > 0; --asterisk_count)
|
||||
standardForm.append("*");
|
||||
std::string keyWord = mTopicsList->getItemNameAt(i);
|
||||
if (separatorReached || !hasSeparator)
|
||||
topics.push_back(keyWord);
|
||||
else if (keyWord == "")
|
||||
separatorReached = true;
|
||||
}
|
||||
|
||||
standardForm =
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
getTranslationDataStorage().topicStandardForm(standardForm);
|
||||
// sort by length to make sure longer topics are replaced first
|
||||
std::sort(topics.begin(), topics.end(), sortByLength);
|
||||
|
||||
if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() )
|
||||
std::vector<MWDialogue::HyperTextToken> hypertext = MWDialogue::ParseHyperText(text);
|
||||
|
||||
size_t historySize = 0;
|
||||
if(mHistory->getClient()->getSubWidgetText() != NULL)
|
||||
{
|
||||
historySize = mHistory->getOnlyText().size();
|
||||
}
|
||||
|
||||
std::string result;
|
||||
size_t hypertextPos = 0;
|
||||
for (size_t i = 0; i < hypertext.size(); ++i)
|
||||
{
|
||||
if (hypertext[i].mLink)
|
||||
{
|
||||
result.append("#686EBA").append(hypertext[i].mText).append("#B29154");
|
||||
size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText);
|
||||
std::string standardForm = hypertext[i].mText;
|
||||
for(; asterisk_count > 0; --asterisk_count)
|
||||
standardForm.append("*");
|
||||
|
||||
mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length();
|
||||
mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm);
|
||||
standardForm =
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
getTranslationDataStorage().topicStandardForm(standardForm);
|
||||
|
||||
if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() )
|
||||
{
|
||||
result.append("#686EBA").append(hypertext[i].mText).append("#B29154");
|
||||
|
||||
mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length();
|
||||
mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm);
|
||||
}
|
||||
else
|
||||
result += hypertext[i].mText;
|
||||
}
|
||||
else
|
||||
result += hypertext[i].mText;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() )
|
||||
{
|
||||
for(std::vector<std::string>::const_iterator it = topics.begin(); it != topics.end(); ++it)
|
||||
if( !MWBase::Environment::get().getWindowManager()->getTranslationDataStorage().hasTranslation() )
|
||||
{
|
||||
addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154");
|
||||
for(std::vector<std::string>::const_iterator it = topics.begin(); it != topics.end(); ++it)
|
||||
{
|
||||
addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154");
|
||||
}
|
||||
}
|
||||
|
||||
result += hypertext[i].mText;
|
||||
}
|
||||
|
||||
result += hypertext[i].mText;
|
||||
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
|
||||
}
|
||||
|
||||
hypertextPos += MyGUI::UString(hypertext[i].mText).length();
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DialogueWindow::addText(std::string text)
|
||||
{
|
||||
mHistory->addDialogText("#B29154"+parseText(text)+"#B29154");
|
||||
}
|
||||
|
||||
void DialogueWindow::addMessageBox(const std::string& text)
|
||||
{
|
||||
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
|
||||
}
|
||||
|
||||
void DialogueWindow::addTitle(std::string text)
|
||||
{
|
||||
// This is called from the dialogue manager, so text is
|
||||
// case-smashed - thus we have to retrieve the correct case
|
||||
// of the text through the topic list.
|
||||
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
void DialogueWindow::addText(std::string text)
|
||||
{
|
||||
std::string item = mTopicsList->getItemNameAt(i);
|
||||
if (lower_string(item) == text)
|
||||
text = item;
|
||||
mHistory->addDialogText("#B29154"+parseText(text)+"#B29154");
|
||||
}
|
||||
|
||||
mHistory->addDialogHeading(text);
|
||||
}
|
||||
|
||||
void DialogueWindow::askQuestion(std::string question)
|
||||
{
|
||||
mHistory->addDialogText("#572D21"+question+"#B29154"+" ");
|
||||
}
|
||||
|
||||
void DialogueWindow::updateOptions()
|
||||
{
|
||||
//Clear the list of topics
|
||||
mTopicsList->clear();
|
||||
mHyperLinks.clear();
|
||||
mHistory->eraseText(0, mHistory->getTextLength());
|
||||
|
||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
void DialogueWindow::addMessageBox(const std::string& text)
|
||||
{
|
||||
mDispositionBar->setProgressRange(100);
|
||||
mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr));
|
||||
mDispositionText->eraseText(0, mDispositionText->getTextLength());
|
||||
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154");
|
||||
mHistory->addDialogText("\n#FFFFFF"+text+"#B29154");
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueWindow::goodbye()
|
||||
{
|
||||
mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString());
|
||||
mTopicsList->setEnabled(false);
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
void DialogueWindow::onReferenceUnavailable()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
|
||||
}
|
||||
|
||||
void DialogueWindow::onFrame()
|
||||
{
|
||||
if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
void DialogueWindow::addTitle(std::string text)
|
||||
{
|
||||
int disp = std::max(0, std::min(100,
|
||||
MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)
|
||||
+ MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()));
|
||||
mDispositionBar->setProgressRange(100);
|
||||
mDispositionBar->setProgressPosition(disp);
|
||||
mDispositionText->eraseText(0, mDispositionText->getTextLength());
|
||||
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(disp)+std::string("/100")+"#B29154");
|
||||
// This is called from the dialogue manager, so text is
|
||||
// case-smashed - thus we have to retrieve the correct case
|
||||
// of the text through the topic list.
|
||||
for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
|
||||
{
|
||||
std::string item = mTopicsList->getItemNameAt(i);
|
||||
if (lower_string(item) == text)
|
||||
text = item;
|
||||
}
|
||||
|
||||
mHistory->addDialogHeading(text);
|
||||
}
|
||||
|
||||
void DialogueWindow::askQuestion(std::string question)
|
||||
{
|
||||
mHistory->addDialogText("#572D21"+question+"#B29154"+" ");
|
||||
}
|
||||
|
||||
void DialogueWindow::updateOptions()
|
||||
{
|
||||
//Clear the list of topics
|
||||
mTopicsList->clear();
|
||||
mHyperLinks.clear();
|
||||
mHistory->eraseText(0, mHistory->getTextLength());
|
||||
|
||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
mDispositionBar->setProgressRange(100);
|
||||
mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr));
|
||||
mDispositionText->eraseText(0, mDispositionText->getTextLength());
|
||||
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154");
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueWindow::goodbye()
|
||||
{
|
||||
mHistory->addDialogText("\n#572D21" + MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sGoodbye")->getString());
|
||||
mTopicsList->setEnabled(false);
|
||||
mEnabled = false;
|
||||
}
|
||||
|
||||
void DialogueWindow::onReferenceUnavailable()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Dialogue);
|
||||
}
|
||||
|
||||
void DialogueWindow::onFrame()
|
||||
{
|
||||
if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
int disp = std::max(0, std::min(100,
|
||||
MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)
|
||||
+ MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()));
|
||||
mDispositionBar->setProgressRange(100);
|
||||
mDispositionBar->setProgressPosition(disp);
|
||||
mDispositionText->eraseText(0, mDispositionText->getTextLength());
|
||||
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(disp)+std::string("/100")+"#B29154");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,65 +12,67 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace Widgets;
|
||||
|
||||
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
|
||||
namespace MWGui
|
||||
{
|
||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
||||
MyGUI::TextIterator iterator(getCaption());
|
||||
while(iterator.moveNext())
|
||||
{
|
||||
size_t pos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if (pos < _pos)
|
||||
continue;
|
||||
else if (pos == _pos)
|
||||
break;
|
||||
}
|
||||
return colour;
|
||||
}
|
||||
|
||||
MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos)
|
||||
{
|
||||
bool breakOnNext = false;
|
||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
||||
MyGUI::UString colour2 = colour;
|
||||
MyGUI::TextIterator iterator(getCaption());
|
||||
MyGUI::TextIterator col_start = iterator;
|
||||
while(iterator.moveNext())
|
||||
MyGUI::UString DialogueHistory::getColorAtPos(size_t _pos)
|
||||
{
|
||||
size_t pos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if(colour != colour2)
|
||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
||||
MyGUI::TextIterator iterator(getCaption());
|
||||
while(iterator.moveNext())
|
||||
{
|
||||
if(breakOnNext)
|
||||
size_t pos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if (pos < _pos)
|
||||
continue;
|
||||
else if (pos == _pos)
|
||||
break;
|
||||
}
|
||||
return colour;
|
||||
}
|
||||
|
||||
MyGUI::UString DialogueHistory::getColorTextAt(size_t _pos)
|
||||
{
|
||||
bool breakOnNext = false;
|
||||
MyGUI::UString colour = MyGUI::TextIterator::convertTagColour(getTextColour());
|
||||
MyGUI::UString colour2 = colour;
|
||||
MyGUI::TextIterator iterator(getCaption());
|
||||
MyGUI::TextIterator col_start = iterator;
|
||||
while(iterator.moveNext())
|
||||
{
|
||||
size_t pos = iterator.getPosition();
|
||||
iterator.getTagColour(colour);
|
||||
if(colour != colour2)
|
||||
{
|
||||
return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition());
|
||||
if(breakOnNext)
|
||||
{
|
||||
return getOnlyText().substr(col_start.getPosition(), iterator.getPosition()-col_start.getPosition());
|
||||
}
|
||||
col_start = iterator;
|
||||
colour2 = colour;
|
||||
}
|
||||
if (pos < _pos)
|
||||
continue;
|
||||
else if (pos == _pos)
|
||||
{
|
||||
breakOnNext = true;
|
||||
}
|
||||
col_start = iterator;
|
||||
colour2 = colour;
|
||||
}
|
||||
if (pos < _pos)
|
||||
continue;
|
||||
else if (pos == _pos)
|
||||
{
|
||||
breakOnNext = true;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void DialogueHistory::addDialogHeading(const MyGUI::UString& parText)
|
||||
{
|
||||
MyGUI::UString head("\n#D8C09A");
|
||||
head.append(parText);
|
||||
head.append("#B29154\n");
|
||||
addText(head);
|
||||
}
|
||||
void DialogueHistory::addDialogHeading(const MyGUI::UString& parText)
|
||||
{
|
||||
MyGUI::UString head("\n#D8C09A");
|
||||
head.append(parText);
|
||||
head.append("#B29154\n");
|
||||
addText(head);
|
||||
}
|
||||
|
||||
void DialogueHistory::addDialogText(const MyGUI::UString& parText)
|
||||
{
|
||||
addText(parText);
|
||||
addText("\n");
|
||||
}
|
||||
|
||||
void DialogueHistory::addDialogText(const MyGUI::UString& parText)
|
||||
{
|
||||
addText(parText);
|
||||
addText("\n");
|
||||
}
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
#include <components/interpreter/defines.hpp>
|
||||
|
||||
#include "../mwscript/interpretercontext.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <OgreUTFString.h>
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
namespace
|
||||
{
|
||||
int convertFromHex(std::string hex)
|
||||
|
@ -79,287 +76,292 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height)
|
||||
namespace MWGui
|
||||
{
|
||||
using Ogre::UTFString;
|
||||
std::vector<std::string> result;
|
||||
|
||||
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
||||
utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext);
|
||||
|
||||
boost::algorithm::replace_all(utf8Text, "\n", "");
|
||||
boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
|
||||
boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
|
||||
|
||||
UTFString text(utf8Text);
|
||||
const int spacing = 48;
|
||||
|
||||
const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<');
|
||||
const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n');
|
||||
const UTFString::unicode_char SPACE = unicodeCharFromChar(' ');
|
||||
|
||||
while (!text.empty())
|
||||
std::vector<std::string> BookTextParser::split(std::string utf8Text, const int width, const int height)
|
||||
{
|
||||
// read in characters until we have exceeded the size, or run out of text
|
||||
int currentWidth = 0;
|
||||
int currentHeight = 0;
|
||||
using Ogre::UTFString;
|
||||
std::vector<std::string> result;
|
||||
|
||||
size_t currentWordStart = 0;
|
||||
size_t index = 0;
|
||||
while (currentHeight <= height - spacing && index < text.size())
|
||||
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
||||
utf8Text = Interpreter::fixDefinesBook(utf8Text, interpreterContext);
|
||||
|
||||
boost::algorithm::replace_all(utf8Text, "\n", "");
|
||||
boost::algorithm::replace_all(utf8Text, "<BR>", "\n");
|
||||
boost::algorithm::replace_all(utf8Text, "<P>", "\n\n");
|
||||
|
||||
UTFString text(utf8Text);
|
||||
const int spacing = 48;
|
||||
|
||||
const UTFString::unicode_char LEFT_ANGLE = unicodeCharFromChar('<');
|
||||
const UTFString::unicode_char NEWLINE = unicodeCharFromChar('\n');
|
||||
const UTFString::unicode_char SPACE = unicodeCharFromChar(' ');
|
||||
|
||||
while (!text.empty())
|
||||
{
|
||||
const UTFString::unicode_char ch = text.getChar(index);
|
||||
if (ch == LEFT_ANGLE)
|
||||
{
|
||||
const size_t tagStart = index + 1;
|
||||
const size_t tagEnd = text.find('>', tagStart);
|
||||
if (tagEnd == UTFString::npos)
|
||||
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
||||
const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8();
|
||||
// read in characters until we have exceeded the size, or run out of text
|
||||
int currentWidth = 0;
|
||||
int currentHeight = 0;
|
||||
|
||||
if (boost::algorithm::starts_with(tag, "IMG"))
|
||||
size_t currentWordStart = 0;
|
||||
size_t index = 0;
|
||||
while (currentHeight <= height - spacing && index < text.size())
|
||||
{
|
||||
const UTFString::unicode_char ch = text.getChar(index);
|
||||
if (ch == LEFT_ANGLE)
|
||||
{
|
||||
const int h = mHeight;
|
||||
parseImage(tag, false);
|
||||
currentHeight += (mHeight - h);
|
||||
currentWidth = 0;
|
||||
}
|
||||
else if (boost::algorithm::starts_with(tag, "FONT"))
|
||||
{
|
||||
parseFont(tag);
|
||||
if (currentWidth != 0) {
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
}
|
||||
currentWidth = 0;
|
||||
}
|
||||
else if (boost::algorithm::starts_with(tag, "DIV"))
|
||||
{
|
||||
parseDiv(tag);
|
||||
if (currentWidth != 0) {
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
}
|
||||
}
|
||||
index = tagEnd;
|
||||
}
|
||||
else if (ch == NEWLINE)
|
||||
{
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
currentWordStart = index;
|
||||
}
|
||||
else if (ch == SPACE)
|
||||
{
|
||||
currentWidth += 3; // keep this in sync with the font's SpaceWidth property
|
||||
currentWordStart = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentWidth += widthForCharGlyph(ch);
|
||||
}
|
||||
|
||||
if (currentWidth > width)
|
||||
{
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
// add size of the current word
|
||||
UTFString word = text.substr(currentWordStart, index - currentWordStart);
|
||||
for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it)
|
||||
currentWidth += widthForCharGlyph(it.getCharacter());
|
||||
}
|
||||
index += UTFString::_utf16_char_length(ch);
|
||||
}
|
||||
const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0)
|
||||
? currentWordStart : index;
|
||||
|
||||
result.push_back(text.substr(0, pageEnd).asUTF8());
|
||||
text.erase(0, pageEnd);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const
|
||||
{
|
||||
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
|
||||
return MyGUI::FontManager::getInstance().getByName(fontName)
|
||||
->getGlyphInfo(unicodeChar)->width;
|
||||
}
|
||||
|
||||
float BookTextParser::currentFontHeight() const
|
||||
{
|
||||
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
|
||||
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
|
||||
}
|
||||
|
||||
MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width)
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
||||
text = Interpreter::fixDefinesBook(text, interpreterContext);
|
||||
|
||||
mParent = parent;
|
||||
mWidth = width;
|
||||
mHeight = 0;
|
||||
|
||||
assert(mParent);
|
||||
while (mParent->getChildCount())
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
|
||||
}
|
||||
|
||||
boost::algorithm::replace_all(text, "\n", "");
|
||||
boost::algorithm::replace_all(text, "<BR>", "\n");
|
||||
boost::algorithm::replace_all(text, "<P>", "\n\n");
|
||||
|
||||
// remove leading newlines
|
||||
// while (text[0] == '\n')
|
||||
// text.erase(0);
|
||||
|
||||
// remove trailing "
|
||||
if (text[text.size()-1] == '\"')
|
||||
text.erase(text.size()-1);
|
||||
|
||||
parseSubText(text);
|
||||
return MyGUI::IntSize(mWidth, mHeight);
|
||||
}
|
||||
|
||||
void BookTextParser::parseImage(std::string tag, bool createWidget)
|
||||
{
|
||||
int src_start = tag.find("SRC=")+5;
|
||||
std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start);
|
||||
|
||||
// fix texture extension to .dds
|
||||
if (image.size() > 4)
|
||||
{
|
||||
image[image.size()-3] = 'd';
|
||||
image[image.size()-2] = 'd';
|
||||
image[image.size()-1] = 's';
|
||||
}
|
||||
|
||||
int width_start = tag.find("WIDTH=")+7;
|
||||
int width = boost::lexical_cast<int>(tag.substr(width_start, tag.find('"', width_start)-width_start));
|
||||
|
||||
int height_start = tag.find("HEIGHT=")+8;
|
||||
int height = boost::lexical_cast<int>(tag.substr(height_start, tag.find('"', height_start)-height_start));
|
||||
|
||||
if (createWidget)
|
||||
{
|
||||
MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox",
|
||||
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
|
||||
box->setImageTexture("bookart\\" + image);
|
||||
box->setProperty("NeedMouse", "false");
|
||||
}
|
||||
|
||||
mWidth = std::max(mWidth, width);
|
||||
mHeight += height;
|
||||
}
|
||||
|
||||
void BookTextParser::parseDiv(std::string tag)
|
||||
{
|
||||
if (tag.find("ALIGN=") == std::string::npos)
|
||||
return;
|
||||
|
||||
int align_start = tag.find("ALIGN=")+7;
|
||||
std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start);
|
||||
if (align == "CENTER")
|
||||
mTextStyle.mTextAlign = MyGUI::Align::HCenter;
|
||||
else if (align == "LEFT")
|
||||
mTextStyle.mTextAlign = MyGUI::Align::Left;
|
||||
}
|
||||
|
||||
void BookTextParser::parseFont(std::string tag)
|
||||
{
|
||||
if (tag.find("COLOR=") != std::string::npos)
|
||||
{
|
||||
int color_start = tag.find("COLOR=")+7;
|
||||
std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start);
|
||||
|
||||
mTextStyle.mColour = MyGUI::Colour(
|
||||
convertFromHex(color.substr(0, 2))/255.0,
|
||||
convertFromHex(color.substr(2, 2))/255.0,
|
||||
convertFromHex(color.substr(4, 2))/255.0);
|
||||
}
|
||||
if (tag.find("FACE=") != std::string::npos)
|
||||
{
|
||||
int face_start = tag.find("FACE=")+6;
|
||||
std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start);
|
||||
|
||||
if (face != "Magic Cards")
|
||||
mTextStyle.mFont = face;
|
||||
}
|
||||
if (tag.find("SIZE=") != std::string::npos)
|
||||
{
|
||||
/// \todo
|
||||
}
|
||||
}
|
||||
|
||||
void BookTextParser::parseSubText(std::string text)
|
||||
{
|
||||
if (text[0] == '<')
|
||||
{
|
||||
const size_t tagStart = 1;
|
||||
const size_t tagEnd = text.find('>', tagStart);
|
||||
if (tagEnd == std::string::npos)
|
||||
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
||||
const std::string tag = text.substr(tagStart, tagEnd - tagStart);
|
||||
|
||||
if (boost::algorithm::starts_with(tag, "IMG"))
|
||||
parseImage(tag);
|
||||
if (boost::algorithm::starts_with(tag, "FONT"))
|
||||
parseFont(tag);
|
||||
if (boost::algorithm::starts_with(tag, "DOV"))
|
||||
parseDiv(tag);
|
||||
|
||||
text.erase(0, tagEnd + 1);
|
||||
}
|
||||
|
||||
size_t tagStart = std::string::npos;
|
||||
std::string realText; // real text, without tags
|
||||
for (size_t i = 0; i<text.size(); ++i)
|
||||
{
|
||||
char c = text[i];
|
||||
if (c == '<')
|
||||
{
|
||||
if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags
|
||||
{
|
||||
while (c != '>')
|
||||
{
|
||||
if (i >= text.size())
|
||||
const size_t tagStart = index + 1;
|
||||
const size_t tagEnd = text.find('>', tagStart);
|
||||
if (tagEnd == UTFString::npos)
|
||||
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
||||
++i;
|
||||
c = text[i];
|
||||
const std::string tag = text.substr(tagStart, tagEnd - tagStart).asUTF8();
|
||||
|
||||
if (boost::algorithm::starts_with(tag, "IMG"))
|
||||
{
|
||||
const int h = mHeight;
|
||||
parseImage(tag, false);
|
||||
currentHeight += (mHeight - h);
|
||||
currentWidth = 0;
|
||||
}
|
||||
else if (boost::algorithm::starts_with(tag, "FONT"))
|
||||
{
|
||||
parseFont(tag);
|
||||
if (currentWidth != 0) {
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
}
|
||||
currentWidth = 0;
|
||||
}
|
||||
else if (boost::algorithm::starts_with(tag, "DIV"))
|
||||
{
|
||||
parseDiv(tag);
|
||||
if (currentWidth != 0) {
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
}
|
||||
}
|
||||
index = tagEnd;
|
||||
}
|
||||
else if (ch == NEWLINE)
|
||||
{
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
currentWordStart = index;
|
||||
}
|
||||
else if (ch == SPACE)
|
||||
{
|
||||
currentWidth += 3; // keep this in sync with the font's SpaceWidth property
|
||||
currentWordStart = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentWidth += widthForCharGlyph(ch);
|
||||
}
|
||||
|
||||
if (currentWidth > width)
|
||||
{
|
||||
currentHeight += currentFontHeight();
|
||||
currentWidth = 0;
|
||||
// add size of the current word
|
||||
UTFString word = text.substr(currentWordStart, index - currentWordStart);
|
||||
for (UTFString::const_iterator it = word.begin(), end = word.end(); it != end; ++it)
|
||||
currentWidth += widthForCharGlyph(it.getCharacter());
|
||||
}
|
||||
index += UTFString::_utf16_char_length(ch);
|
||||
}
|
||||
const size_t pageEnd = (currentHeight > height - spacing && currentWordStart != 0)
|
||||
? currentWordStart : index;
|
||||
|
||||
result.push_back(text.substr(0, pageEnd).asUTF8());
|
||||
text.erase(0, pageEnd);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float BookTextParser::widthForCharGlyph(unsigned unicodeChar) const
|
||||
{
|
||||
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
|
||||
return MyGUI::FontManager::getInstance().getByName(fontName)
|
||||
->getGlyphInfo(unicodeChar)->width;
|
||||
}
|
||||
|
||||
float BookTextParser::currentFontHeight() const
|
||||
{
|
||||
std::string fontName(mTextStyle.mFont == "Default" ? "EB Garamond" : mTextStyle.mFont);
|
||||
return MyGUI::FontManager::getInstance().getByName(fontName)->getDefaultHeight();
|
||||
}
|
||||
|
||||
MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width)
|
||||
{
|
||||
MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor
|
||||
text = Interpreter::fixDefinesBook(text, interpreterContext);
|
||||
|
||||
mParent = parent;
|
||||
mWidth = width;
|
||||
mHeight = 0;
|
||||
|
||||
assert(mParent);
|
||||
while (mParent->getChildCount())
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(mParent->getChildAt(0));
|
||||
}
|
||||
|
||||
boost::algorithm::replace_all(text, "\n", "");
|
||||
boost::algorithm::replace_all(text, "<BR>", "\n");
|
||||
boost::algorithm::replace_all(text, "<P>", "\n\n");
|
||||
|
||||
// remove leading newlines
|
||||
// while (text[0] == '\n')
|
||||
// text.erase(0);
|
||||
|
||||
// remove trailing "
|
||||
if (text[text.size()-1] == '\"')
|
||||
text.erase(text.size()-1);
|
||||
|
||||
parseSubText(text);
|
||||
return MyGUI::IntSize(mWidth, mHeight);
|
||||
}
|
||||
|
||||
void BookTextParser::parseImage(std::string tag, bool createWidget)
|
||||
{
|
||||
int src_start = tag.find("SRC=")+5;
|
||||
std::string image = tag.substr(src_start, tag.find('"', src_start)-src_start);
|
||||
|
||||
// fix texture extension to .dds
|
||||
if (image.size() > 4)
|
||||
{
|
||||
image[image.size()-3] = 'd';
|
||||
image[image.size()-2] = 'd';
|
||||
image[image.size()-1] = 's';
|
||||
}
|
||||
|
||||
int width_start = tag.find("WIDTH=")+7;
|
||||
int width = boost::lexical_cast<int>(tag.substr(width_start, tag.find('"', width_start)-width_start));
|
||||
|
||||
int height_start = tag.find("HEIGHT=")+8;
|
||||
int height = boost::lexical_cast<int>(tag.substr(height_start, tag.find('"', height_start)-height_start));
|
||||
|
||||
if (createWidget)
|
||||
{
|
||||
MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox",
|
||||
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
|
||||
box->setImageTexture("bookart\\" + image);
|
||||
box->setProperty("NeedMouse", "false");
|
||||
}
|
||||
|
||||
mWidth = std::max(mWidth, width);
|
||||
mHeight += height;
|
||||
}
|
||||
|
||||
void BookTextParser::parseDiv(std::string tag)
|
||||
{
|
||||
if (tag.find("ALIGN=") == std::string::npos)
|
||||
return;
|
||||
|
||||
int align_start = tag.find("ALIGN=")+7;
|
||||
std::string align = tag.substr(align_start, tag.find('"', align_start)-align_start);
|
||||
if (align == "CENTER")
|
||||
mTextStyle.mTextAlign = MyGUI::Align::HCenter;
|
||||
else if (align == "LEFT")
|
||||
mTextStyle.mTextAlign = MyGUI::Align::Left;
|
||||
}
|
||||
|
||||
void BookTextParser::parseFont(std::string tag)
|
||||
{
|
||||
if (tag.find("COLOR=") != std::string::npos)
|
||||
{
|
||||
int color_start = tag.find("COLOR=")+7;
|
||||
std::string color = tag.substr(color_start, tag.find('"', color_start)-color_start);
|
||||
|
||||
mTextStyle.mColour = MyGUI::Colour(
|
||||
convertFromHex(color.substr(0, 2))/255.0,
|
||||
convertFromHex(color.substr(2, 2))/255.0,
|
||||
convertFromHex(color.substr(4, 2))/255.0);
|
||||
}
|
||||
if (tag.find("FACE=") != std::string::npos)
|
||||
{
|
||||
int face_start = tag.find("FACE=")+6;
|
||||
std::string face = tag.substr(face_start, tag.find('"', face_start)-face_start);
|
||||
|
||||
if (face != "Magic Cards")
|
||||
mTextStyle.mFont = face;
|
||||
}
|
||||
if (tag.find("SIZE=") != std::string::npos)
|
||||
{
|
||||
/// \todo
|
||||
}
|
||||
}
|
||||
|
||||
void BookTextParser::parseSubText(std::string text)
|
||||
{
|
||||
if (text[0] == '<')
|
||||
{
|
||||
const size_t tagStart = 1;
|
||||
const size_t tagEnd = text.find('>', tagStart);
|
||||
if (tagEnd == std::string::npos)
|
||||
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
||||
const std::string tag = text.substr(tagStart, tagEnd - tagStart);
|
||||
|
||||
if (boost::algorithm::starts_with(tag, "IMG"))
|
||||
parseImage(tag);
|
||||
if (boost::algorithm::starts_with(tag, "FONT"))
|
||||
parseFont(tag);
|
||||
if (boost::algorithm::starts_with(tag, "DOV"))
|
||||
parseDiv(tag);
|
||||
|
||||
text.erase(0, tagEnd + 1);
|
||||
}
|
||||
|
||||
size_t tagStart = std::string::npos;
|
||||
std::string realText; // real text, without tags
|
||||
for (size_t i = 0; i<text.size(); ++i)
|
||||
{
|
||||
char c = text[i];
|
||||
if (c == '<')
|
||||
{
|
||||
if ((i + 1 < text.size()) && text[i+1] == '/') // ignore closing tags
|
||||
{
|
||||
while (c != '>')
|
||||
{
|
||||
if (i >= text.size())
|
||||
throw std::runtime_error("BookTextParser Error: Tag is not terminated");
|
||||
++i;
|
||||
c = text[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagStart = i;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
tagStart = i;
|
||||
break;
|
||||
}
|
||||
realText += c;
|
||||
}
|
||||
|
||||
MyGUI::EditBox* box = mParent->createWidget<MyGUI::EditBox>("NormalText",
|
||||
MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
|
||||
box->setProperty("Static", "true");
|
||||
box->setProperty("MultiLine", "true");
|
||||
box->setProperty("WordWrap", "true");
|
||||
box->setProperty("NeedMouse", "false");
|
||||
box->setMaxTextLength(realText.size());
|
||||
box->setTextAlign(mTextStyle.mTextAlign);
|
||||
box->setTextColour(mTextStyle.mColour);
|
||||
box->setFontName(mTextStyle.mFont);
|
||||
box->setCaption(realText);
|
||||
box->setSize(box->getSize().width, box->getTextSize().height);
|
||||
mHeight += box->getTextSize().height;
|
||||
|
||||
if (tagStart != std::string::npos)
|
||||
{
|
||||
parseSubText(text.substr(tagStart, text.size()));
|
||||
}
|
||||
else
|
||||
realText += c;
|
||||
}
|
||||
|
||||
MyGUI::EditBox* box = mParent->createWidget<MyGUI::EditBox>("NormalText",
|
||||
MyGUI::IntCoord(0, mHeight, mWidth, 24), MyGUI::Align::Left | MyGUI::Align::Top,
|
||||
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
|
||||
box->setProperty("Static", "true");
|
||||
box->setProperty("MultiLine", "true");
|
||||
box->setProperty("WordWrap", "true");
|
||||
box->setProperty("NeedMouse", "false");
|
||||
box->setMaxTextLength(realText.size());
|
||||
box->setTextAlign(mTextStyle.mTextAlign);
|
||||
box->setTextColour(mTextStyle.mColour);
|
||||
box->setFontName(mTextStyle.mFont);
|
||||
box->setCaption(realText);
|
||||
box->setSize(box->getSize().width, box->getTextSize().height);
|
||||
mHeight += box->getTextSize().height;
|
||||
|
||||
if (tagStart != std::string::npos)
|
||||
{
|
||||
parseSubText(text.substr(tagStart, text.size()));
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,28 +1,15 @@
|
|||
#include "inventorywindow.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <components/compiler/locals.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/actiontake.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "widgets.hpp"
|
||||
#include "bookwindow.hpp"
|
||||
#include "scrollwindow.hpp"
|
||||
#include "spellwindow.hpp"
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/journal.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwdialogue/journalentry.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
|
|
@ -8,12 +8,10 @@
|
|||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/fallback.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
|
|
@ -5,159 +5,165 @@
|
|||
#include <MyGUI_ImageBox.h>
|
||||
#include <MyGUI_ScrollBar.h>
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace MWGui::Widgets;
|
||||
|
||||
MWList::MWList() :
|
||||
mClient(0)
|
||||
, mScrollView(0)
|
||||
, mItemHeight(0)
|
||||
namespace MWGui
|
||||
{
|
||||
}
|
||||
|
||||
void MWList::initialiseOverride()
|
||||
{
|
||||
Base::initialiseOverride();
|
||||
|
||||
assignWidget(mClient, "Client");
|
||||
if (mClient == 0)
|
||||
mClient = this;
|
||||
|
||||
mScrollView = mClient->createWidgetReal<MWGui::Widgets::MWScrollView>(
|
||||
"MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView");
|
||||
}
|
||||
|
||||
void MWList::addItem(const std::string& name)
|
||||
{
|
||||
mItems.push_back(name);
|
||||
}
|
||||
|
||||
void MWList::addSeparator()
|
||||
{
|
||||
mItems.push_back("");
|
||||
}
|
||||
|
||||
void MWList::adjustSize()
|
||||
{
|
||||
redraw();
|
||||
}
|
||||
|
||||
void MWList::redraw(bool scrollbarShown)
|
||||
{
|
||||
const int _scrollBarWidth = 24; // fetch this from skin?
|
||||
const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;
|
||||
const int spacing = 3;
|
||||
size_t scrollbarPosition = mScrollView->getScrollPosition();
|
||||
|
||||
while (mScrollView->getChildCount())
|
||||
namespace Widgets
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));
|
||||
}
|
||||
|
||||
mItemHeight = 0;
|
||||
int i=0;
|
||||
for (std::vector<std::string>::const_iterator it=mItems.begin();
|
||||
it!=mItems.end(); ++it)
|
||||
{
|
||||
if (*it != "")
|
||||
MWList::MWList() :
|
||||
mClient(0)
|
||||
, mScrollView(0)
|
||||
, mItemHeight(0)
|
||||
{
|
||||
MyGUI::Button* button = mScrollView->createWidget<MyGUI::Button>(
|
||||
"MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it));
|
||||
button->setCaption((*it));
|
||||
button->getSubWidgetText()->setWordWrap(true);
|
||||
button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left);
|
||||
button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected);
|
||||
|
||||
int height = button->getTextSize().height;
|
||||
button->setSize(MyGUI::IntSize(button->getSize().width, height));
|
||||
button->setUserData(i);
|
||||
|
||||
mItemHeight += height + spacing;
|
||||
}
|
||||
else
|
||||
|
||||
void MWList::initialiseOverride()
|
||||
{
|
||||
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine",
|
||||
MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);
|
||||
separator->setNeedMouseFocus(false);
|
||||
Base::initialiseOverride();
|
||||
|
||||
mItemHeight += 18 + spacing;
|
||||
assignWidget(mClient, "Client");
|
||||
if (mClient == 0)
|
||||
mClient = this;
|
||||
|
||||
mScrollView = mClient->createWidgetReal<MWGui::Widgets::MWScrollView>(
|
||||
"MW_ScrollView", MyGUI::FloatCoord(0.0, 0.0, 1.0, 1.0),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left | MyGUI::Align::Stretch, getName() + "_ScrollView");
|
||||
}
|
||||
++i;
|
||||
|
||||
void MWList::addItem(const std::string& name)
|
||||
{
|
||||
mItems.push_back(name);
|
||||
}
|
||||
|
||||
void MWList::addSeparator()
|
||||
{
|
||||
mItems.push_back("");
|
||||
}
|
||||
|
||||
void MWList::adjustSize()
|
||||
{
|
||||
redraw();
|
||||
}
|
||||
|
||||
void MWList::redraw(bool scrollbarShown)
|
||||
{
|
||||
const int _scrollBarWidth = 24; // fetch this from skin?
|
||||
const int scrollBarWidth = scrollbarShown ? _scrollBarWidth : 0;
|
||||
const int spacing = 3;
|
||||
size_t scrollbarPosition = mScrollView->getScrollPosition();
|
||||
|
||||
while (mScrollView->getChildCount())
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(mScrollView->getChildAt(0));
|
||||
}
|
||||
|
||||
mItemHeight = 0;
|
||||
int i=0;
|
||||
for (std::vector<std::string>::const_iterator it=mItems.begin();
|
||||
it!=mItems.end(); ++it)
|
||||
{
|
||||
if (*it != "")
|
||||
{
|
||||
MyGUI::Button* button = mScrollView->createWidget<MyGUI::Button>(
|
||||
"MW_ListLine", MyGUI::IntCoord(0, mItemHeight, mScrollView->getSize().width - scrollBarWidth - 2, 24),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top, getName() + "_item_" + (*it));
|
||||
button->setCaption((*it));
|
||||
button->getSubWidgetText()->setWordWrap(true);
|
||||
button->getSubWidgetText()->setTextAlign(MyGUI::Align::Left);
|
||||
button->eventMouseWheel += MyGUI::newDelegate(this, &MWList::onMouseWheel);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWList::onItemSelected);
|
||||
|
||||
int height = button->getTextSize().height;
|
||||
button->setSize(MyGUI::IntSize(button->getSize().width, height));
|
||||
button->setUserData(i);
|
||||
|
||||
mItemHeight += height + spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
MyGUI::ImageBox* separator = mScrollView->createWidget<MyGUI::ImageBox>("MW_HLine",
|
||||
MyGUI::IntCoord(2, mItemHeight, mScrollView->getWidth()-4, 18),
|
||||
MyGUI::Align::Left | MyGUI::Align::Top | MyGUI::Align::HStretch);
|
||||
separator->setNeedMouseFocus(false);
|
||||
|
||||
mItemHeight += 18 + spacing;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height));
|
||||
|
||||
if (!scrollbarShown && mItemHeight > mClient->getSize().height)
|
||||
redraw(true);
|
||||
|
||||
size_t scrollbarRange = mScrollView->getScrollRange();
|
||||
if(scrollbarPosition > scrollbarRange)
|
||||
scrollbarPosition = scrollbarRange;
|
||||
mScrollView->setScrollPosition(scrollbarPosition);
|
||||
}
|
||||
|
||||
bool MWList::hasItem(const std::string& name)
|
||||
{
|
||||
return (std::find(mItems.begin(), mItems.end(), name) != mItems.end());
|
||||
}
|
||||
|
||||
unsigned int MWList::getItemCount()
|
||||
{
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
std::string MWList::getItemNameAt(unsigned int at)
|
||||
{
|
||||
assert(at < mItems.size() && "List item out of bounds");
|
||||
return mItems[at];
|
||||
}
|
||||
|
||||
void MWList::removeItem(const std::string& name)
|
||||
{
|
||||
assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() );
|
||||
mItems.erase( std::find(mItems.begin(), mItems.end(), name) );
|
||||
}
|
||||
|
||||
void MWList::clear()
|
||||
{
|
||||
mItems.clear();
|
||||
}
|
||||
|
||||
void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
//NB view offset is negative
|
||||
if (mScrollView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
||||
void MWList::onItemSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string name = static_cast<MyGUI::Button*>(_sender)->getCaption();
|
||||
int id = *_sender->getUserData<int>();
|
||||
eventItemSelected(name, id);
|
||||
eventWidgetSelected(_sender);
|
||||
}
|
||||
|
||||
MyGUI::Widget* MWList::getItemWidget(const std::string& name)
|
||||
{
|
||||
return mScrollView->findWidget (getName() + "_item_" + name);
|
||||
}
|
||||
|
||||
size_t MWScrollView::getScrollPosition()
|
||||
{
|
||||
return getVScroll()->getScrollPosition();
|
||||
}
|
||||
|
||||
void MWScrollView::setScrollPosition(size_t position)
|
||||
{
|
||||
getVScroll()->setScrollPosition(position);
|
||||
}
|
||||
size_t MWScrollView::getScrollRange()
|
||||
{
|
||||
return getVScroll()->getScrollRange();
|
||||
}
|
||||
|
||||
}
|
||||
mScrollView->setCanvasSize(mClient->getSize().width + (_scrollBarWidth-scrollBarWidth), std::max(mItemHeight, mClient->getSize().height));
|
||||
|
||||
if (!scrollbarShown && mItemHeight > mClient->getSize().height)
|
||||
redraw(true);
|
||||
|
||||
size_t scrollbarRange = mScrollView->getScrollRange();
|
||||
if(scrollbarPosition > scrollbarRange)
|
||||
scrollbarPosition = scrollbarRange;
|
||||
mScrollView->setScrollPosition(scrollbarPosition);
|
||||
}
|
||||
|
||||
bool MWList::hasItem(const std::string& name)
|
||||
{
|
||||
return (std::find(mItems.begin(), mItems.end(), name) != mItems.end());
|
||||
}
|
||||
|
||||
unsigned int MWList::getItemCount()
|
||||
{
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
std::string MWList::getItemNameAt(unsigned int at)
|
||||
{
|
||||
assert(at < mItems.size() && "List item out of bounds");
|
||||
return mItems[at];
|
||||
}
|
||||
|
||||
void MWList::removeItem(const std::string& name)
|
||||
{
|
||||
assert( std::find(mItems.begin(), mItems.end(), name) != mItems.end() );
|
||||
mItems.erase( std::find(mItems.begin(), mItems.end(), name) );
|
||||
}
|
||||
|
||||
void MWList::clear()
|
||||
{
|
||||
mItems.clear();
|
||||
}
|
||||
|
||||
void MWList::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
//NB view offset is negative
|
||||
if (mScrollView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mScrollView->setViewOffset(MyGUI::IntPoint(0, mScrollView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
||||
void MWList::onItemSelected(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string name = static_cast<MyGUI::Button*>(_sender)->getCaption();
|
||||
int id = *_sender->getUserData<int>();
|
||||
eventItemSelected(name, id);
|
||||
eventWidgetSelected(_sender);
|
||||
}
|
||||
|
||||
MyGUI::Widget* MWList::getItemWidget(const std::string& name)
|
||||
{
|
||||
return mScrollView->findWidget (getName() + "_item_" + name);
|
||||
}
|
||||
|
||||
size_t MWScrollView::getScrollPosition()
|
||||
{
|
||||
return getVScroll()->getScrollPosition();
|
||||
}
|
||||
|
||||
void MWScrollView::setScrollPosition(size_t position)
|
||||
{
|
||||
getVScroll()->setScrollPosition(position);
|
||||
}
|
||||
size_t MWScrollView::getScrollRange()
|
||||
{
|
||||
return getVScroll()->getScrollRange();
|
||||
}
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
#include "loadingscreen.hpp"
|
||||
|
||||
#include <OgreRenderWindow.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreCompositorManager.h>
|
||||
#include <OgreCompositorChain.h>
|
||||
#include <OgreMaterial.h>
|
||||
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include <components/esm/records.hpp>
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <OgreRoot.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
|
|
|
@ -2,12 +2,8 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <OgreVector2.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreSceneNode.h>
|
||||
|
||||
#include <MyGUI_Gui.h>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -17,430 +13,433 @@
|
|||
|
||||
#include "widgets.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
LocalMapBase::LocalMapBase()
|
||||
: mCurX(0)
|
||||
, mCurY(0)
|
||||
, mInterior(false)
|
||||
, mFogOfWar(true)
|
||||
, mLocalMap(NULL)
|
||||
, mMapDragAndDrop(false)
|
||||
, mPrefix()
|
||||
, mChanged(true)
|
||||
, mLayout(NULL)
|
||||
, mLastPositionX(0.0f)
|
||||
, mLastPositionY(0.0f)
|
||||
, mLastDirectionX(0.0f)
|
||||
, mLastDirectionY(0.0f)
|
||||
, mCompass(NULL)
|
||||
namespace MWGui
|
||||
{
|
||||
}
|
||||
|
||||
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop)
|
||||
{
|
||||
mLocalMap = widget;
|
||||
mLayout = layout;
|
||||
mMapDragAndDrop = mapDragAndDrop;
|
||||
mCompass = compass;
|
||||
|
||||
// create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each
|
||||
const int widgetSize = 512;
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
LocalMapBase::LocalMapBase()
|
||||
: mCurX(0)
|
||||
, mCurY(0)
|
||||
, mInterior(false)
|
||||
, mFogOfWar(true)
|
||||
, mLocalMap(NULL)
|
||||
, mMapDragAndDrop(false)
|
||||
, mPrefix()
|
||||
, mChanged(true)
|
||||
, mLayout(NULL)
|
||||
, mLastPositionX(0.0f)
|
||||
, mLastPositionY(0.0f)
|
||||
, mLastDirectionX(0.0f)
|
||||
, mLastDirectionY(0.0f)
|
||||
, mCompass(NULL)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
}
|
||||
|
||||
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, OEngine::GUI::Layout* layout, bool mapDragAndDrop)
|
||||
{
|
||||
mLocalMap = widget;
|
||||
mLayout = layout;
|
||||
mMapDragAndDrop = mapDragAndDrop;
|
||||
mCompass = compass;
|
||||
|
||||
// create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each
|
||||
const int widgetSize = 512;
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my));
|
||||
|
||||
MyGUI::ImageBox* fog = map->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||
MyGUI::IntCoord(0, 0, widgetSize, widgetSize),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my) + "_fog");
|
||||
|
||||
if (!mMapDragAndDrop)
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
map->setNeedMouseFocus(false);
|
||||
fog->setNeedMouseFocus(false);
|
||||
}
|
||||
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my));
|
||||
|
||||
mMapWidgets.push_back(map);
|
||||
mFogWidgets.push_back(fog);
|
||||
}
|
||||
}
|
||||
}
|
||||
MyGUI::ImageBox* fog = map->createWidget<MyGUI::ImageBox>("ImageBox",
|
||||
MyGUI::IntCoord(0, 0, widgetSize, widgetSize),
|
||||
MyGUI::Align::Top | MyGUI::Align::Left, "Map_" + boost::lexical_cast<std::string>(mx) + "_" + boost::lexical_cast<std::string>(my) + "_fog");
|
||||
|
||||
void LocalMapBase::setCellPrefix(const std::string& prefix)
|
||||
{
|
||||
mPrefix = prefix;
|
||||
mChanged = true;
|
||||
}
|
||||
|
||||
void LocalMapBase::toggleFogOfWar()
|
||||
{
|
||||
mFogOfWar = !mFogOfWar;
|
||||
applyFogOfWar();
|
||||
}
|
||||
|
||||
void LocalMapBase::applyFogOfWar()
|
||||
{
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(mCurX + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(mCurY + (-1*(my-1)));
|
||||
MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx];
|
||||
fog->setImageTexture(mFogOfWar ?
|
||||
((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog"
|
||||
: "black.png" )
|
||||
: "");
|
||||
}
|
||||
}
|
||||
notifyMapChanged ();
|
||||
}
|
||||
|
||||
void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2)
|
||||
{
|
||||
applyFogOfWar ();
|
||||
}
|
||||
|
||||
void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2)
|
||||
{
|
||||
applyFogOfWar ();
|
||||
}
|
||||
|
||||
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
||||
{
|
||||
if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell
|
||||
|
||||
// clear all previous markers
|
||||
for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i)
|
||||
{
|
||||
if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker")
|
||||
{
|
||||
MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
// map
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(x + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(y + (-1*(my-1)));
|
||||
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
|
||||
MyGUI::ImageBox* box = mMapWidgets[my + 3*mx];
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image) != 0)
|
||||
box->setImageTexture(image);
|
||||
else
|
||||
box->setImageTexture("black.png");
|
||||
|
||||
|
||||
// door markers
|
||||
|
||||
// interior map only consists of one cell, so handle the markers only once
|
||||
if (interior && (mx != 2 || my != 2))
|
||||
continue;
|
||||
|
||||
MWWorld::CellStore* cell;
|
||||
if (interior)
|
||||
cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix);
|
||||
else
|
||||
cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1));
|
||||
|
||||
std::vector<MWBase::World::DoorMarker> doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell);
|
||||
|
||||
for (std::vector<MWBase::World::DoorMarker>::iterator it = doors.begin(); it != doors.end(); ++it)
|
||||
{
|
||||
MWBase::World::DoorMarker marker = *it;
|
||||
|
||||
// convert world coordinates to normalized cell coordinates
|
||||
MyGUI::IntCoord widgetCoord;
|
||||
float nX,nY;
|
||||
int cellDx, cellDy;
|
||||
if (!interior)
|
||||
if (!mMapDragAndDrop)
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
nX = (marker.x - cellSize * (x+mx-1)) / cellSize;
|
||||
nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize;
|
||||
|
||||
widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8);
|
||||
map->setNeedMouseFocus(false);
|
||||
fog->setNeedMouseFocus(false);
|
||||
}
|
||||
|
||||
mMapWidgets.push_back(map);
|
||||
mFogWidgets.push_back(fog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMapBase::setCellPrefix(const std::string& prefix)
|
||||
{
|
||||
mPrefix = prefix;
|
||||
mChanged = true;
|
||||
}
|
||||
|
||||
void LocalMapBase::toggleFogOfWar()
|
||||
{
|
||||
mFogOfWar = !mFogOfWar;
|
||||
applyFogOfWar();
|
||||
}
|
||||
|
||||
void LocalMapBase::applyFogOfWar()
|
||||
{
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(mCurX + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(mCurY + (-1*(my-1)));
|
||||
MyGUI::ImageBox* fog = mFogWidgets[my + 3*mx];
|
||||
fog->setImageTexture(mFogOfWar ?
|
||||
((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog"
|
||||
: "black.png" )
|
||||
: "");
|
||||
}
|
||||
}
|
||||
notifyMapChanged ();
|
||||
}
|
||||
|
||||
void LocalMapBase::onMarkerFocused (MyGUI::Widget* w1, MyGUI::Widget* w2)
|
||||
{
|
||||
applyFogOfWar ();
|
||||
}
|
||||
|
||||
void LocalMapBase::onMarkerUnfocused (MyGUI::Widget* w1, MyGUI::Widget* w2)
|
||||
{
|
||||
applyFogOfWar ();
|
||||
}
|
||||
|
||||
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
||||
{
|
||||
if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell
|
||||
|
||||
// clear all previous markers
|
||||
for (unsigned int i=0; i< mLocalMap->getChildCount(); ++i)
|
||||
{
|
||||
if (mLocalMap->getChildAt(i)->getName ().substr (0, 6) == "Marker")
|
||||
{
|
||||
MyGUI::Gui::getInstance ().destroyWidget (mLocalMap->getChildAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
// map
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(x + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(y + (-1*(my-1)));
|
||||
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
|
||||
MyGUI::ImageBox* box = mMapWidgets[my + 3*mx];
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image) != 0)
|
||||
box->setImageTexture(image);
|
||||
else
|
||||
{
|
||||
Ogre::Vector2 position (marker.x, marker.y);
|
||||
MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy);
|
||||
box->setImageTexture("black.png");
|
||||
|
||||
widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8);
|
||||
|
||||
// door markers
|
||||
|
||||
// interior map only consists of one cell, so handle the markers only once
|
||||
if (interior && (mx != 2 || my != 2))
|
||||
continue;
|
||||
|
||||
MWWorld::CellStore* cell;
|
||||
if (interior)
|
||||
cell = MWBase::Environment::get().getWorld ()->getInterior (mPrefix);
|
||||
else
|
||||
cell = MWBase::Environment::get().getWorld ()->getExterior (x+mx-1, y-(my-1));
|
||||
|
||||
std::vector<MWBase::World::DoorMarker> doors = MWBase::Environment::get().getWorld ()->getDoorMarkers (cell);
|
||||
|
||||
for (std::vector<MWBase::World::DoorMarker>::iterator it = doors.begin(); it != doors.end(); ++it)
|
||||
{
|
||||
MWBase::World::DoorMarker marker = *it;
|
||||
|
||||
// convert world coordinates to normalized cell coordinates
|
||||
MyGUI::IntCoord widgetCoord;
|
||||
float nX,nY;
|
||||
int cellDx, cellDy;
|
||||
if (!interior)
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
|
||||
nX = (marker.x - cellSize * (x+mx-1)) / cellSize;
|
||||
nY = 1 - (marker.y - cellSize * (y-(my-1))) / cellSize;
|
||||
|
||||
widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + mx * 512, nY * 512 - 4 + my * 512, 8, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ogre::Vector2 position (marker.x, marker.y);
|
||||
MWBase::Environment::get().getWorld ()->getInteriorMapPosition (position, nX, nY, cellDx, cellDy);
|
||||
|
||||
widgetCoord = MyGUI::IntCoord(nX * 512 - 4 + (1+cellDx-x) * 512, nY * 512 - 4 + (1+cellDy-y) * 512, 8, 8);
|
||||
}
|
||||
|
||||
static int counter = 0;
|
||||
++counter;
|
||||
MyGUI::Button* markerWidget = mLocalMap->createWidget<MyGUI::Button>("ButtonImage",
|
||||
widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast<std::string>(counter));
|
||||
markerWidget->setImageResource("DoorMarker");
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", marker.name);
|
||||
markerWidget->setUserString("IsMarker", "true");
|
||||
markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused);
|
||||
markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused);
|
||||
|
||||
MarkerPosition markerPos;
|
||||
markerPos.interior = interior;
|
||||
markerPos.cellX = interior ? cellDx : x + mx - 1;
|
||||
markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1);
|
||||
markerPos.nX = nX;
|
||||
markerPos.nY = nY;
|
||||
|
||||
markerWidget->setUserData(markerPos);
|
||||
}
|
||||
|
||||
static int counter = 0;
|
||||
++counter;
|
||||
MyGUI::Button* markerWidget = mLocalMap->createWidget<MyGUI::Button>("ButtonImage",
|
||||
widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast<std::string>(counter));
|
||||
markerWidget->setImageResource("DoorMarker");
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", marker.name);
|
||||
markerWidget->setUserString("IsMarker", "true");
|
||||
markerWidget->eventMouseSetFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerFocused);
|
||||
markerWidget->eventMouseLostFocus += MyGUI::newDelegate(this, &LocalMapBase::onMarkerUnfocused);
|
||||
|
||||
MarkerPosition markerPos;
|
||||
markerPos.interior = interior;
|
||||
markerPos.cellX = interior ? cellDx : x + mx - 1;
|
||||
markerPos.cellY = interior ? cellDy : y + ((my - 1)*-1);
|
||||
markerPos.nX = nX;
|
||||
markerPos.nY = nY;
|
||||
|
||||
markerWidget->setUserData(markerPos);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
mInterior = interior;
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
mChanged = false;
|
||||
mInterior = interior;
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
mChanged = false;
|
||||
|
||||
// fog of war
|
||||
applyFogOfWar();
|
||||
// fog of war
|
||||
applyFogOfWar();
|
||||
|
||||
// set the compass texture again, because MyGUI determines sorting of ImageBox widgets
|
||||
// based on the last setImageTexture call
|
||||
std::string tex = "textures\\compass.dds";
|
||||
mCompass->setImageTexture("");
|
||||
mCompass->setImageTexture(tex);
|
||||
}
|
||||
|
||||
|
||||
void LocalMapBase::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (x == mLastPositionX && y == mLastPositionY)
|
||||
return;
|
||||
|
||||
notifyPlayerUpdate ();
|
||||
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16));
|
||||
mLastPositionX = x;
|
||||
mLastPositionY = y;
|
||||
}
|
||||
|
||||
void LocalMapBase::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (x == mLastDirectionX && y == mLastDirectionY)
|
||||
return;
|
||||
|
||||
notifyPlayerUpdate ();
|
||||
|
||||
MyGUI::ISubWidget* main = mCompass->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
|
||||
mLastDirectionX = x;
|
||||
mLastDirectionY = y;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
MapWindow::MapWindow(const std::string& cacheDir)
|
||||
: MWGui::WindowPinnableBase("openmw_map_window.layout")
|
||||
, mGlobal(false)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
|
||||
mGlobalMapRender = new MWRender::GlobalMap(cacheDir);
|
||||
mGlobalMapRender->render();
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mGlobalMapImage, "GlobalMapImage");
|
||||
getWidget(mGlobalMapOverlay, "GlobalMapOverlay");
|
||||
getWidget(mPlayerArrowLocal, "CompassLocal");
|
||||
getWidget(mPlayerArrowGlobal, "CompassGlobal");
|
||||
|
||||
mGlobalMapImage->setImageTexture("GlobalMap.png");
|
||||
mGlobalMapOverlay->setImageTexture("GlobalMapOverlay");
|
||||
|
||||
mGlobalMap->setVisible (false);
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
mButton->setCaptionWithReplacing("#{sWorld}");
|
||||
|
||||
getWidget(mEventBoxGlobal, "EventBoxGlobal");
|
||||
mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
getWidget(mEventBoxLocal, "EventBoxLocal");
|
||||
mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this);
|
||||
}
|
||||
|
||||
MapWindow::~MapWindow()
|
||||
{
|
||||
delete mGlobalMapRender;
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
setTitle("#{sCell=" + cellName + "}");
|
||||
}
|
||||
|
||||
void MapWindow::addVisitedLocation(const std::string& name, int x, int y)
|
||||
{
|
||||
float worldX, worldY;
|
||||
mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY);
|
||||
|
||||
MyGUI::IntCoord widgetCoord(
|
||||
worldX * mGlobalMapRender->getWidth()+6,
|
||||
worldY * mGlobalMapRender->getHeight()+6,
|
||||
12, 12);
|
||||
|
||||
|
||||
static int _counter=0;
|
||||
MyGUI::Button* markerWidget = mGlobalMapImage->createWidget<MyGUI::Button>("ButtonImage",
|
||||
widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast<std::string>(_counter));
|
||||
markerWidget->setImageResource("DoorMarker");
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", name);
|
||||
++_counter;
|
||||
|
||||
markerWidget = mEventBoxGlobal->createWidget<MyGUI::Button>("",
|
||||
widgetCoord, MyGUI::Align::Default);
|
||||
markerWidget->setNeedMouseFocus (true);
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", name);
|
||||
}
|
||||
|
||||
void MapWindow::cellExplored(int x, int y)
|
||||
{
|
||||
mGlobalMapRender->exploreCell(x,y);
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
|
||||
if (!mGlobal)
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
else
|
||||
mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff );
|
||||
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
|
||||
"#{sWorld}");
|
||||
|
||||
if (mGlobal)
|
||||
globalMapUpdatePlayer ();
|
||||
}
|
||||
|
||||
void MapWindow::onPinToggled()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void MapWindow::open()
|
||||
{
|
||||
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
|
||||
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
|
||||
|
||||
for (unsigned int i=0; i<mGlobalMapImage->getChildCount (); ++i)
|
||||
{
|
||||
if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker")
|
||||
mGlobalMapImage->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
|
||||
// set the compass texture again, because MyGUI determines sorting of ImageBox widgets
|
||||
// based on the last setImageTexture call
|
||||
std::string tex = "textures\\compass.dds";
|
||||
mCompass->setImageTexture("");
|
||||
mCompass->setImageTexture(tex);
|
||||
}
|
||||
|
||||
globalMapUpdatePlayer();
|
||||
|
||||
mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds");
|
||||
}
|
||||
|
||||
void MapWindow::globalMapUpdatePlayer ()
|
||||
{
|
||||
Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition ();
|
||||
Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation ();
|
||||
Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y);
|
||||
|
||||
float worldX, worldY;
|
||||
mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY);
|
||||
worldX *= mGlobalMapRender->getWidth();
|
||||
worldY *= mGlobalMapRender->getHeight();
|
||||
|
||||
|
||||
// for interiors, we have no choice other than using the last position & direction.
|
||||
/// \todo save this last position in the savegame?
|
||||
if (MWBase::Environment::get().getWorld ()->isCellExterior ())
|
||||
void LocalMapBase::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16));
|
||||
if (x == mLastPositionX && y == mLastPositionY)
|
||||
return;
|
||||
|
||||
MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain();
|
||||
notifyPlayerUpdate ();
|
||||
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mCompass->setPosition(MyGUI::IntPoint(512+x*512-16, 512+y*512-16));
|
||||
mLastPositionX = x;
|
||||
mLastPositionY = y;
|
||||
}
|
||||
|
||||
void LocalMapBase::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (x == mLastDirectionX && y == mLastDirectionY)
|
||||
return;
|
||||
|
||||
notifyPlayerUpdate ();
|
||||
|
||||
MyGUI::ISubWidget* main = mCompass->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(dir.x, dir.y);
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
|
||||
// set the view offset so that player is in the center
|
||||
MyGUI::IntSize viewsize = mGlobalMap->getSize();
|
||||
MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY);
|
||||
mGlobalMap->setViewOffset(viewoffs);
|
||||
mLastDirectionX = x;
|
||||
mLastDirectionY = y;
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::notifyPlayerUpdate ()
|
||||
{
|
||||
globalMapUpdatePlayer ();
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------
|
||||
|
||||
void MapWindow::notifyMapChanged ()
|
||||
{
|
||||
// workaround to prevent the map from drawing on top of the button
|
||||
MyGUI::IntCoord oldCoord = mButton->getCoord ();
|
||||
MyGUI::Gui::getInstance().destroyWidget (mButton);
|
||||
mButton = mMainWidget->createWidget<MWGui::Widgets::AutoSizedButton>("MW_Button",
|
||||
oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right);
|
||||
mButton->setProperty ("ExpandDirection", "Left");
|
||||
MapWindow::MapWindow(const std::string& cacheDir)
|
||||
: MWGui::WindowPinnableBase("openmw_map_window.layout")
|
||||
, mGlobal(false)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
|
||||
mGlobalMapRender = new MWRender::GlobalMap(cacheDir);
|
||||
mGlobalMapRender->render();
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mGlobalMapImage, "GlobalMapImage");
|
||||
getWidget(mGlobalMapOverlay, "GlobalMapOverlay");
|
||||
getWidget(mPlayerArrowLocal, "CompassLocal");
|
||||
getWidget(mPlayerArrowGlobal, "CompassGlobal");
|
||||
|
||||
mGlobalMapImage->setImageTexture("GlobalMap.png");
|
||||
mGlobalMapOverlay->setImageTexture("GlobalMapOverlay");
|
||||
|
||||
mGlobalMap->setVisible (false);
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
mButton->setCaptionWithReplacing("#{sWorld}");
|
||||
|
||||
getWidget(mEventBoxGlobal, "EventBoxGlobal");
|
||||
mEventBoxGlobal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
mEventBoxGlobal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
getWidget(mEventBoxLocal, "EventBoxLocal");
|
||||
mEventBoxLocal->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, this);
|
||||
}
|
||||
|
||||
MapWindow::~MapWindow()
|
||||
{
|
||||
delete mGlobalMapRender;
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
setTitle("#{sCell=" + cellName + "}");
|
||||
}
|
||||
|
||||
void MapWindow::addVisitedLocation(const std::string& name, int x, int y)
|
||||
{
|
||||
float worldX, worldY;
|
||||
mGlobalMapRender->cellTopLeftCornerToImageSpace (x, y, worldX, worldY);
|
||||
|
||||
MyGUI::IntCoord widgetCoord(
|
||||
worldX * mGlobalMapRender->getWidth()+6,
|
||||
worldY * mGlobalMapRender->getHeight()+6,
|
||||
12, 12);
|
||||
|
||||
|
||||
static int _counter=0;
|
||||
MyGUI::Button* markerWidget = mGlobalMapImage->createWidget<MyGUI::Button>("ButtonImage",
|
||||
widgetCoord, MyGUI::Align::Default, "Marker" + boost::lexical_cast<std::string>(_counter));
|
||||
markerWidget->setImageResource("DoorMarker");
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", name);
|
||||
++_counter;
|
||||
|
||||
markerWidget = mEventBoxGlobal->createWidget<MyGUI::Button>("",
|
||||
widgetCoord, MyGUI::Align::Default);
|
||||
markerWidget->setNeedMouseFocus (true);
|
||||
markerWidget->setUserString("ToolTipType", "Layout");
|
||||
markerWidget->setUserString("ToolTipLayout", "TextToolTipOneLine");
|
||||
markerWidget->setUserString("Caption_TextOneLine", name);
|
||||
}
|
||||
|
||||
void MapWindow::cellExplored(int x, int y)
|
||||
{
|
||||
mGlobalMapRender->exploreCell(x,y);
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
|
||||
if (!mGlobal)
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
else
|
||||
mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff );
|
||||
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
|
||||
"#{sWorld}");
|
||||
|
||||
if (mGlobal)
|
||||
globalMapUpdatePlayer ();
|
||||
}
|
||||
|
||||
void MapWindow::onPinToggled()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->setMinimapVisibility(!mPinned);
|
||||
}
|
||||
|
||||
void MapWindow::open()
|
||||
{
|
||||
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
|
||||
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
|
||||
|
||||
for (unsigned int i=0; i<mGlobalMapImage->getChildCount (); ++i)
|
||||
{
|
||||
if (mGlobalMapImage->getChildAt (i)->getName().substr(0,6) == "Marker")
|
||||
mGlobalMapImage->getChildAt (i)->castType<MyGUI::Button>()->setImageResource("DoorMarker");
|
||||
}
|
||||
|
||||
globalMapUpdatePlayer();
|
||||
|
||||
mPlayerArrowGlobal->setImageTexture ("textures\\compass.dds");
|
||||
}
|
||||
|
||||
void MapWindow::globalMapUpdatePlayer ()
|
||||
{
|
||||
Ogre::Vector3 pos = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedPosition ();
|
||||
Ogre::Quaternion orient = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer().getRefData ().getBaseNode ()->_getDerivedOrientation ();
|
||||
Ogre::Vector2 dir (orient.yAxis ().x, orient.yAxis().y);
|
||||
|
||||
float worldX, worldY;
|
||||
mGlobalMapRender->worldPosToImageSpace (pos.x, pos.y, worldX, worldY);
|
||||
worldX *= mGlobalMapRender->getWidth();
|
||||
worldY *= mGlobalMapRender->getHeight();
|
||||
|
||||
|
||||
// for interiors, we have no choice other than using the last position & direction.
|
||||
/// \todo save this last position in the savegame?
|
||||
if (MWBase::Environment::get().getWorld ()->isCellExterior ())
|
||||
{
|
||||
mPlayerArrowGlobal->setPosition(MyGUI::IntPoint(worldX - 16, worldY - 16));
|
||||
|
||||
MyGUI::ISubWidget* main = mPlayerArrowGlobal->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(dir.x, dir.y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
|
||||
// set the view offset so that player is in the center
|
||||
MyGUI::IntSize viewsize = mGlobalMap->getSize();
|
||||
MyGUI::IntPoint viewoffs(0.5*viewsize.width - worldX, 0.5*viewsize.height - worldY);
|
||||
mGlobalMap->setViewOffset(viewoffs);
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::notifyPlayerUpdate ()
|
||||
{
|
||||
globalMapUpdatePlayer ();
|
||||
}
|
||||
|
||||
void MapWindow::notifyMapChanged ()
|
||||
{
|
||||
// workaround to prevent the map from drawing on top of the button
|
||||
MyGUI::IntCoord oldCoord = mButton->getCoord ();
|
||||
MyGUI::Gui::getInstance().destroyWidget (mButton);
|
||||
mButton = mMainWidget->createWidget<MWGui::Widgets::AutoSizedButton>("MW_Button",
|
||||
oldCoord, MyGUI::Align::Bottom | MyGUI::Align::Right);
|
||||
mButton->setProperty ("ExpandDirection", "Left");
|
||||
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
|
||||
"#{sWorld}");
|
||||
}
|
||||
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
mButton->setCaptionWithReplacing( mGlobal ? "#{sLocal}" :
|
||||
"#{sWorld}");
|
||||
}
|
||||
|
|
|
@ -9,10 +9,7 @@
|
|||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "list.hpp"
|
||||
#include "inventorywindow.hpp"
|
||||
#include "tradewindow.hpp"
|
||||
|
||||
|
|
|
@ -5,411 +5,414 @@
|
|||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
MessageBoxManager::MessageBoxManager ()
|
||||
namespace MWGui
|
||||
{
|
||||
// defines
|
||||
mMessageBoxSpeed = 0.1;
|
||||
mInterMessageBoxe = NULL;
|
||||
}
|
||||
|
||||
void MessageBoxManager::onFrame (float frameDuration)
|
||||
{
|
||||
std::vector<MessageBoxManagerTimer>::iterator it;
|
||||
for(it = mTimers.begin(); it != mTimers.end();)
|
||||
MessageBoxManager::MessageBoxManager ()
|
||||
{
|
||||
// if this messagebox is already deleted, remove the timer and move on
|
||||
if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end())
|
||||
{
|
||||
it = mTimers.erase(it);
|
||||
continue;
|
||||
}
|
||||
// defines
|
||||
mMessageBoxSpeed = 0.1;
|
||||
mInterMessageBoxe = NULL;
|
||||
}
|
||||
|
||||
it->current += frameDuration;
|
||||
if(it->current >= it->max)
|
||||
void MessageBoxManager::onFrame (float frameDuration)
|
||||
{
|
||||
std::vector<MessageBoxManagerTimer>::iterator it;
|
||||
for(it = mTimers.begin(); it != mTimers.end();)
|
||||
{
|
||||
it->messageBox->mMarkedToDelete = true;
|
||||
|
||||
if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one
|
||||
// if this messagebox is already deleted, remove the timer and move on
|
||||
if (std::find(mMessageBoxes.begin(), mMessageBoxes.end(), it->messageBox) == mMessageBoxes.end())
|
||||
{
|
||||
// collect all with mMarkedToDelete and delete them.
|
||||
// and place the other messageboxes on the right position
|
||||
int height = 0;
|
||||
std::vector<MessageBox*>::iterator it2 = mMessageBoxes.begin();
|
||||
while(it2 != mMessageBoxes.end())
|
||||
it = mTimers.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
it->current += frameDuration;
|
||||
if(it->current >= it->max)
|
||||
{
|
||||
it->messageBox->mMarkedToDelete = true;
|
||||
|
||||
if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one
|
||||
{
|
||||
if((*it2)->mMarkedToDelete)
|
||||
// collect all with mMarkedToDelete and delete them.
|
||||
// and place the other messageboxes on the right position
|
||||
int height = 0;
|
||||
std::vector<MessageBox*>::iterator it2 = mMessageBoxes.begin();
|
||||
while(it2 != mMessageBoxes.end())
|
||||
{
|
||||
delete (*it2);
|
||||
it2 = mMessageBoxes.erase(it2);
|
||||
}
|
||||
else {
|
||||
(*it2)->update(height);
|
||||
height += (*it2)->getHeight();
|
||||
it2++;
|
||||
if((*it2)->mMarkedToDelete)
|
||||
{
|
||||
delete (*it2);
|
||||
it2 = mMessageBoxes.erase(it2);
|
||||
}
|
||||
else {
|
||||
(*it2)->update(height);
|
||||
height += (*it2)->getHeight();
|
||||
it2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
it = mTimers.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) {
|
||||
delete mInterMessageBoxe;
|
||||
mInterMessageBoxe = NULL;
|
||||
MWBase::Environment::get().getInputManager()->changeInputMode(
|
||||
MWBase::Environment::get().getWindowManager()->isGuiMode());
|
||||
}
|
||||
}
|
||||
|
||||
void MessageBoxManager::createMessageBox (const std::string& message)
|
||||
{
|
||||
MessageBox *box = new MessageBox(*this, message);
|
||||
|
||||
removeMessageBox(message.length()*mMessageBoxSpeed, box);
|
||||
|
||||
mMessageBoxes.push_back(box);
|
||||
std::vector<MessageBox*>::iterator it;
|
||||
|
||||
if(mMessageBoxes.size() > 3) {
|
||||
delete *mMessageBoxes.begin();
|
||||
mMessageBoxes.erase(mMessageBoxes.begin());
|
||||
}
|
||||
|
||||
int height = 0;
|
||||
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||
{
|
||||
(*it)->update(height);
|
||||
height += (*it)->getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons)
|
||||
{
|
||||
if(mInterMessageBoxe != NULL) {
|
||||
throw std::runtime_error("There is a message box already");
|
||||
}
|
||||
|
||||
mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessageBoxManager::isInteractiveMessageBox ()
|
||||
{
|
||||
return mInterMessageBoxe != NULL;
|
||||
}
|
||||
|
||||
void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox)
|
||||
{
|
||||
MessageBoxManagerTimer timer;
|
||||
timer.current = 0;
|
||||
timer.max = time;
|
||||
timer.messageBox = msgbox;
|
||||
|
||||
mTimers.insert(mTimers.end(), timer);
|
||||
}
|
||||
|
||||
bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)
|
||||
{
|
||||
std::vector<MessageBox*>::iterator it;
|
||||
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||
{
|
||||
if((*it) == msgbox)
|
||||
{
|
||||
delete (*it);
|
||||
mMessageBoxes.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageBoxManager::setMessageBoxSpeed (int speed)
|
||||
{
|
||||
mMessageBoxSpeed = speed;
|
||||
}
|
||||
|
||||
void MessageBoxManager::enterPressed ()
|
||||
{
|
||||
if(mInterMessageBoxe != NULL)
|
||||
mInterMessageBoxe->enterPressed();
|
||||
}
|
||||
|
||||
int MessageBoxManager::readPressedButton ()
|
||||
{
|
||||
if(mInterMessageBoxe != NULL)
|
||||
{
|
||||
return mInterMessageBoxe->readPressedButton();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message)
|
||||
: Layout("openmw_messagebox.layout")
|
||||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mMessage(message)
|
||||
{
|
||||
// defines
|
||||
mFixedWidth = 300;
|
||||
mBottomPadding = 20;
|
||||
mNextBoxPadding = 20;
|
||||
mMarkedToDelete = false;
|
||||
|
||||
getWidget(mMessageWidget, "message");
|
||||
|
||||
mMessageWidget->setOverflowToTheLeft(true);
|
||||
mMessageWidget->setCaptionWithReplacing(mMessage);
|
||||
|
||||
MyGUI::IntSize size;
|
||||
size.width = mFixedWidth;
|
||||
size.height = 100; // dummy
|
||||
|
||||
MyGUI::IntCoord coord;
|
||||
coord.left = 10; // dummy
|
||||
coord.top = 10; // dummy
|
||||
|
||||
mMessageWidget->setSize(size);
|
||||
|
||||
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
|
||||
|
||||
size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box
|
||||
|
||||
mMainWidget->setSize(size);
|
||||
size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0")
|
||||
mMessageWidget->setSize(size);
|
||||
}
|
||||
|
||||
void MessageBox::update (int height)
|
||||
{
|
||||
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
MyGUI::IntCoord coord;
|
||||
coord.left = (gameWindowSize.width - mFixedWidth)/2;
|
||||
coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding);
|
||||
|
||||
MyGUI::IntSize size;
|
||||
size.width = mFixedWidth;
|
||||
size.height = mHeight;
|
||||
|
||||
mMainWidget->setCoord(coord);
|
||||
mMainWidget->setSize(size);
|
||||
mMainWidget->setVisible(true);
|
||||
}
|
||||
|
||||
int MessageBox::getHeight ()
|
||||
{
|
||||
return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox
|
||||
}
|
||||
|
||||
|
||||
|
||||
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
|
||||
: WindowModal("openmw_interactive_messagebox.layout")
|
||||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mButtonPressed(-1)
|
||||
{
|
||||
WindowModal::open();
|
||||
|
||||
int fixedWidth = 500;
|
||||
int textPadding = 10; // padding between text-widget and main-widget
|
||||
int textButtonPadding = 20; // padding between the text-widget und the button-widget
|
||||
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||
int buttonTopPadding = 5; // ^-- if vertical
|
||||
int buttonPadding = 5; // padding between button label and button itself
|
||||
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
|
||||
|
||||
mMarkedToDelete = false;
|
||||
|
||||
|
||||
getWidget(mMessageWidget, "message");
|
||||
getWidget(mButtonsWidget, "buttons");
|
||||
|
||||
mMessageWidget->setOverflowToTheLeft(true);
|
||||
mMessageWidget->setCaptionWithReplacing(message);
|
||||
|
||||
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
|
||||
|
||||
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
|
||||
int biggestButtonWidth = 0;
|
||||
int buttonWidth = 0;
|
||||
int buttonsWidth = 0;
|
||||
int buttonHeight = 0;
|
||||
MyGUI::IntCoord dummyCoord(0, 0, 0, 0);
|
||||
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for(it = buttons.begin(); it != buttons.end(); ++it)
|
||||
{
|
||||
MyGUI::Button* button = mButtonsWidget->createWidget<MyGUI::Button>(
|
||||
MyGUI::WidgetStyle::Child,
|
||||
std::string("MW_Button"),
|
||||
dummyCoord,
|
||||
MyGUI::Align::Default);
|
||||
button->setCaptionWithReplacing(*it);
|
||||
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
|
||||
|
||||
mButtons.push_back(button);
|
||||
|
||||
buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding;
|
||||
buttonsWidth += buttonWidth;
|
||||
buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding;
|
||||
|
||||
if(buttonWidth > biggestButtonWidth)
|
||||
{
|
||||
biggestButtonWidth = buttonWidth;
|
||||
}
|
||||
}
|
||||
buttonsWidth += buttonLeftPadding;
|
||||
|
||||
MyGUI::IntSize mainWidgetSize;
|
||||
if(buttonsWidth < fixedWidth)
|
||||
{
|
||||
// on one line
|
||||
if(textSize.width + 2*textPadding < buttonsWidth)
|
||||
{
|
||||
mainWidgetSize.width = buttonsWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||
}
|
||||
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
|
||||
|
||||
MyGUI::IntCoord absCoord;
|
||||
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||
|
||||
mMainWidget->setCoord(absCoord);
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
MyGUI::IntCoord messageWidgetCoord;
|
||||
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||
messageWidgetCoord.top = textPadding;
|
||||
mMessageWidget->setCoord(messageWidgetCoord);
|
||||
|
||||
mMessageWidget->setSize(textSize);
|
||||
|
||||
MyGUI::IntCoord buttonCord;
|
||||
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
|
||||
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
buttonCord.left = left;
|
||||
buttonCord.top = textSize.height + textButtonPadding;
|
||||
|
||||
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding;
|
||||
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding;
|
||||
|
||||
(*button)->setCoord(buttonCord);
|
||||
(*button)->setSize(buttonSize);
|
||||
|
||||
left += buttonSize.width + buttonLeftPadding;
|
||||
}
|
||||
it = mTimers.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
// among each other
|
||||
if(biggestButtonWidth > textSize.width) {
|
||||
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
|
||||
}
|
||||
else {
|
||||
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||
}
|
||||
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
|
||||
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
MyGUI::IntCoord absCoord;
|
||||
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||
|
||||
mMainWidget->setCoord(absCoord);
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
|
||||
MyGUI::IntCoord messageWidgetCoord;
|
||||
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||
messageWidgetCoord.top = textPadding;
|
||||
mMessageWidget->setCoord(messageWidgetCoord);
|
||||
|
||||
mMessageWidget->setSize(textSize);
|
||||
|
||||
MyGUI::IntCoord buttonCord;
|
||||
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||
|
||||
int top = textButtonPadding + buttonTopPadding + textSize.height;
|
||||
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2;
|
||||
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2;
|
||||
|
||||
buttonCord.top = top;
|
||||
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
|
||||
|
||||
(*button)->setCoord(buttonCord);
|
||||
(*button)->setSize(buttonSize);
|
||||
|
||||
top += buttonSize.height + 2*buttonTopPadding;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) {
|
||||
delete mInterMessageBoxe;
|
||||
mInterMessageBoxe = NULL;
|
||||
MWBase::Environment::get().getInputManager()->changeInputMode(
|
||||
MWBase::Environment::get().getWindowManager()->isGuiMode());
|
||||
}
|
||||
}
|
||||
|
||||
void MessageBoxManager::createMessageBox (const std::string& message)
|
||||
{
|
||||
MessageBox *box = new MessageBox(*this, message);
|
||||
|
||||
removeMessageBox(message.length()*mMessageBoxSpeed, box);
|
||||
|
||||
mMessageBoxes.push_back(box);
|
||||
std::vector<MessageBox*>::iterator it;
|
||||
|
||||
if(mMessageBoxes.size() > 3) {
|
||||
delete *mMessageBoxes.begin();
|
||||
mMessageBoxes.erase(mMessageBoxes.begin());
|
||||
}
|
||||
|
||||
int height = 0;
|
||||
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||
void InteractiveMessageBox::enterPressed()
|
||||
{
|
||||
(*it)->update(height);
|
||||
height += (*it)->getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons)
|
||||
{
|
||||
if(mInterMessageBoxe != NULL) {
|
||||
throw std::runtime_error("There is a message box already");
|
||||
}
|
||||
|
||||
mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessageBoxManager::isInteractiveMessageBox ()
|
||||
{
|
||||
return mInterMessageBoxe != NULL;
|
||||
}
|
||||
|
||||
void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox)
|
||||
{
|
||||
MessageBoxManagerTimer timer;
|
||||
timer.current = 0;
|
||||
timer.max = time;
|
||||
timer.messageBox = msgbox;
|
||||
|
||||
mTimers.insert(mTimers.end(), timer);
|
||||
}
|
||||
|
||||
bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)
|
||||
{
|
||||
std::vector<MessageBox*>::iterator it;
|
||||
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||
{
|
||||
if((*it) == msgbox)
|
||||
{
|
||||
delete (*it);
|
||||
mMessageBoxes.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageBoxManager::setMessageBoxSpeed (int speed)
|
||||
{
|
||||
mMessageBoxSpeed = speed;
|
||||
}
|
||||
|
||||
void MessageBoxManager::enterPressed ()
|
||||
{
|
||||
if(mInterMessageBoxe != NULL)
|
||||
mInterMessageBoxe->enterPressed();
|
||||
}
|
||||
|
||||
int MessageBoxManager::readPressedButton ()
|
||||
{
|
||||
if(mInterMessageBoxe != NULL)
|
||||
{
|
||||
return mInterMessageBoxe->readPressedButton();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message)
|
||||
: Layout("openmw_messagebox.layout")
|
||||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mMessage(message)
|
||||
{
|
||||
// defines
|
||||
mFixedWidth = 300;
|
||||
mBottomPadding = 20;
|
||||
mNextBoxPadding = 20;
|
||||
mMarkedToDelete = false;
|
||||
|
||||
getWidget(mMessageWidget, "message");
|
||||
|
||||
mMessageWidget->setOverflowToTheLeft(true);
|
||||
mMessageWidget->setCaptionWithReplacing(mMessage);
|
||||
|
||||
MyGUI::IntSize size;
|
||||
size.width = mFixedWidth;
|
||||
size.height = 100; // dummy
|
||||
|
||||
MyGUI::IntCoord coord;
|
||||
coord.left = 10; // dummy
|
||||
coord.top = 10; // dummy
|
||||
|
||||
mMessageWidget->setSize(size);
|
||||
|
||||
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
|
||||
|
||||
size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box
|
||||
|
||||
mMainWidget->setSize(size);
|
||||
size.width -= 15; // this is to center the text (see messagebox.layout, Widget type="Edit" position="-2 -3 0 0")
|
||||
mMessageWidget->setSize(size);
|
||||
}
|
||||
|
||||
void MessageBox::update (int height)
|
||||
{
|
||||
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
MyGUI::IntCoord coord;
|
||||
coord.left = (gameWindowSize.width - mFixedWidth)/2;
|
||||
coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding);
|
||||
|
||||
MyGUI::IntSize size;
|
||||
size.width = mFixedWidth;
|
||||
size.height = mHeight;
|
||||
|
||||
mMainWidget->setCoord(coord);
|
||||
mMainWidget->setSize(size);
|
||||
mMainWidget->setVisible(true);
|
||||
}
|
||||
|
||||
int MessageBox::getHeight ()
|
||||
{
|
||||
return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox
|
||||
}
|
||||
|
||||
|
||||
|
||||
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
|
||||
: WindowModal("openmw_interactive_messagebox.layout")
|
||||
, mMessageBoxManager(parMessageBoxManager)
|
||||
, mButtonPressed(-1)
|
||||
{
|
||||
WindowModal::open();
|
||||
|
||||
int fixedWidth = 500;
|
||||
int textPadding = 10; // padding between text-widget and main-widget
|
||||
int textButtonPadding = 20; // padding between the text-widget und the button-widget
|
||||
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||
int buttonTopPadding = 5; // ^-- if vertical
|
||||
int buttonPadding = 5; // padding between button label and button itself
|
||||
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
|
||||
|
||||
mMarkedToDelete = false;
|
||||
|
||||
|
||||
getWidget(mMessageWidget, "message");
|
||||
getWidget(mButtonsWidget, "buttons");
|
||||
|
||||
mMessageWidget->setOverflowToTheLeft(true);
|
||||
mMessageWidget->setCaptionWithReplacing(message);
|
||||
|
||||
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
|
||||
|
||||
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||
|
||||
int biggestButtonWidth = 0;
|
||||
int buttonWidth = 0;
|
||||
int buttonsWidth = 0;
|
||||
int buttonHeight = 0;
|
||||
MyGUI::IntCoord dummyCoord(0, 0, 0, 0);
|
||||
|
||||
std::vector<std::string>::const_iterator it;
|
||||
for(it = buttons.begin(); it != buttons.end(); ++it)
|
||||
{
|
||||
MyGUI::Button* button = mButtonsWidget->createWidget<MyGUI::Button>(
|
||||
MyGUI::WidgetStyle::Child,
|
||||
std::string("MW_Button"),
|
||||
dummyCoord,
|
||||
MyGUI::Align::Default);
|
||||
button->setCaptionWithReplacing(*it);
|
||||
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
|
||||
|
||||
mButtons.push_back(button);
|
||||
|
||||
buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding;
|
||||
buttonsWidth += buttonWidth;
|
||||
buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding;
|
||||
|
||||
if(buttonWidth > biggestButtonWidth)
|
||||
{
|
||||
biggestButtonWidth = buttonWidth;
|
||||
}
|
||||
}
|
||||
buttonsWidth += buttonLeftPadding;
|
||||
|
||||
MyGUI::IntSize mainWidgetSize;
|
||||
if(buttonsWidth < fixedWidth)
|
||||
{
|
||||
// on one line
|
||||
if(textSize.width + 2*textPadding < buttonsWidth)
|
||||
{
|
||||
mainWidgetSize.width = buttonsWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||
}
|
||||
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
|
||||
|
||||
MyGUI::IntCoord absCoord;
|
||||
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||
|
||||
mMainWidget->setCoord(absCoord);
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
MyGUI::IntCoord messageWidgetCoord;
|
||||
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||
messageWidgetCoord.top = textPadding;
|
||||
mMessageWidget->setCoord(messageWidgetCoord);
|
||||
|
||||
mMessageWidget->setSize(textSize);
|
||||
|
||||
MyGUI::IntCoord buttonCord;
|
||||
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
|
||||
|
||||
std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
buttonCord.left = left;
|
||||
buttonCord.top = textSize.height + textButtonPadding;
|
||||
|
||||
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding;
|
||||
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding;
|
||||
|
||||
(*button)->setCoord(buttonCord);
|
||||
(*button)->setSize(buttonSize);
|
||||
|
||||
left += buttonSize.width + buttonLeftPadding;
|
||||
if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok)
|
||||
{
|
||||
buttonActivated(*button);
|
||||
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
|
||||
{
|
||||
// among each other
|
||||
if(biggestButtonWidth > textSize.width) {
|
||||
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
|
||||
}
|
||||
else {
|
||||
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||
}
|
||||
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
|
||||
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
MyGUI::IntCoord absCoord;
|
||||
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||
|
||||
mMainWidget->setCoord(absCoord);
|
||||
mMainWidget->setSize(mainWidgetSize);
|
||||
|
||||
|
||||
MyGUI::IntCoord messageWidgetCoord;
|
||||
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||
messageWidgetCoord.top = textPadding;
|
||||
mMessageWidget->setCoord(messageWidgetCoord);
|
||||
|
||||
mMessageWidget->setSize(textSize);
|
||||
|
||||
MyGUI::IntCoord buttonCord;
|
||||
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||
|
||||
int top = textButtonPadding + buttonTopPadding + textSize.height;
|
||||
buttonActivated (pressed);
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed)
|
||||
{
|
||||
mMarkedToDelete = true;
|
||||
int index = 0;
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2;
|
||||
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2;
|
||||
|
||||
buttonCord.top = top;
|
||||
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
|
||||
|
||||
(*button)->setCoord(buttonCord);
|
||||
(*button)->setSize(buttonSize);
|
||||
|
||||
top += buttonSize.height + 2*buttonTopPadding;
|
||||
if(*button == pressed)
|
||||
{
|
||||
mButtonPressed = index;
|
||||
mMessageBoxManager.onButtonPressed(mButtonPressed);
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::enterPressed()
|
||||
{
|
||||
|
||||
std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
int InteractiveMessageBox::readPressedButton ()
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase((*button)->getCaption()) == ok)
|
||||
{
|
||||
buttonActivated(*button);
|
||||
MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f);
|
||||
break;
|
||||
}
|
||||
int pressed = mButtonPressed;
|
||||
mButtonPressed = -1;
|
||||
return pressed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
|
||||
{
|
||||
buttonActivated (pressed);
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::buttonActivated (MyGUI::Widget* pressed)
|
||||
{
|
||||
mMarkedToDelete = true;
|
||||
int index = 0;
|
||||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
if(*button == pressed)
|
||||
{
|
||||
mButtonPressed = index;
|
||||
mMessageBoxManager.onButtonPressed(mButtonPressed);
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
int InteractiveMessageBox::readPressedButton ()
|
||||
{
|
||||
int pressed = mButtonPressed;
|
||||
mButtonPressed = -1;
|
||||
return pressed;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellsuccess.hpp"
|
||||
#include "../mwgui/inventorywindow.hpp"
|
||||
#include "../mwgui/bookwindow.hpp"
|
||||
|
|
|
@ -1,450 +1,385 @@
|
|||
#include "race.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "widgets.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace Widgets;
|
||||
|
||||
namespace
|
||||
{
|
||||
int wrap(int index, int max)
|
||||
{
|
||||
if (index < 0)
|
||||
return max - 1;
|
||||
else if (index >= max)
|
||||
return 0;
|
||||
else
|
||||
return index;
|
||||
}
|
||||
|
||||
int countParts(const std::string &part, const std::string &race, bool male)
|
||||
{
|
||||
/// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs
|
||||
const MWWorld::Store<ESM::BodyPart> &store =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
|
||||
|
||||
std::string prefix =
|
||||
"b_n_" + race + ((male) ? "_m_" : "_f_") + part;
|
||||
|
||||
std::string suffix;
|
||||
suffix.reserve(prefix.size() + 3);
|
||||
|
||||
int count = -1;
|
||||
do {
|
||||
++count;
|
||||
suffix = "_" + (boost::format("%02d") % (count + 1)).str();
|
||||
int wrap(int index, int max)
|
||||
{
|
||||
if (index < 0)
|
||||
return max - 1;
|
||||
else if (index >= max)
|
||||
return 0;
|
||||
else
|
||||
return index;
|
||||
}
|
||||
while (store.search(prefix + suffix) != 0);
|
||||
|
||||
if (count == 0 && part == "hair") {
|
||||
count = -1;
|
||||
do {
|
||||
++count;
|
||||
suffix = (boost::format("%02d") % (count + 1)).str();
|
||||
}
|
||||
while (store.search(prefix + suffix) != 0);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
RaceDialog::RaceDialog()
|
||||
: WindowModal("openmw_chargen_race.layout")
|
||||
, mGenderIndex(0)
|
||||
, mFaceIndex(0)
|
||||
, mHairIndex(0)
|
||||
, mFaceCount(10)
|
||||
, mHairCount(14)
|
||||
, mCurrentAngle(0)
|
||||
namespace MWGui
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance"));
|
||||
getWidget(mPreviewImage, "PreviewImage");
|
||||
RaceDialog::RaceDialog()
|
||||
: WindowModal("openmw_chargen_race.layout")
|
||||
, mGenderIndex(0)
|
||||
, mFaceIndex(0)
|
||||
, mHairIndex(0)
|
||||
, mCurrentAngle(0)
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
getWidget(mHeadRotate, "HeadRotate");
|
||||
mHeadRotate->setScrollRange(50);
|
||||
mHeadRotate->setScrollPosition(25);
|
||||
mHeadRotate->setScrollViewPage(10);
|
||||
mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
|
||||
setText("AppearanceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu1", "Appearance"));
|
||||
getWidget(mPreviewImage, "PreviewImage");
|
||||
|
||||
// Set up next/previous buttons
|
||||
MyGUI::Button *prevButton, *nextButton;
|
||||
getWidget(mHeadRotate, "HeadRotate");
|
||||
mHeadRotate->setScrollRange(50);
|
||||
mHeadRotate->setScrollPosition(25);
|
||||
mHeadRotate->setScrollViewPage(10);
|
||||
mHeadRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
|
||||
|
||||
setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex"));
|
||||
getWidget(prevButton, "PrevGenderButton");
|
||||
getWidget(nextButton, "NextGenderButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender);
|
||||
// Set up next/previous buttons
|
||||
MyGUI::Button *prevButton, *nextButton;
|
||||
|
||||
setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face"));
|
||||
getWidget(prevButton, "PrevFaceButton");
|
||||
getWidget(nextButton, "NextFaceButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace);
|
||||
setText("GenderChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu2", "Change Sex"));
|
||||
getWidget(prevButton, "PrevGenderButton");
|
||||
getWidget(nextButton, "NextGenderButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender);
|
||||
|
||||
setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair"));
|
||||
getWidget(prevButton, "PrevHairButton");
|
||||
getWidget(nextButton, "NextHairButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);
|
||||
setText("FaceChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu3", "Change Face"));
|
||||
getWidget(prevButton, "PrevFaceButton");
|
||||
getWidget(nextButton, "NextFaceButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace);
|
||||
|
||||
setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race"));
|
||||
getWidget(mRaceList, "RaceList");
|
||||
mRaceList->setScrollVisible(true);
|
||||
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
setText("HairChoiceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu4", "Change Hair"));
|
||||
getWidget(prevButton, "PrevHairButton");
|
||||
getWidget(nextButton, "NextHairButton");
|
||||
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);
|
||||
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);
|
||||
|
||||
setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus"));
|
||||
getWidget(mSkillList, "SkillList");
|
||||
setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials"));
|
||||
getWidget(mSpellPowerList, "SpellPowerList");
|
||||
setText("RaceT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu5", "Race"));
|
||||
getWidget(mRaceList, "RaceList");
|
||||
mRaceList->setScrollVisible(true);
|
||||
mRaceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
mRaceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
mRaceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
|
||||
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
|
||||
setText("SkillsT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sBonusSkillTitle", "Skill Bonus"));
|
||||
getWidget(mSkillList, "SkillList");
|
||||
setText("SpellPowerT", MWBase::Environment::get().getWindowManager()->getGameSettingString("sRaceMenu7", "Specials"));
|
||||
getWidget(mSpellPowerList, "SpellPowerList");
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
|
||||
|
||||
updateRaces();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
void RaceDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
|
||||
|
||||
void RaceDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
updateRaces();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
updateRaces();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
|
||||
mPreview = new MWRender::RaceSelectionPreview();
|
||||
mPreview->setup();
|
||||
mPreview->update (0);
|
||||
|
||||
const ESM::NPC proto = mPreview->getPrototype();
|
||||
setRaceId(proto.mRace);
|
||||
recountParts();
|
||||
|
||||
std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2);
|
||||
mFaceIndex = boost::lexical_cast<int>(index) - 1;
|
||||
|
||||
index = proto.mHair.substr(proto.mHair.size() - 2, 2);
|
||||
mHairIndex = boost::lexical_cast<int>(index) - 1;
|
||||
|
||||
mPreviewImage->setImageTexture ("CharacterHeadPreview");
|
||||
}
|
||||
|
||||
|
||||
void RaceDialog::setRaceId(const std::string &raceId)
|
||||
{
|
||||
mCurrentRaceId = raceId;
|
||||
mRaceList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
size_t count = mRaceList->getItemCount();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
void RaceDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
if (boost::iequals(*mRaceList->getItemDataAt<std::string>(i), raceId))
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
|
||||
void RaceDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
|
||||
updateRaces();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
|
||||
mPreview = new MWRender::RaceSelectionPreview();
|
||||
mPreview->setup();
|
||||
mPreview->update (0);
|
||||
|
||||
const ESM::NPC proto = mPreview->getPrototype();
|
||||
setRaceId(proto.mRace);
|
||||
recountParts();
|
||||
|
||||
std::string index = proto.mHead.substr(proto.mHead.size() - 2, 2);
|
||||
mFaceIndex = boost::lexical_cast<int>(index) - 1;
|
||||
|
||||
index = proto.mHair.substr(proto.mHair.size() - 2, 2);
|
||||
mHairIndex = boost::lexical_cast<int>(index) - 1;
|
||||
|
||||
mPreviewImage->setImageTexture ("CharacterHeadPreview");
|
||||
}
|
||||
|
||||
|
||||
void RaceDialog::setRaceId(const std::string &raceId)
|
||||
{
|
||||
mCurrentRaceId = raceId;
|
||||
mRaceList->setIndexSelected(MyGUI::ITEM_NONE);
|
||||
size_t count = mRaceList->getItemCount();
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
mRaceList->setIndexSelected(i);
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
break;
|
||||
if (boost::iequals(*mRaceList->getItemDataAt<std::string>(i), raceId))
|
||||
{
|
||||
mRaceList->setIndexSelected(i);
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
void RaceDialog::close()
|
||||
{
|
||||
delete mPreview;
|
||||
mPreview = 0;
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void RaceDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void RaceDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position)
|
||||
{
|
||||
float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2;
|
||||
float diff = angle - mCurrentAngle;
|
||||
mPreview->update (diff);
|
||||
mCurrentAngle += diff;
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousGender(MyGUI::Widget*)
|
||||
{
|
||||
mGenderIndex = wrap(mGenderIndex - 1, 2);
|
||||
|
||||
recountParts();
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextGender(MyGUI::Widget*)
|
||||
{
|
||||
mGenderIndex = wrap(mGenderIndex + 1, 2);
|
||||
|
||||
recountParts();
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousFace(MyGUI::Widget*)
|
||||
{
|
||||
mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextFace(MyGUI::Widget*)
|
||||
{
|
||||
mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousHair(MyGUI::Widget*)
|
||||
{
|
||||
mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextHair(MyGUI::Widget*)
|
||||
{
|
||||
mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)
|
||||
{
|
||||
if (_index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
const std::string *raceId = mRaceList->getItemDataAt<std::string>(_index);
|
||||
if (boost::iequals(mCurrentRaceId, *raceId))
|
||||
return;
|
||||
|
||||
mCurrentRaceId = *raceId;
|
||||
|
||||
recountParts();
|
||||
|
||||
updatePreview();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
void RaceDialog::getBodyParts (int part, std::vector<std::string>& out)
|
||||
{
|
||||
out.clear();
|
||||
const MWWorld::Store<ESM::BodyPart> &store =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
|
||||
|
||||
for (MWWorld::Store<ESM::BodyPart>::iterator it = store.begin(); it != store.end(); ++it)
|
||||
{
|
||||
const ESM::BodyPart& bodypart = *it;
|
||||
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
||||
continue;
|
||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||
continue;
|
||||
if (bodypart.mData.mPart != static_cast<ESM::BodyPart::MeshPart>(part))
|
||||
continue;
|
||||
if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
|
||||
continue;
|
||||
bool firstPerson = (bodypart.mId.size() >= 3)
|
||||
&& bodypart.mId[bodypart.mId.size()-3] == '1'
|
||||
&& bodypart.mId[bodypart.mId.size()-2] == 's'
|
||||
&& bodypart.mId[bodypart.mId.size()-1] == 't';
|
||||
if (firstPerson)
|
||||
continue;
|
||||
if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId))
|
||||
out.push_back(bodypart.mId);
|
||||
}
|
||||
}
|
||||
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
void RaceDialog::close()
|
||||
{
|
||||
delete mPreview;
|
||||
mPreview = 0;
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void RaceDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if(mRaceList->getIndexSelected() == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void RaceDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position)
|
||||
{
|
||||
float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2;
|
||||
float diff = angle - mCurrentAngle;
|
||||
mPreview->update (diff);
|
||||
mCurrentAngle += diff;
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousGender(MyGUI::Widget*)
|
||||
{
|
||||
mGenderIndex = wrap(mGenderIndex - 1, 2);
|
||||
|
||||
recountParts();
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextGender(MyGUI::Widget*)
|
||||
{
|
||||
mGenderIndex = wrap(mGenderIndex + 1, 2);
|
||||
|
||||
recountParts();
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousFace(MyGUI::Widget*)
|
||||
{
|
||||
do
|
||||
mFaceIndex = wrap(mFaceIndex - 1, mFaceCount);
|
||||
while (!isFacePlayable());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextFace(MyGUI::Widget*)
|
||||
{
|
||||
do
|
||||
mFaceIndex = wrap(mFaceIndex + 1, mFaceCount);
|
||||
while (!isFacePlayable());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectPreviousHair(MyGUI::Widget*)
|
||||
{
|
||||
do
|
||||
mHairIndex = wrap(mHairIndex - 1, mHairCount);
|
||||
while (!isHairPlayable());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectNextHair(MyGUI::Widget*)
|
||||
{
|
||||
do
|
||||
mHairIndex = wrap(mHairIndex + 1, mHairCount);
|
||||
while (!isHairPlayable());
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
bool RaceDialog::isFacePlayable()
|
||||
{
|
||||
std::string prefix =
|
||||
"b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_");
|
||||
|
||||
std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str();
|
||||
|
||||
const MWWorld::Store<ESM::BodyPart> &parts =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
|
||||
|
||||
if (parts.search(prefix + "head_" + headIndex) == 0)
|
||||
return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
|
||||
else
|
||||
return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
|
||||
}
|
||||
|
||||
bool RaceDialog::isHairPlayable()
|
||||
{
|
||||
std::string prefix =
|
||||
"b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_");
|
||||
|
||||
std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str();
|
||||
|
||||
const MWWorld::Store<ESM::BodyPart> &parts =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
|
||||
if (parts.search(prefix + "hair_" + hairIndex) == 0)
|
||||
return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
|
||||
else
|
||||
return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable);
|
||||
}
|
||||
|
||||
void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)
|
||||
{
|
||||
if (_index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
const std::string *raceId = mRaceList->getItemDataAt<std::string>(_index);
|
||||
if (boost::iequals(mCurrentRaceId, *raceId))
|
||||
return;
|
||||
|
||||
mCurrentRaceId = *raceId;
|
||||
|
||||
recountParts();
|
||||
|
||||
updatePreview();
|
||||
updateSkills();
|
||||
updateSpellPowers();
|
||||
}
|
||||
|
||||
void RaceDialog::recountParts()
|
||||
{
|
||||
mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0);
|
||||
mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0);
|
||||
|
||||
mFaceIndex = 0;
|
||||
mHairIndex = 0;
|
||||
|
||||
while (!isHairPlayable())
|
||||
mHairIndex = wrap(mHairIndex + 1, mHairCount);
|
||||
while (!isFacePlayable())
|
||||
mFaceIndex = wrap(mFaceIndex + 1, mFaceCount);
|
||||
}
|
||||
|
||||
// update widget content
|
||||
|
||||
void RaceDialog::updatePreview()
|
||||
{
|
||||
ESM::NPC record = mPreview->getPrototype();
|
||||
record.mRace = mCurrentRaceId;
|
||||
record.setIsMale(mGenderIndex == 0);
|
||||
|
||||
std::string prefix =
|
||||
"b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_");
|
||||
|
||||
std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str();
|
||||
std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str();
|
||||
|
||||
record.mHead = prefix + "head_" + headIndex;
|
||||
record.mHair = prefix + "hair_" + hairIndex;
|
||||
|
||||
const MWWorld::Store<ESM::BodyPart> &parts =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BodyPart>();
|
||||
|
||||
if (parts.search(record.mHair) == 0) {
|
||||
record.mHair = prefix + "hair" + hairIndex;
|
||||
}
|
||||
mPreview->setPrototype(record);
|
||||
}
|
||||
|
||||
void RaceDialog::updateRaces()
|
||||
{
|
||||
mRaceList->removeAllItems();
|
||||
|
||||
const MWWorld::Store<ESM::Race> &races =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>();
|
||||
|
||||
|
||||
int index = 0;
|
||||
MWWorld::Store<ESM::Race>::iterator it = races.begin();
|
||||
for (; it != races.end(); ++it)
|
||||
void RaceDialog::recountParts()
|
||||
{
|
||||
bool playable = it->mData.mFlags & ESM::Race::Playable;
|
||||
if (!playable) // Only display playable races
|
||||
continue;
|
||||
getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs);
|
||||
getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads);
|
||||
|
||||
mRaceList->addItem(it->mName, it->mId);
|
||||
if (boost::iequals(it->mId, mCurrentRaceId))
|
||||
mRaceList->setIndexSelected(index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void RaceDialog::updateSkills()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSkillItems.clear();
|
||||
|
||||
if (mCurrentRaceId.empty())
|
||||
return;
|
||||
|
||||
MWSkillPtr skillWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);
|
||||
int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE?
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int skillId = race->mData.mBonus[i].mSkill;
|
||||
if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes
|
||||
continue;
|
||||
|
||||
skillWidget = mSkillList->createWidget<MWSkill>("MW_StatNameValue", coord1, MyGUI::Align::Default,
|
||||
std::string("Skill") + boost::lexical_cast<std::string>(i));
|
||||
skillWidget->setSkillNumber(skillId);
|
||||
skillWidget->setSkillValue(MWSkill::SkillValue(race->mData.mBonus[i].mBonus));
|
||||
ToolTips::createSkillToolTip(skillWidget, skillId);
|
||||
|
||||
|
||||
mSkillItems.push_back(skillWidget);
|
||||
|
||||
coord1.top += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void RaceDialog::updateSpellPowers()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSpellPowerItems.clear();
|
||||
|
||||
if (mCurrentRaceId.empty())
|
||||
return;
|
||||
|
||||
MWSpellPtr spellPowerWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);
|
||||
|
||||
std::vector<std::string>::const_iterator it = race->mPowers.mList.begin();
|
||||
std::vector<std::string>::const_iterator end = race->mPowers.mList.end();
|
||||
for (int i = 0; it != end; ++it)
|
||||
{
|
||||
const std::string &spellpower = *it;
|
||||
spellPowerWidget = mSpellPowerList->createWidget<MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast<std::string>(i));
|
||||
spellPowerWidget->setSpellId(spellpower);
|
||||
spellPowerWidget->setUserString("ToolTipType", "Spell");
|
||||
spellPowerWidget->setUserString("Spell", spellpower);
|
||||
|
||||
mSpellPowerItems.push_back(spellPowerWidget);
|
||||
|
||||
coord.top += lineHeight;
|
||||
++i;
|
||||
mFaceIndex = 0;
|
||||
mHairIndex = 0;
|
||||
}
|
||||
|
||||
// update widget content
|
||||
|
||||
void RaceDialog::updatePreview()
|
||||
{
|
||||
ESM::NPC record = mPreview->getPrototype();
|
||||
record.mRace = mCurrentRaceId;
|
||||
record.setIsMale(mGenderIndex == 0);
|
||||
|
||||
record.mHead = mAvailableHeads[mFaceIndex];
|
||||
record.mHair = mAvailableHairs[mHairIndex];
|
||||
|
||||
mPreview->setPrototype(record);
|
||||
}
|
||||
|
||||
void RaceDialog::updateRaces()
|
||||
{
|
||||
mRaceList->removeAllItems();
|
||||
|
||||
const MWWorld::Store<ESM::Race> &races =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>();
|
||||
|
||||
|
||||
int index = 0;
|
||||
MWWorld::Store<ESM::Race>::iterator it = races.begin();
|
||||
for (; it != races.end(); ++it)
|
||||
{
|
||||
bool playable = it->mData.mFlags & ESM::Race::Playable;
|
||||
if (!playable) // Only display playable races
|
||||
continue;
|
||||
|
||||
mRaceList->addItem(it->mName, it->mId);
|
||||
if (boost::iequals(it->mId, mCurrentRaceId))
|
||||
mRaceList->setIndexSelected(index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void RaceDialog::updateSkills()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSkillItems.begin(); it != mSkillItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSkillItems.clear();
|
||||
|
||||
if (mCurrentRaceId.empty())
|
||||
return;
|
||||
|
||||
Widgets::MWSkillPtr skillWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord1(0, 0, mSkillList->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);
|
||||
int count = sizeof(race->mData.mBonus)/sizeof(race->mData.mBonus[0]); // TODO: Find a portable macro for this ARRAYSIZE?
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int skillId = race->mData.mBonus[i].mSkill;
|
||||
if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes
|
||||
continue;
|
||||
|
||||
skillWidget = mSkillList->createWidget<Widgets::MWSkill>("MW_StatNameValue", coord1, MyGUI::Align::Default,
|
||||
std::string("Skill") + boost::lexical_cast<std::string>(i));
|
||||
skillWidget->setSkillNumber(skillId);
|
||||
skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(race->mData.mBonus[i].mBonus));
|
||||
ToolTips::createSkillToolTip(skillWidget, skillId);
|
||||
|
||||
|
||||
mSkillItems.push_back(skillWidget);
|
||||
|
||||
coord1.top += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void RaceDialog::updateSpellPowers()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSpellPowerItems.begin(); it != mSpellPowerItems.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSpellPowerItems.clear();
|
||||
|
||||
if (mCurrentRaceId.empty())
|
||||
return;
|
||||
|
||||
Widgets::MWSpellPtr spellPowerWidget;
|
||||
const int lineHeight = 18;
|
||||
MyGUI::IntCoord coord(0, 0, mSpellPowerList->getWidth(), 18);
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mCurrentRaceId);
|
||||
|
||||
std::vector<std::string>::const_iterator it = race->mPowers.mList.begin();
|
||||
std::vector<std::string>::const_iterator end = race->mPowers.mList.end();
|
||||
for (int i = 0; it != end; ++it)
|
||||
{
|
||||
const std::string &spellpower = *it;
|
||||
spellPowerWidget = mSpellPowerList->createWidget<Widgets::MWSpell>("MW_StatName", coord, MyGUI::Align::Default, std::string("SpellPower") + boost::lexical_cast<std::string>(i));
|
||||
spellPowerWidget->setSpellId(spellpower);
|
||||
spellPowerWidget->setUserString("ToolTipType", "Spell");
|
||||
spellPowerWidget->setUserString("Spell", spellpower);
|
||||
|
||||
mSpellPowerItems.push_back(spellPowerWidget);
|
||||
|
||||
coord.top += lineHeight;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,8 +76,10 @@ namespace MWGui
|
|||
void updatePreview();
|
||||
void recountParts();
|
||||
|
||||
bool isHairPlayable();
|
||||
bool isFacePlayable();
|
||||
void getBodyParts (int part, std::vector<std::string>& out);
|
||||
|
||||
std::vector<std::string> mAvailableHeads;
|
||||
std::vector<std::string> mAvailableHairs;
|
||||
|
||||
MyGUI::ImageBox* mPreviewImage;
|
||||
MyGUI::ListBox* mRaceList;
|
||||
|
@ -90,7 +92,6 @@ namespace MWGui
|
|||
std::vector<MyGUI::Widget*> mSpellPowerItems;
|
||||
|
||||
int mGenderIndex, mFaceIndex, mHairIndex;
|
||||
int mFaceCount, mHairCount;
|
||||
|
||||
std::string mCurrentRaceId;
|
||||
|
||||
|
|
|
@ -18,17 +18,17 @@ namespace MWGui
|
|||
|
||||
void ReferenceInterface::checkReferenceAvailable()
|
||||
{
|
||||
if (mPtr.isEmpty())
|
||||
return;
|
||||
|
||||
MWWorld::Ptr::CellStore* playerCell = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell();
|
||||
|
||||
// check if player has changed cell, or count of the reference has become 0
|
||||
if ((playerCell != mCurrentPlayerCell && mCurrentPlayerCell != NULL)
|
||||
|| mPtr.getRefData().getCount() == 0)
|
||||
|| (!mPtr.isEmpty() && mPtr.getRefData().getCount() == 0))
|
||||
{
|
||||
mPtr = MWWorld::Ptr();
|
||||
onReferenceUnavailable();
|
||||
if (!mPtr.isEmpty())
|
||||
{
|
||||
mPtr = MWWorld::Ptr();
|
||||
onReferenceUnavailable();
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentPlayerCell = playerCell;
|
||||
|
|
|
@ -1,373 +1,369 @@
|
|||
#include "review.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "widgets.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
using namespace MWGui;
|
||||
using namespace Widgets;
|
||||
|
||||
const int ReviewDialog::sLineHeight = 18;
|
||||
|
||||
ReviewDialog::ReviewDialog()
|
||||
: WindowModal("openmw_chargen_review.layout")
|
||||
, mLastPos(0)
|
||||
namespace MWGui
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
// Setup static stats
|
||||
MyGUI::Button* button;
|
||||
getWidget(mNameWidget, "NameText");
|
||||
getWidget(button, "NameButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);;
|
||||
const int ReviewDialog::sLineHeight = 18;
|
||||
|
||||
getWidget(mRaceWidget, "RaceText");
|
||||
getWidget(button, "RaceButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);;
|
||||
|
||||
getWidget(mClassWidget, "ClassText");
|
||||
getWidget(button, "ClassButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);;
|
||||
|
||||
getWidget(mBirthSignWidget, "SignText");
|
||||
getWidget(button, "SignButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);;
|
||||
|
||||
// Setup dynamic stats
|
||||
getWidget(mHealth, "Health");
|
||||
mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", ""));
|
||||
mHealth->setValue(45, 45);
|
||||
|
||||
getWidget(mMagicka, "Magicka");
|
||||
mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", ""));
|
||||
mMagicka->setValue(50, 50);
|
||||
|
||||
getWidget(mFatigue, "Fatigue");
|
||||
mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", ""));
|
||||
mFatigue->setValue(160, 160);
|
||||
|
||||
// Setup attributes
|
||||
|
||||
MWAttributePtr attribute;
|
||||
for (int idx = 0; idx < ESM::Attribute::Length; ++idx)
|
||||
ReviewDialog::ReviewDialog()
|
||||
: WindowModal("openmw_chargen_review.layout")
|
||||
, mLastPos(0)
|
||||
{
|
||||
getWidget(attribute, std::string("Attribute") + boost::lexical_cast<std::string>(idx));
|
||||
mAttributeWidgets.insert(std::make_pair(static_cast<int>(ESM::Attribute::sAttributeIds[idx]), attribute));
|
||||
attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]);
|
||||
attribute->setAttributeValue(MWAttribute::AttributeValue(0, 0));
|
||||
}
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
// Setup skills
|
||||
getWidget(mSkillView, "SkillView");
|
||||
mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
// Setup static stats
|
||||
MyGUI::Button* button;
|
||||
getWidget(mNameWidget, "NameText");
|
||||
getWidget(button, "NameButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);;
|
||||
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
{
|
||||
mSkillValues.insert(std::make_pair(i, MWMechanics::Stat<float>()));
|
||||
mSkillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*> (0)));
|
||||
}
|
||||
getWidget(mRaceWidget, "RaceText");
|
||||
getWidget(button, "RaceButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);;
|
||||
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);
|
||||
getWidget(mClassWidget, "ClassText");
|
||||
getWidget(button, "ClassButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);;
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);
|
||||
}
|
||||
getWidget(mBirthSignWidget, "SignText");
|
||||
getWidget(button, "SignButton");
|
||||
adjustButtonSize(button);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);;
|
||||
|
||||
void ReviewDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
updateSkillArea();
|
||||
}
|
||||
// Setup dynamic stats
|
||||
getWidget(mHealth, "Health");
|
||||
mHealth->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sHealth", ""));
|
||||
mHealth->setValue(45, 45);
|
||||
|
||||
void ReviewDialog::setPlayerName(const std::string &name)
|
||||
{
|
||||
mNameWidget->setCaption(name);
|
||||
}
|
||||
getWidget(mMagicka, "Magicka");
|
||||
mMagicka->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sMagic", ""));
|
||||
mMagicka->setValue(50, 50);
|
||||
|
||||
void ReviewDialog::setRace(const std::string &raceId)
|
||||
{
|
||||
mRaceId = raceId;
|
||||
getWidget(mFatigue, "Fatigue");
|
||||
mFatigue->setTitle(MWBase::Environment::get().getWindowManager()->getGameSettingString("sFatigue", ""));
|
||||
mFatigue->setValue(160, 160);
|
||||
|
||||
const ESM::Race *race =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(mRaceId);
|
||||
if (race)
|
||||
{
|
||||
ToolTips::createRaceToolTip(mRaceWidget, race);
|
||||
mRaceWidget->setCaption(race->mName);
|
||||
}
|
||||
}
|
||||
// Setup attributes
|
||||
|
||||
void ReviewDialog::setClass(const ESM::Class& class_)
|
||||
{
|
||||
mKlass = class_;
|
||||
mClassWidget->setCaption(mKlass.mName);
|
||||
ToolTips::createClassToolTip(mClassWidget, mKlass);
|
||||
}
|
||||
|
||||
void ReviewDialog::setBirthSign(const std::string& signId)
|
||||
{
|
||||
mBirthSignId = signId;
|
||||
|
||||
const ESM::BirthSign *sign =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().search(mBirthSignId);
|
||||
if (sign)
|
||||
{
|
||||
mBirthSignWidget->setCaption(sign->mName);
|
||||
ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId);
|
||||
}
|
||||
}
|
||||
|
||||
void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mHealth->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setMagicka(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mMagicka->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mFatigue->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat<int>& value)
|
||||
{
|
||||
std::map<int, MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId));
|
||||
if (attr == mAttributeWidgets.end())
|
||||
return;
|
||||
|
||||
attr->second->setAttributeValue(value);
|
||||
}
|
||||
|
||||
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat<float>& value)
|
||||
{
|
||||
mSkillValues[skillId] = value;
|
||||
MyGUI::TextBox* widget = mSkillWidgetMap[skillId];
|
||||
if (widget)
|
||||
{
|
||||
float modified = value.getModified(), base = value.getBase();
|
||||
std::string text = boost::lexical_cast<std::string>(std::floor(modified));
|
||||
std::string state = "normal";
|
||||
if (modified > base)
|
||||
state = "increased";
|
||||
else if (modified < base)
|
||||
state = "decreased";
|
||||
|
||||
widget->setCaption(text);
|
||||
widget->_setWidgetState(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
{
|
||||
mMajorSkills = major;
|
||||
mMinorSkills = minor;
|
||||
|
||||
// Update misc skills with the remaining skills not in major or minor
|
||||
std::set<int> skillSet;
|
||||
std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
boost::array<ESM::Skill::SkillEnum, ESM::Skill::Length>::const_iterator end = ESM::Skill::sSkillIds.end();
|
||||
mMiscSkills.clear();
|
||||
for (boost::array<ESM::Skill::SkillEnum, ESM::Skill::Length>::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it)
|
||||
{
|
||||
int skill = *it;
|
||||
if (skillSet.find(skill) == skillSet.end())
|
||||
mMiscSkills.push_back(skill);
|
||||
}
|
||||
|
||||
updateSkillArea();
|
||||
}
|
||||
|
||||
void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::ImageBox* separator = mSkillView->createWidget<MyGUI::ImageBox>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
|
||||
separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(separator);
|
||||
|
||||
coord1.top += separator->getHeight();
|
||||
coord2.top += separator->getHeight();
|
||||
}
|
||||
|
||||
void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* groupWidget = mSkillView->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
|
||||
groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
groupWidget->setCaption(label);
|
||||
mSkillWidgets.push_back(groupWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
}
|
||||
|
||||
MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* skillNameWidget;
|
||||
MyGUI::TextBox* skillValueWidget;
|
||||
|
||||
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1, MyGUI::Align::Default);
|
||||
skillNameWidget->setCaption(text);
|
||||
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right);
|
||||
skillValueWidget->setCaption(value);
|
||||
skillValueWidget->_setWidgetState(state);
|
||||
skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(skillNameWidget);
|
||||
mSkillWidgets.push_back(skillValueWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
|
||||
return skillValueWidget;
|
||||
}
|
||||
|
||||
void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* skillNameWidget;
|
||||
|
||||
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
|
||||
skillNameWidget->setCaption(text);
|
||||
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(skillNameWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
}
|
||||
|
||||
void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
// Add a line separator if there are items above
|
||||
if (!mSkillWidgets.empty())
|
||||
{
|
||||
addSeparator(coord1, coord2);
|
||||
}
|
||||
|
||||
addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);
|
||||
|
||||
SkillList::const_iterator end = skills.end();
|
||||
for (SkillList::const_iterator it = skills.begin(); it != end; ++it)
|
||||
{
|
||||
int skillId = *it;
|
||||
if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes
|
||||
continue;
|
||||
assert(skillId >= 0 && skillId < ESM::Skill::Length);
|
||||
const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId];
|
||||
const MWMechanics::Stat<float> &stat = mSkillValues.find(skillId)->second;
|
||||
float base = stat.getBase();
|
||||
float modified = stat.getModified();
|
||||
|
||||
std::string state = "normal";
|
||||
if (modified > base)
|
||||
state = "increased";
|
||||
else if (modified < base)
|
||||
state = "decreased";
|
||||
MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), state, coord1, coord2);
|
||||
|
||||
for (int i=0; i<2; ++i)
|
||||
Widgets::MWAttributePtr attribute;
|
||||
for (int idx = 0; idx < ESM::Attribute::Length; ++idx)
|
||||
{
|
||||
ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId);
|
||||
getWidget(attribute, std::string("Attribute") + boost::lexical_cast<std::string>(idx));
|
||||
mAttributeWidgets.insert(std::make_pair(static_cast<int>(ESM::Attribute::sAttributeIds[idx]), attribute));
|
||||
attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]);
|
||||
attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0));
|
||||
}
|
||||
|
||||
mSkillWidgetMap[skillId] = widget;
|
||||
}
|
||||
}
|
||||
// Setup skills
|
||||
getWidget(mSkillView, "SkillView");
|
||||
mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
void ReviewDialog::updateSkillArea()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it)
|
||||
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||
{
|
||||
mSkillValues.insert(std::make_pair(i, MWMechanics::Stat<float>()));
|
||||
mSkillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*> (0)));
|
||||
}
|
||||
|
||||
MyGUI::Button* backButton;
|
||||
getWidget(backButton, "BackButton");
|
||||
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);
|
||||
}
|
||||
|
||||
void ReviewDialog::open()
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
WindowModal::open();
|
||||
updateSkillArea();
|
||||
}
|
||||
mSkillWidgets.clear();
|
||||
|
||||
const int valueSize = 40;
|
||||
MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18);
|
||||
MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height);
|
||||
void ReviewDialog::setPlayerName(const std::string &name)
|
||||
{
|
||||
mNameWidget->setCaption(name);
|
||||
}
|
||||
|
||||
if (!mMajorSkills.empty())
|
||||
addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2);
|
||||
void ReviewDialog::setRace(const std::string &raceId)
|
||||
{
|
||||
mRaceId = raceId;
|
||||
|
||||
if (!mMinorSkills.empty())
|
||||
addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2);
|
||||
const ESM::Race *race =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().search(mRaceId);
|
||||
if (race)
|
||||
{
|
||||
ToolTips::createRaceToolTip(mRaceWidget, race);
|
||||
mRaceWidget->setCaption(race->mName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mMiscSkills.empty())
|
||||
addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2);
|
||||
void ReviewDialog::setClass(const ESM::Class& class_)
|
||||
{
|
||||
mKlass = class_;
|
||||
mClassWidget->setCaption(mKlass.mName);
|
||||
ToolTips::createClassToolTip(mClassWidget, mKlass);
|
||||
}
|
||||
|
||||
mClientHeight = coord1.top;
|
||||
void ReviewDialog::setBirthSign(const std::string& signId)
|
||||
{
|
||||
mBirthSignId = signId;
|
||||
|
||||
const ESM::BirthSign *sign =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::BirthSign>().search(mBirthSignId);
|
||||
if (sign)
|
||||
{
|
||||
mBirthSignWidget->setCaption(sign->mName);
|
||||
ToolTips::createBirthsignToolTip(mBirthSignWidget, mBirthSignId);
|
||||
}
|
||||
}
|
||||
|
||||
void ReviewDialog::setHealth(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mHealth->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setMagicka(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mMagicka->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setFatigue(const MWMechanics::DynamicStat<float>& value)
|
||||
{
|
||||
mFatigue->setValue(value.getCurrent(), value.getModified());
|
||||
std::string valStr = boost::lexical_cast<std::string>(value.getCurrent()) + "/" + boost::lexical_cast<std::string>(value.getModified());
|
||||
mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
|
||||
}
|
||||
|
||||
void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat<int>& value)
|
||||
{
|
||||
std::map<int, Widgets::MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId));
|
||||
if (attr == mAttributeWidgets.end())
|
||||
return;
|
||||
|
||||
attr->second->setAttributeValue(value);
|
||||
}
|
||||
|
||||
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat<float>& value)
|
||||
{
|
||||
mSkillValues[skillId] = value;
|
||||
MyGUI::TextBox* widget = mSkillWidgetMap[skillId];
|
||||
if (widget)
|
||||
{
|
||||
float modified = value.getModified(), base = value.getBase();
|
||||
std::string text = boost::lexical_cast<std::string>(std::floor(modified));
|
||||
std::string state = "normal";
|
||||
if (modified > base)
|
||||
state = "increased";
|
||||
else if (modified < base)
|
||||
state = "decreased";
|
||||
|
||||
widget->setCaption(text);
|
||||
widget->_setWidgetState(state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||
{
|
||||
mMajorSkills = major;
|
||||
mMinorSkills = minor;
|
||||
|
||||
// Update misc skills with the remaining skills not in major or minor
|
||||
std::set<int> skillSet;
|
||||
std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin()));
|
||||
boost::array<ESM::Skill::SkillEnum, ESM::Skill::Length>::const_iterator end = ESM::Skill::sSkillIds.end();
|
||||
mMiscSkills.clear();
|
||||
for (boost::array<ESM::Skill::SkillEnum, ESM::Skill::Length>::const_iterator it = ESM::Skill::sSkillIds.begin(); it != end; ++it)
|
||||
{
|
||||
int skill = *it;
|
||||
if (skillSet.find(skill) == skillSet.end())
|
||||
mMiscSkills.push_back(skill);
|
||||
}
|
||||
|
||||
updateSkillArea();
|
||||
}
|
||||
|
||||
void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::ImageBox* separator = mSkillView->createWidget<MyGUI::ImageBox>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
|
||||
separator->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(separator);
|
||||
|
||||
coord1.top += separator->getHeight();
|
||||
coord2.top += separator->getHeight();
|
||||
}
|
||||
|
||||
void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* groupWidget = mSkillView->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
|
||||
groupWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
groupWidget->setCaption(label);
|
||||
mSkillWidgets.push_back(groupWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
}
|
||||
|
||||
MyGUI::TextBox* ReviewDialog::addValueItem(const std::string& text, const std::string &value, const std::string& state, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* skillNameWidget;
|
||||
MyGUI::TextBox* skillValueWidget;
|
||||
|
||||
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1, MyGUI::Align::Default);
|
||||
skillNameWidget->setCaption(text);
|
||||
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
skillValueWidget = mSkillView->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Top | MyGUI::Align::Right);
|
||||
skillValueWidget->setCaption(value);
|
||||
skillValueWidget->_setWidgetState(state);
|
||||
skillValueWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(skillNameWidget);
|
||||
mSkillWidgets.push_back(skillValueWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
|
||||
return skillValueWidget;
|
||||
}
|
||||
|
||||
void ReviewDialog::addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
MyGUI::TextBox* skillNameWidget;
|
||||
|
||||
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
|
||||
skillNameWidget->setCaption(text);
|
||||
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel);
|
||||
|
||||
mSkillWidgets.push_back(skillNameWidget);
|
||||
|
||||
coord1.top += sLineHeight;
|
||||
coord2.top += sLineHeight;
|
||||
}
|
||||
|
||||
void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
|
||||
{
|
||||
// Add a line separator if there are items above
|
||||
if (!mSkillWidgets.empty())
|
||||
{
|
||||
addSeparator(coord1, coord2);
|
||||
}
|
||||
|
||||
addGroup(MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2);
|
||||
|
||||
SkillList::const_iterator end = skills.end();
|
||||
for (SkillList::const_iterator it = skills.begin(); it != end; ++it)
|
||||
{
|
||||
int skillId = *it;
|
||||
if (skillId < 0 || skillId > ESM::Skill::Length) // Skip unknown skill indexes
|
||||
continue;
|
||||
assert(skillId >= 0 && skillId < ESM::Skill::Length);
|
||||
const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId];
|
||||
const MWMechanics::Stat<float> &stat = mSkillValues.find(skillId)->second;
|
||||
float base = stat.getBase();
|
||||
float modified = stat.getModified();
|
||||
|
||||
std::string state = "normal";
|
||||
if (modified > base)
|
||||
state = "increased";
|
||||
else if (modified < base)
|
||||
state = "decreased";
|
||||
MyGUI::TextBox* widget = addValueItem(MWBase::Environment::get().getWindowManager()->getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), state, coord1, coord2);
|
||||
|
||||
for (int i=0; i<2; ++i)
|
||||
{
|
||||
ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size()-1-i], skillId);
|
||||
}
|
||||
|
||||
mSkillWidgetMap[skillId] = widget;
|
||||
}
|
||||
}
|
||||
|
||||
void ReviewDialog::updateSkillArea()
|
||||
{
|
||||
for (std::vector<MyGUI::Widget*>::iterator it = mSkillWidgets.begin(); it != mSkillWidgets.end(); ++it)
|
||||
{
|
||||
MyGUI::Gui::getInstance().destroyWidget(*it);
|
||||
}
|
||||
mSkillWidgets.clear();
|
||||
|
||||
const int valueSize = 40;
|
||||
MyGUI::IntCoord coord1(10, 0, mSkillView->getWidth() - (10 + valueSize) - 24, 18);
|
||||
MyGUI::IntCoord coord2(coord1.left + coord1.width, coord1.top, valueSize, coord1.height);
|
||||
|
||||
if (!mMajorSkills.empty())
|
||||
addSkills(mMajorSkills, "sSkillClassMajor", "Major Skills", coord1, coord2);
|
||||
|
||||
if (!mMinorSkills.empty())
|
||||
addSkills(mMinorSkills, "sSkillClassMinor", "Minor Skills", coord1, coord2);
|
||||
|
||||
if (!mMiscSkills.empty())
|
||||
addSkills(mMiscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2);
|
||||
|
||||
mClientHeight = coord1.top;
|
||||
|
||||
mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight));
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void ReviewDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void ReviewDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void ReviewDialog::onNameClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(NAME_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(RACE_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onClassClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(CLASS_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(BIRTHSIGN_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mSkillView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mSkillView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
||||
mSkillView->setCanvasSize (mSkillView->getWidth(), std::max(mSkillView->getHeight(), mClientHeight));
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void ReviewDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void ReviewDialog::onBackClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventBack();
|
||||
}
|
||||
|
||||
void ReviewDialog::onNameClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(NAME_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onRaceClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(RACE_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onClassClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(CLASS_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onBirthSignClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventActivateDialog(BIRTHSIGN_DIALOG);
|
||||
}
|
||||
|
||||
void ReviewDialog::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mSkillView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mSkillView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mSkillView->setViewOffset(MyGUI::IntPoint(0, mSkillView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
|
|
|
@ -10,71 +10,73 @@
|
|||
|
||||
#include "formatting.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
ScrollWindow::ScrollWindow ()
|
||||
: WindowBase("openmw_scroll.layout")
|
||||
, mTakeButtonShow(true)
|
||||
, mTakeButtonAllowed(true)
|
||||
namespace MWGui
|
||||
{
|
||||
getWidget(mTextView, "TextView");
|
||||
|
||||
getWidget(mCloseButton, "CloseButton");
|
||||
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked);
|
||||
ScrollWindow::ScrollWindow ()
|
||||
: WindowBase("openmw_scroll.layout")
|
||||
, mTakeButtonShow(true)
|
||||
, mTakeButtonAllowed(true)
|
||||
{
|
||||
getWidget(mTextView, "TextView");
|
||||
|
||||
getWidget(mTakeButton, "TakeButton");
|
||||
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked);
|
||||
getWidget(mCloseButton, "CloseButton");
|
||||
mCloseButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onCloseButtonClicked);
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void ScrollWindow::open (MWWorld::Ptr scroll)
|
||||
{
|
||||
// no 3d sounds because the object could be in a container.
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0);
|
||||
|
||||
mScroll = scroll;
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>();
|
||||
|
||||
BookTextParser parser;
|
||||
MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390);
|
||||
|
||||
if (size.height > mTextView->getSize().height)
|
||||
mTextView->setCanvasSize(MyGUI::IntSize(410, size.height));
|
||||
else
|
||||
mTextView->setCanvasSize(410, mTextView->getSize().height);
|
||||
|
||||
mTextView->setViewOffset(MyGUI::IntPoint(0,0));
|
||||
|
||||
setTakeButtonShow(true);
|
||||
}
|
||||
|
||||
void ScrollWindow::setTakeButtonShow(bool show)
|
||||
{
|
||||
mTakeButtonShow = show;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void ScrollWindow::setInventoryAllowed(bool allowed)
|
||||
{
|
||||
mTakeButtonAllowed = allowed;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
|
||||
}
|
||||
|
||||
void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
|
||||
MWWorld::ActionTake take(mScroll);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
|
||||
getWidget(mTakeButton, "TakeButton");
|
||||
mTakeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ScrollWindow::onTakeButtonClicked);
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void ScrollWindow::open (MWWorld::Ptr scroll)
|
||||
{
|
||||
// no 3d sounds because the object could be in a container.
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0);
|
||||
|
||||
mScroll = scroll;
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Book> *ref = mScroll.get<ESM::Book>();
|
||||
|
||||
BookTextParser parser;
|
||||
MyGUI::IntSize size = parser.parse(ref->mBase->mText, mTextView, 390);
|
||||
|
||||
if (size.height > mTextView->getSize().height)
|
||||
mTextView->setCanvasSize(MyGUI::IntSize(410, size.height));
|
||||
else
|
||||
mTextView->setCanvasSize(410, mTextView->getSize().height);
|
||||
|
||||
mTextView->setViewOffset(MyGUI::IntPoint(0,0));
|
||||
|
||||
setTakeButtonShow(true);
|
||||
}
|
||||
|
||||
void ScrollWindow::setTakeButtonShow(bool show)
|
||||
{
|
||||
mTakeButtonShow = show;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void ScrollWindow::setInventoryAllowed(bool allowed)
|
||||
{
|
||||
mTakeButtonAllowed = allowed;
|
||||
mTakeButton->setVisible(mTakeButtonShow && mTakeButtonAllowed);
|
||||
}
|
||||
|
||||
void ScrollWindow::onCloseButtonClicked (MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("scroll", 1.0, 1.0);
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
|
||||
}
|
||||
|
||||
void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
|
||||
MWWorld::ActionTake take(mScroll);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Scroll);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
#include "settingswindow.hpp"
|
||||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRenderSystem.h>
|
||||
#include <OgrePlugin.h>
|
||||
#include <OgreString.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/math/common_factor_rt.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwrender/renderingmanager.hpp"
|
||||
|
||||
#include "confirmationdialog.hpp"
|
||||
|
||||
namespace
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "soulgemdialog.hpp"
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "messagebox.hpp"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "spellbuyingwindow.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -11,9 +9,7 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "inventorywindow.hpp"
|
||||
|
|
|
@ -4,22 +4,14 @@
|
|||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellsuccess.hpp"
|
||||
|
||||
|
||||
#include "tooltips.hpp"
|
||||
#include "widgets.hpp"
|
||||
#include "class.hpp"
|
||||
#include "inventorywindow.hpp"
|
||||
#include "tradewindow.hpp"
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
#include "spellicons.hpp"
|
||||
|
||||
#include <MyGUI_Widget.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -14,7 +10,6 @@
|
|||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/activespells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "tooltips.hpp"
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
#include "spellwindow.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/actionequip.hpp"
|
||||
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellsuccess.hpp"
|
||||
|
||||
#include "spellicons.hpp"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,68 +3,71 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
TextInputDialog::TextInputDialog()
|
||||
: WindowModal("openmw_text_input.layout")
|
||||
namespace MWGui
|
||||
{
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
getWidget(mTextEdit, "TextEdit");
|
||||
mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted);
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);
|
||||
|
||||
// Make sure the edit box has focus
|
||||
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
|
||||
}
|
||||
|
||||
void TextInputDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
|
||||
void TextInputDialog::setTextLabel(const std::string &label)
|
||||
{
|
||||
setText("LabelT", label);
|
||||
}
|
||||
|
||||
void TextInputDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
// Make sure the edit box has focus
|
||||
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void TextInputDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if (mTextEdit->getCaption() == "")
|
||||
TextInputDialog::TextInputDialog()
|
||||
: WindowModal("openmw_text_input.layout")
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}");
|
||||
MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit);
|
||||
}
|
||||
else
|
||||
eventDone(this);
|
||||
}
|
||||
// Centre dialog
|
||||
center();
|
||||
|
||||
void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender)
|
||||
{
|
||||
if (mTextEdit->getCaption() == "")
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}");
|
||||
MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit);
|
||||
getWidget(mTextEdit, "TextEdit");
|
||||
mTextEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted);
|
||||
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);
|
||||
|
||||
// Make sure the edit box has focus
|
||||
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
|
||||
}
|
||||
else
|
||||
eventDone(this);
|
||||
|
||||
void TextInputDialog::setNextButtonShow(bool shown)
|
||||
{
|
||||
MyGUI::Button* okButton;
|
||||
getWidget(okButton, "OKButton");
|
||||
|
||||
if (shown)
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNext", ""));
|
||||
else
|
||||
okButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sOK", ""));
|
||||
}
|
||||
|
||||
void TextInputDialog::setTextLabel(const std::string &label)
|
||||
{
|
||||
setText("LabelT", label);
|
||||
}
|
||||
|
||||
void TextInputDialog::open()
|
||||
{
|
||||
WindowModal::open();
|
||||
// Make sure the edit box has focus
|
||||
MyGUI::InputManager::getInstance().setKeyFocusWidget(mTextEdit);
|
||||
}
|
||||
|
||||
// widget controls
|
||||
|
||||
void TextInputDialog::onOkClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
if (mTextEdit->getCaption() == "")
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}");
|
||||
MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit);
|
||||
}
|
||||
else
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender)
|
||||
{
|
||||
if (mTextEdit->getCaption() == "")
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}");
|
||||
MyGUI::InputManager::getInstance ().setKeyFocusWidget (mTextEdit);
|
||||
}
|
||||
else
|
||||
eventDone(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,7 +9,6 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
#include "travelwindow.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
|
||||
#include "../mwmechanics/spells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "inventorywindow.hpp"
|
||||
#include "tradewindow.hpp"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "waitdialog.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <libs/openengine/ogre/fader.hpp>
|
||||
|
@ -11,9 +9,7 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/timestamp.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,5 @@
|
|||
#include "windowbase.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,28 +1,27 @@
|
|||
#include "windowpinnablebase.hpp"
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "exposedwindow.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
WindowPinnableBase::WindowPinnableBase(const std::string& parLayout)
|
||||
: WindowBase(parLayout), mPinned(false), mVisible(false)
|
||||
namespace MWGui
|
||||
{
|
||||
ExposedWindow* window = static_cast<ExposedWindow*>(mMainWidget);
|
||||
mPinButton = window->getSkinWidget ("Button");
|
||||
WindowPinnableBase::WindowPinnableBase(const std::string& parLayout)
|
||||
: WindowBase(parLayout), mPinned(false), mVisible(false)
|
||||
{
|
||||
ExposedWindow* window = static_cast<ExposedWindow*>(mMainWidget);
|
||||
mPinButton = window->getSkinWidget ("Button");
|
||||
|
||||
mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked);
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mPinned = !mPinned;
|
||||
|
||||
if (mPinned)
|
||||
mPinButton->changeWidgetSkin ("PinDown");
|
||||
else
|
||||
mPinButton->changeWidgetSkin ("PinUp");
|
||||
|
||||
onPinToggled();
|
||||
mPinButton->eventMouseButtonClick += MyGUI::newDelegate(this, &WindowPinnableBase::onPinButtonClicked);
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onPinButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mPinned = !mPinned;
|
||||
|
||||
if (mPinned)
|
||||
mPinButton->changeWidgetSkin ("PinDown");
|
||||
else
|
||||
mPinButton->changeWidgetSkin ("PinUp");
|
||||
|
||||
onPinToggled();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,6 +290,13 @@ namespace MWMechanics
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
|
||||
{
|
||||
PtrControllerMap::iterator iter = mActors.find(ptr);
|
||||
if(iter != mActors.end())
|
||||
iter->second.forceStateUpdate();
|
||||
}
|
||||
|
||||
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
||||
{
|
||||
PtrControllerMap::iterator iter = mActors.find(ptr);
|
||||
|
|
|
@ -78,6 +78,8 @@ namespace MWMechanics
|
|||
int countDeaths (const std::string& id) const;
|
||||
///< Return the number of deaths for actors with the given ID.
|
||||
|
||||
void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||
|
||||
void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
||||
void skipAnimation(const MWWorld::Ptr& ptr);
|
||||
};
|
||||
|
|
|
@ -103,14 +103,13 @@ static void getStateInfo(CharacterState state, std::string *group)
|
|||
|
||||
|
||||
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop)
|
||||
: mPtr(ptr), mAnimation(anim), mState(state), mSkipAnim(false)
|
||||
: mPtr(ptr), mAnimation(anim), mCharState(state), mSkipAnim(false), mMovingAnim(false)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
|
||||
mAnimation->setController(this);
|
||||
|
||||
getStateInfo(mState, &mCurrentGroup);
|
||||
std::string group;
|
||||
getStateInfo(mCharState, &group);
|
||||
if(MWWorld::Class::get(mPtr).isActor())
|
||||
{
|
||||
/* Accumulate along X/Y only for now, until we can figure out how we should
|
||||
|
@ -122,19 +121,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
/* Don't accumulate with non-actors. */
|
||||
mAnimation->setAccumulation(Ogre::Vector3(0.0f));
|
||||
}
|
||||
if(mAnimation->hasAnimation(mCurrentGroup))
|
||||
mAnimation->play(mCurrentGroup, "stop", "stop", loop);
|
||||
}
|
||||
|
||||
CharacterController::CharacterController(const CharacterController &rhs)
|
||||
: mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimQueue(rhs.mAnimQueue)
|
||||
, mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState)
|
||||
, mSkipAnim(rhs.mSkipAnim)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
/* We've been copied. Update the animation with the new controller. */
|
||||
mAnimation->setController(this);
|
||||
if(mAnimation->hasAnimation(group))
|
||||
mMovingAnim = mAnimation->play(group, "start", "stop", 1.0f, loop ? (~(size_t)0) : 0);
|
||||
}
|
||||
|
||||
CharacterController::~CharacterController()
|
||||
|
@ -148,31 +136,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
|
|||
}
|
||||
|
||||
|
||||
void CharacterController::markerEvent(float time, const std::string &evt)
|
||||
{
|
||||
if(evt == "stop")
|
||||
{
|
||||
if(mAnimQueue.size() >= 2 && mAnimQueue[0] == mAnimQueue[1])
|
||||
{
|
||||
mAnimQueue.pop_front();
|
||||
mAnimation->play(mCurrentGroup, "loop start", "stop", false);
|
||||
}
|
||||
else if(mAnimQueue.size() > 0)
|
||||
{
|
||||
mAnimQueue.pop_front();
|
||||
if(mAnimQueue.size() > 0)
|
||||
{
|
||||
mCurrentGroup = mAnimQueue.front();
|
||||
mAnimation->play(mCurrentGroup, "start", "stop", false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr<< "Unhandled animation event: "<<evt <<std::endl;
|
||||
}
|
||||
|
||||
|
||||
void CharacterController::update(float duration, Movement &movement)
|
||||
{
|
||||
float speed = 0.0f;
|
||||
|
@ -214,11 +177,13 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
if(vec.x > 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
|
||||
: (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight)), true);
|
||||
|
||||
else if(vec.x < 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft)
|
||||
: (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft)), true);
|
||||
|
||||
// If this animation isn't moving us sideways, do it manually
|
||||
if(!mMovingAnim)
|
||||
movement.mPosition[0] += vec.x * (speed*duration);
|
||||
// Apply any forward/backward movement manually
|
||||
movement.mPosition[1] += vec.y * (speed*duration);
|
||||
}
|
||||
|
@ -227,12 +192,15 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
if(vec.y > 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
|
||||
: (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward)), true);
|
||||
|
||||
else if(vec.y < 0.0f)
|
||||
setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
|
||||
: (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack)), true);
|
||||
|
||||
// Apply any sideways movement manually
|
||||
movement.mPosition[0] += vec.x * (speed*duration);
|
||||
// If this animation isn't moving us forward/backward, do it manually
|
||||
if(!mMovingAnim)
|
||||
movement.mPosition[1] += vec.y * (speed*duration);
|
||||
}
|
||||
else if(rot.z != 0.0f && !inwater && !sneak)
|
||||
{
|
||||
|
@ -241,8 +209,18 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
else if(rot.z < 0.0f)
|
||||
setState(CharState_TurnLeft, true);
|
||||
}
|
||||
else if(mAnimQueue.size() == 0)
|
||||
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true);
|
||||
else if(getState() != CharState_SpecialIdle || !mAnimation->isPlaying(0))
|
||||
{
|
||||
if(mAnimQueue.size() == 0)
|
||||
setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)), true);
|
||||
else
|
||||
{
|
||||
mMovingAnim = mAnimation->play(mAnimQueue.front().first,
|
||||
"start", "stop", 0.0f,
|
||||
mAnimQueue.front().second);
|
||||
mAnimQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
movement.mRotation[0] += rot.x * duration;
|
||||
movement.mRotation[1] += rot.y * duration;
|
||||
|
@ -268,20 +246,17 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int
|
|||
else
|
||||
{
|
||||
count = std::max(count, 1);
|
||||
if(mode != 0 || mAnimQueue.size() == 0)
|
||||
if(mode != 0 || getState() != CharState_SpecialIdle)
|
||||
{
|
||||
mAnimQueue.clear();
|
||||
while(count-- > 0)
|
||||
mAnimQueue.push_back(groupname);
|
||||
mCurrentGroup = groupname;
|
||||
mState = CharState_SpecialIdle;
|
||||
mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start"), "stop", false);
|
||||
mCharState = CharState_SpecialIdle;
|
||||
mLooping = false;
|
||||
mMovingAnim = mAnimation->play(groupname, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1);
|
||||
}
|
||||
else if(mode == 0)
|
||||
{
|
||||
mAnimQueue.resize(1);
|
||||
while(count-- > 0)
|
||||
mAnimQueue.push_back(groupname);
|
||||
mAnimQueue.clear();
|
||||
mAnimQueue.push_back(std::make_pair(groupname, count-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,25 +269,24 @@ void CharacterController::skipAnim()
|
|||
|
||||
void CharacterController::setState(CharacterState state, bool loop)
|
||||
{
|
||||
if(mState == state)
|
||||
{
|
||||
if(mAnimation)
|
||||
mAnimation->setLooping(loop);
|
||||
if(mCharState == state)
|
||||
return;
|
||||
}
|
||||
mState = state;
|
||||
mCharState = state;
|
||||
mLooping = loop;
|
||||
|
||||
forceStateUpdate();
|
||||
}
|
||||
|
||||
void CharacterController::forceStateUpdate()
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
mAnimQueue.clear();
|
||||
|
||||
std::string anim;
|
||||
getStateInfo(mState, &anim);
|
||||
if(mAnimation->hasAnimation(anim))
|
||||
{
|
||||
mCurrentGroup = anim;
|
||||
mAnimation->play(mCurrentGroup, "start", "stop", loop);
|
||||
}
|
||||
getStateInfo(mCharState, &anim);
|
||||
if((mMovingAnim=mAnimation->hasAnimation(anim)) != false)
|
||||
mMovingAnim = mAnimation->play(anim, "start", "stop", 0.0f, mLooping ? (~(size_t)0) : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,22 +72,17 @@ class CharacterController
|
|||
MWWorld::Ptr mPtr;
|
||||
MWRender::Animation *mAnimation;
|
||||
|
||||
typedef std::deque<std::string> AnimationQueue;
|
||||
typedef std::deque<std::pair<std::string,size_t> > AnimationQueue;
|
||||
AnimationQueue mAnimQueue;
|
||||
|
||||
std::string mCurrentGroup;
|
||||
CharacterState mState;
|
||||
CharacterState mCharState;
|
||||
bool mLooping;
|
||||
bool mSkipAnim;
|
||||
|
||||
protected:
|
||||
/* Called by the animation whenever a new text key is reached. */
|
||||
void markerEvent(float time, const std::string &evt);
|
||||
|
||||
friend class MWRender::Animation;
|
||||
bool mMovingAnim;
|
||||
|
||||
public:
|
||||
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state, bool loop);
|
||||
CharacterController(const CharacterController &rhs);
|
||||
virtual ~CharacterController();
|
||||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
@ -99,7 +94,9 @@ public:
|
|||
|
||||
void setState(CharacterState state, bool loop);
|
||||
CharacterState getState() const
|
||||
{ return mState; }
|
||||
{ return mCharState; }
|
||||
|
||||
void forceStateUpdate();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -548,7 +548,7 @@ namespace MWMechanics
|
|||
|
||||
float bribeMod;
|
||||
if (type == PT_Bribe10) bribeMod = gmst.find("fBribe10Mod")->getFloat();
|
||||
if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat();
|
||||
else if (type == PT_Bribe100) bribeMod = gmst.find("fBribe100Mod")->getFloat();
|
||||
else bribeMod = gmst.find("fBribe1000Mod")->getFloat();
|
||||
|
||||
float target3 = d * (playerRating3 - npcRating3 + 50) + bribeMod;
|
||||
|
@ -654,6 +654,12 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
void MechanicsManager::forceStateUpdate(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if(MWWorld::Class::get(ptr).isActor())
|
||||
mActors.forceStateUpdate(ptr);
|
||||
}
|
||||
|
||||
void MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
||||
{
|
||||
if(MWWorld::Class::get(ptr).isActor())
|
||||
|
|
|
@ -96,6 +96,8 @@ namespace MWMechanics
|
|||
void toLower(std::string npcFaction);
|
||||
///< Perform a persuasion action on NPC
|
||||
|
||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||
|
||||
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
||||
virtual void skipAnimation(const MWWorld::Ptr& ptr);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "pathfinding.hpp"
|
||||
#include <boost/graph/astar_search.hpp>
|
||||
#include <boost/graph/dijkstra_shortest_paths.hpp>
|
||||
#include <boost/graph/adjacency_list.hpp>
|
||||
#include "boost/tuple/tuple.hpp"
|
||||
#include "OgreMath.h"
|
||||
|
@ -55,7 +55,7 @@ namespace
|
|||
|
||||
struct found_path {};
|
||||
|
||||
class goalVisited : public boost::default_astar_visitor
|
||||
/*class goalVisited : public boost::default_astar_visitor
|
||||
{
|
||||
public:
|
||||
goalVisited(PointID goal) : mGoal(goal) {}
|
||||
|
@ -69,7 +69,7 @@ namespace
|
|||
PointID mGoal;
|
||||
};
|
||||
|
||||
class DistanceHeuristic : public boost::astar_heuristic <PathGridGraph, float>
|
||||
class DistanceHeuristic : public boost::atasr_heuristic <PathGridGraph, float>
|
||||
{
|
||||
public:
|
||||
DistanceHeuristic(const PathGridGraph & l, PointID goal)
|
||||
|
@ -87,11 +87,23 @@ namespace
|
|||
private:
|
||||
const PathGridGraph & mGraph;
|
||||
PointID mGoal;
|
||||
};
|
||||
}
|
||||
};*/
|
||||
|
||||
class goalVisited : public boost::default_dijkstra_visitor
|
||||
{
|
||||
public:
|
||||
goalVisited(PointID goal) : mGoal(goal) {}
|
||||
|
||||
void examine_vertex(PointID u, const PathGridGraph g)
|
||||
{
|
||||
if(u == mGoal)
|
||||
throw found_path();
|
||||
}
|
||||
private:
|
||||
PointID mGoal;
|
||||
};
|
||||
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
PathGridGraph buildGraph(const ESM::Pathgrid* pathgrid,float xCell = 0,float yCell = 0)
|
||||
{
|
||||
PathGridGraph graph;
|
||||
|
@ -126,13 +138,12 @@ namespace MWMechanics
|
|||
std::list<ESM::Pathgrid::Point> shortest_path;
|
||||
|
||||
try {
|
||||
boost::astar_search
|
||||
boost::dijkstra_shortest_paths
|
||||
(
|
||||
graph,
|
||||
start,
|
||||
DistanceHeuristic(graph,end),
|
||||
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(goalVisited(end))//.weight_map(boost::get(&Edge::distance, graph))
|
||||
);
|
||||
);
|
||||
|
||||
} catch(found_path fg) {
|
||||
for(PointID v = end;; v = p[v]) {
|
||||
|
@ -146,6 +157,10 @@ namespace MWMechanics
|
|||
|
||||
//end of helpers functions
|
||||
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
PathFinder::PathFinder()
|
||||
{
|
||||
mIsPathConstructed = false;
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "activatoranimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreSubEntity.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -24,28 +19,10 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
|
|||
assert (ref->mBase != NULL);
|
||||
if(!ref->mBase->mModel.empty())
|
||||
{
|
||||
std::string mesh = "meshes\\" + ref->mBase->mModel;
|
||||
const std::string name = "meshes\\"+ref->mBase->mModel;
|
||||
|
||||
createObjectList(mPtr.getRefData().getBaseNode(), mesh);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectList.mEntities[i];
|
||||
ent->setVisibilityFlags(RV_Misc);
|
||||
|
||||
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
part->setVisibilityFlags(RV_Misc);
|
||||
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
setAnimationSource(mesh);
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), name, false);
|
||||
setRenderProperties(mObjects.back().mObjectList, RV_Misc, RQG_Main, RQG_Alpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,9 @@ void Actors::insertActivator (const MWWorld::Ptr& ptr)
|
|||
|
||||
bool Actors::deleteObject (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (mAllActors.find(ptr) == mAllActors.end())
|
||||
return false;
|
||||
|
||||
mRendering->removeWaterRippleEmitter (ptr);
|
||||
|
||||
delete mAllActors[ptr];
|
||||
|
@ -139,6 +142,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
|
|||
Ogre::SceneNode *base = celliter->second;
|
||||
base->removeAndDestroyAllChildren();
|
||||
mRend.getScene()->destroySceneNode(base);
|
||||
|
||||
mCellSceneNodes.erase(celliter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <OgreSkeletonManager.h>
|
||||
#include <OgreSkeletonInstance.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreBone.h>
|
||||
#include <OgreSubMesh.h>
|
||||
#include <OgreSceneManager.h>
|
||||
|
@ -16,6 +18,32 @@
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
Animation::AnimLayer::AnimLayer()
|
||||
: mControllers(NULL)
|
||||
, mTextKeys(NULL)
|
||||
, mTime(0.0f)
|
||||
, mPlaying(false)
|
||||
, mLoopCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Ogre::Real Animation::AnimationValue::getValue() const
|
||||
{
|
||||
size_t idx = mIndex;
|
||||
while(idx > 0 && mAnimation->mLayer[idx].mGroupName.empty())
|
||||
idx--;
|
||||
if(!mAnimation->mLayer[idx].mGroupName.empty())
|
||||
return mAnimation->mLayer[idx].mTime;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Animation::AnimationValue::setValue(Ogre::Real value)
|
||||
{
|
||||
mAnimation->mLayer[mIndex].mTime = value;
|
||||
}
|
||||
|
||||
|
||||
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
|
||||
{
|
||||
for(size_t i = 0;i < objects.mParticles.size();i++)
|
||||
|
@ -31,22 +59,22 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL
|
|||
|
||||
Animation::Animation(const MWWorld::Ptr &ptr)
|
||||
: mPtr(ptr)
|
||||
, mController(NULL)
|
||||
, mInsert(NULL)
|
||||
, mSkelBase(NULL)
|
||||
, mAccumRoot(NULL)
|
||||
, mNonAccumRoot(NULL)
|
||||
, mAccumulate(Ogre::Vector3::ZERO)
|
||||
, mNonAccumCtrl(NULL)
|
||||
, mAccumulate(0.0f)
|
||||
, mLastPosition(0.0f)
|
||||
, mCurrentControllers(NULL)
|
||||
, mCurrentKeys(NULL)
|
||||
, mCurrentAnim(NULL)
|
||||
, mCurrentTime(0.0f)
|
||||
, mStopTime(0.0f)
|
||||
, mPlaying(false)
|
||||
, mLooping(false)
|
||||
, mAnimVelocity(0.0f)
|
||||
, mAnimSpeedMult(1.0f)
|
||||
{
|
||||
for(size_t i = 0;i < sMaxLayers;i++)
|
||||
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this, i));
|
||||
|
||||
/* As long as we remain under 128 active controllers, we can avoid
|
||||
* reallocations. */
|
||||
mActiveCtrls.reserve(128);
|
||||
}
|
||||
|
||||
Animation::~Animation()
|
||||
|
@ -54,105 +82,34 @@ Animation::~Animation()
|
|||
if(mInsert)
|
||||
{
|
||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||
destroyObjectList(sceneMgr, mObjectList);
|
||||
|
||||
for(size_t i = 0;i < mAnimationSources.size();i++)
|
||||
destroyObjectList(sceneMgr, mAnimationSources[i]);
|
||||
mAnimationSources.clear();
|
||||
for(size_t i = 0;i < mObjects.size();i++)
|
||||
destroyObjectList(sceneMgr, mObjects[i].mObjectList);
|
||||
mObjects.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::setAnimationSources(const std::vector<std::string> &names)
|
||||
void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly)
|
||||
{
|
||||
if(!mObjectList.mSkelBase)
|
||||
return;
|
||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||
|
||||
mCurrentControllers = &mObjectList.mControllers;
|
||||
mCurrentAnim = NULL;
|
||||
mCurrentKeys = NULL;
|
||||
mAnimVelocity = 0.0f;
|
||||
mAccumRoot = NULL;
|
||||
mNonAccumRoot = NULL;
|
||||
mTextKeys.clear();
|
||||
for(size_t i = 0;i < mAnimationSources.size();i++)
|
||||
destroyObjectList(sceneMgr, mAnimationSources[i]);
|
||||
mAnimationSources.clear();
|
||||
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
|
||||
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
|
||||
std::vector<std::string>::const_iterator nameiter;
|
||||
for(nameiter = names.begin();nameiter != names.end();nameiter++)
|
||||
if(!mInsert)
|
||||
{
|
||||
mAnimationSources.push_back(NifOgre::Loader::createObjectBase(sceneMgr, *nameiter));
|
||||
if(!mAnimationSources.back().mSkelBase)
|
||||
{
|
||||
std::cerr<< "Failed to get skeleton source "<<*nameiter <<std::endl;
|
||||
destroyObjectList(sceneMgr, mAnimationSources.back());
|
||||
mAnimationSources.pop_back();
|
||||
continue;
|
||||
}
|
||||
NifOgre::ObjectList &objects = mAnimationSources.back();
|
||||
|
||||
for(size_t i = 0;i < objects.mControllers.size();i++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objects.mControllers[i].getDestination().getPointer());
|
||||
if(!dstval) continue;
|
||||
|
||||
const Ogre::String &trgtname = dstval->getNode()->getName();
|
||||
if(!skelinst->hasBone(trgtname)) continue;
|
||||
|
||||
Ogre::Bone *bone = skelinst->getBone(trgtname);
|
||||
dstval->setNode(bone);
|
||||
}
|
||||
|
||||
for(size_t i = 0;i < objects.mControllers.size();i++)
|
||||
{
|
||||
if(objects.mControllers[i].getSource().isNull())
|
||||
objects.mControllers[i].setSource(ctrlval);
|
||||
}
|
||||
|
||||
Ogre::Entity *ent = objects.mSkelBase;
|
||||
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(ent->getSkeleton()->getName());
|
||||
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
|
||||
while(boneiter.hasMoreElements())
|
||||
{
|
||||
Ogre::Bone *bone = boneiter.getNext();
|
||||
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
|
||||
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
|
||||
if(data.isEmpty() || !Ogre::any_cast<bool>(data))
|
||||
continue;
|
||||
|
||||
if(!mNonAccumRoot)
|
||||
{
|
||||
mAccumRoot = mInsert;
|
||||
mNonAccumRoot = mObjectList.mSkelBase->getSkeleton()->getBone(bone->getName());
|
||||
}
|
||||
|
||||
for(int i = 0;i < skel->getNumAnimations();i++)
|
||||
{
|
||||
Ogre::Animation *anim = skel->getAnimation(i);
|
||||
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
|
||||
"@"+anim->getName());
|
||||
if(!groupdata.isEmpty())
|
||||
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
mInsert = node->createChildSceneNode();
|
||||
assert(mInsert);
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model)
|
||||
{
|
||||
mInsert = node->createChildSceneNode();
|
||||
assert(mInsert);
|
||||
mObjects.push_back(ObjectInfo());
|
||||
ObjectInfo &obj = mObjects.back();
|
||||
obj.mActiveLayers = 0;
|
||||
obj.mObjectList = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
|
||||
NifOgre::Loader::createObjectBase(mInsert, model));
|
||||
|
||||
mObjectList = NifOgre::Loader::createObjects(mInsert, model);
|
||||
if(mObjectList.mSkelBase)
|
||||
NifOgre::ObjectList &objlist = obj.mObjectList;
|
||||
if(objlist.mSkelBase)
|
||||
{
|
||||
Ogre::AnimationStateSet *aset = mObjectList.mSkelBase->getAllAnimationStates();
|
||||
if(mObjects.size() == 1)
|
||||
mSkelBase = objlist.mSkelBase;
|
||||
|
||||
Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates();
|
||||
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
|
||||
while(asiter.hasMoreElements())
|
||||
{
|
||||
|
@ -164,27 +121,184 @@ void Animation::createObjectList(Ogre::SceneNode *node, const std::string &model
|
|||
// Set the bones as manually controlled since we're applying the
|
||||
// transformations manually (needed if we want to apply an animation
|
||||
// from one skeleton onto another).
|
||||
Ogre::SkeletonInstance *skelinst = mObjectList.mSkelBase->getSkeleton();
|
||||
Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton();
|
||||
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
|
||||
while(boneiter.hasMoreElements())
|
||||
boneiter.getNext()->setManuallyControlled(true);
|
||||
}
|
||||
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > ctrlval(OGRE_NEW AnimationValue(this));
|
||||
for(size_t i = 0;i < mObjectList.mControllers.size();i++)
|
||||
if(objlist.mSkelBase && mSkelBase)
|
||||
{
|
||||
if(mObjectList.mControllers[i].getSource().isNull())
|
||||
mObjectList.mControllers[i].setSource(ctrlval);
|
||||
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
if(mSkelBase == objlist.mSkelBase)
|
||||
{
|
||||
if(objlist.mTextKeys.size() > 0)
|
||||
{
|
||||
mAccumRoot = mInsert;
|
||||
mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
|
||||
if(!dstval) continue;
|
||||
|
||||
const Ogre::String &trgtname = dstval->getNode()->getName();
|
||||
if(!baseinst->hasBone(trgtname)) continue;
|
||||
|
||||
Ogre::Bone *bone = baseinst->getBone(trgtname);
|
||||
dstval->setNode(bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
if(objlist.mControllers[i].getSource().isNull())
|
||||
objlist.mControllers[i].setSource(mAnimationValuePtr[0]);
|
||||
}
|
||||
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
|
||||
}
|
||||
|
||||
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
|
||||
{
|
||||
for(size_t i = 0;i < objlist.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = objlist.mEntities[i];
|
||||
if(visflags != 0)
|
||||
ent->setVisibilityFlags(visflags);
|
||||
|
||||
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objlist.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = objlist.mParticles[i];
|
||||
if(visflags != 0)
|
||||
part->setVisibilityFlags(visflags);
|
||||
// TODO: Check particle material for actual transparency
|
||||
part->setRenderQueueGroup(transqueue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::clearExtraSources()
|
||||
{
|
||||
for(size_t layer = 0;layer < sMaxLayers;layer++)
|
||||
{
|
||||
mLayer[layer].mGroupName.clear();
|
||||
mLayer[layer].mTextKeys = NULL;
|
||||
mLayer[layer].mControllers = NULL;
|
||||
mLayer[layer].mTime = 0.0f;
|
||||
mLayer[layer].mLoopCount = 0;
|
||||
mLayer[layer].mPlaying = false;
|
||||
}
|
||||
mNonAccumCtrl = NULL;
|
||||
mAnimVelocity = 0.0f;
|
||||
|
||||
mLastPosition = Ogre::Vector3(0.0f);
|
||||
if(mAccumRoot)
|
||||
mAccumRoot->setPosition(mLastPosition);
|
||||
|
||||
if(mObjects.size() > 1)
|
||||
{
|
||||
mObjects.resize(1);
|
||||
mObjects[0].mActiveLayers = 0;
|
||||
|
||||
NifOgre::ObjectList &objlist = mObjects[0].mObjectList;
|
||||
mActiveCtrls.clear();
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), objlist.mControllers.begin(), objlist.mControllers.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Animation::updateActiveControllers()
|
||||
{
|
||||
mActiveCtrls.clear();
|
||||
|
||||
/* First, get all controllers that don't target a node, or that target
|
||||
* nodes that don't belong to any particular layer.
|
||||
*/
|
||||
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
|
||||
for(;obj != mObjects.end();obj++)
|
||||
{
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(obj->mObjectList.mControllers.begin());
|
||||
for(;ctrl != obj->mObjectList.mControllers.end();ctrl++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
|
||||
if(dstval)
|
||||
{
|
||||
/*if(getLayerByName(dstval->getNode()->getName()) >= 0)*/
|
||||
continue;
|
||||
}
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *ctrls = NULL;
|
||||
size_t layer = 0;
|
||||
while(layer < sMaxLayers)
|
||||
{
|
||||
/* Now get controllers that target nodes that belong to this layer from
|
||||
* whatever objectlist is active on this layer.
|
||||
*/
|
||||
std::vector<ObjectInfo>::iterator obj(mObjects.begin());
|
||||
for(;obj != mObjects.end();obj++)
|
||||
{
|
||||
if((obj->mActiveLayers&(1<<layer)))
|
||||
{
|
||||
ctrls = &obj->mObjectList.mControllers;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ctrls == NULL)
|
||||
{
|
||||
layer++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if any objectlists are active on subsequent layers. Include
|
||||
* those layers if not.
|
||||
*/
|
||||
size_t nextlayer = layer+1;
|
||||
for(;nextlayer < sMaxLayers;nextlayer++)
|
||||
{
|
||||
for(obj = mObjects.begin();obj != mObjects.end();obj++)
|
||||
{
|
||||
if((obj->mActiveLayers&(1<<nextlayer)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::const_iterator ctrl(ctrls->begin());
|
||||
for(;ctrl != ctrls->end();ctrl++)
|
||||
{
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrl->getDestination().getPointer());
|
||||
if(dstval)
|
||||
{
|
||||
/*ssize_t idx = getLayerByName(dstval->getNode()->getName());
|
||||
if(idx >= (ssize_t)layer && idx < (ssize_t)nextlayer)*/
|
||||
mActiveCtrls.insert(mActiveCtrls.end(), *ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
layer = nextlayer;
|
||||
}
|
||||
mCurrentControllers = &mObjectList.mControllers;
|
||||
}
|
||||
|
||||
|
||||
Ogre::Node *Animation::getNode(const std::string &name)
|
||||
{
|
||||
if(mObjectList.mSkelBase)
|
||||
if(mSkelBase)
|
||||
{
|
||||
Ogre::SkeletonInstance *skel = mObjectList.mSkelBase->getSkeleton();
|
||||
Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton();
|
||||
if(skel->hasBone(name))
|
||||
return skel->getBone(name);
|
||||
}
|
||||
|
@ -192,20 +306,34 @@ Ogre::Node *Animation::getNode(const std::string &name)
|
|||
}
|
||||
|
||||
|
||||
bool Animation::hasAnimation(const std::string &anim)
|
||||
NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname)
|
||||
{
|
||||
for(std::vector<NifOgre::ObjectList>::const_iterator iter(mAnimationSources.begin());iter != mAnimationSources.end();iter++)
|
||||
NifOgre::TextKeyMap::const_iterator iter(keys.begin());
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mSkelBase->hasAnimationState(anim))
|
||||
return true;
|
||||
if(iter->second.compare(0, groupname.size(), groupname) == 0 &&
|
||||
iter->second.compare(groupname.size(), 2, ": ") == 0)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
void Animation::setController(MWMechanics::CharacterController *controller)
|
||||
bool Animation::hasAnimation(const std::string &anim)
|
||||
{
|
||||
mController = controller;
|
||||
if(!mSkelBase)
|
||||
return false;
|
||||
|
||||
for(std::vector<ObjectInfo>::const_iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
{
|
||||
if(iter->mObjectList.mTextKeys.size() == 0)
|
||||
continue;
|
||||
|
||||
const NifOgre::TextKeyMap &keys = iter->mObjectList.mTextKeys.begin()->second;
|
||||
if(findGroupStart(keys, anim) != keys.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,10 +349,6 @@ void Animation::setSpeed(float speed)
|
|||
mAnimSpeedMult = speed / mAnimVelocity;
|
||||
}
|
||||
|
||||
void Animation::setLooping(bool loop)
|
||||
{
|
||||
mLooping = loop;
|
||||
}
|
||||
|
||||
void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
|
@ -232,47 +356,36 @@ void Animation::updatePtr(const MWWorld::Ptr &ptr)
|
|||
}
|
||||
|
||||
|
||||
void Animation::calcAnimVelocity()
|
||||
float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const Ogre::Vector3 &accum, const std::string &groupname)
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = 0;
|
||||
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(!track && trackiter.hasMoreElements())
|
||||
const std::string start = groupname+": start";
|
||||
const std::string loopstart = groupname+": loop start";
|
||||
const std::string loopstop = groupname+": loop stop";
|
||||
const std::string stop = groupname+": stop";
|
||||
float starttime = std::numeric_limits<float>::max();
|
||||
float stoptime = 0.0f;
|
||||
NifOgre::TextKeyMap::const_iterator keyiter(keys.begin());
|
||||
while(keyiter != keys.end())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
|
||||
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
track = cur;
|
||||
if(keyiter->second == start || keyiter->second == loopstart)
|
||||
starttime = keyiter->first;
|
||||
else if(keyiter->second == loopstop || keyiter->second == stop)
|
||||
{
|
||||
stoptime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
keyiter++;
|
||||
}
|
||||
|
||||
if(track && track->getNumKeyFrames() > 1)
|
||||
if(stoptime > starttime)
|
||||
{
|
||||
float loopstarttime = 0.0f;
|
||||
float loopstoptime = mCurrentAnim->getLength();
|
||||
NifOgre::TextKeyMap::const_iterator keyiter = mCurrentKeys->begin();
|
||||
while(keyiter != mCurrentKeys->end())
|
||||
{
|
||||
if(keyiter->second == "loop start")
|
||||
loopstarttime = keyiter->first;
|
||||
else if(keyiter->second == "loop stop")
|
||||
{
|
||||
loopstoptime = keyiter->first;
|
||||
break;
|
||||
}
|
||||
keyiter++;
|
||||
}
|
||||
Ogre::Vector3 startpos = nonaccumctrl->getTranslation(starttime) * accum;
|
||||
Ogre::Vector3 endpos = nonaccumctrl->getTranslation(stoptime) * accum;
|
||||
|
||||
if(loopstoptime > loopstarttime)
|
||||
{
|
||||
Ogre::TransformKeyFrame startkf(0, loopstarttime);
|
||||
Ogre::TransformKeyFrame endkf(0, loopstoptime);
|
||||
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstarttime), &startkf);
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(loopstoptime), &endkf);
|
||||
|
||||
mAnimVelocity = startkf.getTranslate().distance(endkf.getTranslate()) /
|
||||
(loopstoptime-loopstarttime);
|
||||
}
|
||||
return startpos.distance(endpos) / (stoptime-starttime);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
|
||||
|
@ -313,93 +426,90 @@ void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Og
|
|||
}
|
||||
|
||||
|
||||
Ogre::Vector3 Animation::updatePosition()
|
||||
void Animation::updatePosition(Ogre::Vector3 &position)
|
||||
{
|
||||
Ogre::Vector3 posdiff;
|
||||
|
||||
Ogre::TransformKeyFrame kf(0, mCurrentTime);
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(trackiter.hasMoreElements())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = trackiter.getNext();
|
||||
if(track->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
{
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the non-accumulation root's difference from the last update. */
|
||||
posdiff = (kf.getTranslate() - mLastPosition) * mAccumulate;
|
||||
/* Get the non-accumulation root's difference from the last update, and move the position
|
||||
* accordingly.
|
||||
*/
|
||||
posdiff = (mNonAccumCtrl->getTranslation(mLayer[0].mTime) - mLastPosition) * mAccumulate;
|
||||
position += posdiff;
|
||||
|
||||
/* Translate the accumulation root back to compensate for the move. */
|
||||
mLastPosition += posdiff;
|
||||
mAccumRoot->setPosition(-mLastPosition);
|
||||
|
||||
return posdiff;
|
||||
}
|
||||
|
||||
void Animation::reset(const std::string &start, const std::string &stop)
|
||||
bool Animation::reset(size_t layeridx, const NifOgre::TextKeyMap &keys, NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
|
||||
while(mNextKey != mCurrentKeys->end() && mNextKey->second != start)
|
||||
mNextKey++;
|
||||
if(mNextKey != mCurrentKeys->end())
|
||||
mCurrentTime = mNextKey->first;
|
||||
else
|
||||
std::string tag = groupname+": "+start;
|
||||
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
startkey++;
|
||||
if(startkey == keys.end() && start == "loop start")
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
while(mNextKey != mCurrentKeys->end() && mNextKey->second != "start")
|
||||
mNextKey++;
|
||||
if(mNextKey != mCurrentKeys->end())
|
||||
mCurrentTime = mNextKey->first;
|
||||
else
|
||||
{
|
||||
mNextKey = mCurrentKeys->begin();
|
||||
mCurrentTime = 0.0f;
|
||||
}
|
||||
tag = groupname+": start";
|
||||
startkey = keys.begin();
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
startkey++;
|
||||
}
|
||||
if(startkey == keys.end())
|
||||
return false;
|
||||
|
||||
tag = groupname+": "+stop;
|
||||
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
|
||||
while(stopkey != keys.end() && stopkey->second != tag)
|
||||
stopkey++;
|
||||
if(stopkey == keys.end())
|
||||
return false;
|
||||
|
||||
if(startkey == stopkey)
|
||||
return false;
|
||||
|
||||
mLayer[layeridx].mStartKey = startkey;
|
||||
mLayer[layeridx].mLoopStartKey = startkey;
|
||||
mLayer[layeridx].mStopKey = stopkey;
|
||||
mLayer[layeridx].mNextKey = startkey;
|
||||
|
||||
mLayer[layeridx].mTime = mLayer[layeridx].mStartKey->first + ((mLayer[layeridx].mStopKey->first-
|
||||
mLayer[layeridx].mStartKey->first) * startpoint);
|
||||
|
||||
tag = groupname+": loop start";
|
||||
while(mLayer[layeridx].mNextKey->first <= mLayer[layeridx].mTime && mLayer[layeridx].mNextKey != mLayer[layeridx].mStopKey)
|
||||
{
|
||||
if(mLayer[layeridx].mNextKey->second == tag)
|
||||
mLayer[layeridx].mLoopStartKey = mLayer[layeridx].mNextKey;
|
||||
mLayer[layeridx].mNextKey++;
|
||||
}
|
||||
|
||||
if(stop.length() > 0)
|
||||
{
|
||||
NifOgre::TextKeyMap::const_iterator stopKey = mNextKey;
|
||||
while(stopKey != mCurrentKeys->end() && stopKey->second != stop)
|
||||
stopKey++;
|
||||
if(stopKey != mCurrentKeys->end())
|
||||
mStopTime = stopKey->first;
|
||||
else
|
||||
mStopTime = mCurrentAnim->getLength();
|
||||
}
|
||||
if(layeridx == 0 && nonaccumctrl)
|
||||
mLastPosition = nonaccumctrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
|
||||
|
||||
if(mNonAccumRoot)
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *track = 0;
|
||||
Ogre::Animation::NodeTrackIterator trackiter = mCurrentAnim->getNodeTrackIterator();
|
||||
while(!track && trackiter.hasMoreElements())
|
||||
{
|
||||
const Ogre::NodeAnimationTrack *cur = trackiter.getNext();
|
||||
if(cur->getAssociatedNode()->getName() == mNonAccumRoot->getName())
|
||||
track = cur;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(track)
|
||||
{
|
||||
Ogre::TransformKeyFrame kf(0, mCurrentTime);
|
||||
track->getInterpolatedKeyFrame(mCurrentAnim->_getTimeIndex(mCurrentTime), &kf);
|
||||
mLastPosition = kf.getTranslate() * mAccumulate;
|
||||
}
|
||||
}
|
||||
bool Animation::doLoop(size_t layeridx)
|
||||
{
|
||||
if(mLayer[layeridx].mLoopCount == 0)
|
||||
return false;
|
||||
mLayer[layeridx].mLoopCount--;
|
||||
|
||||
mLayer[layeridx].mTime = mLayer[layeridx].mLoopStartKey->first;
|
||||
mLayer[layeridx].mNextKey = mLayer[layeridx].mLoopStartKey;
|
||||
mLayer[layeridx].mNextKey++;
|
||||
mLayer[layeridx].mPlaying = true;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
mLastPosition = mNonAccumCtrl->getTranslation(mLayer[layeridx].mTime) * mAccumulate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Animation::handleEvent(float time, const std::string &evt)
|
||||
bool Animation::handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key)
|
||||
{
|
||||
if(evt == "start" || evt == "loop start")
|
||||
{
|
||||
/* Do nothing */
|
||||
return true;
|
||||
}
|
||||
float time = key->first;
|
||||
const std::string &evt = key->second;
|
||||
|
||||
if(evt.compare(0, 7, "sound: ") == 0)
|
||||
{
|
||||
|
@ -414,105 +524,214 @@ bool Animation::handleEvent(float time, const std::string &evt)
|
|||
return true;
|
||||
}
|
||||
|
||||
if(evt == "loop stop")
|
||||
if(evt.compare(0, mLayer[layeridx].mGroupName.size(), mLayer[layeridx].mGroupName) != 0 ||
|
||||
evt.compare(mLayer[layeridx].mGroupName.size(), 2, ": ") != 0)
|
||||
{
|
||||
if(mLooping)
|
||||
// Not ours, skip it
|
||||
return true;
|
||||
}
|
||||
size_t off = mLayer[layeridx].mGroupName.size()+2;
|
||||
size_t len = evt.size() - off;
|
||||
|
||||
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
|
||||
{
|
||||
mLayer[layeridx].mLoopStartKey = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
|
||||
{
|
||||
if(doLoop(layeridx))
|
||||
{
|
||||
reset("loop start", "");
|
||||
if(mCurrentTime >= time)
|
||||
if(mLayer[layeridx].mTime >= time)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if(evt == "stop")
|
||||
{
|
||||
if(mLooping)
|
||||
{
|
||||
reset("loop start", "");
|
||||
if(mCurrentTime >= time)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// fall-through
|
||||
}
|
||||
if(mController)
|
||||
mController->markerEvent(time, evt);
|
||||
|
||||
std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
|
||||
bool Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops)
|
||||
{
|
||||
try {
|
||||
bool found = false;
|
||||
/* Look in reverse; last-inserted source has priority. */
|
||||
for(std::vector<NifOgre::ObjectList>::reverse_iterator iter(mAnimationSources.rbegin());iter != mAnimationSources.rend();iter++)
|
||||
// TODO: parameterize this
|
||||
size_t layeridx = 0;
|
||||
|
||||
if(!mSkelBase)
|
||||
return false;
|
||||
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
iter->mActiveLayers &= ~(1<<layeridx);
|
||||
|
||||
mLayer[layeridx].mGroupName.clear();
|
||||
mLayer[layeridx].mTextKeys = NULL;
|
||||
mLayer[layeridx].mControllers = NULL;
|
||||
mLayer[layeridx].mTime = 0.0f;
|
||||
mLayer[layeridx].mLoopCount = 0;
|
||||
mLayer[layeridx].mPlaying = false;
|
||||
|
||||
bool movinganim = false;
|
||||
bool foundanim = false;
|
||||
|
||||
/* Look in reverse; last-inserted source has priority. */
|
||||
for(std::vector<ObjectInfo>::reverse_iterator iter(mObjects.rbegin());iter != mObjects.rend();iter++)
|
||||
{
|
||||
NifOgre::ObjectList &objlist = iter->mObjectList;
|
||||
if(objlist.mTextKeys.size() == 0)
|
||||
continue;
|
||||
|
||||
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
|
||||
if(layeridx == 0 && mNonAccumRoot)
|
||||
{
|
||||
if(iter->mSkelBase->hasAnimationState(groupname))
|
||||
for(size_t i = 0;i < objlist.mControllers.size();i++)
|
||||
{
|
||||
mCurrentAnim = iter->mSkelBase->getSkeleton()->getAnimation(groupname);
|
||||
mCurrentKeys = &mTextKeys[groupname];
|
||||
mCurrentControllers = &iter->mControllers;
|
||||
mAnimVelocity = 0.0f;
|
||||
|
||||
if(mNonAccumRoot)
|
||||
calcAnimVelocity();
|
||||
|
||||
found = true;
|
||||
break;
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
|
||||
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
|
||||
if(dstval && dstval->getNode() == mNonAccumRoot)
|
||||
{
|
||||
nonaccumctrl = dstval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
throw std::runtime_error("Failed to find animation "+groupname);
|
||||
|
||||
reset(start, stop);
|
||||
setLooping(loop);
|
||||
mPlaying = true;
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< e.what() <<std::endl;
|
||||
if(!foundanim)
|
||||
{
|
||||
if(!reset(layeridx, keys, nonaccumctrl, groupname, start, stop, startpoint))
|
||||
continue;
|
||||
|
||||
mLayer[layeridx].mGroupName = groupname;
|
||||
mLayer[layeridx].mTextKeys = &keys;
|
||||
mLayer[layeridx].mControllers = &objlist.mControllers;
|
||||
mLayer[layeridx].mLoopCount = loops;
|
||||
mLayer[layeridx].mPlaying = true;
|
||||
|
||||
if(layeridx == 0)
|
||||
{
|
||||
mNonAccumCtrl = nonaccumctrl;
|
||||
mAnimVelocity = 0.0f;
|
||||
}
|
||||
|
||||
iter->mActiveLayers |= (1<<layeridx);
|
||||
foundanim = true;
|
||||
|
||||
if(mAccumulate == Ogre::Vector3(0.0f))
|
||||
break;
|
||||
}
|
||||
|
||||
if(!nonaccumctrl)
|
||||
break;
|
||||
|
||||
mAnimVelocity = calcAnimVelocity(keys, nonaccumctrl, mAccumulate, groupname);
|
||||
if(mAnimVelocity > 1.0f)
|
||||
{
|
||||
movinganim = (nonaccumctrl==mNonAccumCtrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!foundanim)
|
||||
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
|
||||
|
||||
updateActiveControllers();
|
||||
|
||||
return movinganim;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Animation::runAnimation(float timepassed)
|
||||
void Animation::disable(size_t layeridx)
|
||||
{
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
return;
|
||||
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
iter->mActiveLayers &= ~(1<<layeridx);
|
||||
|
||||
mLayer[layeridx].mGroupName.clear();
|
||||
mLayer[layeridx].mTextKeys = NULL;
|
||||
mLayer[layeridx].mControllers = NULL;
|
||||
mLayer[layeridx].mTime = 0.0f;
|
||||
mLayer[layeridx].mLoopCount = 0;
|
||||
mLayer[layeridx].mPlaying = false;
|
||||
|
||||
updateActiveControllers();
|
||||
}
|
||||
|
||||
bool Animation::getInfo(size_t layeridx, float *complete, std::string *groupname, std::string *start, std::string *stop) const
|
||||
{
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
{
|
||||
if(complete) *complete = 0.0f;
|
||||
if(groupname) *groupname = "";
|
||||
if(start) *start = "";
|
||||
if(stop) *stop = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(complete) *complete = (mLayer[layeridx].mTime - mLayer[layeridx].mStartKey->first) /
|
||||
(mLayer[layeridx].mStopKey->first - mLayer[layeridx].mStartKey->first);
|
||||
if(groupname) *groupname = mLayer[layeridx].mGroupName;
|
||||
if(start) *start = mLayer[layeridx].mStartKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
|
||||
if(stop) *stop = mLayer[layeridx].mStopKey->second.substr(mLayer[layeridx].mGroupName.size()+2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Ogre::Vector3 Animation::runAnimation(float duration)
|
||||
{
|
||||
Ogre::Vector3 movement(0.0f);
|
||||
|
||||
timepassed *= mAnimSpeedMult;
|
||||
while(mCurrentAnim && mPlaying)
|
||||
duration *= mAnimSpeedMult;
|
||||
for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++)
|
||||
{
|
||||
float targetTime = mCurrentTime + timepassed;
|
||||
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
|
||||
if(mLayer[layeridx].mGroupName.empty())
|
||||
continue;
|
||||
|
||||
float timepassed = duration;
|
||||
while(mLayer[layeridx].mPlaying)
|
||||
{
|
||||
mCurrentTime = std::min(mStopTime, targetTime);
|
||||
if(mNonAccumRoot)
|
||||
movement += updatePosition();
|
||||
mPlaying = (mLooping || mStopTime > mCurrentTime);
|
||||
timepassed = targetTime - mCurrentTime;
|
||||
break;
|
||||
float targetTime = mLayer[layeridx].mTime + timepassed;
|
||||
if(mLayer[layeridx].mNextKey->first > targetTime)
|
||||
{
|
||||
mLayer[layeridx].mTime = targetTime;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
updatePosition(movement);
|
||||
break;
|
||||
}
|
||||
|
||||
NifOgre::TextKeyMap::const_iterator key(mLayer[layeridx].mNextKey++);
|
||||
mLayer[layeridx].mTime = key->first;
|
||||
if(layeridx == 0 && mNonAccumCtrl)
|
||||
updatePosition(movement);
|
||||
|
||||
mLayer[layeridx].mPlaying = (key != mLayer[layeridx].mStopKey);
|
||||
timepassed = targetTime - mLayer[layeridx].mTime;
|
||||
|
||||
if(!handleTextKey(layeridx, key))
|
||||
break;
|
||||
}
|
||||
|
||||
float time = mNextKey->first;
|
||||
const std::string &evt = mNextKey->second;
|
||||
mNextKey++;
|
||||
|
||||
mCurrentTime = time;
|
||||
if(mNonAccumRoot)
|
||||
movement += updatePosition();
|
||||
mPlaying = (mLooping || mStopTime > mCurrentTime);
|
||||
timepassed = targetTime - mCurrentTime;
|
||||
|
||||
if(!handleEvent(time, evt))
|
||||
break;
|
||||
}
|
||||
for(size_t i = 0;i < mCurrentControllers->size();i++)
|
||||
(*mCurrentControllers)[i].update();
|
||||
|
||||
if(mObjectList.mSkelBase)
|
||||
for(size_t i = 0;i < mActiveCtrls.size();i++)
|
||||
mActiveCtrls[i].update();
|
||||
|
||||
if(mSkelBase)
|
||||
{
|
||||
// HACK: Dirty the animation state set so that Ogre will apply the
|
||||
// transformations to entities this skeleton instance is shared with.
|
||||
mObjectList.mSkelBase->getAllAnimationStates()->_notifyDirty();
|
||||
const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
for(std::vector<ObjectInfo>::iterator iter(mObjects.begin());iter != mObjects.end();iter++)
|
||||
{
|
||||
Ogre::Entity *ent = iter->mObjectList.mSkelBase;
|
||||
if(!ent) continue;
|
||||
|
||||
Ogre::SkeletonInstance *inst = ent->getSkeleton();
|
||||
if(baseinst != inst)
|
||||
updateSkeletonInstance(baseinst, inst);
|
||||
|
||||
// HACK: Dirty the animation state set so that Ogre will apply the
|
||||
// transformations to entities this skeleton instance is shared with.
|
||||
ent->getAllAnimationStates()->_notifyDirty();
|
||||
}
|
||||
}
|
||||
|
||||
return movement;
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class CharacterController;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -23,48 +19,67 @@ protected:
|
|||
{
|
||||
private:
|
||||
Animation *mAnimation;
|
||||
size_t mIndex;
|
||||
|
||||
public:
|
||||
AnimationValue(Animation *anim) : mAnimation(anim)
|
||||
AnimationValue(Animation *anim, size_t layeridx)
|
||||
: mAnimation(anim), mIndex(layeridx)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
return mAnimation->mCurrentTime;
|
||||
}
|
||||
virtual Ogre::Real getValue() const;
|
||||
virtual void setValue(Ogre::Real value);
|
||||
};
|
||||
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{
|
||||
mAnimation->mCurrentTime = value;
|
||||
}
|
||||
struct ObjectInfo {
|
||||
NifOgre::ObjectList mObjectList;
|
||||
/* Bit-field specifying which animation layers this object list is
|
||||
* explicitly animating on (1 = layer 0, 2 = layer 1, 4 = layer 2.
|
||||
* etc).
|
||||
*/
|
||||
int mActiveLayers;
|
||||
};
|
||||
|
||||
struct AnimLayer {
|
||||
std::string mGroupName;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
|
||||
const NifOgre::TextKeyMap *mTextKeys;
|
||||
NifOgre::TextKeyMap::const_iterator mStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mStopKey;
|
||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
||||
|
||||
float mTime;
|
||||
|
||||
bool mPlaying;
|
||||
size_t mLoopCount;
|
||||
|
||||
AnimLayer();
|
||||
};
|
||||
|
||||
MWWorld::Ptr mPtr;
|
||||
MWMechanics::CharacterController *mController;
|
||||
|
||||
Ogre::SceneNode* mInsert;
|
||||
NifOgre::ObjectList mObjectList;
|
||||
std::map<std::string,NifOgre::TextKeyMap> mTextKeys;
|
||||
Ogre::SceneNode *mInsert;
|
||||
Ogre::Entity *mSkelBase;
|
||||
std::vector<ObjectInfo> mObjects;
|
||||
Ogre::Node *mAccumRoot;
|
||||
Ogre::Bone *mNonAccumRoot;
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
|
||||
Ogre::Vector3 mAccumulate;
|
||||
Ogre::Vector3 mLastPosition;
|
||||
|
||||
std::vector<NifOgre::ObjectList> mAnimationSources;
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > *mCurrentControllers;
|
||||
NifOgre::TextKeyMap *mCurrentKeys;
|
||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
||||
Ogre::Animation *mCurrentAnim;
|
||||
float mCurrentTime;
|
||||
float mStopTime;
|
||||
bool mPlaying;
|
||||
bool mLooping;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mActiveCtrls;
|
||||
|
||||
float mAnimVelocity;
|
||||
float mAnimSpeedMult;
|
||||
|
||||
void calcAnimVelocity();
|
||||
static const size_t sMaxLayers = 1;
|
||||
AnimLayer mLayer[sMaxLayers];
|
||||
Ogre::SharedPtr<Ogre::ControllerValue<Ogre::Real> > mAnimationValuePtr[sMaxLayers];
|
||||
|
||||
static float calcAnimVelocity(const NifOgre::TextKeyMap &keys,
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
|
||||
const Ogre::Vector3 &accum,
|
||||
const std::string &groupname);
|
||||
|
||||
/* Updates a skeleton instance so that all bones matching the source skeleton (based on
|
||||
* bone names) are positioned identically. */
|
||||
|
@ -72,34 +87,39 @@ protected:
|
|||
|
||||
/* Updates the position of the accum root node for the current time, and
|
||||
* returns the wanted movement vector from the previous update. */
|
||||
Ogre::Vector3 updatePosition();
|
||||
void updatePosition(Ogre::Vector3 &position);
|
||||
|
||||
static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname);
|
||||
|
||||
/* Resets the animation to the time of the specified start marker, without
|
||||
* moving anything, and set the end time to the specified stop marker. If
|
||||
* the marker is not found, it resets to the beginning or end respectively.
|
||||
* the marker is not found, or if the markers are the same, it returns
|
||||
* false.
|
||||
*/
|
||||
void reset(const std::string &start, const std::string &stop);
|
||||
bool reset(size_t layeridx, const NifOgre::TextKeyMap &keys,
|
||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl,
|
||||
const std::string &groupname, const std::string &start, const std::string &stop,
|
||||
float startpoint);
|
||||
|
||||
bool handleEvent(float time, const std::string &evt);
|
||||
bool doLoop(size_t layeridx);
|
||||
|
||||
/* Specifies a list of skeleton names to use as animation sources. */
|
||||
void setAnimationSources(const std::vector<std::string> &names);
|
||||
bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
|
||||
|
||||
/* Specifies a single skeleton name to use as an animation source. */
|
||||
void setAnimationSource(const std::string &name)
|
||||
{
|
||||
std::vector<std::string> names(1, name);
|
||||
setAnimationSources(names);
|
||||
}
|
||||
|
||||
void createObjectList(Ogre::SceneNode *node, const std::string &model);
|
||||
void addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly);
|
||||
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
|
||||
|
||||
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
|
||||
|
||||
void updateActiveControllers();
|
||||
|
||||
public:
|
||||
Animation(const MWWorld::Ptr &ptr);
|
||||
virtual ~Animation();
|
||||
|
||||
void setController(MWMechanics::CharacterController *controller);
|
||||
/** Clears all ObjectLists except the first one. As a consequence, any
|
||||
* playing animations are stopped.
|
||||
*/
|
||||
void clearExtraSources();
|
||||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
|
@ -112,10 +132,38 @@ public:
|
|||
|
||||
void setSpeed(float speed);
|
||||
|
||||
void setLooping(bool loop);
|
||||
/** Plays an animation.
|
||||
* \param groupname Name of the animation group to play.
|
||||
* \param start Key marker from which to start.
|
||||
* \param stop Key marker to stop at.
|
||||
* \param startpoint How far in between the two markers to start. 0 starts
|
||||
* at the start marker, 1 starts at the stop marker.
|
||||
* \param loops How many times to loop the animation. This will use the
|
||||
* "loop start" and "loop stop" markers if they exist,
|
||||
* otherwise it will use "start" and "stop".
|
||||
* \return Boolean specifying whether the animation will return movement
|
||||
* for the character at all.
|
||||
*/
|
||||
bool play(const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, size_t loops);
|
||||
|
||||
void play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop);
|
||||
virtual Ogre::Vector3 runAnimation(float timepassed);
|
||||
/** Stops and removes the animation from the given layer. */
|
||||
void disable(size_t layeridx);
|
||||
|
||||
/** Gets info about the given animation layer.
|
||||
* \param layeridx Layer index to get info about.
|
||||
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
|
||||
* \param groupname Stores animation group being played.
|
||||
* \param start Stores the start key
|
||||
* \param stop Stores the stop key
|
||||
* \return True if an animation is active on the layer, false otherwise.
|
||||
*/
|
||||
bool getInfo(size_t layeridx, float *complete=NULL, std::string *groupname=NULL, std::string *start=NULL, std::string *stop=NULL) const;
|
||||
|
||||
virtual Ogre::Vector3 runAnimation(float duration);
|
||||
|
||||
/* Returns if there's an animation playing on the given layer. */
|
||||
bool isPlaying(size_t layeridx) const
|
||||
{ return mLayer[layeridx].mPlaying; }
|
||||
|
||||
Ogre::Node *getNode(const std::string &name);
|
||||
};
|
||||
|
|
|
@ -21,13 +21,15 @@ namespace MWRender
|
|||
|
||||
CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name,
|
||||
Ogre::Vector3 position, Ogre::Vector3 lookAt)
|
||||
: mSizeX(sizeX)
|
||||
, mSizeY(sizeY)
|
||||
, mName(name)
|
||||
|
||||
: mSceneMgr (0)
|
||||
, mPosition(position)
|
||||
, mLookAt(lookAt)
|
||||
, mCharacter(character)
|
||||
, mAnimation(NULL)
|
||||
, mName(name)
|
||||
, mSizeX(sizeX)
|
||||
, mSizeY(sizeY)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -88,16 +90,20 @@ namespace MWRender
|
|||
|
||||
CharacterPreview::~CharacterPreview ()
|
||||
{
|
||||
//Ogre::TextureManager::getSingleton().remove(mName);
|
||||
mSceneMgr->destroyCamera (mName);
|
||||
delete mAnimation;
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
if (mSceneMgr)
|
||||
{
|
||||
//Ogre::TextureManager::getSingleton().remove(mName);
|
||||
mSceneMgr->destroyAllCameras();
|
||||
delete mAnimation;
|
||||
Ogre::Root::getSingleton().destroySceneManager(mSceneMgr);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterPreview::rebuild()
|
||||
{
|
||||
assert(mAnimation);
|
||||
delete mAnimation;
|
||||
mAnimation = 0;
|
||||
|
||||
mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter),
|
||||
0, (renderHeadOnly() ? NpcAnimation::VM_HeadOnly : NpcAnimation::VM_Normal));
|
||||
|
@ -149,7 +155,7 @@ namespace MWRender
|
|||
if (!mSelectionBuffer)
|
||||
mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, 0);
|
||||
|
||||
mAnimation->play("inventoryhandtohand", "start", "stop", false);
|
||||
mAnimation->play("inventoryhandtohand", "start", "stop", 0.0f, 0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
@ -183,7 +189,7 @@ namespace MWRender
|
|||
|
||||
void RaceSelectionPreview::onSetup ()
|
||||
{
|
||||
mAnimation->play("idle", "start", "stop", false);
|
||||
mAnimation->play("idle", "start", "stop", 0.0f, 0);
|
||||
|
||||
updateCamera();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
#include "creatureanimation.hpp"
|
||||
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreParticleSystem.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreSubEntity.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -26,31 +21,11 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
|
|||
{
|
||||
std::string model = "meshes\\"+ref->mBase->mModel;
|
||||
|
||||
createObjectList(mPtr.getRefData().getBaseNode(), model);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectList.mEntities[i];
|
||||
ent->setVisibilityFlags(RV_Actors);
|
||||
|
||||
for(unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
part->setVisibilityFlags(RV_Actors);
|
||||
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
if((ref->mBase->mFlags&ESM::Creature::Biped))
|
||||
names.push_back("meshes\\base_anim.nif");
|
||||
names.push_back(model);
|
||||
setAnimationSources(names);
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), "meshes\\base_anim.nif", true);
|
||||
|
||||
addObjectList(mPtr.getRefData().getBaseNode(), model, false);
|
||||
setRenderProperties(mObjects.back().mObjectList, RV_Actors, RQG_Main, RQG_Alpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
|
@ -96,39 +97,24 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
|
|||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||
|
||||
createObjectList(node, smodel);
|
||||
for(size_t i = 0;i < mObjectList.mEntities.size();i++)
|
||||
{
|
||||
Ogre::Entity *base = mObjectList.mEntities[i];
|
||||
|
||||
base->getUserObjectBindings().setUserAny(Ogre::Any(-1));
|
||||
if (mVisibilityFlags != 0)
|
||||
base->setVisibilityFlags(mVisibilityFlags);
|
||||
|
||||
for(unsigned int j=0; j < base->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = base->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < mObjectList.mParticles.size();i++)
|
||||
{
|
||||
Ogre::ParticleSystem *part = mObjectList.mParticles[i];
|
||||
|
||||
part->getUserObjectBindings().setUserAny(Ogre::Any(-1));
|
||||
if(mVisibilityFlags != 0)
|
||||
part->setVisibilityFlags(mVisibilityFlags);
|
||||
part->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
std::vector<std::string> skelnames(1, smodel);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
skelnames.push_back("meshes\\base_anim_female.nif");
|
||||
else if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
skelnames.push_back("meshes\\argonian_swimkna.nif");
|
||||
addObjectList(node, smodel, true);
|
||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
addObjectList(node, "meshes\\argonian_swimkna.nif", true);
|
||||
else if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.nif", true);
|
||||
if(mNpc->mModel.length() > 0)
|
||||
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
|
||||
setAnimationSources(skelnames);
|
||||
addObjectList(node, "meshes\\"+mNpc->mModel, true);
|
||||
if(mViewMode == VM_FirstPerson)
|
||||
{
|
||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
||||
*/
|
||||
addObjectList(node, "meshes\\base_anim.1st.nif", true);
|
||||
if(isBeast)
|
||||
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
|
||||
}
|
||||
|
||||
forceUpdate();
|
||||
}
|
||||
|
@ -138,28 +124,31 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
|||
assert(viewMode != VM_HeadOnly);
|
||||
mViewMode = viewMode;
|
||||
|
||||
/* FIXME: Enable this once first-person animations work. */
|
||||
#if 0
|
||||
Ogre::SceneNode *node = mInsert->getParentSceneNode();
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
||||
|
||||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||
|
||||
std::vector<std::string> skelnames(1, smodel);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
skelnames.push_back("meshes\\base_anim_female.nif");
|
||||
else if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
skelnames.push_back("meshes\\argonian_swimkna.nif");
|
||||
clearExtraSources();
|
||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||
addObjectList(node, "meshes\\argonian_swimkna.nif", true);
|
||||
else if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.nif", true);
|
||||
if(mNpc->mModel.length() > 0)
|
||||
skelnames.push_back("meshes\\"+Misc::StringUtils::lowerCase(mNpc->mModel));
|
||||
addObjectList(node, "meshes\\"+mNpc->mModel, true);
|
||||
if(mViewMode == VM_FirstPerson)
|
||||
{
|
||||
smodel = (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif");
|
||||
skelnames.push_back(smodel);
|
||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
||||
*/
|
||||
addObjectList(node, "meshes\\base_anim.1st.nif", true);
|
||||
if(isBeast)
|
||||
addObjectList(node, "meshes\\base_animkna.1st.nif", true);
|
||||
if(!mNpc->isMale() && !isBeast)
|
||||
addObjectList(node, "meshes\\base_anim_female.1st.nif", true);
|
||||
}
|
||||
setAnimationSources(skelnames);
|
||||
#endif
|
||||
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
||||
|
||||
for(size_t i = 0;i < sPartListSize;i++)
|
||||
removeIndividualPart(i);
|
||||
|
@ -305,81 +294,95 @@ void NpcAnimation::updateParts(bool forceupdate)
|
|||
if(mViewMode == VM_HeadOnly)
|
||||
return;
|
||||
|
||||
static const struct {
|
||||
ESM::PartReferenceType type;
|
||||
const char name[2][12];
|
||||
} PartTypeList[] = {
|
||||
{ ESM::PRT_Neck, { "neck", "" } },
|
||||
{ ESM::PRT_Cuirass, { "chest", "" } },
|
||||
{ ESM::PRT_Groin, { "groin", "" } },
|
||||
{ ESM::PRT_RHand, { "hand", "hands" } },
|
||||
{ ESM::PRT_LHand, { "hand", "hands" } },
|
||||
{ ESM::PRT_RWrist, { "wrist", "" } },
|
||||
{ ESM::PRT_LWrist, { "wrist", "" } },
|
||||
{ ESM::PRT_RForearm, { "forearm", "" } },
|
||||
{ ESM::PRT_LForearm, { "forearm", "" } },
|
||||
{ ESM::PRT_RUpperarm, { "upper arm", "" } },
|
||||
{ ESM::PRT_LUpperarm, { "upper arm", "" } },
|
||||
{ ESM::PRT_RFoot, { "foot", "feet" } },
|
||||
{ ESM::PRT_LFoot, { "foot", "feet" } },
|
||||
{ ESM::PRT_RAnkle, { "ankle", "" } },
|
||||
{ ESM::PRT_LAnkle, { "ankle", "" } },
|
||||
{ ESM::PRT_RKnee, { "knee", "" } },
|
||||
{ ESM::PRT_LKnee, { "knee", "" } },
|
||||
{ ESM::PRT_RLeg, { "upper leg", "" } },
|
||||
{ ESM::PRT_LLeg, { "upper leg", "" } },
|
||||
{ ESM::PRT_Tail, { "tail", "" } }
|
||||
};
|
||||
std::map<int, int> bodypartMap;
|
||||
bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck;
|
||||
bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest;
|
||||
bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin;
|
||||
bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand;
|
||||
bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand;
|
||||
bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist;
|
||||
bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist;
|
||||
bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm;
|
||||
bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm;
|
||||
bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm;
|
||||
bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm;
|
||||
bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot;
|
||||
bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot;
|
||||
bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle;
|
||||
bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle;
|
||||
bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee;
|
||||
bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee;
|
||||
bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg;
|
||||
bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg;
|
||||
bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail;
|
||||
|
||||
const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : "";
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++)
|
||||
|
||||
const int Flag_Female = 0x01;
|
||||
const int Flag_FirstPerson = 0x02;
|
||||
|
||||
int flags = 0;
|
||||
if (!mNpc->isMale())
|
||||
flags |= Flag_Female;
|
||||
if (mViewMode == VM_FirstPerson)
|
||||
flags |= Flag_FirstPerson;
|
||||
|
||||
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
|
||||
static std::map< std::pair<std::string, int> , std::vector<const ESM::BodyPart*> > sRaceMapping;
|
||||
std::string race = Misc::StringUtils::lowerCase(mNpc->mRace);
|
||||
std::pair<std::string, int> thisCombination = std::make_pair(race, flags);
|
||||
if (sRaceMapping.find(thisCombination) == sRaceMapping.end())
|
||||
{
|
||||
if(mPartPriorities[PartTypeList[i].type] < 1)
|
||||
sRaceMapping[thisCombination].resize(ESM::PRT_Count);
|
||||
for (int i=0; i<ESM::PRT_Count; ++i)
|
||||
sRaceMapping[thisCombination][i] = NULL;
|
||||
|
||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
||||
|
||||
for (MWWorld::Store<ESM::BodyPart>::iterator it = partStore.begin(); it != partStore.end(); ++it)
|
||||
{
|
||||
const ESM::BodyPart *part = NULL;
|
||||
const MWWorld::Store<ESM::BodyPart> &partStore = store.get<ESM::BodyPart>();
|
||||
|
||||
if(!mNpc->isMale())
|
||||
const ESM::BodyPart& bodypart = *it;
|
||||
if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable)
|
||||
continue;
|
||||
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
|
||||
{
|
||||
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext);
|
||||
if(part == 0)
|
||||
part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext);
|
||||
continue;
|
||||
}
|
||||
if(part == 0)
|
||||
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext);
|
||||
if(part == 0)
|
||||
part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext);
|
||||
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
|
||||
continue;
|
||||
if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
|
||||
continue;
|
||||
|
||||
if(part)
|
||||
addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel);
|
||||
bool firstPerson = (bodypart.mId.size() >= 3)
|
||||
&& bodypart.mId[bodypart.mId.size()-3] == '1'
|
||||
&& bodypart.mId[bodypart.mId.size()-2] == 's'
|
||||
&& bodypart.mId[bodypart.mId.size()-1] == 't';
|
||||
if (firstPerson != (mViewMode == VM_FirstPerson))
|
||||
continue;
|
||||
for (std::map<int, int>::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt )
|
||||
if (bIt->second == bodypart.mData.mPart)
|
||||
sRaceMapping[thisCombination][bIt->first] = &*it;
|
||||
}
|
||||
}
|
||||
|
||||
for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part)
|
||||
{
|
||||
const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part];
|
||||
if (mPartPriorities[part] < 1 && bodypart)
|
||||
addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel);
|
||||
}
|
||||
}
|
||||
|
||||
NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename)
|
||||
{
|
||||
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mObjectList.mSkelBase, bonename,
|
||||
mInsert, model);
|
||||
for(size_t i = 0;i < objects.mEntities.size();i++)
|
||||
{
|
||||
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
if(mVisibilityFlags != 0)
|
||||
objects.mEntities[i]->setVisibilityFlags(mVisibilityFlags);
|
||||
NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
|
||||
setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha);
|
||||
|
||||
for(unsigned int j=0; j < objects.mEntities[i]->getNumSubEntities(); ++j)
|
||||
{
|
||||
Ogre::SubEntity* subEnt = objects.mEntities[i]->getSubEntity(j);
|
||||
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
}
|
||||
for(size_t i = 0;i < objects.mEntities.size();i++)
|
||||
objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
for(size_t i = 0;i < objects.mParticles.size();i++)
|
||||
{
|
||||
objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group));
|
||||
if(mVisibilityFlags != 0)
|
||||
objects.mParticles[i]->setVisibilityFlags(mVisibilityFlags);
|
||||
objects.mParticles[i]->setRenderQueueGroup(RQG_Alpha);
|
||||
}
|
||||
|
||||
if(objects.mSkelBase)
|
||||
{
|
||||
Ogre::AnimationStateSet *aset = objects.mSkelBase->getAllAnimationStates();
|
||||
|
@ -395,6 +398,7 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in
|
|||
while(boneiter.hasMoreElements())
|
||||
boneiter.getNext()->setManuallyControlled(true);
|
||||
}
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
|
@ -408,14 +412,16 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
mTimeToChange -= timepassed;
|
||||
|
||||
Ogre::Vector3 ret = Animation::runAnimation(timepassed);
|
||||
const Ogre::SkeletonInstance *skelsrc = mObjectList.mSkelBase->getSkeleton();
|
||||
|
||||
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
|
||||
for(size_t i = 0;i < sPartListSize;i++)
|
||||
{
|
||||
Ogre::Entity *ent = mObjectParts[i].mSkelBase;
|
||||
if(!ent) continue;
|
||||
updateSkeletonInstance(skelsrc, ent->getSkeleton());
|
||||
updateSkeletonInstance(baseinst, ent->getSkeleton());
|
||||
ent->getAllAnimationStates()->_notifyDirty();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "../mwbase/inputmanager.hpp" // FIXME
|
||||
#include "../mwbase/windowmanager.hpp" // FIXME
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
|
@ -315,13 +317,15 @@ void RenderingManager::update (float duration, bool paused)
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
|
||||
int blind = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
|
||||
mRendering.getFader()->setFactor(1.f-(blind / 100.f));
|
||||
|
||||
setAmbientMode();
|
||||
|
||||
// player position
|
||||
MWWorld::RefData &data =
|
||||
MWBase::Environment::get()
|
||||
.getWorld()
|
||||
->getPlayer()
|
||||
.getPlayer()
|
||||
.getRefData();
|
||||
MWWorld::RefData &data = player.getRefData();
|
||||
float *_playerPos = data.getPosition().pos;
|
||||
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
|
||||
|
||||
|
@ -597,8 +601,15 @@ void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
|
|||
|
||||
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
|
||||
{
|
||||
mRendering.getScene()->setAmbientLight(colour);
|
||||
mTerrainManager->setAmbient(colour);
|
||||
mAmbientColor = colour;
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
int nightEye = MWWorld::Class::get(player).getCreatureStats(player).getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::NightEye)).mMagnitude;
|
||||
Ogre::ColourValue final = colour;
|
||||
final += Ogre::ColourValue(0.7,0.7,0.7,0) * std::min(1.f, (nightEye/100.f));
|
||||
|
||||
mRendering.getScene()->setAmbientLight(final);
|
||||
mTerrainManager->setAmbient(final);
|
||||
}
|
||||
|
||||
void RenderingManager::sunEnable(bool real)
|
||||
|
|
|
@ -788,8 +788,8 @@ void VideoState::decode_thread_loop(VideoState *self)
|
|||
// main decode loop
|
||||
while(!self->quit)
|
||||
{
|
||||
if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) ||
|
||||
(self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE))
|
||||
if((self->audio_st && self->audioq.size > MAX_AUDIOQ_SIZE) ||
|
||||
(self->video_st && self->videoq.size > MAX_VIDEOQ_SIZE))
|
||||
{
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
|
||||
continue;
|
||||
|
|
|
@ -13,8 +13,8 @@ namespace ESM
|
|||
|
||||
namespace MWWorld
|
||||
{
|
||||
/// List all (Ogre-)handles.
|
||||
struct ListHandles
|
||||
/// List all (Ogre-)handles, then reset RefData::mBaseNode to 0.
|
||||
struct ListAndResetHandles
|
||||
{
|
||||
std::vector<Ogre::SceneNode*> mHandles;
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace MWWorld
|
|||
Ogre::SceneNode* handle = data.getBaseNode();
|
||||
if (handle)
|
||||
mHandles.push_back (handle);
|
||||
|
||||
data.setBaseNode(0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -193,59 +193,68 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std::
|
|||
{
|
||||
count = std::abs(count); /// \todo implement item restocking (indicated by negative count)
|
||||
|
||||
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
|
||||
|
||||
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
|
||||
try
|
||||
{
|
||||
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase;
|
||||
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
|
||||
ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id);
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel();
|
||||
|
||||
failChance += levItem->mChanceNone;
|
||||
|
||||
if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each)
|
||||
if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name())
|
||||
{
|
||||
for (int i=0; i<count; ++i)
|
||||
addInitialItem(id, owner, 1, failChance, false);
|
||||
return;
|
||||
}
|
||||
const ESM::ItemLevList* levItem = ref.getPtr().get<ESM::ItemLevList>()->mBase;
|
||||
const std::vector<ESM::LeveledListBase::LevelItem>& items = levItem->mList;
|
||||
|
||||
float random = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
if (random >= failChance/100.f)
|
||||
{
|
||||
std::vector<std::string> candidates;
|
||||
int highestLevel = 0;
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
int playerLevel = MWWorld::Class::get(player).getCreatureStats(player).getLevel();
|
||||
|
||||
failChance += levItem->mChanceNone;
|
||||
|
||||
if (topLevel && count > 1 && levItem->mFlags & ESM::ItemLevList::Each)
|
||||
{
|
||||
if (it->mLevel > highestLevel)
|
||||
highestLevel = it->mLevel;
|
||||
for (int i=0; i<count; ++i)
|
||||
addInitialItem(id, owner, 1, failChance, false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::pair<int, std::string> highest = std::make_pair(-1, "");
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
float random = static_cast<float> (std::rand()) / RAND_MAX;
|
||||
if (random >= failChance/100.f)
|
||||
{
|
||||
if (playerLevel >= it->mLevel
|
||||
&& (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel))
|
||||
std::vector<std::string> candidates;
|
||||
int highestLevel = 0;
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
candidates.push_back(it->mId);
|
||||
if (it->mLevel >= highest.first)
|
||||
highest = std::make_pair(it->mLevel, it->mId);
|
||||
if (it->mLevel > highestLevel)
|
||||
highestLevel = it->mLevel;
|
||||
}
|
||||
|
||||
std::pair<int, std::string> highest = std::make_pair(-1, "");
|
||||
for (std::vector<ESM::LeveledListBase::LevelItem>::const_iterator it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if (playerLevel >= it->mLevel
|
||||
&& (levItem->mFlags & ESM::ItemLevList::AllLevels || it->mLevel == highestLevel))
|
||||
{
|
||||
candidates.push_back(it->mId);
|
||||
if (it->mLevel >= highest.first)
|
||||
highest = std::make_pair(it->mLevel, it->mId);
|
||||
}
|
||||
|
||||
}
|
||||
if (!candidates.size())
|
||||
return;
|
||||
std::string item = candidates[std::rand()%candidates.size()];
|
||||
addInitialItem(item, owner, count, failChance, false);
|
||||
}
|
||||
if (!candidates.size())
|
||||
return;
|
||||
std::string item = candidates[std::rand()%candidates.size()];
|
||||
addInitialItem(item, owner, count, failChance, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref.getPtr().getRefData().setCount (count);
|
||||
ref.getPtr().getCellRef().mOwner = owner;
|
||||
addImp (ref.getPtr());
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (std::logic_error& e)
|
||||
{
|
||||
ref.getPtr().getRefData().setCount (count);
|
||||
ref.getPtr().getCellRef().mOwner = owner;
|
||||
addImp (ref.getPtr());
|
||||
// Vanilla doesn't fail on nonexistent items in levelled lists
|
||||
std::cerr << "Warning: ignoring nonexistent item '" << id << "'" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,27 +76,28 @@ namespace MWWorld
|
|||
void Scene::unloadCell (CellStoreCollection::iterator iter)
|
||||
{
|
||||
std::cout << "Unloading cell\n";
|
||||
ListHandles functor;
|
||||
ListAndResetHandles functor;
|
||||
|
||||
(*iter)->forEach<ListHandles>(functor);
|
||||
(*iter)->forEach<ListAndResetHandles>(functor);
|
||||
{
|
||||
// silence annoying g++ warning
|
||||
for (std::vector<Ogre::SceneNode*>::const_iterator iter2 (functor.mHandles.begin());
|
||||
iter2!=functor.mHandles.end(); ++iter2){
|
||||
Ogre::SceneNode* node = *iter2;
|
||||
iter2!=functor.mHandles.end(); ++iter2)
|
||||
{
|
||||
Ogre::SceneNode* node = *iter2;
|
||||
mPhysics->removeObject (node->getName());
|
||||
}
|
||||
}
|
||||
|
||||
if ((*iter)->mCell->isExterior())
|
||||
{
|
||||
ESM::Land* land =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Land>().search(
|
||||
(*iter)->mCell->getGridX(),
|
||||
(*iter)->mCell->getGridY()
|
||||
);
|
||||
if (land)
|
||||
mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() );
|
||||
}
|
||||
if ((*iter)->mCell->isExterior())
|
||||
{
|
||||
ESM::Land* land =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Land>().search(
|
||||
(*iter)->mCell->getGridX(),
|
||||
(*iter)->mCell->getGridY()
|
||||
);
|
||||
if (land)
|
||||
mPhysics->removeHeightField( (*iter)->mCell->getGridX(), (*iter)->mCell->getGridY() );
|
||||
}
|
||||
|
||||
mRendering.removeCell(*iter);
|
||||
|
|
|
@ -415,8 +415,18 @@ namespace MWWorld
|
|||
}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
||||
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt;
|
||||
struct DynamicExtCmp
|
||||
{
|
||||
bool operator()(const std::pair<int, int> &left, const std::pair<int, int> &right) const {
|
||||
if (left.first == right.first) {
|
||||
return left.second < right.second;
|
||||
}
|
||||
return left.first < right.first;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
||||
typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp> DynamicExt;
|
||||
|
||||
DynamicInt mInt;
|
||||
DynamicExt mExt;
|
||||
|
@ -465,7 +475,7 @@ namespace MWWorld
|
|||
cell.mData.mX = x, cell.mData.mY = y;
|
||||
|
||||
std::pair<int, int> key(x, y);
|
||||
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||
DynamicExt::const_iterator it = mExt.find(key);
|
||||
if (it != mExt.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
|
@ -483,7 +493,7 @@ namespace MWWorld
|
|||
cell.mData.mX = x, cell.mData.mY = y;
|
||||
|
||||
std::pair<int, int> key(x, y);
|
||||
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||
DynamicExt::const_iterator it = mExt.find(key);
|
||||
if (it != mExt.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
|
@ -524,7 +534,7 @@ namespace MWWorld
|
|||
|
||||
void setUp() {
|
||||
//typedef std::vector<ESM::Cell>::iterator Iterator;
|
||||
typedef std::map<std::pair<int, int>, ESM::Cell>::iterator ExtIterator;
|
||||
typedef DynamicExt::iterator ExtIterator;
|
||||
typedef std::map<std::string, ESM::Cell>::iterator IntIterator;
|
||||
|
||||
//std::sort(mInt.begin(), mInt.end(), RecordCmp());
|
||||
|
@ -862,7 +872,28 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
void setUp() {
|
||||
std::sort(mStatic.begin(), mStatic.end(), Compare());
|
||||
/// \note This method sorts indexed values for further
|
||||
/// searches. Every loaded item is present in storage, but
|
||||
/// latest loaded shadows any previous while searching.
|
||||
/// If memory cost will be too high, it is possible to remove
|
||||
/// unused values.
|
||||
|
||||
Compare cmp;
|
||||
|
||||
std::stable_sort(mStatic.begin(), mStatic.end(), cmp);
|
||||
|
||||
typename std::vector<T>::iterator first, next;
|
||||
next = first = mStatic.begin();
|
||||
|
||||
while (first != mStatic.end() && ++next != mStatic.end()) {
|
||||
while (next != mStatic.end() && !cmp(*first, *next)) {
|
||||
++next;
|
||||
}
|
||||
if (first != --next) {
|
||||
std::swap(*first, *next);
|
||||
}
|
||||
first = ++next;
|
||||
}
|
||||
}
|
||||
|
||||
const T *search(int index) const {
|
||||
|
|
|
@ -38,7 +38,9 @@ enum PartReferenceType
|
|||
PRT_RPauldron = 23,
|
||||
PRT_LPauldron = 24,
|
||||
PRT_Weapon = 25,
|
||||
PRT_Tail = 26
|
||||
PRT_Tail = 26,
|
||||
|
||||
PRT_Count = 27
|
||||
};
|
||||
|
||||
// Reference to body parts
|
||||
|
|
|
@ -9,13 +9,13 @@ namespace ESM
|
|||
void BodyPart::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNString("FNAM");
|
||||
mRace = esm.getHNString("FNAM");
|
||||
esm.getHNT(mData, "BYDT", 4);
|
||||
}
|
||||
void BodyPart::save(ESMWriter &esm)
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNCString("FNAM", mName);
|
||||
esm.writeHNCString("FNAM", mRace);
|
||||
esm.writeHNT("BYDT", mData, 4);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@ struct BodyPart
|
|||
MP_Knee = 11,
|
||||
MP_Upperleg = 12,
|
||||
MP_Clavicle = 13,
|
||||
MP_Tail = 14
|
||||
MP_Tail = 14,
|
||||
|
||||
MP_Count = 15
|
||||
};
|
||||
|
||||
enum Flags
|
||||
|
@ -52,7 +54,7 @@ struct BodyPart
|
|||
};
|
||||
|
||||
BYDTstruct mData;
|
||||
std::string mId, mModel, mName;
|
||||
std::string mId, mModel, mRace;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm);
|
||||
|
|
|
@ -303,5 +303,28 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class NiFlipController : public Controller
|
||||
{
|
||||
public:
|
||||
int mTexSlot;
|
||||
float mDelta; // Time between two flips. delta = (start_time - stop_time) / num_sources
|
||||
NiSourceTextureList mSources;
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
Controller::read(nif);
|
||||
mTexSlot = nif->getUInt();
|
||||
/*unknown=*/nif->getUInt();/*0?*/
|
||||
mDelta = nif->getFloat();
|
||||
mSources.read(nif);
|
||||
}
|
||||
|
||||
void post(NIFFile *nif)
|
||||
{
|
||||
Controller::post(nif);
|
||||
mSources.post(nif);
|
||||
}
|
||||
};
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -208,7 +208,7 @@ static const RecordFactoryEntry recordFactories [] = {
|
|||
|
||||
{ "NiNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "AvoidNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "NiBSParticleNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
|
||||
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
|
||||
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode },
|
||||
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
|
||||
|
@ -235,6 +235,7 @@ static const RecordFactoryEntry recordFactories [] = {
|
|||
{ "NiMaterialColorController", &construct <NiMaterialColorController >, RC_NiMaterialColorController },
|
||||
{ "NiBSPArrayController", &construct <NiBSPArrayController >, RC_NiBSPArrayController },
|
||||
{ "NiParticleSystemController", &construct <NiParticleSystemController >, RC_NiParticleSystemController },
|
||||
{ "NiFlipController", &construct <NiFlipController >, RC_NiFlipController },
|
||||
{ "NiAmbientLight", &construct <NiLight >, RC_NiLight },
|
||||
{ "NiDirectionalLight", &construct <NiLight >, RC_NiLight },
|
||||
{ "NiTextureEffect", &construct <NiTextureEffect >, RC_NiTextureEffect },
|
||||
|
|
|
@ -128,13 +128,17 @@ struct NiNode : Node
|
|||
NodeList children;
|
||||
NodeList effects;
|
||||
|
||||
/* Known NiNode flags:
|
||||
0x01 hidden
|
||||
0x02 use mesh for collision
|
||||
0x04 use bounding box for collision (?)
|
||||
0x08 unknown, but common
|
||||
0x20, 0x40, 0x80 unknown
|
||||
*/
|
||||
enum Flags {
|
||||
Flag_Hidden = 0x0001,
|
||||
Flag_MeshCollision = 0x0002,
|
||||
Flag_BBoxCollision = 0x0004
|
||||
};
|
||||
enum BSAnimFlags {
|
||||
AnimFlag_AutoPlay = 0x0020
|
||||
};
|
||||
enum BSParticleFlags {
|
||||
ParticleFlag_AutoPlay = 0x0020
|
||||
};
|
||||
|
||||
void read(NIFStream *nif)
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@ enum RecordType
|
|||
RC_NiTriShape,
|
||||
RC_NiRotatingParticles,
|
||||
RC_NiAutoNormalParticles,
|
||||
RC_NiBSParticleNode,
|
||||
RC_NiCamera,
|
||||
RC_NiTexturingProperty,
|
||||
RC_NiMaterialProperty,
|
||||
|
@ -59,6 +60,7 @@ enum RecordType
|
|||
RC_NiMaterialColorController,
|
||||
RC_NiBSPArrayController,
|
||||
RC_NiParticleSystemController,
|
||||
RC_NiFlipController,
|
||||
RC_NiBSAnimationNode,
|
||||
RC_NiLight,
|
||||
RC_NiTextureEffect,
|
||||
|
|
|
@ -163,6 +163,7 @@ typedef RecordPtrT<NiAutoNormalParticlesData> NiAutoNormalParticlesDataPtr;
|
|||
|
||||
typedef RecordListT<Node> NodeList;
|
||||
typedef RecordListT<Property> PropertyList;
|
||||
typedef RecordListT<NiSourceTexture> NiSourceTextureList;
|
||||
|
||||
} // Namespace
|
||||
#endif
|
||||
|
|
|
@ -255,9 +255,9 @@ void ManualBulletShapeLoader::handleNiTriShape(btTriangleMesh* mesh, const Nif::
|
|||
assert(shape != NULL);
|
||||
|
||||
// Interpret flags
|
||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
||||
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
|
||||
bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0;
|
||||
bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0;
|
||||
bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0;
|
||||
|
||||
// If the object was marked "NCO" earlier, it shouldn't collide with
|
||||
// anything. So don't do anything.
|
||||
|
|
|
@ -109,13 +109,26 @@ NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
|
|||
|
||||
void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape)
|
||||
{
|
||||
Ogre::SkeletonPtr skel;
|
||||
const Nif::NiTriShapeData *data = shape->data.getPtr();
|
||||
const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
|
||||
std::vector<Ogre::Vector3> srcVerts = data->vertices;
|
||||
std::vector<Ogre::Vector3> srcNorms = data->normals;
|
||||
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
|
||||
bool vertShadowBuffer = false;
|
||||
|
||||
if(!shape->controller.empty())
|
||||
{
|
||||
Nif::ControllerPtr ctrl = shape->controller;
|
||||
do {
|
||||
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||
{
|
||||
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
|
||||
vertShadowBuffer = true;
|
||||
break;
|
||||
}
|
||||
} while(!(ctrl=ctrl->next).empty());
|
||||
}
|
||||
|
||||
if(skin != NULL)
|
||||
{
|
||||
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
|
||||
|
@ -125,10 +138,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
// explicitly attached later.
|
||||
mesh->setSkeletonName(mName);
|
||||
|
||||
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
|
||||
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
|
||||
skel = skelMgr->getByName(mName);
|
||||
|
||||
// Convert vertices and normals to bone space from bind position. It would be
|
||||
// better to transform the bones into bind position, but there doesn't seem to
|
||||
// be a reliable way to do that.
|
||||
|
@ -139,11 +148,10 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
const Nif::NodeList &bones = skin->bones;
|
||||
for(size_t b = 0;b < bones.length();b++)
|
||||
{
|
||||
Ogre::Bone *bone = skel->getBone(bones[b]->name);
|
||||
Ogre::Matrix4 mat;
|
||||
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
|
||||
Ogre::Quaternion(data->bones[b].trafo.rotation));
|
||||
mat = bone->_getFullTransform() * mat;
|
||||
mat = bones[b]->getWorldTransform() * mat;
|
||||
|
||||
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
|
||||
for(size_t i = 0;i < weights.size();i++)
|
||||
|
@ -260,13 +268,24 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
|
||||
// Texture UV coordinates
|
||||
size_t numUVs = data->uvlist.size();
|
||||
for(size_t i = 0;i < numUVs;i++)
|
||||
if (numUVs)
|
||||
{
|
||||
vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2),
|
||||
srcVerts.size(), Ogre::HardwareBuffer::HBU_STATIC);
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), &data->uvlist[i][0], true);
|
||||
size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
|
||||
|
||||
for(size_t i = 0; i < numUVs; i++)
|
||||
decl->addElement(nextBuf, elemSize*i, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
|
||||
|
||||
vbuf = hwBufMgr->createVertexBuffer(decl->getVertexSize(nextBuf), srcVerts.size(),
|
||||
Ogre::HardwareBuffer::HBU_STATIC);
|
||||
|
||||
std::vector<Ogre::Vector2> allUVs;
|
||||
allUVs.reserve(srcVerts.size()*numUVs);
|
||||
for (size_t vert = 0; vert<srcVerts.size(); ++vert)
|
||||
for(size_t i = 0; i < numUVs; i++)
|
||||
allUVs.push_back(data->uvlist[i][vert]);
|
||||
|
||||
vbuf->writeData(0, elemSize*srcVerts.size()*numUVs, &allUVs[0], true);
|
||||
|
||||
decl->addElement(nextBuf, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, i);
|
||||
bind->setBinding(nextBuf++, vbuf);
|
||||
}
|
||||
|
||||
|
@ -285,6 +304,8 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
|
|||
// Assign bone weights for this TriShape
|
||||
if(skin != NULL)
|
||||
{
|
||||
Ogre::SkeletonPtr skel = Ogre::SkeletonManager::getSingleton().getByName(mName);
|
||||
|
||||
const Nif::NiSkinData *data = skin->data.getPtr();
|
||||
const Nif::NodeList &bones = skin->bones;
|
||||
for(size_t i = 0;i < bones.length();i++)
|
||||
|
|
|
@ -44,15 +44,6 @@
|
|||
#include "material.hpp"
|
||||
#include "mesh.hpp"
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
// TODO: Do something useful
|
||||
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
|
||||
{ return o; }
|
||||
|
||||
}
|
||||
|
||||
namespace NifOgre
|
||||
{
|
||||
|
||||
|
@ -74,11 +65,7 @@ public:
|
|||
, mStopTime(ctrl->timeStop)
|
||||
{
|
||||
if(mDeltaInput)
|
||||
{
|
||||
mDeltaCount = mPhase;
|
||||
while(mDeltaCount < mStartTime)
|
||||
mDeltaCount += (mStopTime-mStartTime);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ogre::Real calculate(Ogre::Real value)
|
||||
|
@ -86,6 +73,9 @@ public:
|
|||
if(mDeltaInput)
|
||||
{
|
||||
mDeltaCount += value*mFrequency;
|
||||
if(mDeltaCount < mStartTime)
|
||||
mDeltaCount = mStopTime - std::fmod(mStartTime - mDeltaCount,
|
||||
mStopTime - mStartTime);
|
||||
mDeltaCount = std::fmod(mDeltaCount - mStartTime,
|
||||
mStopTime - mStartTime) + mStartTime;
|
||||
return mDeltaCount;
|
||||
|
@ -104,7 +94,7 @@ public:
|
|||
private:
|
||||
std::vector<Nif::NiVisData::VisData> mData;
|
||||
|
||||
virtual bool calculate(Ogre::Real time)
|
||||
bool calculate(Ogre::Real time) const
|
||||
{
|
||||
if(mData.size() == 0)
|
||||
return true;
|
||||
|
@ -144,10 +134,19 @@ public:
|
|||
, mData(data->mVis)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(float time) const
|
||||
{ return Ogre::Quaternion(); }
|
||||
|
||||
virtual Ogre::Vector3 getTranslation(float time) const
|
||||
{ return Ogre::Vector3(0.0f); }
|
||||
|
||||
virtual Ogre::Vector3 getScale(float time) const
|
||||
{ return Ogre::Vector3(1.0f); }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
return 1.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue(Ogre::Real time)
|
||||
|
@ -170,6 +169,60 @@ public:
|
|||
Nif::Vector3KeyList mTranslations;
|
||||
Nif::FloatKeyList mScales;
|
||||
|
||||
static float interpKey(const Nif::FloatKeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return last->mValue + ((iter->mValue - last->mValue)*a);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
static Ogre::Vector3 interpKey(const Nif::Vector3KeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return last->mValue + ((iter->mValue - last->mValue)*a);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
static Ogre::Quaternion interpKey(const Nif::QuaternionKeyList::VecType &keys, float time)
|
||||
{
|
||||
if(time <= keys.front().mTime)
|
||||
return keys.front().mValue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator iter(keys.begin()+1);
|
||||
for(;iter != keys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
return Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue);
|
||||
}
|
||||
return keys.back().mValue;
|
||||
}
|
||||
|
||||
public:
|
||||
Value(Ogre::Node *target, const Nif::NiKeyframeData *data)
|
||||
: NodeTargetValue<Ogre::Real>(target)
|
||||
|
@ -178,6 +231,27 @@ public:
|
|||
, mScales(data->mScales)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(float time) const
|
||||
{
|
||||
if(mRotations.mKeys.size() > 0)
|
||||
return interpKey(mRotations.mKeys, time);
|
||||
return mNode->getOrientation();
|
||||
}
|
||||
|
||||
virtual Ogre::Vector3 getTranslation(float time) const
|
||||
{
|
||||
if(mTranslations.mKeys.size() > 0)
|
||||
return interpKey(mTranslations.mKeys, time);
|
||||
return mNode->getPosition();
|
||||
}
|
||||
|
||||
virtual Ogre::Vector3 getScale(float time) const
|
||||
{
|
||||
if(mScales.mKeys.size() > 0)
|
||||
return Ogre::Vector3(interpKey(mScales.mKeys, time));
|
||||
return mNode->getScale();
|
||||
}
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
|
@ -187,68 +261,11 @@ public:
|
|||
virtual void setValue(Ogre::Real time)
|
||||
{
|
||||
if(mRotations.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mRotations.mKeys.front().mTime)
|
||||
mNode->setOrientation(mRotations.mKeys.front().mValue);
|
||||
else if(time >= mRotations.mKeys.back().mTime)
|
||||
mNode->setOrientation(mRotations.mKeys.back().mValue);
|
||||
else
|
||||
{
|
||||
Nif::QuaternionKeyList::VecType::const_iterator iter(mRotations.mKeys.begin()+1);
|
||||
for(;iter != mRotations.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setOrientation(Ogre::Quaternion::nlerp(a, last->mValue, iter->mValue));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setOrientation(interpKey(mRotations.mKeys, time));
|
||||
if(mTranslations.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mTranslations.mKeys.front().mTime)
|
||||
mNode->setPosition(mTranslations.mKeys.front().mValue);
|
||||
else if(time >= mTranslations.mKeys.back().mTime)
|
||||
mNode->setPosition(mTranslations.mKeys.back().mValue);
|
||||
else
|
||||
{
|
||||
Nif::Vector3KeyList::VecType::const_iterator iter(mTranslations.mKeys.begin()+1);
|
||||
for(;iter != mTranslations.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::Vector3KeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setPosition(last->mValue + ((iter->mValue - last->mValue)*a));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setPosition(interpKey(mTranslations.mKeys, time));
|
||||
if(mScales.mKeys.size() > 0)
|
||||
{
|
||||
if(time <= mScales.mKeys.front().mTime)
|
||||
mNode->setScale(Ogre::Vector3(mScales.mKeys.front().mValue));
|
||||
else if(time >= mScales.mKeys.back().mTime)
|
||||
mNode->setScale(Ogre::Vector3(mScales.mKeys.back().mValue));
|
||||
else
|
||||
{
|
||||
Nif::FloatKeyList::VecType::const_iterator iter(mScales.mKeys.begin()+1);
|
||||
for(;iter != mScales.mKeys.end();iter++)
|
||||
{
|
||||
if(iter->mTime < time)
|
||||
continue;
|
||||
|
||||
Nif::FloatKeyList::VecType::const_iterator last(iter-1);
|
||||
float a = (time-last->mTime) / (iter->mTime-last->mTime);
|
||||
mNode->setScale(Ogre::Vector3(last->mValue + ((iter->mValue - last->mValue)*a)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mNode->setScale(Ogre::Vector3(interpKey(mScales.mKeys, time)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -289,7 +306,7 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
Value(const Ogre::MaterialPtr &material, Nif::NiUVData *data)
|
||||
Value(const Ogre::MaterialPtr &material, const Nif::NiUVData *data)
|
||||
: mMaterial(material)
|
||||
, mUTrans(data->mKeyList[0])
|
||||
, mVTrans(data->mKeyList[1])
|
||||
|
@ -329,11 +346,70 @@ public:
|
|||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
class ParticleSystemController
|
||||
{
|
||||
public:
|
||||
class Value : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Ogre::ParticleSystem *mParticleSys;
|
||||
float mEmitStart;
|
||||
float mEmitStop;
|
||||
|
||||
public:
|
||||
Value(Ogre::ParticleSystem *psys, const Nif::NiParticleSystemController *pctrl)
|
||||
: mParticleSys(psys)
|
||||
, mEmitStart(pctrl->startTime)
|
||||
, mEmitStop(pctrl->stopTime)
|
||||
{
|
||||
}
|
||||
|
||||
Ogre::Real getValue() const
|
||||
{ return 0.0f; }
|
||||
|
||||
void setValue(Ogre::Real value)
|
||||
{
|
||||
mParticleSys->setEmitting(value >= mEmitStart && value < mEmitStop);
|
||||
}
|
||||
};
|
||||
|
||||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
class GeomMorpherController
|
||||
{
|
||||
public:
|
||||
class Value : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
Ogre::SubEntity *mSubEntity;
|
||||
std::vector<Nif::NiMorphData::MorphData> mMorphs;
|
||||
|
||||
public:
|
||||
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data)
|
||||
: mSubEntity(subent)
|
||||
, mMorphs(data->mMorphs)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Real getValue() const
|
||||
{
|
||||
// Should not be called
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{
|
||||
// TODO: Implement
|
||||
}
|
||||
};
|
||||
|
||||
typedef DefaultFunction Function;
|
||||
};
|
||||
|
||||
|
||||
/** Manual resource loader for NIF objects (meshes, particle systems, etc).
|
||||
* This is the main class responsible for translating the internal NIF
|
||||
* structures into something Ogre can use.
|
||||
/** Object creator for NIFs. This is the main class responsible for creating
|
||||
* "live" Ogre objects (entities, particle systems, controllers, etc) from
|
||||
* their NIF equivalents.
|
||||
*/
|
||||
class NIFObjectLoader
|
||||
{
|
||||
|
@ -349,14 +425,79 @@ class NIFObjectLoader
|
|||
}
|
||||
|
||||
|
||||
static void createEntity(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
|
||||
const Nif::Node *node, int flags, int animflags)
|
||||
{
|
||||
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
|
||||
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
|
||||
if(shape->name.length() > 0)
|
||||
fullname += "@shape="+shape->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
|
||||
if(meshMgr.getByName(fullname).isNull())
|
||||
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
|
||||
|
||||
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
|
||||
entity->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
|
||||
|
||||
objectlist.mEntities.push_back(entity);
|
||||
if(objectlist.mSkelBase)
|
||||
{
|
||||
if(entity->hasSkeleton())
|
||||
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
|
||||
else
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiUVController)
|
||||
{
|
||||
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
|
||||
|
||||
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiGeomMorpherController)
|
||||
{
|
||||
const Nif::NiGeomMorpherController *geom = static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
|
||||
|
||||
Ogre::SubEntity *subent = entity->getSubEntity(0);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(subent, geom->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void createParticleEmitterAffectors(Ogre::ParticleSystem *partsys, const Nif::NiParticleSystemController *partctrl)
|
||||
{
|
||||
Ogre::ParticleEmitter *emitter = partsys->addEmitter("Nif");
|
||||
emitter->setParticleVelocity(partctrl->velocity-partctrl->velocityRandom,
|
||||
partctrl->velocity+partctrl->velocityRandom);
|
||||
emitter->setParticleVelocity(partctrl->velocity - partctrl->velocityRandom*0.5f,
|
||||
partctrl->velocity + partctrl->velocityRandom*0.5f);
|
||||
emitter->setEmissionRate(partctrl->emitRate);
|
||||
emitter->setTimeToLive(partctrl->lifetime-partctrl->lifetimeRandom,
|
||||
partctrl->lifetime+partctrl->lifetimeRandom);
|
||||
emitter->setTimeToLive(partctrl->lifetime - partctrl->lifetimeRandom*0.5f,
|
||||
partctrl->lifetime + partctrl->lifetimeRandom*0.5f);
|
||||
emitter->setParameter("width", Ogre::StringConverter::toString(partctrl->offsetRandom.x));
|
||||
emitter->setParameter("height", Ogre::StringConverter::toString(partctrl->offsetRandom.y));
|
||||
emitter->setParameter("depth", Ogre::StringConverter::toString(partctrl->offsetRandom.z));
|
||||
|
@ -416,9 +557,9 @@ class NIFObjectLoader
|
|||
}
|
||||
}
|
||||
|
||||
static Ogre::ParticleSystem *createParticleSystem(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, Ogre::Entity *entitybase,
|
||||
const Nif::Node *partnode)
|
||||
static void createParticleSystem(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, ObjectList &objectlist,
|
||||
const Nif::Node *partnode, int flags, int partflags)
|
||||
{
|
||||
const Nif::NiAutoNormalParticlesData *particledata = NULL;
|
||||
if(partnode->recType == Nif::RC_NiAutoNormalParticles)
|
||||
|
@ -426,73 +567,139 @@ class NIFObjectLoader
|
|||
else if(partnode->recType == Nif::RC_NiRotatingParticles)
|
||||
particledata = static_cast<const Nif::NiRotatingParticles*>(partnode)->data.getPtr();
|
||||
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
|
||||
if(partnode->name.length() > 0)
|
||||
fullname += "@type="+partnode->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::ParticleSystem *partsys = sceneMgr->createParticleSystem();
|
||||
try {
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(partnode->recIndex);
|
||||
if(partnode->name.length() > 0)
|
||||
fullname += "@type="+partnode->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
const Nif::NiTexturingProperty *texprop = NULL;
|
||||
const Nif::NiMaterialProperty *matprop = NULL;
|
||||
const Nif::NiAlphaProperty *alphaprop = NULL;
|
||||
const Nif::NiVertexColorProperty *vertprop = NULL;
|
||||
const Nif::NiZBufferProperty *zprop = NULL;
|
||||
const Nif::NiSpecularProperty *specprop = NULL;
|
||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||
bool needTangents = false;
|
||||
const Nif::NiTexturingProperty *texprop = NULL;
|
||||
const Nif::NiMaterialProperty *matprop = NULL;
|
||||
const Nif::NiAlphaProperty *alphaprop = NULL;
|
||||
const Nif::NiVertexColorProperty *vertprop = NULL;
|
||||
const Nif::NiZBufferProperty *zprop = NULL;
|
||||
const Nif::NiSpecularProperty *specprop = NULL;
|
||||
const Nif::NiWireframeProperty *wireprop = NULL;
|
||||
bool needTangents = false;
|
||||
|
||||
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, needTangents));
|
||||
partnode->getProperties(texprop, matprop, alphaprop, vertprop, zprop, specprop, wireprop);
|
||||
partsys->setMaterialName(NIFMaterialLoader::getMaterial(particledata, fullname, group,
|
||||
texprop, matprop, alphaprop,
|
||||
vertprop, zprop, specprop,
|
||||
wireprop, needTangents));
|
||||
|
||||
partsys->setDefaultDimensions(particledata->particleRadius*2.0f,
|
||||
particledata->particleRadius*2.0f);
|
||||
partsys->setCullIndividually(false);
|
||||
partsys->setParticleQuota(particledata->numParticles);
|
||||
// TODO: There is probably a field or flag to specify this, as some
|
||||
// particle effects have it and some don't.
|
||||
partsys->setKeepParticlesInLocalSpace(true);
|
||||
partsys->setDefaultDimensions(particledata->particleRadius*2.0f,
|
||||
particledata->particleRadius*2.0f);
|
||||
partsys->setCullIndividually(false);
|
||||
partsys->setParticleQuota(particledata->numParticles);
|
||||
// TODO: There is probably a field or flag to specify this, as some
|
||||
// particle effects have it and some don't.
|
||||
partsys->setKeepParticlesInLocalSpace(true);
|
||||
|
||||
Nif::ControllerPtr ctrl = partnode->controller;
|
||||
while(!ctrl.empty())
|
||||
Nif::ControllerPtr ctrl = partnode->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiParticleSystemController)
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiParticleSystemController)
|
||||
const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr());
|
||||
|
||||
createParticleEmitterAffectors(partsys, partctrl);
|
||||
if(!partctrl->emitter.empty() && !partsys->isAttached())
|
||||
{
|
||||
const Nif::NiParticleSystemController *partctrl = static_cast<const Nif::NiParticleSystemController*>(ctrl.getPtr());
|
||||
|
||||
createParticleEmitterAffectors(partsys, partctrl);
|
||||
if(!partctrl->emitter.empty() && !partsys->isAttached())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
|
||||
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
|
||||
entitybase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
}
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
if(!partsys->isAttached())
|
||||
Ogre::ControllerValueRealPtr srcval((partflags&Nif::NiNode::ParticleFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW ParticleSystemController::Value(partsys, partctrl));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW ParticleSystemController::Function(partctrl, (partflags&Nif::NiNode::ParticleFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
if(!partsys->isAttached())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
}
|
||||
|
||||
partsys->setVisible(!(flags&Nif::NiNode::Flag_Hidden));
|
||||
objectlist.mParticles.push_back(partsys);
|
||||
}
|
||||
|
||||
|
||||
static void createNodeControllers(const std::string &name, Nif::ControllerPtr ctrl, ObjectList &objectlist, int animflags)
|
||||
{
|
||||
do {
|
||||
if(ctrl->recType == Nif::RC_NiVisController)
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
|
||||
Ogre::Bone *trgtbone = entitybase->getSkeleton()->getBone(trgtid);
|
||||
entitybase->attachObjectToBone(trgtbone->getName(), partsys);
|
||||
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
{
|
||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
if(!key->data.empty())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
|
||||
Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
|
||||
Ogre::ControllerValueRealPtr());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, (animflags&Nif::NiNode::AnimFlag_AutoPlay)));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
} while(!ctrl.empty());
|
||||
}
|
||||
|
||||
|
||||
static void extractTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap &textkeys)
|
||||
{
|
||||
for(size_t i = 0;i < tk->list.size();i++)
|
||||
{
|
||||
const std::string &str = tk->list[i].text;
|
||||
std::string::size_type pos = 0;
|
||||
while(pos < str.length())
|
||||
{
|
||||
if(::isspace(str[pos]))
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
|
||||
std::string result = str.substr(pos, nextpos-pos);
|
||||
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
|
||||
|
||||
pos = nextpos;
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< "Particles exception: "<<e.what() <<std::endl;
|
||||
sceneMgr->destroyParticleSystem(partsys);
|
||||
partsys = NULL;
|
||||
};
|
||||
return partsys;
|
||||
}
|
||||
|
||||
|
||||
static void createObjects(const std::string &name, const std::string &group,
|
||||
Ogre::SceneManager *sceneMgr, const Nif::Node *node,
|
||||
ObjectList &objectlist, int flags)
|
||||
ObjectList &objectlist, int flags, int animflags, int partflags)
|
||||
{
|
||||
// Do not create objects for the collision shape (includes all children)
|
||||
if(node->recType == Nif::RC_RootCollisionNode)
|
||||
|
@ -503,12 +710,24 @@ class NIFObjectLoader
|
|||
if (node->name.find("marker") != std::string::npos)
|
||||
return;
|
||||
|
||||
flags |= node->flags;
|
||||
if(node->recType == Nif::RC_NiBSAnimationNode)
|
||||
animflags |= node->flags;
|
||||
else if(node->recType == Nif::RC_NiBSParticleNode)
|
||||
partflags |= node->flags;
|
||||
else
|
||||
flags |= node->flags;
|
||||
|
||||
Nif::ExtraPtr e = node->extra;
|
||||
while(!e.empty())
|
||||
{
|
||||
if(e->recType == Nif::RC_NiStringExtraData)
|
||||
if(e->recType == Nif::RC_NiTextKeyExtraData)
|
||||
{
|
||||
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
|
||||
extractTextKeys(tk, objectlist.mTextKeys[trgtid]);
|
||||
}
|
||||
else if(e->recType == Nif::RC_NiStringExtraData)
|
||||
{
|
||||
const Nif::NiStringExtraData *sd = static_cast<const Nif::NiStringExtraData*>(e.getPtr());
|
||||
// String markers may contain important information
|
||||
|
@ -520,9 +739,13 @@ class NIFObjectLoader
|
|||
flags |= 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
e = e->extra;
|
||||
}
|
||||
|
||||
if(!node->controller.empty())
|
||||
createNodeControllers(name, node->controller, objectlist, animflags);
|
||||
|
||||
if(node->recType == Nif::RC_NiCamera)
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
|
||||
|
@ -530,94 +753,15 @@ class NIFObjectLoader
|
|||
objectlist.mCameras.push_back(trgtbone);
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiVisController)
|
||||
{
|
||||
const Nif::NiVisController *vis = static_cast<const Nif::NiVisController*>(ctrl.getPtr());
|
||||
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW VisController::Value(trgtbone, vis->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW VisController::Function(vis, false));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
else if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
{
|
||||
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
|
||||
if(!key->data.empty())
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, ctrl->target->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
Ogre::ControllerValueRealPtr srcval; /* Filled in later */
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
if(node->recType == Nif::RC_NiTriShape && !(flags&0x80000000))
|
||||
{
|
||||
const Nif::NiTriShape *shape = static_cast<const Nif::NiTriShape*>(node);
|
||||
|
||||
std::string fullname = name+"@index="+Ogre::StringConverter::toString(shape->recIndex);
|
||||
if(shape->name.length() > 0)
|
||||
fullname += "@shape="+shape->name;
|
||||
Misc::StringUtils::toLower(fullname);
|
||||
|
||||
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
|
||||
if(meshMgr.getByName(fullname).isNull())
|
||||
NIFMeshLoader::createMesh(name, fullname, group, shape->recIndex);
|
||||
|
||||
Ogre::Entity *entity = sceneMgr->createEntity(fullname);
|
||||
entity->setVisible(!(flags&0x01));
|
||||
|
||||
objectlist.mEntities.push_back(entity);
|
||||
if(objectlist.mSkelBase)
|
||||
{
|
||||
if(entity->hasSkeleton())
|
||||
entity->shareSkeletonInstanceWith(objectlist.mSkelBase);
|
||||
else
|
||||
{
|
||||
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
|
||||
Ogre::Bone *trgtbone = objectlist.mSkelBase->getSkeleton()->getBone(trgtid);
|
||||
objectlist.mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiUVController)
|
||||
{
|
||||
const Nif::NiUVController *uv = static_cast<const Nif::NiUVController*>(ctrl.getPtr());
|
||||
|
||||
const Ogre::MaterialPtr &material = entity->getSubEntity(0)->getMaterial();
|
||||
Ogre::ControllerValueRealPtr srcval(Ogre::ControllerManager::getSingleton().getFrameTimeSource());
|
||||
Ogre::ControllerValueRealPtr dstval(OGRE_NEW UVController::Value(material, uv->data.getPtr()));
|
||||
Ogre::ControllerFunctionRealPtr func(OGRE_NEW UVController::Function(uv, true));
|
||||
|
||||
objectlist.mControllers.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
|
||||
}
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
createEntity(name, group, sceneMgr, objectlist, node, flags, animflags);
|
||||
}
|
||||
|
||||
if((node->recType == Nif::RC_NiAutoNormalParticles ||
|
||||
node->recType == Nif::RC_NiRotatingParticles) && !(flags&0x40000000))
|
||||
{
|
||||
Ogre::ParticleSystem *partsys = createParticleSystem(name, group, sceneMgr, objectlist.mSkelBase, node);
|
||||
if(partsys != NULL)
|
||||
{
|
||||
partsys->setVisible(!(flags&0x01));
|
||||
objectlist.mParticles.push_back(partsys);
|
||||
}
|
||||
createParticleSystem(name, group, sceneMgr, objectlist, node, flags, partflags);
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
|
@ -627,7 +771,7 @@ class NIFObjectLoader
|
|||
for(size_t i = 0;i < children.length();i++)
|
||||
{
|
||||
if(!children[i].empty())
|
||||
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags);
|
||||
createObjects(name, group, sceneMgr, children[i].getPtr(), objectlist, flags, animflags, partflags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +818,7 @@ public:
|
|||
// Create a base skeleton entity if this NIF needs one
|
||||
createSkelBase(name, group, sceneMgr, node, objectlist);
|
||||
}
|
||||
createObjects(name, group, sceneMgr, node, objectlist, flags);
|
||||
createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -757,12 +901,15 @@ ObjectList Loader::createObjects(Ogre::Entity *parent, const std::string &bonena
|
|||
}
|
||||
|
||||
|
||||
ObjectList Loader::createObjectBase(Ogre::SceneManager *sceneMgr, std::string name, const std::string &group)
|
||||
ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
|
||||
{
|
||||
ObjectList objectlist;
|
||||
|
||||
Misc::StringUtils::toLower(name);
|
||||
NIFObjectLoader::load(sceneMgr, objectlist, name, group, 0xC0000000);
|
||||
NIFObjectLoader::load(parentNode->getCreator(), objectlist, name, group, 0xC0000000);
|
||||
|
||||
if(objectlist.mSkelBase)
|
||||
parentNode->attachObject(objectlist.mSkelBase);
|
||||
|
||||
return objectlist;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ struct ObjectList {
|
|||
// bones in the mSkelBase which are NiCamera nodes.
|
||||
std::vector<Ogre::Bone*> mCameras;
|
||||
|
||||
std::map<int,TextKeyMap> mTextKeys;
|
||||
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mControllers;
|
||||
|
||||
ObjectList() : mSkelBase(0)
|
||||
|
@ -69,7 +71,7 @@ public:
|
|||
std::string name,
|
||||
const std::string &group="General");
|
||||
|
||||
static ObjectList createObjectBase(Ogre::SceneManager *sceneMgr,
|
||||
static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
|
||||
std::string name,
|
||||
const std::string &group="General");
|
||||
};
|
||||
|
@ -85,6 +87,10 @@ public:
|
|||
NodeTargetValue(Ogre::Node *target) : mNode(target)
|
||||
{ }
|
||||
|
||||
virtual Ogre::Quaternion getRotation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getTranslation(T value) const = 0;
|
||||
virtual Ogre::Vector3 getScale(T value) const = 0;
|
||||
|
||||
void setNode(Ogre::Node *target)
|
||||
{ mNode = target; }
|
||||
Ogre::Node *getNode() const
|
||||
|
@ -94,14 +100,4 @@ typedef Ogre::SharedPtr<NodeTargetValue<Ogre::Real> > NodeTargetValueRealPtr;
|
|||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
// These operators allow extra data types to be stored in an Ogre::Any
|
||||
// object, which can then be stored in user object bindings on the nodes
|
||||
|
||||
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,142 +11,7 @@
|
|||
namespace NifOgre
|
||||
{
|
||||
|
||||
void NIFSkeletonLoader::buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime)
|
||||
{
|
||||
Ogre::Animation *anim = skel->createAnimation(name, stopTime);
|
||||
|
||||
for(size_t i = 0;i < ctrls.size();i++)
|
||||
{
|
||||
const Nif::NiKeyframeController *kfc = ctrls[i];
|
||||
if(kfc->data.empty())
|
||||
continue;
|
||||
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
|
||||
|
||||
/* Get the keyframes and make sure they're sorted first to last */
|
||||
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
|
||||
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
|
||||
const Nif::FloatKeyList &scalekeys = kf->mScales;
|
||||
|
||||
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
|
||||
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
|
||||
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
|
||||
|
||||
Ogre::Bone *bone = skel->getBone(targets[i]);
|
||||
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
|
||||
// the bone ID
|
||||
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
|
||||
anim->getNodeTrack(bone->getHandle()) :
|
||||
anim->createNodeTrack(bone->getHandle(), bone);
|
||||
|
||||
Ogre::Quaternion lastquat, curquat;
|
||||
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
|
||||
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
|
||||
if(quatiter != quatkeys.mKeys.end())
|
||||
lastquat = curquat = quatiter->mValue;
|
||||
if(traniter != trankeys.mKeys.end())
|
||||
lasttrans = curtrans = traniter->mValue;
|
||||
if(scaleiter != scalekeys.mKeys.end())
|
||||
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
|
||||
|
||||
bool didlast = false;
|
||||
while(!didlast)
|
||||
{
|
||||
float curtime = std::numeric_limits<float>::max();
|
||||
|
||||
//Get latest time
|
||||
if(quatiter != quatkeys.mKeys.end())
|
||||
curtime = std::min(curtime, quatiter->mTime);
|
||||
if(traniter != trankeys.mKeys.end())
|
||||
curtime = std::min(curtime, traniter->mTime);
|
||||
if(scaleiter != scalekeys.mKeys.end())
|
||||
curtime = std::min(curtime, scaleiter->mTime);
|
||||
|
||||
curtime = std::max(curtime, startTime);
|
||||
if(curtime >= stopTime)
|
||||
{
|
||||
didlast = true;
|
||||
curtime = stopTime;
|
||||
}
|
||||
|
||||
// Get the latest quaternions, translations, and scales for the
|
||||
// current time
|
||||
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
|
||||
{
|
||||
lastquat = curquat;
|
||||
if(++quatiter != quatkeys.mKeys.end())
|
||||
curquat = quatiter->mValue;
|
||||
}
|
||||
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
|
||||
{
|
||||
lasttrans = curtrans;
|
||||
if(++traniter != trankeys.mKeys.end())
|
||||
curtrans = traniter->mValue;
|
||||
}
|
||||
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
|
||||
{
|
||||
lastscale = curscale;
|
||||
if(++scaleiter != scalekeys.mKeys.end())
|
||||
curscale = Ogre::Vector3(scaleiter->mValue);
|
||||
}
|
||||
|
||||
Ogre::TransformKeyFrame *kframe;
|
||||
kframe = nodetrack->createNodeKeyFrame(curtime);
|
||||
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
|
||||
kframe->setRotation(curquat);
|
||||
else
|
||||
{
|
||||
Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1;
|
||||
float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
|
||||
kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
|
||||
}
|
||||
if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
|
||||
kframe->setTranslate(curtrans);
|
||||
else
|
||||
{
|
||||
Nif::Vector3KeyList::VecType::const_iterator last = traniter-1;
|
||||
float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
|
||||
kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
|
||||
}
|
||||
if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
|
||||
kframe->setScale(curscale);
|
||||
else
|
||||
{
|
||||
Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1;
|
||||
float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
|
||||
kframe->setScale(lastscale + ((curscale-lastscale)*diff));
|
||||
}
|
||||
}
|
||||
}
|
||||
anim->optimise();
|
||||
}
|
||||
|
||||
|
||||
TextKeyMap NIFSkeletonLoader::extractTextKeys(const Nif::NiTextKeyExtraData *tk)
|
||||
{
|
||||
TextKeyMap textkeys;
|
||||
for(size_t i = 0;i < tk->list.size();i++)
|
||||
{
|
||||
const std::string &str = tk->list[i].text;
|
||||
std::string::size_type pos = 0;
|
||||
while(pos < str.length())
|
||||
{
|
||||
if(::isspace(str[pos]))
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
|
||||
std::string result = str.substr(pos, nextpos-pos);
|
||||
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
|
||||
|
||||
pos = nextpos;
|
||||
}
|
||||
}
|
||||
return textkeys;
|
||||
}
|
||||
|
||||
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent)
|
||||
void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent)
|
||||
{
|
||||
Ogre::Bone *bone;
|
||||
if(!skel->hasBone(node->name))
|
||||
|
@ -164,6 +29,8 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
|
||||
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
|
||||
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
|
||||
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
|
||||
node->recType == Nif::RC_NiBSParticleNode ||
|
||||
node->recType == Nif::RC_NiCamera ||
|
||||
node->recType == Nif::RC_NiAutoNormalParticles ||
|
||||
node->recType == Nif::RC_NiRotatingParticles
|
||||
|
@ -173,28 +40,16 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
Nif::ControllerPtr ctrl = node->controller;
|
||||
while(!ctrl.empty())
|
||||
{
|
||||
if(ctrl->recType == Nif::RC_NiKeyframeController)
|
||||
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
|
||||
else if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
|
||||
ctrl->recType == Nif::RC_NiVisController ||
|
||||
ctrl->recType == Nif::RC_NiUVController
|
||||
))
|
||||
if(!(ctrl->recType == Nif::RC_NiParticleSystemController ||
|
||||
ctrl->recType == Nif::RC_NiVisController ||
|
||||
ctrl->recType == Nif::RC_NiUVController ||
|
||||
ctrl->recType == Nif::RC_NiKeyframeController ||
|
||||
ctrl->recType == Nif::RC_NiGeomMorpherController
|
||||
))
|
||||
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
|
||||
ctrl = ctrl->next;
|
||||
}
|
||||
|
||||
Nif::ExtraPtr e = node->extra;
|
||||
while(!e.empty())
|
||||
{
|
||||
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
|
||||
{
|
||||
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
|
||||
textkeys = extractTextKeys(tk);
|
||||
animroot = bone;
|
||||
}
|
||||
e = e->extra;
|
||||
}
|
||||
|
||||
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
|
||||
if(ninode)
|
||||
{
|
||||
|
@ -202,7 +57,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
|
|||
for(size_t i = 0;i < children.length();i++)
|
||||
{
|
||||
if(!children[i].empty())
|
||||
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone);
|
||||
buildBones(skel, children[i].getPtr(), bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,84 +70,14 @@ void NIFSkeletonLoader::loadResource(Ogre::Resource *resource)
|
|||
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
|
||||
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRoot(0));
|
||||
|
||||
std::vector<const Nif::NiKeyframeController*> ctrls;
|
||||
Ogre::Bone *animroot = NULL;
|
||||
TextKeyMap textkeys;
|
||||
try {
|
||||
buildBones(skel, node, animroot, textkeys, ctrls);
|
||||
buildBones(skel, node);
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
|
||||
std::cerr<< e.what() <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Animations without textkeys don't get Ogre::Animation objects. */
|
||||
if(!animroot)
|
||||
return;
|
||||
|
||||
std::vector<std::string> targets;
|
||||
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
|
||||
if(ctrls.size() == 0) // No animations? Then we're done.
|
||||
return;
|
||||
|
||||
float maxtime = 0.0f;
|
||||
for(size_t i = 0;i < ctrls.size();i++)
|
||||
{
|
||||
const Nif::NiKeyframeController *ctrl = ctrls[i];
|
||||
maxtime = std::max(maxtime, ctrl->timeStop);
|
||||
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
|
||||
if(target != NULL)
|
||||
targets.push_back(target->name);
|
||||
}
|
||||
|
||||
if(targets.size() != ctrls.size())
|
||||
{
|
||||
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
|
||||
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
|
||||
return;
|
||||
}
|
||||
|
||||
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
|
||||
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
|
||||
|
||||
std::string currentgroup;
|
||||
TextKeyMap::const_iterator keyiter = textkeys.begin();
|
||||
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
|
||||
{
|
||||
std::string::size_type sep = keyiter->second.find(':');
|
||||
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
|
||||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
|
||||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
|
||||
continue;
|
||||
currentgroup = keyiter->second.substr(0, sep);
|
||||
|
||||
if(skel->hasAnimation(currentgroup))
|
||||
continue;
|
||||
|
||||
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
|
||||
while((--lastkeyiter)->first > keyiter->first)
|
||||
{
|
||||
if(lastkeyiter->second.find(':') == currentgroup.length() &&
|
||||
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
|
||||
|
||||
TextKeyMap::const_iterator insiter(keyiter);
|
||||
TextKeyMap groupkeys;
|
||||
do {
|
||||
sep = insiter->second.find(':');
|
||||
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
|
||||
groupkeys.insert(std::make_pair(insiter->first, insiter->second.substr(sep+2)));
|
||||
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
|
||||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
|
||||
groupkeys.insert(std::make_pair(insiter->first, insiter->second));
|
||||
} while(insiter++ != lastkeyiter);
|
||||
|
||||
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -300,16 +85,16 @@ Ogre::SkeletonPtr NIFSkeletonLoader::createSkeleton(const std::string &name, con
|
|||
{
|
||||
/* We need to be a little aggressive here, since some NIFs have a crap-ton
|
||||
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
|
||||
* There are no bones used for skinning, there are no controllers on non-
|
||||
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree
|
||||
* consists of NiNode, NiTriShape, and RootCollisionNode types only.
|
||||
* There are no bones used for skinning, there are no controllers, there
|
||||
* are no nodes named "AttachLight", and the tree consists of NiNode,
|
||||
* NiTriShape, and RootCollisionNode types only.
|
||||
*/
|
||||
if(!node->boneTrafo)
|
||||
{
|
||||
if(node->recType == Nif::RC_NiTriShape)
|
||||
return Ogre::SkeletonPtr();
|
||||
if(node->controller.empty() && node->name != "AttachLight")
|
||||
{
|
||||
if(node->recType == Nif::RC_NiTriShape)
|
||||
return Ogre::SkeletonPtr();
|
||||
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
|
||||
{
|
||||
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
|
||||
|
|
|
@ -36,10 +36,7 @@ class NIFSkeletonLoader : public Ogre::ManualResourceLoader
|
|||
abort();
|
||||
}
|
||||
|
||||
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime);
|
||||
|
||||
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk);
|
||||
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL);
|
||||
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *parent=NULL);
|
||||
|
||||
// Lookup to retrieve an Ogre bone handle for a given Nif record index
|
||||
std::map<int,int> mNifToOgreHandleMap;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<Property key="OffsetHeight" value="0"/>
|
||||
<Codes>
|
||||
<Code range="33 126"/>
|
||||
<Code range="160"/> <!-- Non-breaking space -->
|
||||
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="2026"/> <!-- Ellipsis -->
|
||||
|
|
|
@ -511,8 +511,8 @@ namespace Physic
|
|||
|
||||
void PhysicEngine::stepSimulation(double deltaT)
|
||||
{
|
||||
// This isn't needed as there are no dynamic objects at this point
|
||||
//dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
|
||||
// This seems to be needed for character controller objects
|
||||
dynamicsWorld->stepSimulation(deltaT,10, 1/60.0);
|
||||
if(isDebugCreated)
|
||||
{
|
||||
mDebugDrawer->step();
|
||||
|
|
|
@ -559,6 +559,8 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
|
|||
assert(mgr);
|
||||
|
||||
mSceneMgr = mgr;
|
||||
mShaderRenderManager = NULL;
|
||||
mRenderManager = NULL;
|
||||
|
||||
using namespace MyGUI;
|
||||
|
||||
|
@ -574,7 +576,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
|
|||
|
||||
// Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later.
|
||||
mLogManager = new LogManager();
|
||||
mRenderManager = new MyGUI::ShaderBasedRenderManager();
|
||||
if (!Ogre::Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_FIXED_FUNCTION))
|
||||
mShaderRenderManager = new MyGUI::ShaderBasedRenderManager();
|
||||
else
|
||||
mRenderManager = new MyGUI::OgreRenderManager();
|
||||
mDataManager = new MyGUI::FixedOgreDataManager();
|
||||
|
||||
LogManager::getInstance().setSTDOutputEnabled(logging);
|
||||
|
@ -582,7 +587,10 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
|
|||
if (!theLogFile.empty())
|
||||
LogManager::getInstance().createDefaultSource(theLogFile);
|
||||
|
||||
mRenderManager->initialise(wnd, mgr);
|
||||
if (mShaderRenderManager)
|
||||
mShaderRenderManager->initialise(wnd, mgr);
|
||||
else
|
||||
mRenderManager->initialise(wnd, mgr);
|
||||
mDataManager->initialise("General");
|
||||
|
||||
// Create GUI
|
||||
|
@ -592,8 +600,16 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool
|
|||
|
||||
void MyGUIManager::updateWindow (Ogre::RenderWindow *wnd)
|
||||
{
|
||||
mRenderManager->setRenderWindow (wnd);
|
||||
mRenderManager->setActiveViewport(0);
|
||||
if (mShaderRenderManager)
|
||||
{
|
||||
mShaderRenderManager->setRenderWindow (wnd);
|
||||
mShaderRenderManager->setActiveViewport(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mRenderManager->setRenderWindow (wnd);
|
||||
mRenderManager->setActiveViewport(0);
|
||||
}
|
||||
}
|
||||
|
||||
void MyGUIManager::shutdown()
|
||||
|
@ -606,6 +622,12 @@ void MyGUIManager::shutdown()
|
|||
delete mRenderManager;
|
||||
mRenderManager = NULL;
|
||||
}
|
||||
if(mShaderRenderManager)
|
||||
{
|
||||
mShaderRenderManager->shutdown();
|
||||
delete mShaderRenderManager;
|
||||
mShaderRenderManager = NULL;
|
||||
}
|
||||
if(mDataManager)
|
||||
{
|
||||
mDataManager->shutdown();
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace MyGUI
|
|||
class Gui;
|
||||
class LogManager;
|
||||
class OgreDataManager;
|
||||
class OgreRenderManager;
|
||||
class ShaderBasedRenderManager;
|
||||
}
|
||||
|
||||
|
@ -25,12 +26,12 @@ namespace GUI
|
|||
MyGUI::Gui *mGui;
|
||||
MyGUI::LogManager* mLogManager;
|
||||
MyGUI::OgreDataManager* mDataManager;
|
||||
MyGUI::ShaderBasedRenderManager* mRenderManager;
|
||||
MyGUI::OgreRenderManager* mRenderManager;
|
||||
MyGUI::ShaderBasedRenderManager* mShaderRenderManager;
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
|
||||
public:
|
||||
MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {}
|
||||
MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string(""))
|
||||
{
|
||||
setup(wnd,mgr,logging, logDir);
|
||||
|
|
|
@ -19,6 +19,7 @@ Fader::Fader(Ogre::SceneManager* sceneMgr)
|
|||
, mTargetAlpha(0.f)
|
||||
, mCurrentAlpha(0.f)
|
||||
, mStartAlpha(0.f)
|
||||
, mFactor(1.f)
|
||||
{
|
||||
// Create the fading material
|
||||
MaterialPtr material = MaterialManager::getSingleton().create("FadeInOutMaterial", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME );
|
||||
|
@ -62,19 +63,20 @@ void Fader::update(float dt)
|
|||
mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha);
|
||||
if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha;
|
||||
}
|
||||
|
||||
applyAlpha();
|
||||
|
||||
|
||||
mRemainingTime -= dt;
|
||||
}
|
||||
|
||||
if (mCurrentAlpha == 0.f) mRectangle->setVisible(false);
|
||||
if (1.f-((1.f-mCurrentAlpha) * mFactor) == 0.f)
|
||||
mRectangle->setVisible(false);
|
||||
else
|
||||
applyAlpha();
|
||||
}
|
||||
|
||||
void Fader::applyAlpha()
|
||||
{
|
||||
mRectangle->setVisible(true);
|
||||
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha);
|
||||
mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, 1.f-((1.f-mCurrentAlpha) * mFactor));
|
||||
}
|
||||
|
||||
void Fader::fadeIn(float time)
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Render
|
|||
void fadeOut(const float time);
|
||||
void fadeTo(const int percent, const float time);
|
||||
|
||||
void setFactor (float factor) { mFactor = factor; }
|
||||
|
||||
private:
|
||||
enum FadingMode
|
||||
{
|
||||
|
@ -49,6 +51,8 @@ namespace Render
|
|||
float mCurrentAlpha;
|
||||
float mStartAlpha;
|
||||
|
||||
float mFactor;
|
||||
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
};
|
||||
}}
|
||||
|
|
64
readme.txt
64
readme.txt
|
@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
|
|||
OpenMW is an attempt at recreating the engine for the popular role-playing game
|
||||
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
|
||||
|
||||
Version: 0.22.0
|
||||
Version: 0.23.0
|
||||
License: GPL (see GPL3.txt for more information)
|
||||
Website: http://www.openmw.org
|
||||
|
||||
|
@ -94,6 +94,68 @@ Allowed options:
|
|||
|
||||
CHANGELOG
|
||||
|
||||
0.23.0
|
||||
|
||||
Bug #522: Player collides with placeable items
|
||||
Bug #553: Open/Close sounds played when accessing main menu w/ Journal Open
|
||||
Bug #561: Tooltip word wrapping delay
|
||||
Bug #578: Bribing works incorrectly
|
||||
Bug #601: PositionCell fails on negative coordinates
|
||||
Bug #606: Some NPCs hairs not rendered with Better Heads addon
|
||||
Bug #609: Bad rendering of bone boots
|
||||
Bug #613: Messagebox causing assert to fail
|
||||
Bug #631: Segfault on shutdown
|
||||
Bug #634: Exception when talking to Calvus Horatius in Mournhold, royal palace courtyard
|
||||
Bug #635: Scale NPCs depending on race
|
||||
Bug #643: Dialogue Race select function is inverted
|
||||
Bug #646: Twohanded weapons don't work properly
|
||||
Bug #654: Crash when dropping objects without a collision shape
|
||||
Bug #655/656: Objects that were disabled or deleted (but not both) were added to the scene when re-entering a cell
|
||||
Bug #660: "g" in "change" cut off in Race Menu
|
||||
Bug #661: Arrille sells me the key to his upstairs room
|
||||
Bug #662: Day counter starts at 2 instead of 1
|
||||
Bug #663: Cannot select "come unprepared" topic in dialog with Dagoth Ur
|
||||
Bug #665: Pickpocket -> "Grab all" grabs all NPC inventory, even not listed in container window.
|
||||
Bug #666: Looking up/down problem
|
||||
Bug #667: Active effects border visible during loading
|
||||
Bug #669: incorrect player position at new game start
|
||||
Bug #670: race selection menu: sex, face and hair left button not totally clickable
|
||||
Bug #671: new game: player is naked
|
||||
Bug #674: buying or selling items doesn't change amount of gold
|
||||
Bug #675: fatigue is not set to its maximum when starting a new game
|
||||
Bug #678: Wrong rotation order causes RefData's rotation to be stored incorrectly
|
||||
Bug #680: different gold coins in Tel Mara
|
||||
Bug #682: Race menu ignores playable flag for some hairs and faces
|
||||
Bug #685: Script compiler does not accept ":" after a function name
|
||||
Bug #688: dispose corpse makes cross-hair to disappear
|
||||
Bug #691: Auto equipping ignores equipment conditions
|
||||
Bug #692: OpenMW doesnt load "loose file" texture packs that places resources directly in data folder
|
||||
Bug #696: Draugr incorrect head offset
|
||||
Bug #697: Sail transparency issue
|
||||
Bug #700: "On the rocks" mod does not load its UV coordinates correctly.
|
||||
Bug #702: Some race mods don't work
|
||||
Bug #711: Crash during character creation
|
||||
Bug #715: Growing Tauryon
|
||||
Bug #725: Auto calculate stats
|
||||
Bug #728: Failure to open container and talk dialogue
|
||||
Bug #731: Crash with Mush-Mere's "background" topic
|
||||
Feature #55/657: Item Repairing
|
||||
Feature #62/87: Enchanting
|
||||
Feature #99: Pathfinding
|
||||
Feature #104: AI Package: Travel
|
||||
Feature #129: Levelled items
|
||||
Feature #204: Texture animations
|
||||
Feature #239: Fallback-Settings
|
||||
Feature #535: Console object selection improvements
|
||||
Feature #629: Add levelup description in levelup layout dialog
|
||||
Feature #630: Optional format subrecord in (tes3) header
|
||||
Feature #641: Armor rating
|
||||
Feature #645: OnDeath script function
|
||||
Feature #683: Companion item UI
|
||||
Feature #698: Basic Particles
|
||||
Task #648: Split up components/esm/loadlocks
|
||||
Task #695: mwgui cleanup
|
||||
|
||||
0.22.0
|
||||
|
||||
Bug #311: Potential infinite recursion in script compiler
|
||||
|
|
Loading…
Reference in a new issue