summaryrefslogtreecommitdiff
path: root/src/gui/windows/charcreatedialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/windows/charcreatedialog.cpp')
-rw-r--r--src/gui/windows/charcreatedialog.cpp698
1 files changed, 698 insertions, 0 deletions
diff --git a/src/gui/windows/charcreatedialog.cpp b/src/gui/windows/charcreatedialog.cpp
new file mode 100644
index 000000000..53f1a5bce
--- /dev/null
+++ b/src/gui/windows/charcreatedialog.cpp
@@ -0,0 +1,698 @@
+/*
+ * The ManaPlus Client
+ * Copyright (C) 2004-2009 The Mana World Development Team
+ * Copyright (C) 2009-2010 The Mana Developers
+ * Copyright (C) 2011-2013 The ManaPlus Developers
+ *
+ * This file is part of The ManaPlus Client.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gui/windows/charcreatedialog.h"
+
+#include "main.h"
+
+#include "input/keydata.h"
+#include "input/keyevent.h"
+
+#include "gui/windows/okdialog.h"
+
+#include "gui/widgets/button.h"
+#include "gui/widgets/label.h"
+#include "gui/widgets/playerbox.h"
+#include "gui/widgets/radiobutton.h"
+#include "gui/widgets/slider.h"
+#include "gui/widgets/textfield.h"
+
+#include "net/net.h"
+
+#include "resources/chardb.h"
+#include "resources/colordb.h"
+#include "resources/itemdb.h"
+#include "resources/iteminfo.h"
+
+#include "utils/gettext.h"
+
+#include "debug.h"
+
+extern int serverVersion;
+
+static const Being::Action actions[] =
+{
+ Being::STAND, Being::SIT, Being::MOVE, Being::ATTACK, Being::DEAD
+};
+
+static const uint8_t directions[] =
+{
+ Being::DOWN, Being::RIGHT, Being::UP, Being::LEFT
+};
+
+CharCreateDialog::CharCreateDialog(CharSelectDialog *const parent,
+ const int slot) :
+ // TRANSLATORS: char create dialog name
+ Window(_("New Character"), true, parent, "charcreate.xml"),
+ gcn::ActionListener(),
+ gcn::KeyListener(),
+ mCharSelectDialog(parent),
+ mNameField(new TextField(this, "")),
+ // TRANSLATORS: char create dialog label
+ mNameLabel(new Label(this, _("Name:"))),
+ // TRANSLATORS: This is a narrow symbol used to denote 'next'.
+ // You may change this symbol if your language uses another.
+ // TRANSLATORS: char create dialog button
+ mNextHairColorButton(new Button(this, _(">"), "nextcolor", this)),
+ // TRANSLATORS: This is a narrow symbol used to denote 'previous'.
+ // You may change this symbol if your language uses another.
+ // TRANSLATORS: char create dialog button
+ mPrevHairColorButton(new Button(this, _("<"), "prevcolor", this)),
+ // TRANSLATORS: char create dialog label
+ mHairColorLabel(new Label(this, _("Hair color:"))),
+ mHairColorNameLabel(new Label(this, "")),
+ // TRANSLATORS: char create dialog button
+ mNextHairStyleButton(new Button(this, _(">"), "nextstyle", this)),
+ // TRANSLATORS: char create dialog button
+ mPrevHairStyleButton(new Button(this, _("<"), "prevstyle", this)),
+ // TRANSLATORS: char create dialog label
+ mHairStyleLabel(new Label(this, _("Hair style:"))),
+ mHairStyleNameLabel(new Label(this, "")),
+ mNextRaceButton(nullptr),
+ mPrevRaceButton(nullptr),
+ mRaceLabel(nullptr),
+ mRaceNameLabel(nullptr),
+ mNextLookButton(nullptr),
+ mPrevLookButton(nullptr),
+ mLookLabel(nullptr),
+ mLookNameLabel(nullptr),
+ // TRANSLATORS: char create dialog button
+ mActionButton(new Button(this, _("^"), "action", this)),
+ // TRANSLATORS: char create dialog button
+ mRotateButton(new Button(this, _(">"), "rotate", this)),
+ // TRANSLATORS: char create dialog button
+ mMale(new RadioButton(this, _("Male"), "gender")),
+ // TRANSLATORS: char create dialog button
+ mFemale(new RadioButton(this, _("Female"), "gender")),
+ // TRANSLATORS: char create dialog button
+ mOther(new RadioButton(this, _("Other"), "gender")),
+ mAttributeSlider(),
+ mAttributeLabel(),
+ mAttributeValue(),
+ mAttributesLeft(new Label(this,
+ // TRANSLATORS: char create dialog label
+ strprintf(_("Please distribute %d points"), 99))),
+ mMaxPoints(0),
+ mUsedPoints(0),
+ // TRANSLATORS: char create dialog button
+ mCreateButton(new Button(this, _("Create"), "create", this)),
+ // TRANSLATORS: char create dialog button
+ mCancelButton(new Button(this, _("Cancel"), "cancel", this)),
+ mRace(0),
+ mLook(0),
+ mMinLook(CharDB::getMinLook()),
+ mMaxLook(CharDB::getMaxLook()),
+ mPlayer(new Being(0, ActorSprite::PLAYER, static_cast<uint16_t>(mRace),
+ nullptr)),
+ mPlayerBox(new PlayerBox(mPlayer, "charcreate_playerbox.xml",
+ "charcreate_selectedplayerbox.xml")),
+ mHairStyle(0),
+ mHairColor(0),
+ mSlot(slot),
+ maxHairColor(CharDB::getMaxHairColor()),
+ minHairColor(CharDB::getMinHairColor()),
+ maxHairStyle(CharDB::getMaxHairStyle()),
+ minHairStyle(CharDB::getMinHairStyle()),
+ mAction(0),
+ mDirection(0)
+{
+ setStickyButtonLock(true);
+ setSticky(true);
+ setWindowName("NewCharacter");
+
+ mPlayer->setGender(GENDER_MALE);
+ const std::vector<int> &items = CharDB::getDefaultItems();
+ int i = 1;
+ for (std::vector<int>::const_iterator it = items.begin(),
+ it_end = items.end();
+ it != it_end; ++ it, i ++)
+ {
+ mPlayer->setSprite(i, *it);
+ }
+
+ if (!maxHairColor)
+ maxHairColor = ColorDB::getHairSize();
+ if (!maxHairStyle)
+ maxHairStyle = mPlayer->getNumOfHairstyles();
+
+ if (maxHairStyle)
+ mHairStyle = (rand() % maxHairStyle) + minHairStyle;
+ else
+ mHairStyle = 0;
+ if (maxHairColor)
+ mHairColor = (rand() % maxHairColor) + minHairColor;
+ else
+ mHairColor = 0;
+
+ mNameField->setMaximum(24);
+
+ if (serverVersion >= 2)
+ {
+ // TRANSLATORS: char create dialog button
+ mNextRaceButton = new Button(this, _(">"), "nextrace", this);
+ // TRANSLATORS: char create dialog button
+ mPrevRaceButton = new Button(this, _("<"), "prevrace", this);
+ // TRANSLATORS: char create dialog label
+ mRaceLabel = new Label(this, _("Race:"));
+ mRaceNameLabel = new Label(this, "");
+ }
+ if (serverVersion >= 9 && mMinLook < mMaxLook)
+ {
+ // TRANSLATORS: char create dialog button
+ mNextLookButton = new Button(this, _(">"), "nextlook", this);
+ // TRANSLATORS: char create dialog button
+ mPrevLookButton = new Button(this, _("<"), "prevlook", this);
+ // TRANSLATORS: char create dialog label
+ mLookLabel = new Label(this, _("Look:"));
+ mLookNameLabel = new Label(this, "");
+ }
+
+ // Default to a Male character
+ mMale->setSelected(true);
+
+ mMale->setActionEventId("gender");
+ mFemale->setActionEventId("gender");
+ mOther->setActionEventId("gender");
+
+ mMale->addActionListener(this);
+ mFemale->addActionListener(this);
+ mOther->addActionListener(this);
+
+ mPlayerBox->setWidth(74);
+
+ mNameField->setActionEventId("create");
+ mNameField->addActionListener(this);
+
+ const int w = 480;
+ const int h = 350;
+
+ setContentSize(w, h);
+ mPlayerBox->setDimension(gcn::Rectangle(360, 0, 110, 90));
+ mActionButton->setPosition(385, 100);
+ mRotateButton->setPosition(415, 100);
+
+ mNameLabel->setPosition(5, 2);
+ mNameField->setDimension(
+ gcn::Rectangle(60, 2, 300, mNameField->getHeight()));
+
+ const int leftX = 120;
+ const int rightX = 300;
+ const int labelX = 5;
+ const int nameX = 145;
+ int y = 30;
+ mPrevHairColorButton->setPosition(leftX, y);
+ mNextHairColorButton->setPosition(rightX, y);
+ y += 5;
+ mHairColorLabel->setPosition(labelX, y);
+ mHairColorNameLabel->setPosition(nameX, y);
+ y += 24;
+ mPrevHairStyleButton->setPosition(leftX, y);
+ mNextHairStyleButton->setPosition(rightX, y);
+ y += 5;
+ mHairStyleLabel->setPosition(labelX, y);
+ mHairStyleNameLabel->setPosition(nameX, y);
+
+ if (serverVersion >= 9 && mMinLook < mMaxLook)
+ {
+ y += 24;
+ mPrevLookButton->setPosition(leftX, y);
+ mNextLookButton->setPosition(rightX, y);
+ y += 5;
+ mLookLabel->setPosition(labelX, y);
+ mLookNameLabel->setPosition(nameX, y); // 93
+ }
+ if (serverVersion >= 2)
+ {
+ y += 24;
+ mPrevRaceButton->setPosition(leftX, y);
+ mNextRaceButton->setPosition(rightX, y);
+ y += 5;
+ mRaceLabel->setPosition(labelX, y);
+ mRaceNameLabel->setPosition(nameX, y);
+ }
+
+ updateSliders();
+ setButtonsPosition(w, h);
+
+ mMale->setPosition(30, 120);
+ mFemale->setPosition(100, 120);
+ mOther->setPosition(170, 120);
+
+ add(mPlayerBox);
+ add(mNameField);
+ add(mNameLabel);
+ add(mNextHairColorButton);
+ add(mPrevHairColorButton);
+ add(mHairColorLabel);
+ add(mHairColorNameLabel);
+ add(mNextHairStyleButton);
+ add(mPrevHairStyleButton);
+ add(mHairStyleLabel);
+ add(mHairStyleNameLabel);
+ add(mActionButton);
+ add(mRotateButton);
+
+ if (serverVersion >= 9 && mMinLook < mMaxLook)
+ {
+ add(mNextLookButton);
+ add(mPrevLookButton);
+ add(mLookLabel);
+ add(mLookNameLabel);
+ }
+
+ if (serverVersion >= 2)
+ {
+ add(mNextRaceButton);
+ add(mPrevRaceButton);
+ add(mRaceLabel);
+ add(mRaceNameLabel);
+ }
+
+ add(mAttributesLeft);
+ add(mCreateButton);
+ add(mCancelButton);
+
+ add(mMale);
+ add(mFemale);
+ add(mOther);
+
+ center();
+ setVisible(true);
+ mNameField->requestFocus();
+
+ updateHair();
+ if (serverVersion >= 2)
+ updateRace();
+ if (serverVersion >= 9 && mMinLook < mMaxLook)
+ updateLook();
+ updatePlayer();
+
+ addKeyListener(this);
+}
+
+CharCreateDialog::~CharCreateDialog()
+{
+ delete mPlayer;
+ mPlayer = nullptr;
+
+ if (Net::getCharServerHandler())
+ Net::getCharServerHandler()->setCharCreateDialog(nullptr);
+}
+
+void CharCreateDialog::action(const gcn::ActionEvent &event)
+{
+ const std::string id = event.getId();
+ if (id == "create")
+ {
+ if (
+#ifdef MANASERV_SUPPORT
+ Net::getNetworkType() == ServerInfo::MANASERV ||
+#endif
+ getName().length() >= 4)
+ {
+ // Attempt to create the character
+ mCreateButton->setEnabled(false);
+
+ std::vector<int> atts;
+ for (size_t i = 0, sz = mAttributeSlider.size(); i < sz; i++)
+ {
+ atts.push_back(static_cast<int>(
+ mAttributeSlider[i]->getValue()));
+ }
+
+#ifdef MANASERV_SUPPORT
+ int characterSlot = mSlot;
+ // On Manaserv, the slots start at 1, so we offset them.
+ if (Net::getNetworkType() == ServerInfo::MANASERV)
+ ++characterSlot;
+#else
+ const int characterSlot = mSlot;
+#endif
+
+ Net::getCharServerHandler()->newCharacter(getName(), characterSlot,
+ mFemale->isSelected(), mHairStyle, mHairColor,
+ static_cast<unsigned char>(mRace), mLook, atts);
+ }
+ else
+ {
+ // TRANSLATORS: char creation error
+ new OkDialog(_("Error"),
+ // TRANSLATORS: char creation error
+ _("Your name needs to be at least 4 characters."),
+ DIALOG_ERROR, true, this);
+ }
+ }
+ else if (id == "cancel")
+ {
+ scheduleDelete();
+ }
+ else if (id == "nextcolor")
+ {
+ mHairColor ++;
+ updateHair();
+ }
+ else if (id == "prevcolor")
+ {
+ mHairColor --;
+ updateHair();
+ }
+ else if (id == "nextstyle")
+ {
+ mHairStyle ++;
+ updateHair();
+ }
+ else if (id == "prevstyle")
+ {
+ mHairStyle --;
+ updateHair();
+ }
+ else if (id == "nextrace")
+ {
+ mRace ++;
+ updateRace();
+ }
+ else if (id == "prevrace")
+ {
+ mRace --;
+ updateRace();
+ }
+ else if (id == "nextlook")
+ {
+ mLook ++;
+ updateLook();
+ }
+ else if (id == "prevlook")
+ {
+ mLook --;
+ updateLook();
+ }
+ else if (id == "statslider")
+ {
+ updateSliders();
+ }
+ else if (id == "gender")
+ {
+ if (mMale->isSelected())
+ mPlayer->setGender(GENDER_MALE);
+ else
+ mPlayer->setGender(GENDER_FEMALE);
+ }
+ else if (id == "action")
+ {
+ mAction ++;
+ if (mAction >= 5)
+ mAction = 0;
+ updatePlayer();
+ }
+ else if (id == "rotate")
+ {
+ mDirection ++;
+ if (mDirection >= 4)
+ mDirection = 0;
+ updatePlayer();
+ }
+}
+
+std::string CharCreateDialog::getName() const
+{
+ std::string name = mNameField->getText();
+ trim(name);
+ return name;
+}
+
+void CharCreateDialog::updateSliders()
+{
+ for (size_t i = 0, sz = mAttributeSlider.size(); i < sz; i++)
+ {
+ // Update captions
+ mAttributeValue[i]->setCaption(
+ toString(static_cast<int>(mAttributeSlider[i]->getValue())));
+ mAttributeValue[i]->adjustSize();
+ }
+
+ // Update distributed points
+ const int pointsLeft = mMaxPoints - getDistributedPoints();
+ if (pointsLeft == 0)
+ {
+ // TRANSLATORS: char create dialog label
+ mAttributesLeft->setCaption(_("Character stats OK"));
+ mCreateButton->setEnabled(true);
+ }
+ else
+ {
+ mCreateButton->setEnabled(false);
+ if (pointsLeft > 0)
+ {
+ mAttributesLeft->setCaption(
+ // TRANSLATORS: char create dialog label
+ strprintf(_("Please distribute %d points"), pointsLeft));
+ }
+ else
+ {
+ mAttributesLeft->setCaption(
+ // TRANSLATORS: char create dialog label
+ strprintf(_("Please remove %d points"), -pointsLeft));
+ }
+ }
+
+ mAttributesLeft->adjustSize();
+}
+
+void CharCreateDialog::unlock()
+{
+ mCreateButton->setEnabled(true);
+}
+
+int CharCreateDialog::getDistributedPoints() const
+{
+ int points = 0;
+
+ for (size_t i = 0, sz = mAttributeSlider.size(); i < sz; i++)
+ points += static_cast<int>(mAttributeSlider[i]->getValue());
+ return points;
+}
+
+void CharCreateDialog::setAttributes(const StringVect &labels,
+ const int available,
+ const int min, const int max)
+{
+ mMaxPoints = available;
+
+ for (unsigned i = 0; i < mAttributeLabel.size(); i++)
+ {
+ remove(mAttributeLabel[i]);
+ delete mAttributeLabel[i];
+ mAttributeLabel[i] = nullptr;
+ remove(mAttributeSlider[i]);
+ delete mAttributeSlider[i];
+ mAttributeSlider[i] = nullptr;
+ remove(mAttributeValue[i]);
+ delete mAttributeValue[i];
+ mAttributeValue[i] = nullptr;
+ }
+
+ mAttributeLabel.resize(labels.size());
+ mAttributeSlider.resize(labels.size());
+ mAttributeValue.resize(labels.size());
+
+ const int w = 480;
+ const int h = 350;
+ const int y = 118 + 29;
+
+ for (unsigned i = 0, sz = static_cast<unsigned>(labels.size());
+ i < sz; i++)
+ {
+ mAttributeLabel[i] = new Label(this, labels[i]);
+ mAttributeLabel[i]->setWidth(70);
+ mAttributeLabel[i]->setPosition(5, y + i * 24);
+ mAttributeLabel[i]->adjustSize();
+ add(mAttributeLabel[i]);
+
+ mAttributeSlider[i] = new Slider(min, max);
+ mAttributeSlider[i]->setDimension(gcn::Rectangle(140, y + i * 24,
+ 150, 12));
+ mAttributeSlider[i]->setActionEventId("statslider");
+ mAttributeSlider[i]->addActionListener(this);
+ add(mAttributeSlider[i]);
+
+ mAttributeValue[i] = new Label(this, toString(min));
+ mAttributeValue[i]->setPosition(295, y + i * 24);
+ add(mAttributeValue[i]);
+ }
+
+ updateSliders();
+ setButtonsPosition(w, h);
+}
+
+void CharCreateDialog::setFixedGender(const bool fixed, const Gender gender)
+{
+ if (gender == GENDER_FEMALE)
+ {
+ mFemale->setSelected(true);
+ mMale->setSelected(false);
+ mOther->setSelected(false);
+ }
+ else if (gender == GENDER_MALE)
+ {
+ mFemale->setSelected(false);
+ mMale->setSelected(true);
+ mOther->setSelected(false);
+ }
+ else
+ {
+ mFemale->setSelected(false);
+ mMale->setSelected(false);
+ mOther->setSelected(true);
+ }
+
+ mPlayer->setGender(gender);
+
+ if (fixed)
+ {
+ mMale->setVisible(false);
+ mFemale->setVisible(false);
+ mOther->setVisible(false);
+ }
+}
+
+void CharCreateDialog::updateHair()
+{
+ if (mHairStyle <= 0)
+ mHairStyle = Being::getNumOfHairstyles() - 1;
+ else
+ mHairStyle %= Being::getNumOfHairstyles();
+ if (mHairStyle < static_cast<signed>(minHairStyle)
+ || mHairStyle > static_cast<signed>(maxHairStyle))
+ {
+ mHairStyle = minHairStyle;
+ }
+ const ItemInfo &item = ItemDB::get(-mHairStyle);
+ mHairStyleNameLabel->setCaption(item.getName());
+ mHairStyleNameLabel->adjustSize();
+
+ if (ColorDB::getHairSize())
+ mHairColor %= ColorDB::getHairSize();
+ else
+ mHairColor = 0;
+ if (mHairColor < 0)
+ mHairColor += ColorDB::getHairSize();
+ if (mHairColor < static_cast<signed>(minHairColor)
+ || mHairColor > static_cast<signed>(maxHairColor))
+ {
+ mHairColor = minHairColor;
+ }
+ mHairColorNameLabel->setCaption(ColorDB::getHairColorName(mHairColor));
+ mHairColorNameLabel->adjustSize();
+
+ mPlayer->setSprite(Net::getCharServerHandler()->hairSprite(),
+ mHairStyle * -1, item.getDyeColorsString(mHairColor));
+}
+
+void CharCreateDialog::updateRace()
+{
+ if (mRace < 0)
+ mRace = Being::getNumOfRaces() - 1;
+ else if (mRace >= Being::getNumOfRaces())
+ mRace = 0;
+
+ updateLook();
+}
+
+void CharCreateDialog::updateLook()
+{
+ const ItemInfo &item = ItemDB::get(-100 - mRace);
+ const int sz = item.getColorsSize();
+ if (sz > 0 && serverVersion >= 9)
+ {
+ if (mLook < 0)
+ mLook = sz - 1;
+ if (mLook > mMaxLook)
+ mLook = mMinLook;
+ if (mLook >= sz)
+ mLook = mMinLook;
+ }
+ else
+ {
+ mLook = 0;
+ }
+ mPlayer->setSubtype(static_cast<uint16_t>(mRace), mLook);
+ if (mRaceNameLabel)
+ {
+ mRaceNameLabel->setCaption(item.getName());
+ mRaceNameLabel->adjustSize();
+ }
+ if (mLookNameLabel)
+ {
+ mLookNameLabel->setCaption(item.getColorName(mLook));
+ mLookNameLabel->adjustSize();
+ }
+}
+
+void CharCreateDialog::logic()
+{
+ if (mPlayer)
+ mPlayer->logic();
+}
+
+void CharCreateDialog::updatePlayer()
+{
+ if (mPlayer)
+ {
+ mPlayer->setDirection(directions[mDirection]);
+ mPlayer->setAction(actions[mAction]);
+ }
+}
+
+void CharCreateDialog::keyPressed(gcn::KeyEvent &keyEvent)
+{
+ const int actionId = static_cast<KeyEvent*>(&keyEvent)->getActionId();
+ switch (actionId)
+ {
+ case Input::KEY_GUI_CANCEL:
+ keyEvent.consume();
+ action(gcn::ActionEvent(mCancelButton,
+ mCancelButton->getActionEventId()));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CharCreateDialog::setButtonsPosition(const int w, const int h)
+{
+ if (mainGraphics->getHeight() < 480)
+ {
+ mCreateButton->setPosition(340, 150);
+ mCancelButton->setPosition(340, 160 + mCreateButton->getHeight());
+ }
+ else
+ {
+ mCancelButton->setPosition(
+ w / 2,
+ h - 5 - mCancelButton->getHeight());
+ mCreateButton->setPosition(
+ mCancelButton->getX() - 5 - mCreateButton->getWidth(),
+ h - 5 - mCancelButton->getHeight());
+ }
+ mAttributesLeft->setPosition(15, 260 + 29);
+}