Merge branch 'master' of https://github.com/zinnschlag/openmw
commit
475163bd4a
@ -0,0 +1,159 @@
|
||||
#include "trainingwindow.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <openengine/ogre/fader.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
#include "inventorywindow.hpp"
|
||||
#include "tradewindow.hpp"
|
||||
#include "tooltips.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
TrainingWindow::TrainingWindow(MWBase::WindowManager &parWindowManager)
|
||||
: WindowBase("openmw_trainingwindow.layout", parWindowManager)
|
||||
, mFadeTimeRemaining(0)
|
||||
{
|
||||
getWidget(mTrainingOptions, "TrainingOptions");
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
getWidget(mPlayerGold, "PlayerGold");
|
||||
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onCancelButtonClicked);
|
||||
}
|
||||
|
||||
void TrainingWindow::open()
|
||||
{
|
||||
center();
|
||||
}
|
||||
|
||||
void TrainingWindow::startTraining (MWWorld::Ptr actor)
|
||||
{
|
||||
mPtr = actor;
|
||||
|
||||
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast<std::string>(mWindowManager.getInventoryWindow()->getPlayerGold()));
|
||||
|
||||
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(actor).getNpcStats (actor);
|
||||
|
||||
// NPC can train you in his best 3 skills
|
||||
std::vector< std::pair<int, int> > bestSkills;
|
||||
bestSkills.push_back (std::make_pair(-1, -1));
|
||||
bestSkills.push_back (std::make_pair(-1, -1));
|
||||
bestSkills.push_back (std::make_pair(-1, -1));
|
||||
|
||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||
{
|
||||
int value = npcStats.getSkill (i).getBase ();
|
||||
|
||||
for (int j=0; j<3; ++j)
|
||||
{
|
||||
if (value > bestSkills[j].second)
|
||||
{
|
||||
if (j<2)
|
||||
{
|
||||
bestSkills[j+1] = bestSkills[j];
|
||||
}
|
||||
bestSkills[j] = std::make_pair(i, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator ();
|
||||
MyGUI::Gui::getInstance ().destroyWidgets (widgets);
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ();
|
||||
MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
/// \todo mercantile skill
|
||||
int price = pcStats.getSkill (bestSkills[i].first).getBase() * MWBase::Environment::get().getWorld ()->getStore ().gameSettings.find("iTrainingMod")->getInt ();
|
||||
|
||||
std::string skin = (price > mWindowManager.getInventoryWindow ()->getPlayerGold ()) ? "SandTextGreyedOut" : "SandTextButton";
|
||||
|
||||
MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>(skin,
|
||||
MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default);
|
||||
|
||||
button->setUserData(bestSkills[i].first);
|
||||
button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);
|
||||
|
||||
button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[bestSkills[i].first] + "} - " + boost::lexical_cast<std::string>(price));
|
||||
|
||||
button->setSize(button->getTextSize ().width+12, button->getSize().height);
|
||||
|
||||
ToolTips::createSkillToolTip (button, bestSkills[i].first);
|
||||
}
|
||||
|
||||
center();
|
||||
}
|
||||
|
||||
void TrainingWindow::onReferenceUnavailable ()
|
||||
{
|
||||
mWindowManager.removeGuiMode(GM_Training);
|
||||
}
|
||||
|
||||
void TrainingWindow::onCancelButtonClicked (MyGUI::Widget *sender)
|
||||
{
|
||||
mWindowManager.removeGuiMode (GM_Training);
|
||||
}
|
||||
|
||||
void TrainingWindow::onTrainingSelected (MyGUI::Widget *sender)
|
||||
{
|
||||
int skillId = *sender->getUserData<int>();
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ();
|
||||
MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player);
|
||||
|
||||
/// \todo mercantile skill
|
||||
int price = pcStats.getSkill (skillId).getBase() * MWBase::Environment::get().getWorld ()->getStore ().gameSettings.find("iTrainingMod")->getInt ();
|
||||
|
||||
if (mWindowManager.getInventoryWindow()->getPlayerGold()<price)
|
||||
return;
|
||||
|
||||
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mPtr).getNpcStats (mPtr);
|
||||
if (npcStats.getSkill (skillId).getBase () <= pcStats.getSkill (skillId).getBase ())
|
||||
{
|
||||
mWindowManager.messageBox ("#{sServiceTrainingWords}", std::vector<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
// increase skill
|
||||
MWWorld::LiveCellRef<ESM::NPC> *playerRef = player.get<ESM::NPC>();
|
||||
const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find (
|
||||
playerRef->base->mClass);
|
||||
pcStats.increaseSkill (skillId, *class_, true);
|
||||
|
||||
// remove gold
|
||||
mWindowManager.getTradeWindow()->addOrRemoveGold(-price);
|
||||
|
||||
// go back to game mode
|
||||
mWindowManager.removeGuiMode (GM_Training);
|
||||
mWindowManager.removeGuiMode (GM_Dialogue);
|
||||
|
||||
// advance time
|
||||
MWBase::Environment::get().getWorld ()->advanceTime (2);
|
||||
|
||||
MWBase::Environment::get().getWorld ()->getFader()->fadeOut(0.25);
|
||||
mFadeTimeRemaining = 0.5;
|
||||
}
|
||||
|
||||
void TrainingWindow::onFrame(float dt)
|
||||
{
|
||||
if (mFadeTimeRemaining <= 0)
|
||||
return;
|
||||
|
||||
mFadeTimeRemaining -= dt;
|
||||
|
||||
if (mFadeTimeRemaining <= 0)
|
||||
MWBase::Environment::get().getWorld ()->getFader()->fadeIn(0.25);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
#ifndef MWGUI_TRAININGWINDOW_H
|
||||
#define MWGUI_TRAININGWINDOW_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
#include "referenceinterface.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
class TrainingWindow : public WindowBase, public ReferenceInterface
|
||||
{
|
||||
public:
|
||||
TrainingWindow(MWBase::WindowManager& parWindowManager);
|
||||
|
||||
virtual void open();
|
||||
|
||||
void startTraining(MWWorld::Ptr actor);
|
||||
|
||||
void onFrame(float dt);
|
||||
|
||||
protected:
|
||||
virtual void onReferenceUnavailable ();
|
||||
|
||||
void onCancelButtonClicked (MyGUI::Widget* sender);
|
||||
void onTrainingSelected(MyGUI::Widget* sender);
|
||||
|
||||
MyGUI::Widget* mTrainingOptions;
|
||||
MyGUI::Button* mCancelButton;
|
||||
MyGUI::TextBox* mPlayerGold;
|
||||
|
||||
float mFadeTimeRemaining;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,187 @@
|
||||
#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"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
const int TravelWindow::sLineHeight = 18;
|
||||
|
||||
TravelWindow::TravelWindow(MWBase::WindowManager& parWindowManager) :
|
||||
WindowBase("openmw_travel_window.layout", parWindowManager)
|
||||
, mCurrentY(0)
|
||||
, mLastPos(0)
|
||||
{
|
||||
setCoord(0, 0, 450, 300);
|
||||
|
||||
getWidget(mCancelButton, "CancelButton");
|
||||
getWidget(mPlayerGold, "PlayerGold");
|
||||
getWidget(mSelect, "Select");
|
||||
getWidget(mDestinations, "Travel");
|
||||
getWidget(mDestinationsView, "DestinationsView");
|
||||
|
||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onCancelButtonClicked);
|
||||
|
||||
mDestinations->setCoord(450/2-mDestinations->getTextSize().width/2,
|
||||
mDestinations->getTop(),
|
||||
mDestinations->getTextSize().width,
|
||||
mDestinations->getHeight());
|
||||
mSelect->setCoord(8,
|
||||
mSelect->getTop(),
|
||||
mSelect->getTextSize().width,
|
||||
mSelect->getHeight());
|
||||
}
|
||||
|
||||
void TravelWindow::addDestination(const std::string& travelId,ESM::Position pos,bool interior)
|
||||
{
|
||||
int price = 0;
|
||||
|
||||
if(interior)
|
||||
{
|
||||
price = MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fMagesGuildTravel")->getFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
ESM::Position PlayerPos = player.getRefData().getPosition();
|
||||
float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) );
|
||||
price = d/MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fTravelMult")->getFloat();
|
||||
}
|
||||
|
||||
MyGUI::Button* toAdd = mDestinationsView->createWidget<MyGUI::Button>((price>mWindowManager.getInventoryWindow()->getPlayerGold()) ? "SandTextGreyedOut" : "SandTextButton", 0, mCurrentY, 200, sLineHeight, MyGUI::Align::Default);
|
||||
mCurrentY += sLineHeight;
|
||||
/// \todo price adjustment depending on merchantile skill
|
||||
if(interior)
|
||||
toAdd->setUserString("interior","y");
|
||||
else
|
||||
toAdd->setUserString("interior","n");
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << price;
|
||||
toAdd->setUserString("price",oss.str());
|
||||
|
||||
toAdd->setCaptionWithReplacing(travelId+" - "+boost::lexical_cast<std::string>(price)+"#{sgp}");
|
||||
toAdd->setSize(toAdd->getTextSize().width,sLineHeight);
|
||||
toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel);
|
||||
toAdd->setUserString("Destination", travelId);
|
||||
toAdd->setUserData(pos);
|
||||
toAdd->eventMouseButtonClick += MyGUI::newDelegate(this, &TravelWindow::onTravelButtonClick);
|
||||
mDestinationsWidgetMap.insert(std::make_pair (toAdd, travelId));
|
||||
}
|
||||
|
||||
void TravelWindow::clearDestinations()
|
||||
{
|
||||
mDestinationsView->setViewOffset(MyGUI::IntPoint(0,0));
|
||||
mCurrentY = 0;
|
||||
while (mDestinationsView->getChildCount())
|
||||
MyGUI::Gui::getInstance().destroyWidget(mDestinationsView->getChildAt(0));
|
||||
mDestinationsWidgetMap.clear();
|
||||
}
|
||||
|
||||
void TravelWindow::startTravel(const MWWorld::Ptr& actor)
|
||||
{
|
||||
center();
|
||||
mPtr = actor;
|
||||
clearDestinations();
|
||||
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
|
||||
for(unsigned int i = 0;i<mPtr.get<ESM::NPC>()->base->mTransport.size();i++)
|
||||
{
|
||||
std::string cellname = mPtr.get<ESM::NPC>()->base->mTransport[i].mCellName;
|
||||
bool interior = true;
|
||||
int x,y;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex(mPtr.get<ESM::NPC>()->base->mTransport[i].mPos.pos[0],
|
||||
mPtr.get<ESM::NPC>()->base->mTransport[i].mPos.pos[1],x,y);
|
||||
if(cellname == "") {cellname = MWBase::Environment::get().getWorld()->getExterior(x,y)->cell->mName; interior= false;}
|
||||
addDestination(cellname,mPtr.get<ESM::NPC>()->base->mTransport[i].mPos,interior);
|
||||
}
|
||||
|
||||
updateLabels();
|
||||
mDestinationsView->setCanvasSize (MyGUI::IntSize(mDestinationsView->getWidth(), std::max(mDestinationsView->getHeight(), mCurrentY)));
|
||||
}
|
||||
|
||||
void TravelWindow::onTravelButtonClick(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::istringstream iss(_sender->getUserString("price"));
|
||||
int price;
|
||||
iss >> price;
|
||||
|
||||
if (mWindowManager.getInventoryWindow()->getPlayerGold()<price)
|
||||
return;
|
||||
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(1);
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
ESM::Position pos = *_sender->getUserData<ESM::Position>();
|
||||
std::string cellname = _sender->getUserString("Destination");
|
||||
int x,y;
|
||||
bool interior = _sender->getUserString("interior") == "y";
|
||||
MWBase::Environment::get().getWorld()->positionToIndex(pos.pos[0],pos.pos[1],x,y);
|
||||
MWWorld::CellStore* cell;
|
||||
if(interior) cell = MWBase::Environment::get().getWorld()->getInterior(cellname);
|
||||
else
|
||||
{
|
||||
cell = MWBase::Environment::get().getWorld()->getExterior(x,y);
|
||||
ESM::Position PlayerPos = player.getRefData().getPosition();
|
||||
float d = sqrt( pow(pos.pos[0] - PlayerPos.pos[0],2) + pow(pos.pos[1] - PlayerPos.pos[1],2) + pow(pos.pos[2] - PlayerPos.pos[2],2) );
|
||||
int time = int(d /MWBase::Environment::get().getWorld()->getStore().gameSettings.find("fTravelTimeMult")->getFloat());
|
||||
for(int i = 0;i < time;i++)
|
||||
{
|
||||
MWBase::Environment::get().getMechanicsManager ()->restoreDynamicStats ();
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->advanceTime(time);
|
||||
}
|
||||
MWBase::Environment::get().getWorld()->moveObject(player,*cell,pos.pos[0],pos.pos[1],pos.pos[2]);
|
||||
mWindowManager.removeGuiMode(GM_Travel);
|
||||
mWindowManager.removeGuiMode(GM_Dialogue);
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeOut(0);
|
||||
MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(1);
|
||||
}
|
||||
|
||||
void TravelWindow::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mWindowManager.removeGuiMode(GM_Travel);
|
||||
}
|
||||
|
||||
void TravelWindow::updateLabels()
|
||||
{
|
||||
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + boost::lexical_cast<std::string>(mWindowManager.getInventoryWindow()->getPlayerGold()));
|
||||
mPlayerGold->setCoord(8,
|
||||
mPlayerGold->getTop(),
|
||||
mPlayerGold->getTextSize().width,
|
||||
mPlayerGold->getHeight());
|
||||
}
|
||||
|
||||
void TravelWindow::onReferenceUnavailable()
|
||||
{
|
||||
mWindowManager.removeGuiMode(GM_Travel);
|
||||
mWindowManager.removeGuiMode(GM_Dialogue);
|
||||
}
|
||||
|
||||
void TravelWindow::onMouseWheel(MyGUI::Widget* _sender, int _rel)
|
||||
{
|
||||
if (mDestinationsView->getViewOffset().top + _rel*0.3 > 0)
|
||||
mDestinationsView->setViewOffset(MyGUI::IntPoint(0, 0));
|
||||
else
|
||||
mDestinationsView->setViewOffset(MyGUI::IntPoint(0, mDestinationsView->getViewOffset().top + _rel*0.3));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
#ifndef MWGUI_TravelWINDOW_H
|
||||
#define MWGUI_TravelWINDOW_H
|
||||
|
||||
#include "container.hpp"
|
||||
#include "window_base.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
class Gui;
|
||||
class Widget;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
}
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class TravelWindow : public ReferenceInterface, public WindowBase
|
||||
{
|
||||
public:
|
||||
TravelWindow(MWBase::WindowManager& parWindowManager);
|
||||
|
||||
void startTravel(const MWWorld::Ptr& actor);
|
||||
|
||||
protected:
|
||||
MyGUI::Button* mCancelButton;
|
||||
MyGUI::TextBox* mPlayerGold;
|
||||
MyGUI::TextBox* mDestinations;
|
||||
MyGUI::TextBox* mSelect;
|
||||
|
||||
MyGUI::ScrollView* mDestinationsView;
|
||||
|
||||
std::map<MyGUI::Widget*, std::string> mDestinationsWidgetMap;
|
||||
|
||||
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
||||
void onTravelButtonClick(MyGUI::Widget* _sender);
|
||||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
void addDestination(const std::string& destinationID,ESM::Position pos,bool interior);
|
||||
void clearDestinations();
|
||||
int mLastPos,mCurrentY;
|
||||
|
||||
static const int sLineHeight;
|
||||
|
||||
void updateLabels();
|
||||
|
||||
virtual void onReferenceUnavailable();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,490 @@
|
||||
|
||||
#include "alchemy.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/loadskil.hpp>
|
||||
#include <components/esm/loadappa.hpp>
|
||||
#include <components/esm/loadgmst.hpp>
|
||||
#include <components/esm/loadmgef.hpp>
|
||||
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/manualref.hpp"
|
||||
|
||||
#include "magiceffects.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "npcstats.hpp"
|
||||
|
||||
std::set<MWMechanics::EffectKey> MWMechanics::Alchemy::listEffects() const
|
||||
{
|
||||
std::set<EffectKey> effects;
|
||||
|
||||
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
|
||||
{
|
||||
if (!iter->isEmpty())
|
||||
{
|
||||
const MWWorld::LiveCellRef<ESM::Ingredient> *ingredient = iter->get<ESM::Ingredient>();
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
if (ingredient->base->mData.mEffectID[i]!=-1)
|
||||
effects.insert (EffectKey (
|
||||
ingredient->base->mData.mEffectID[i], ingredient->base->mData.mSkills[i]!=-1 ?
|
||||
ingredient->base->mData.mSkills[i] : ingredient->base->mData.mAttributes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return effects;
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::filterEffects (std::set<EffectKey>& effects) const
|
||||
{
|
||||
std::set<EffectKey>::iterator iter = effects.begin();
|
||||
|
||||
while (iter!=effects.end())
|
||||
{
|
||||
bool remove = false;
|
||||
|
||||
const EffectKey& key = *iter;
|
||||
|
||||
{ // dodge pointless g++ warning
|
||||
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
if (iter->isEmpty())
|
||||
continue;
|
||||
|
||||
const MWWorld::LiveCellRef<ESM::Ingredient> *ingredient = iter->get<ESM::Ingredient>();
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
if (key.mId==ingredient->base->mData.mEffectID[i] &&
|
||||
(key.mArg==ingredient->base->mData.mSkills[i] ||
|
||||
key.mArg==ingredient->base->mData.mAttributes[i]))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remove)
|
||||
effects.erase (iter++);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::applyTools (int flags, float& value) const
|
||||
{
|
||||
bool magnitude = !(flags & ESM::MagicEffect::NoMagnitude);
|
||||
bool duration = !(flags & ESM::MagicEffect::NoDuration);
|
||||
bool negative = flags & (ESM::MagicEffect::Negative | ESM::MagicEffect::Harmful);
|
||||
|
||||
int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic;
|
||||
|
||||
int setup = 0;
|
||||
|
||||
if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty())
|
||||
setup = 1;
|
||||
else if (!mTools[tool].isEmpty())
|
||||
setup = 2;
|
||||
else if (!mTools[ESM::Apparatus::Calcinator].isEmpty())
|
||||
setup = 3;
|
||||
else
|
||||
return;
|
||||
|
||||
float toolQuality = setup==1 || setup==2 ? mTools[tool].get<ESM::Apparatus>()->base->mData.mQuality : 0;
|
||||
float calcinatorQuality = setup==1 || setup==3 ?
|
||||
mTools[ESM::Apparatus::Calcinator].get<ESM::Apparatus>()->base->mData.mQuality : 0;
|
||||
|
||||
float quality = 1;
|
||||
|
||||
switch (setup)
|
||||
{
|
||||
case 1:
|
||||
|
||||
quality = negative ? 2 * toolQuality + 3 * calcinatorQuality :
|
||||
(magnitude && duration ?
|
||||
2 * toolQuality + calcinatorQuality : 2/3.0 * (toolQuality + calcinatorQuality) + 0.5);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
|
||||
quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5;
|
||||
break;
|
||||
}
|
||||
|
||||
if (setup==3 || !negative)
|
||||
{
|
||||
value += quality;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (quality==0)
|
||||
throw std::runtime_error ("invalid derived alchemy apparatus quality");
|
||||
|
||||
value /= quality;
|
||||
}
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::updateEffects()
|
||||
{
|
||||
mEffects.clear();
|
||||
mValue = 0;
|
||||
|
||||
if (countIngredients()<2 || mAlchemist.isEmpty() || mTools[ESM::Apparatus::MortarPestle].isEmpty())
|
||||
return;
|
||||
|
||||
// find effects
|
||||
std::set<EffectKey> effects (listEffects());
|
||||
filterEffects (effects);
|
||||
|
||||
// general alchemy factor
|
||||
float x = getChance();
|
||||
|
||||
x *= mTools[ESM::Apparatus::MortarPestle].get<ESM::Apparatus>()->base->mData.mQuality;
|
||||
x *= MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionStrengthMult")->getFloat();
|
||||
|
||||
// value
|
||||
mValue = static_cast<int> (
|
||||
x * MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("iAlchemyMod")->getFloat());
|
||||
|
||||
// build quantified effect list
|
||||
for (std::set<EffectKey>::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter)
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().magicEffects.find (iter->mId);
|
||||
|
||||
if (magicEffect->mData.mBaseCost<=0)
|
||||
throw std::runtime_error ("invalid base cost for magic effect " + iter->mId);
|
||||
|
||||
float fPotionT1MagMul =
|
||||
MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionT1MagMult")->getFloat();
|
||||
|
||||
if (fPotionT1MagMul<=0)
|
||||
throw std::runtime_error ("invalid gmst: fPotionT1MagMul");
|
||||
|
||||
float fPotionT1DurMult =
|
||||
MWBase::Environment::get().getWorld()->getStore().gameSettings.find ("fPotionT1DurMult")->getFloat();
|
||||
|
||||
if (fPotionT1DurMult<=0)
|
||||
throw std::runtime_error ("invalid gmst: fPotionT1DurMult");
|
||||
|
||||
float magnitude = magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude ?
|
||||
1 : (x / fPotionT1MagMul) / magicEffect->mData.mBaseCost;
|
||||
float duration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ?
|
||||
1 : (x / fPotionT1DurMult) / magicEffect->mData.mBaseCost;
|
||||
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
|
||||
applyTools (magicEffect->mData.mFlags, magnitude);
|
||||
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
|
||||
applyTools (magicEffect->mData.mFlags, duration);
|
||||
|
||||
duration = static_cast<int> (duration+0.5);
|
||||
magnitude = static_cast<int> (magnitude+0.5);
|
||||
|
||||
if (magnitude>0 && duration>0)
|
||||
{
|
||||
ESM::ENAMstruct effect;
|
||||
effect.mEffectID = iter->mId;
|
||||
|
||||
effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work
|
||||
|
||||
effect.mRange = 0;
|
||||
effect.mArea = 0;
|
||||
|
||||
effect.mDuration = duration;
|
||||
effect.mMagnMin = effect.mMagnMax = magnitude;
|
||||
|
||||
mEffects.push_back (effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ESM::Potion *MWMechanics::Alchemy::getRecord() const
|
||||
{
|
||||
for (ESMS::RecListWithIDT<ESM::Potion>::MapType::const_iterator iter (
|
||||
MWBase::Environment::get().getWorld()->getStore().potions.list.begin());
|
||||
iter!=MWBase::Environment::get().getWorld()->getStore().potions.list.end(); ++iter)
|
||||
{
|
||||
if (iter->second.mEffects.mList.size()!=mEffects.size())
|
||||
continue;
|
||||
|
||||
bool mismatch = false;
|
||||
|
||||
for (int i=0; i<static_cast<int> (iter->second.mEffects.mList.size()); ++iter)
|
||||
{
|
||||
const ESM::ENAMstruct& first = iter->second.mEffects.mList[i];
|
||||
const ESM::ENAMstruct& second = mEffects[i];
|
||||
|
||||
if (first.mEffectID!=second.mEffectID ||
|
||||
first.mArea!=second.mArea ||
|
||||
first.mRange!=second.mRange ||
|
||||
first.mSkill!=second.mSkill ||
|
||||
first.mAttribute!=second.mAttribute ||
|
||||
first.mMagnMin!=second.mMagnMin ||
|
||||
first.mMagnMax!=second.mMagnMax ||
|
||||
first.mDuration!=second.mDuration)
|
||||
{
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mismatch)
|
||||
return &iter->second;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::removeIngredients()
|
||||
{
|
||||
bool needsUpdate = false;
|
||||
|
||||
for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
|
||||
if (!iter->isEmpty())
|
||||
{
|
||||
iter->getRefData().setCount (iter->getRefData().getCount()-1);
|
||||
if (iter->getRefData().getCount()<1)
|
||||
{
|
||||
needsUpdate = true;
|
||||
*iter = MWWorld::Ptr();
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate)
|
||||
updateEffects();
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::addPotion (const std::string& name)
|
||||
{
|
||||
const ESM::Potion *record = getRecord();
|
||||
|
||||
if (!record)
|
||||
{
|
||||
ESM::Potion newRecord;
|
||||
|
||||
newRecord.mData.mWeight = 0;
|
||||
|
||||
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
|
||||
if (!iter->isEmpty())
|
||||
newRecord.mData.mWeight += iter->get<ESM::Ingredient>()->base->mData.mWeight;
|
||||
|
||||
newRecord.mData.mWeight /= countIngredients();
|
||||
|
||||
newRecord.mData.mValue = mValue;
|
||||
newRecord.mData.mAutoCalc = 0;
|
||||
|
||||
newRecord.mName = name;
|
||||
|
||||
int index = static_cast<int> (std::rand()/static_cast<double> (RAND_MAX)*6);
|
||||
assert (index>=0 && index<6);
|
||||
|
||||
static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" };
|
||||
|
||||
newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif";
|
||||
newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds";
|
||||
|
||||
newRecord.mEffects.mList = mEffects;
|
||||
|
||||
record = MWBase::Environment::get().getWorld()->createRecord (newRecord).second;
|
||||
}
|
||||
|
||||
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId);
|
||||
MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr());
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::increaseSkill()
|
||||
{
|
||||
MWWorld::Class::get (mAlchemist).skillUsageSucceeded (mAlchemist, ESM::Skill::Alchemy, 0);
|
||||
}
|
||||
|
||||
float MWMechanics::Alchemy::getChance() const
|
||||
{
|
||||
const CreatureStats& creatureStats = MWWorld::Class::get (mAlchemist).getCreatureStats (mAlchemist);
|
||||
const NpcStats& npcStats = MWWorld::Class::get (mAlchemist).getNpcStats (mAlchemist);
|
||||
|
||||
return
|
||||
(npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
|
||||
0.1 * creatureStats.getAttribute (1).getModified()
|
||||
+ 0.1 * creatureStats.getAttribute (7).getModified());
|
||||
}
|
||||
|
||||
int MWMechanics::Alchemy::countIngredients() const
|
||||
{
|
||||
int ingredients = 0;
|
||||
|
||||
for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter)
|
||||
if (!iter->isEmpty())
|
||||
++ingredients;
|
||||
|
||||
return ingredients;
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc)
|
||||
{
|
||||
mAlchemist = npc;
|
||||
|
||||
mIngredients.resize (4);
|
||||
|
||||
std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr());
|
||||
|
||||
mTools.resize (4);
|
||||
|
||||
std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr());
|
||||
|
||||
mEffects.clear();
|
||||
|
||||
MWWorld::ContainerStore& store = MWWorld::Class::get (npc).getContainerStore (npc);
|
||||
|
||||
for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus));
|
||||
iter!=store.end(); ++iter)
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Apparatus>* ref = iter->get<ESM::Apparatus>();
|
||||
|
||||
int type = ref->base->mData.mType;
|
||||
|
||||
if (type<0 || type>=static_cast<int> (mTools.size()))
|
||||
throw std::runtime_error ("invalid apparatus type");
|
||||
|
||||
if (!mTools[type].isEmpty())
|
||||
if (ref->base->mData.mQuality<=mTools[type].get<ESM::Apparatus>()->base->mData.mQuality)
|
||||
continue;
|
||||
|
||||
mTools[type] = *iter;
|
||||
}
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::beginTools() const
|
||||
{
|
||||
return mTools.begin();
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TToolsIterator MWMechanics::Alchemy::endTools() const
|
||||
{
|
||||
return mTools.end();
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::beginIngredients() const
|
||||
{
|
||||
return mIngredients.begin();
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TIngredientsIterator MWMechanics::Alchemy::endIngredients() const
|
||||
{
|
||||
return mIngredients.end();
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::clear()
|
||||
{
|
||||
mAlchemist = MWWorld::Ptr();
|
||||
mTools.clear();
|
||||
mIngredients.clear();
|
||||
mEffects.clear();
|
||||
}
|
||||
|
||||
int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
|
||||
{
|
||||
// find a free slot
|
||||
int slot = -1;
|
||||
|
||||
for (int i=0; i<static_cast<int> (mIngredients.size()); ++i)
|
||||
if (mIngredients[i].isEmpty())
|
||||
{
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (slot==-1)
|
||||
return -1;
|
||||
|
||||
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
|
||||
if (!iter->isEmpty() && ingredient.get<ESM::Ingredient>()==iter->get<ESM::Ingredient>())
|
||||
return -1;
|
||||
|
||||
mIngredients[slot] = ingredient;
|
||||
|
||||
updateEffects();
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
void MWMechanics::Alchemy::removeIngredient (int index)
|
||||
{
|
||||
if (index>=0 && index<static_cast<int> (mIngredients.size()))
|
||||
{
|
||||
mIngredients[index] = MWWorld::Ptr();
|
||||
updateEffects();
|
||||
}
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::beginEffects() const
|
||||
{
|
||||
return mEffects.begin();
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
|
||||
{
|
||||
return mEffects.end();
|
||||
}
|
||||
|
||||
std::string MWMechanics::Alchemy::getPotionName() const
|
||||
{
|
||||
if (const ESM::Potion *potion = getRecord())
|
||||
return potion->mName;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& name)
|
||||
{
|
||||
if (mTools[ESM::Apparatus::MortarPestle].isEmpty())
|
||||
return Result_NoMortarAndPestle;
|
||||
|
||||
if (countIngredients()<2)
|
||||
return Result_LessThanTwoIngredients;
|
||||
|
||||
if (name.empty() && getPotionName().empty())
|
||||
return Result_NoName;
|
||||
|
||||
if (beginEffects()==endEffects())
|
||||
return Result_NoEffects;
|
||||
|
||||
if (getChance()<std::rand()/static_cast<double> (RAND_MAX)*100)
|
||||
{
|
||||
removeIngredients();
|
||||
return Result_RandomFailure;
|
||||
}
|
||||
|
||||
addPotion (name);
|
||||
|
||||
removeIngredients();
|
||||
|
||||
increaseSkill();
|
||||
|
||||
return Result_Success;
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
#ifndef GAME_MWMECHANICS_ALCHEMY_H
|
||||
#define GAME_MWMECHANICS_ALCHEMY_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/effectlist.hpp>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
struct EffectKey;
|
||||
|
||||
/// \brief Potion creation via alchemy skill
|
||||
class Alchemy
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<MWWorld::Ptr> TToolsContainer;
|
||||
typedef TToolsContainer::const_iterator TToolsIterator;
|
||||
|
||||
typedef std::vector<MWWorld::Ptr> TIngredientsContainer;
|
||||
typedef TIngredientsContainer::const_iterator TIngredientsIterator;
|
||||
|
||||
typedef std::vector<ESM::ENAMstruct> TEffectsContainer;
|
||||
typedef TEffectsContainer::const_iterator TEffectsIterator;
|
||||
|
||||
enum Result
|
||||
{
|
||||
Result_Success,
|
||||
|
||||
Result_NoMortarAndPestle,
|
||||
Result_LessThanTwoIngredients,
|
||||
Result_NoName,
|
||||
Result_NoEffects,
|
||||
Result_RandomFailure
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
MWWorld::Ptr mAlchemist;
|
||||
TToolsContainer mTools;
|
||||
TIngredientsContainer mIngredients;
|
||||
TEffectsContainer mEffects;
|
||||
int mValue;
|
||||
|
||||
std::set<EffectKey> listEffects() const;
|
||||
///< List all effects of all used ingredients.
|
||||
|
||||
void filterEffects (std::set<EffectKey>& effects) const;
|
||||
///< Filter out effects not shared by all ingredients.
|
||||
|
||||
void applyTools (int flags, float& value) const;
|
||||
|
||||
void updateEffects();
|
||||
|
||||
const ESM::Potion *getRecord() const;
|
||||
///< Return existing recrod for created potion (may return 0)
|
||||
|
||||
void removeIngredients();
|
||||
///< Remove selected ingredients from alchemist's inventory, cleanup selected ingredients and
|
||||
/// update effect list accordingly.
|
||||
|
||||
void addPotion (const std::string& name);
|
||||
///< Add a potion to the alchemist's inventory.
|
||||
|
||||
void increaseSkill();
|
||||
///< Increase alchemist's skill.
|
||||
|
||||
float getChance() const;
|
||||
///< Return chance of success.
|
||||
|
||||
int countIngredients() const;
|
||||
|
||||
public:
|
||||
|
||||
void setAlchemist (const MWWorld::Ptr& npc);
|
||||
///< Set alchemist and configure alchemy setup accordingly. \a npc may be empty to indicate that
|
||||
/// there is no alchemist (alchemy session has ended).
|
||||
|
||||
TToolsIterator beginTools() const;
|
||||
///< \attention Iterates over tool slots, not over tools. Some of the slots may be empty.
|
||||
|
||||
TToolsIterator endTools() const;
|
||||
|
||||
TIngredientsIterator beginIngredients() const;
|
||||
///< \attention Iterates over ingredient slots, not over ingredients. Some of the slots may be empty.
|
||||
|
||||
TIngredientsIterator endIngredients() const;
|
||||
|
||||
void clear();
|
||||
///< Remove alchemist, tools and ingredients.
|
||||
|
||||
int addIngredient (const MWWorld::Ptr& ingredient);
|
||||
///< Add ingredient into the next free slot.
|
||||
///
|
||||
/// \return Slot index or -1, if adding failed because of no free slot or the ingredient type being
|
||||
/// listed already.
|
||||
|
||||
void removeIngredient (int index);
|
||||
///< Remove ingredient from slot (calling this function on an empty slot is a no-op).
|
||||
|
||||
TEffectsIterator beginEffects() const;
|
||||
|
||||
TEffectsIterator endEffects() const;
|
||||
|
||||
std::string getPotionName() const;
|
||||
///< Return the name of the potion that would be created when calling create (if a record for such
|
||||
/// a potion already exists) or return an empty string.
|
||||
|
||||
Result create (const std::string& name);
|
||||
///< Try to create a potion from the ingredients, place it in the inventory of the alchemist and
|
||||
/// adjust the skills of the alchemist accordingly.
|
||||
/// \param name must not be an empty string, unless there is already a potion record (
|
||||
/// getPotionName() does not return an empty string).
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 319 200" name="_Main">
|
||||
|
||||
<Widget type="TextBox" skin="NormalText" position="0 5 319 24" name="Select" align="Right Top">
|
||||
<Property key="TextAlign" value="Center"/>
|
||||
<Property key="Caption" value="#{sServiceTrainingTitle}"/>
|
||||
</Widget>
|
||||
<Widget type="TextBox" skin="SandText" position="5 30 319 24" name="Travel" align="Right Top">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
<Property key="Caption" value="#{sTrainingServiceTitle}"/>
|
||||
</Widget>
|
||||
|
||||
|
||||
<Widget type="Widget" skin="MW_Box" position="6 54 299 100" align="Left Top" name="TrainingOptions">
|
||||
</Widget>
|
||||
|
||||
|
||||
<Widget type="TextBox" skin="SandText" position="8 161 200 24" name="PlayerGold" align="Right Top">
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="244 161 60 24" name="CancelButton" align="Right Top">
|
||||
<Property key="ExpandDirection" value="Left"/>
|
||||
<Property key="Caption" value="#{sCancel}"/>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
||||
</MyGUI>
|
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 450 300" name="_Main">
|
||||
<Property key="Visible" value="false"/>
|
||||
|
||||
|
||||
<Widget type="TextBox" skin="SandText" position="8 10 24 24" name="Select" align="Right Top">
|
||||
<Property key="TextAlign" value="Right"/>
|
||||
<Property key="Caption" value="#{sTravelServiceTitle}"/>
|
||||
</Widget>
|
||||
<Widget type="TextBox" skin="SandText" position="0 0 24 24" name="Travel" align="Right Top">
|
||||
<Property key="TextAlign" value="Right"/>
|
||||
<Property key="Caption" value="#D8C09A#{sTravel}"/>
|
||||
</Widget>
|
||||
|
||||
|
||||
<Widget type="Widget" skin="MW_Box" position="6 31 430 225" align="ALIGN_LEFT ALIGN_STRETCH">
|
||||
<Widget type="ScrollView" skin="MW_ScrollView" position="4 4 422 217" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH" name="DestinationsView">
|
||||
<Property key="CanvasAlign" value="Left"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
|
||||
<Widget type="TextBox" skin="SandText" position="8 255 24 24" name="PlayerGold" align="Right Top">
|
||||
<Property key="TextAlign" value="Right"/>
|
||||
</Widget>
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="375 260 60 24" name="CancelButton" align="Right Top">
|
||||
<Property key="ExpandDirection" value="Left"/>
|
||||
<Property key="Caption" value="#{sOK}"/>
|
||||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
||||
</MyGUI>
|
Loading…
Reference in New Issue