Fixed adding/deleting characters.
[tmw:aethyra.git] / src / eathena / gui / charselect.cpp
1 /*
2  *  Aethyra
3  *  Copyright (C) 2004  The Mana World Development Team
4  *
5  *  This file is part of Aethyra based on original code
6  *  from The Mana World.
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, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <string>
24
25 #include <guichan/font.hpp>
26
27 #include "charselect.h"
28
29 #include "../db/colordb.h"
30
31 #include "../net/logindata.h"
32 #include "../net/messageout.h"
33
34 #include "../../main.h"
35
36 #include "../../bindings/guichan/layout.h"
37
38 #include "../../bindings/guichan/dialogs/confirmdialog.h"
39 #include "../../bindings/guichan/dialogs/okdialog.h"
40
41 #include "../../bindings/guichan/widgets/beingbox.h"
42 #include "../../bindings/guichan/widgets/button.h"
43 #include "../../bindings/guichan/widgets/container.h"
44 #include "../../bindings/guichan/widgets/label.h"
45 #include "../../bindings/guichan/widgets/textfield.h"
46
47 #include "../../core/map/sprite/localplayer.h"
48
49 #include "../../core/utils/dtor.h"
50 #include "../../core/utils/gettext.h"
51 #include "../../core/utils/lockedarray.h"
52 #include "../../core/utils/stringutils.h"
53
54 CharCreateDialog *charCreateDialog = NULL;
55 CharSelectDialog *charSelectDialog = NULL;
56
57 /**
58  * Listener for confirming character deletion.
59  */
60 class CharDeleteConfirm : public ConfirmDialog
61 {
62     public:
63         CharDeleteConfirm(CharSelectDialog *master);
64         void action(const gcn::ActionEvent &event);
65     private:
66         CharSelectDialog *master;
67 };
68
69 CharDeleteConfirm::CharDeleteConfirm(CharSelectDialog *m):
70     ConfirmDialog(_("Confirm Character Delete"),
71                   _("Are you sure you want to delete this character?"), m),
72     master(m)
73 {
74 }
75
76 void CharDeleteConfirm::action(const gcn::ActionEvent &event)
77 {
78     //ConfirmDialog::action(event);
79     if (event.getId() == "yes")
80         master->attemptCharDelete();
81
82     ConfirmDialog::action(event);
83 }
84
85 CharSelectDialog::CharSelectDialog(LockedArray<LocalPlayer*> *charInfo,
86                                    Gender gender):
87     Window(_("Select Character")),
88     mCharInfo(charInfo),
89     mCharDeleteConfirm(NULL),
90     mGender(gender),
91     mCharSelected(false)
92 {
93     charSelectDialog = this;
94
95     // Control that shows the Player
96     mBeingBox = new BeingBox();
97     mBeingBox->setWidth(74);
98
99     mNameLabel = new Label(strprintf(_("Name: %s"), ""));
100     mLevelLabel = new Label(strprintf(_("Level: %d"), 0));
101     mJobLevelLabel = new Label(strprintf(_("Job Level: %d"), 0));
102     mMoneyLabel = new Label(strprintf(_("Money: %d"), 0));
103
104     mPreviousButton = new Button(_("Previous"), "previous", this);
105     mNextButton = new Button(_("Next"), "next", this);
106     mNewDelCharButton = new Button(_("New"), "newdel", this);
107     mSelectButton = new Button(_("OK"), "ok", this);
108     mCancelButton = new Button(_("Cancel"), "cancel", this);
109
110     updatePlayerInfo();
111 }
112
113 CharSelectDialog::~CharSelectDialog()
114 {
115     destroy(mCharDeleteConfirm);
116     mCharInfo->clear();
117
118     charSelectDialog = NULL;
119 }
120
121 void CharSelectDialog::fontChanged()
122 {
123     Window::fontChanged();
124
125     if (mWidgets.size() > 0)
126         clear();
127
128     ContainerPlacer place;
129     place = getPlacer(0, 0);
130
131     place(0, 0, mBeingBox, 1, 6).setPadding(3);
132     place(1, 0, mNewDelCharButton);
133     place(1, 1, mNameLabel, 5);
134     place(1, 2, mLevelLabel, 5);
135     place(1, 3, mJobLevelLabel, 5);
136     place(1, 4, mMoneyLabel, 5);
137     place.getCell().matchColWidth(1, 4);
138     place = getPlacer(0, 2);
139     place(0, 0, mPreviousButton);
140     place(1, 0, mNextButton);
141     place(4, 0, mCancelButton);
142     place(5, 0, mSelectButton);
143
144     reflowLayout(250, 0);
145
146     restoreFocus();
147 }
148
149 void CharSelectDialog::action(const gcn::ActionEvent &event)
150 {
151     if (event.getId() == "ok" && loginData.slots > 0)
152     {
153         // Start game
154         mNewDelCharButton->setEnabled(false);
155         mSelectButton->setEnabled(false);
156         mPreviousButton->setEnabled(false);
157         mNextButton->setEnabled(false);
158         mCancelButton->setEnabled(false);
159         mCharSelected = true;
160         attemptCharSelect();
161     }
162     else if (event.getId() == "cancel")
163         state = EXIT_STATE;
164     else if (event.getId() == "newdel")
165     {
166         // Check for a character
167         if (mCharInfo->getEntry())
168         {
169             if (!mCharDeleteConfirm)
170                 mCharDeleteConfirm = new CharDeleteConfirm(this);
171             else
172                 mCharDeleteConfirm->setVisible(true);
173         }
174         // Start new character dialog
175         else if (loginData.slots < MAX_PLAYER_SLOTS)
176             charCreateDialog = new CharCreateDialog(this, mCharInfo->getPos(),
177                                                     mGender);
178     }
179     else if (event.getId() == "previous")
180     {
181         mCharInfo->prev();
182         updatePlayerInfo();
183     }
184     else if (event.getId() == "next")
185     {
186         mCharInfo->next();
187         updatePlayerInfo();
188     }
189 }
190
191 void CharSelectDialog::updatePlayerInfo()
192 {
193     LocalPlayer *pi = mCharInfo->getEntry();
194
195     if (pi)
196     {
197         mNameLabel->setCaption(strprintf(_("Name: %s"), pi->getName().c_str()));
198         mLevelLabel->setCaption(strprintf(_("Level: %d"), pi->mLevel));
199         mJobLevelLabel->setCaption(strprintf(_("Job Level: %d"), pi->mJobLevel));
200         mMoneyLabel->setCaption(strprintf(_("Gold: %d"), pi->mGp));
201         if (!mCharSelected)
202         {
203             mNewDelCharButton->setCaption(_("Delete"));
204             mNewDelCharButton->adjustSize();
205             fontChanged();
206             mSelectButton->setEnabled(true);
207             mSelectButton->requestFocus();
208         }
209     }
210     else
211     {
212         mNameLabel->setCaption(strprintf(_("Name: %s"), ""));
213         mLevelLabel->setCaption(strprintf(_("Level: %d"), 0));
214         mJobLevelLabel->setCaption(strprintf(_("Job Level: %d"), 0));
215         mMoneyLabel->setCaption(strprintf(_("Money: %s"), ""));
216         mNewDelCharButton->setCaption(_("New"));
217         mNewDelCharButton->adjustSize();
218         fontChanged();
219         mSelectButton->setEnabled(false);
220     }
221
222     mBeingBox->setBeing(pi);
223 }
224
225 void CharSelectDialog::attemptCharDelete()
226 {
227     // Request character deletion
228     MessageOut outMsg(0x0068);
229     outMsg.writeInt32(mCharInfo->getEntry()->mCharId);
230     outMsg.writeString("a@a.com", 40);
231     mCharInfo->lock();
232 }
233
234 void CharSelectDialog::attemptCharSelect()
235 {
236     // Request character selection
237     MessageOut outMsg(0x0066);
238     outMsg.writeInt8(mCharInfo->getPos());
239     mCharInfo->lock();
240 }
241
242 void CharSelectDialog::requestFocus()
243 {
244     Window::requestFocus();
245
246     if (mSelectButton->isEnabled())
247         mSelectButton->requestFocus();
248     else
249         mNewDelCharButton->requestFocus();
250 }
251
252 bool CharSelectDialog::selectByName(const std::string &name)
253 {
254     if (mCharInfo->isLocked())
255         return false;
256
257     unsigned int oldPos = mCharInfo->getPos();
258
259     mCharInfo->select(0);
260     do
261     {
262         LocalPlayer *player = mCharInfo->getEntry();
263
264         if (player && player->getName() == name)
265             return true;
266
267         mCharInfo->next();
268     } while (mCharInfo->getPos());
269
270     mCharInfo->select(oldPos);
271
272     return false;
273 }
274
275 CharCreateDialog::CharCreateDialog(Window *parent, int slot, Gender gender):
276     Window(_("Create Character"), true, parent),
277     mSlot(slot)
278 {
279     mPlayer = new Player(0, 0, NULL);
280     mPlayer->setGender(gender);
281
282     int numberOfHairColors = ColorDB::size();
283
284     mPlayer->setHairStyle(rand() % mPlayer->getNumOfHairstyles(),
285                           rand() % numberOfHairColors);
286
287     mNameField = new TextField("");
288     mNameLabel = new Label(_("Name:"));
289     mNextHairColorButton = new Button(">", "nextcolor", this);
290     mPrevHairColorButton = new Button("<", "prevcolor", this);
291     mHairColorLabel = new Label(_("Hair Color:"));
292     mNextHairStyleButton = new Button(">", "nextstyle", this);
293     mPrevHairStyleButton = new Button("<", "prevstyle", this);
294     mHairStyleLabel = new Label(_("Hair Style:"));
295     mCreateButton = new Button(_("Create"), "create", this);
296     mCancelButton = new Button(_("Cancel"), "cancel", this);
297     mBeingBox = new BeingBox(mPlayer);
298
299     mBeingBox->setWidth(74);
300
301     mNameField->setActionEventId("create");
302     mNameField->addActionListener(this);
303
304     fontChanged();
305
306     setLocationRelativeTo(getParent());
307     setVisible(true);
308
309     mNameField->requestFocus();
310 }
311
312 void CharCreateDialog::fontChanged()
313 {
314     Window::fontChanged();
315
316     if (mWidgets.size() > 0)
317         clear();
318
319     ContainerPlacer place;
320     place = getPlacer(0, 0);
321
322     place(0, 0, mNameLabel, 1);
323     place(1, 0, mNameField, 6);
324     place(0, 1, mHairStyleLabel, 1);
325     place(1, 1, mPrevHairStyleButton);
326     place(2, 1, mBeingBox, 1, 8).setPadding(3);
327     place(3, 1, mNextHairStyleButton);
328     place(0, 2, mHairColorLabel, 1);
329     place(1, 2, mPrevHairColorButton);
330     place(3, 2, mNextHairColorButton);
331     place.getCell().matchColWidth(0, 2);
332     place = getPlacer(0, 2);
333     place(4, 0, mCancelButton);
334     place(5, 0, mCreateButton);
335
336     reflowLayout(225, 0);
337
338     restoreFocus();
339 }
340
341 CharCreateDialog::~CharCreateDialog()
342 {
343     destroy(mPlayer);
344     charCreateDialog = NULL;
345 }
346
347 void CharCreateDialog::action(const gcn::ActionEvent &event)
348 {
349     const int numberOfColors = ColorDB::size();
350     const int numberOfHair = mPlayer->getNumOfHairstyles();
351     if (event.getId() == "create")
352     {
353         if (getName().length() >= 4)
354         {
355             // Attempt to create the character
356             mCreateButton->setEnabled(false);
357             attemptCharCreate();
358         }
359         else
360         {
361             new OkDialog("Error",
362                          "Your name needs to be at least 4 characters.", this);
363         }
364     }
365     else if (event.getId() == "cancel")
366         close();
367     else if (event.getId() == "nextcolor")
368         mPlayer->setHairStyle(mPlayer->getHairStyle(),
369                              (mPlayer->getHairColor() + 1) % numberOfColors);
370     else if (event.getId() == "prevcolor")
371         mPlayer->setHairStyle(mPlayer->getHairStyle(), (mPlayer->getHairColor() +
372                               numberOfColors - 1) % numberOfColors);
373     else if (event.getId() == "nextstyle")
374         mPlayer->setHairStyle((mPlayer->getHairStyle() + 1) % numberOfHair,
375                               mPlayer->getHairColor());
376     else if (event.getId() == "prevstyle")
377         mPlayer->setHairStyle((mPlayer->getHairStyle() + numberOfHair - 1) %
378                                numberOfHair, mPlayer->getHairColor());
379 }
380
381 void CharCreateDialog::close()
382 {
383     Window::close();
384     windowContainer->scheduleDelete(this);
385 }
386
387 std::string CharCreateDialog::getName()
388 {
389     std::string name = mNameField->getText();
390     trim(name);
391     return name;
392 }
393
394 void CharCreateDialog::unlock()
395 {
396     mCreateButton->setEnabled(true);
397 }
398
399 void CharCreateDialog::attemptCharCreate()
400 {
401     // Send character infos
402     MessageOut outMsg(0x0067);
403     outMsg.writeString(getName(), 24);
404     outMsg.writeInt8(5);
405     outMsg.writeInt8(5);
406     outMsg.writeInt8(5);
407     outMsg.writeInt8(5);
408     outMsg.writeInt8(5);
409     outMsg.writeInt8(5);
410     outMsg.writeInt8(mSlot);
411     outMsg.writeInt16(mPlayer->getHairColor());
412     outMsg.writeInt16(mPlayer->getHairStyle());
413 }