Mana
Loading...
Searching...
No Matches
charcreatedialog.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include "localplayer.h"
25
27#include "gui/okdialog.h"
28
29#include "gui/widgets/button.h"
30#include "gui/widgets/label.h"
33#include "gui/widgets/slider.h"
35
36#include "net/charhandler.h"
37#include "net/net.h"
38
39#include "resources/chardb.h"
40#include "resources/hairdb.h"
41
42#include "utils/gettext.h"
43#include "utils/stringutils.h"
44
45#include <guichan/font.hpp>
46
48 Window(_("Create Character"), true, parent),
49 mCharSelectDialog(parent),
50 mSlot(slot)
51{
52 mPlayer = new Being(0, ActorSprite::PLAYER, 0, nullptr);
54
55 const std::vector<int> &items = CharDB::getDefaultItems();
56 for (size_t i = 0; i < items.size(); ++i)
57 mPlayer->setSprite(i + 1, items.at(i));
58
60 Net::getCharHandler()->getCharCreateMaxHairStyleId());
61 mHairStyleId = rand() * mHairStylesIds.size() / RAND_MAX;
62
64 Net::getCharHandler()->getCharCreateMinHairColorId(),
65 Net::getCharHandler()->getCharCreateMaxHairColorId());
66 mHairColorId = rand() * mHairColorsIds.size() / RAND_MAX;
67
68 updateHair();
69
70 mNameField = new TextField(std::string());
71 mNameLabel = new Label(_("Name:"));
72
73 mCreateButton = new Button(_("Create"), "create", this);
74 mCancelButton = new Button(_("Cancel"), "cancel", this);
75 mMale = new RadioButton(_("Male"), "gender");
76 mFemale = new RadioButton(_("Female"), "gender");
77
78 // Default to a Male character
79 mMale->setSelected(true);
80
81 mMale->setActionEventId("gender");
82 mFemale->setActionEventId("gender");
83
84 mMale->addActionListener(this);
85 mFemale->addActionListener(this);
86
88 mPlayerBox->setWidth(74);
89
90 mNameField->setActionEventId("create");
91 mNameField->addActionListener(this);
92
94 strprintf(_("Please distribute %d points"), 99));
95
96 int w = 200;
97 int h = 330;
98 setContentSize(w, h);
99 mPlayerBox->setDimension(gcn::Rectangle(80, 30, 110, 85));
100 mNameLabel->setPosition(5, 5);
101 mNameField->setDimension(
102 gcn::Rectangle(45, 5, w - 45 - 7, mNameField->getHeight()));
103 mAttributesLeft->setPosition(15, 280);
105 mCancelButton->setPosition(
106 w - 5 - mCancelButton->getWidth(),
107 h - 5 - mCancelButton->getHeight());
108 mCreateButton->setPosition(
109 mCancelButton->getX() - 5 - mCreateButton->getWidth(),
110 h - 5 - mCancelButton->getHeight());
111
112 mMale->setPosition(30, 120);
113 mFemale->setPosition(100, 120);
114
115 add(mPlayerBox);
116 add(mNameField);
117 add(mNameLabel);
118
119 if (mHairColorsIds.size() > 1)
120 {
121 auto hairColorLabel = new Label(_("Hair color:"));
122 auto nextHairColorButton = new Button("", "nextcolor", this);
123 auto prevHairColorButton = new Button("", "prevcolor", this);
124 prevHairColorButton->setButtonIcon("tab_arrows_left.png");
125 nextHairColorButton->setButtonIcon("tab_arrows_right.png");
126
127 hairColorLabel->setPosition(5, 40);
128 prevHairColorButton->setPosition(90, 35);
129 nextHairColorButton->setPosition(165, 35);
130
131 add(nextHairColorButton);
132 add(prevHairColorButton);
133 add(hairColorLabel);
134 }
135
136 if (mHairStylesIds.size() > 1)
137 {
138 auto hairStyleLabel = new Label(_("Hair style:"));
139 auto nextHairStyleButton = new Button("", "nextstyle", this);
140 auto prevHairStyleButton = new Button("", "prevstyle", this);
141 prevHairStyleButton->setButtonIcon("tab_arrows_left.png");
142 nextHairStyleButton->setButtonIcon("tab_arrows_right.png");
143
144 hairStyleLabel->setPosition(5, 70);
145 prevHairStyleButton->setPosition(90, 64);
146 nextHairStyleButton->setPosition(165, 64);
147
148 add(nextHairStyleButton);
149 add(prevHairStyleButton);
150 add(hairStyleLabel);
151 }
152
153 add(mAttributesLeft);
154 add(mCreateButton);
155 add(mCancelButton);
156
157 add(mMale);
158 add(mFemale);
159
160 center();
161 setVisible(true);
162 mNameField->requestFocus();
163}
164
166{
167 delete mPlayer;
168
169 // Make sure the char server handler knows that we're gone
170 if (auto charHandler = Net::getCharHandler())
172}
173
174void CharCreateDialog::action(const gcn::ActionEvent &event)
175{
176 if (event.getId() == "create")
177 {
179 || getName().length() >= 4)
180 {
181 // Attempt to create the character
182 mCreateButton->setEnabled(false);
183
184 int characterSlot = mSlot;
185 // On Manaserv, the slots start at 1, so we offset them.
187 ++characterSlot;
188
189 // Should avoid the most common crash case
190 int hairStyle = mHairStylesIds.empty() ?
192 int hairColor = mHairColorsIds.empty() ?
194
195 Gender gender = mFemale->isSelected() ? Gender::Female : Gender::Male;
196 Net::getCharHandler()->newCharacter(getName(), characterSlot,
197 gender,
198 hairStyle, hairColor,
200 }
201 else
202 {
203 new OkDialog(_("Error"),
204 _("Your name needs to be at least 4 characters."),
205 true, this);
206 }
207 }
208 else if (event.getId() == "cancel")
209 {
211 }
212 else if (event.getId() == "nextcolor")
213 {
214 ++mHairColorId;
215 updateHair();
216 }
217 else if (event.getId() == "prevcolor")
218 {
219 --mHairColorId;
220 updateHair();
221 }
222 else if (event.getId() == "nextstyle")
223 {
224 ++mHairStyleId;
225 updateHair();
226 }
227 else if (event.getId() == "prevstyle")
228 {
229 --mHairStyleId;
230 updateHair();
231 }
232 else if (event.getId() == "statslider")
233 {
235 }
236 else if (event.getId() == "gender")
237 {
238 if (mMale->isSelected())
240 else
242 }
243}
244
245std::string CharCreateDialog::getName() const
246{
247 std::string name = mNameField->getText();
248 trim(name);
249 return name;
250}
251
253{
254 int distributedPoints = 0;
255
256 // Update captions and synchronize values
257 for (unsigned i = 0; i < mAttributeSlider.size(); i++)
258 {
259 gcn::Slider *slider = mAttributeSlider[i];
260 gcn::Label *valueLabel = mAttributeValue[i];
261 int value = static_cast<int>(slider->getValue());
262
263 valueLabel->setCaption(toString(value));
264 valueLabel->adjustSize();
265
266 mAttributes[i] = value;
267 distributedPoints += value;
268 }
269
270 // Update distributed points
271 int pointsLeft = mMaxPoints - distributedPoints;
272 if (pointsLeft == 0)
273 {
274 mAttributesLeft->setCaption(_("Character stats OK"));
275 mCreateButton->setEnabled(true);
276 }
277 else
278 {
279 mCreateButton->setEnabled(false);
280 if (pointsLeft > 0)
281 {
282 mAttributesLeft->setCaption(
283 strprintf(_("Please distribute %d points"), pointsLeft));
284 }
285 else
286 {
287 mAttributesLeft->setCaption(
288 strprintf(_("Please remove %d points"), -pointsLeft));
289 }
290 }
291
292 mAttributesLeft->adjustSize();
293}
294
296{
297 mCreateButton->setEnabled(true);
298}
299
300void CharCreateDialog::setAttributes(const std::vector<std::string> &labels,
301 unsigned available, unsigned min,
302 unsigned max)
303{
304 mMaxPoints = available;
305
306 for (unsigned i = 0; i < mAttributeLabel.size(); i++)
307 {
308 remove(mAttributeLabel[i]);
309 delete mAttributeLabel[i];
310 remove(mAttributeSlider[i]);
311 delete mAttributeSlider[i];
312 remove(mAttributeValue[i]);
313 delete mAttributeValue[i];
314 }
315
316 mAttributeLabel.clear();
317 mAttributeSlider.clear();
318 mAttributeValue.clear();
319
320 mAttributes.resize(labels.size(), min);
321
322 int w = 200;
323 int h = 190;
324
325 // No attribute sliders when they can not be adapted by the user
326 if (min == max)
327 {
328 mAttributesLeft->setVisible(false);
329 mCreateButton->setEnabled(true);
330 }
331 else
332 {
333 h += 20 * labels.size() + 20;
334
335 mAttributeLabel.resize(labels.size());
336 mAttributeSlider.resize(labels.size());
337 mAttributeValue.resize(labels.size());
338
339 for (unsigned i = 0; i < labels.size(); i++)
340 {
341 const int y = 140 + i * 20;
342
343 auto *attributeLabel = new Label(labels[i]);
344 attributeLabel->setWidth(70);
345 attributeLabel->setPosition(5, y);
346 add(attributeLabel);
347
348 auto *attributeSlider = new Slider(min, max);
349 attributeSlider->setDimension(gcn::Rectangle(75, y, 100, attributeSlider->getHeight()));
350 attributeSlider->setActionEventId("statslider");
351 attributeSlider->addActionListener(this);
352 add(attributeSlider);
353
354 auto *attributeValue = new Label(toString(min));
355 attributeValue->setPosition(180, y);
356 add(attributeValue);
357
358 mAttributeLabel[i] = attributeLabel;
359 mAttributeSlider[i] = attributeSlider;
360 mAttributeValue[i] = attributeValue;
361 }
362
363 mAttributesLeft->setVisible(true);
364 mAttributesLeft->setPosition(15, h - 50);
366 }
367
368 mCancelButton->setPosition(
369 w - 5 - mCancelButton->getWidth(),
370 h - 5 - mCancelButton->getHeight());
371 mCreateButton->setPosition(
372 mCancelButton->getX() - 5 - mCreateButton->getWidth(),
373 h - 5 - mCancelButton->getHeight());
374
375 setContentSize(w, h);
376 center();
377}
378
380{
381 if (gender == Gender::Female)
382 {
383 mFemale->setSelected(true);
384 mMale->setSelected(false);
385 }
386 else
387 {
388 mMale->setSelected(true);
389 mFemale->setSelected(false);
390 }
391
392 mPlayer->setGender(gender);
393}
394
396{
397 if (mHairColorId < 0)
398 mHairColorId = mHairColorsIds.size() - 1;
399
400 if (mHairColorId > (int)mHairColorsIds.size() - 1)
401 mHairColorId = 0;
402
403 if (mHairStyleId < 0)
404 mHairStyleId = mHairStylesIds.size() - 1;
405
406 if (mHairStyleId > (int)mHairStylesIds.size() - 1)
407 mHairStyleId = 0;
408
409 // Should avoid the most common crash case
410 int hairStyle = mHairStylesIds.empty() ?
412 int hairColor = mHairColorsIds.empty() ?
414
415 mPlayer->setSprite(Net::getCharHandler()->hairSprite(),
416 hairStyle * -1, hairDB.getHairColor(hairColor));
417}
Gender
Definition being.h:58
Definition being.h:65
void setSprite(unsigned slot, int id, const std::string &color=std::string(), bool isWeapon=false)
Sets visible equipments for this being.
Definition being.cpp:1254
void setGender(Gender gender)
Sets the gender of this being.
Definition being.cpp:1313
Button widget.
Definition button.h:38
void setAttributes(const std::vector< std::string > &labels, unsigned available, unsigned min, unsigned max)
std::vector< int > mAttributes
gcn::RadioButton * mFemale
CharCreateDialog(CharSelectDialog *parent, int slot)
gcn::Label * mAttributesLeft
std::vector< gcn::Label * > mAttributeLabel
gcn::RadioButton * mMale
gcn::TextField * mNameField
std::vector< gcn::Label * > mAttributeValue
std::vector< gcn::Slider * > mAttributeSlider
gcn::Button * mCancelButton
std::string getName() const
Returns the name of the character to create.
void setDefaultGender(Gender gender)
gcn::Button * mCreateButton
gcn::Label * mNameLabel
std::vector< int > mHairColorsIds
std::vector< int > mHairStylesIds
void unlock()
Unlocks the dialog, enabling the create character button again.
void action(const gcn::ActionEvent &event) override
Character selection dialog.
std::vector< int > getHairStyleIds(int maxId) const
Returns the available hair style ids.
Definition hairdb.cpp:94
const std::string & getHairColor(int id) const
Definition hairdb.cpp:79
std::vector< int > getHairColorIds(int minId, int maxId) const
Returns the available hair color ids.
Definition hairdb.cpp:107
Label widget.
Definition label.h:34
virtual void newCharacter(const std::string &name, int slot, Gender gender, int hairstyle, int hairColor, const std::vector< int > &stats)=0
virtual void setCharCreateDialog(CharCreateDialog *window)=0
An 'Ok' button dialog.
Definition okdialog.h:34
A box showing a player character.
Definition playerbox.h:34
Guichan based RadioButton with custom look.
Definition radiobutton.h:30
Slider widget.
Definition slider.h:32
A text field.
Definition textfield.h:72
A window.
Definition window.h:59
void center()
Positions the window in the center of it's parent.
Definition window.cpp:768
virtual void setVisible(bool visible)
Overloads window setVisible by Guichan to allow sticky window handling.
Definition window.cpp:282
void setContentSize(int width, int height)
Sets the size of this window.
Definition window.cpp:151
void scheduleDelete()
Schedule this window for deletion.
Definition window.cpp:299
HairDB hairDB
Hair styles and colors info database.
Definition client.cpp:107
#define _(s)
Definition gettext.h:38
Net::CharHandler * charHandler
Definition net.cpp:47
const std::vector< int > & getDefaultItems()
Definition chardb.cpp:129
ServerType getNetworkType()
Definition net.cpp:200
CharHandler * getCharHandler()
Definition net.cpp:65
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
std::string & trim(std::string &str)
Trims spaces off the end and the beginning of the given string.
std::string toString(const T &arg)
Converts the given value to a string using std::stringstream.
Definition stringutils.h:68