1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2007-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "gui/windows/outfitwindow.h"
25 
26 #include "configuration.h"
27 #include "dragdrop.h"
28 #include "game.h"
29 
30 #include "being/playerinfo.h"
31 
32 #include "const/emoteshortcut.h"
33 
34 #include "enums/gui/layouttype.h"
35 
36 #include "input/inputactionoperators.h"
37 #include "input/inputmanager.h"
38 
39 #include "gui/viewport.h"
40 
41 #include "gui/popups/popupmenu.h"
42 
43 #include "gui/windows/setupwindow.h"
44 
45 #include "gui/widgets/button.h"
46 #include "gui/widgets/checkbox.h"
47 #include "gui/widgets/label.h"
48 #include "gui/widgets/layout.h"
49 
50 #include "utils/gettext.h"
51 
52 #include "resources/inventory/inventory.h"
53 
54 #include <sstream>
55 
56 #include "debug.h"
57 
58 OutfitWindow *outfitWindow = nullptr;
59 
OutfitWindow()60 OutfitWindow::OutfitWindow() :
61     // TRANSLATORS: outfits window name
62     Window(_("Outfits"), Modal_false, nullptr, "outfits.xml"),
63     ActionListener(),
64     // TRANSLATORS: outfits window button
65     mPreviousButton(new Button(this, _("<"), "previous", BUTTON_SKIN, this)),
66     // TRANSLATORS: outfits window button
67     mNextButton(new Button(this, _(">"), "next", BUTTON_SKIN, this)),
68     // TRANSLATORS: outfits window button
69     mEquipBottom(new Button(this, _("Equip"), "equip", BUTTON_SKIN, this)),
70     // TRANSLATORS: outfits window label
71     mCurrentLabel(new Label(this, strprintf(_("Outfit: %d"), 1))),
72     // TRANSLATORS: outfits window checkbox
73     mUnequipCheck(new CheckBox(this, _("Unequip first"),
74         serverConfig.getValueBool("OutfitUnequip0", true),
75         nullptr, std::string())),
76     // TRANSLATORS: outfits window checkbox
77     mAwayOutfitCheck(new CheckBox(this, _("Away outfit"),
78         serverConfig.getValue("OutfitAwayIndex", OUTFITS_COUNT - 1) != 0U,
79         nullptr, std::string())),
80     // TRANSLATORS: outfits window label
81     mKeyLabel(new Label(this, strprintf(_("Key: %s"),
82         keyName(0).c_str()))),
83     mBorderColor(getThemeColor(ThemeColorId::BORDER, 64)),
84     mCurrentOutfit(0),
85     mBoxWidth(33),
86     mBoxHeight(33),
87     mGridWidth(4),
88     mGridHeight(4),
89     mItems(),
90     mAwayOutfit(0),
91     mItemColors(),
92     mItemClicked(false),
93     mItemsUnequip()
94 {
95     setWindowName("Outfits");
96     setResizable(true);
97     setCloseButton(true);
98     setStickyButtonLock(true);
99 
100     mBackgroundColor = getThemeColor(ThemeColorId::BACKGROUND, 32);
101 
102     setDefaultSize(250, 400, 150, 290);
103     setMinWidth(145);
104     setMinHeight(220);
105 
106     if (setupWindow != nullptr)
107         setupWindow->registerWindowForReset(this);
108 
109     mCurrentLabel->setAlignment(Graphics::CENTER);
110     mKeyLabel->setAlignment(Graphics::CENTER);
111 
112     mUnequipCheck->setActionEventId("unequip");
113     mUnequipCheck->addActionListener(this);
114 
115     mAwayOutfitCheck->setActionEventId("away");
116     mAwayOutfitCheck->addActionListener(this);
117 
118     place(1, 3, mEquipBottom, 2, 1);
119     place(0, 4, mKeyLabel, 4, 1);
120     place(0, 5, mPreviousButton, 1, 1);
121     place(1, 5, mCurrentLabel, 2, 1);
122     place(3, 5, mNextButton, 1, 1);
123     place(0, 6, mUnequipCheck, 4, 1);
124     place(0, 7, mAwayOutfitCheck, 4, 1);
125 
126     Layout &layout = getLayout();
127     layout.setRowHeight(0, LayoutType::SET);
128     layout.setColWidth(4, Layout::CENTER);
129 
130     loadWindowState();
131 
132     enableVisibleSound(true);
133     load();
134 }
135 
~OutfitWindow()136 OutfitWindow::~OutfitWindow()
137 {
138     save();
139 }
140 
load()141 void OutfitWindow::load()
142 {
143     const Configuration *cfg = &serverConfig;
144 
145     memset(mItems, -1, sizeof(mItems));
146     memset(mItemColors, 1, sizeof(mItemColors));
147 
148     for (unsigned o = 0; o < OUTFITS_COUNT; o++)
149     {
150         std::string outfit = cfg->getValue("Outfit" + toString(o), "-1");
151         std::string buf;
152         std::stringstream ss(outfit);
153 
154         STD_VECTOR<int> tokens;
155 
156         while (ss >> buf)
157             tokens.push_back(atoi(buf.c_str()));
158 
159         for (size_t i = 0, sz = tokens.size();
160              i < sz && i < OUTFIT_ITEM_COUNT; i++)
161         {
162             mItems[o][i] = tokens[i];
163         }
164 
165         outfit = cfg->getValue("OutfitColor" + toString(o), "1");
166         std::stringstream ss2(outfit);
167 
168         tokens.clear();
169 
170         STD_VECTOR<unsigned char> tokens2;
171         while (ss2 >> buf)
172             tokens2.push_back(CAST_U8(atoi(buf.c_str())));
173 
174         for (size_t i = 0, sz = tokens2.size();
175              i < sz && i < OUTFIT_ITEM_COUNT; i++)
176         {
177             mItemColors[o][i] = fromInt(tokens2[i], ItemColor);
178         }
179 
180         mItemsUnequip[o] = cfg->getValueBool("OutfitUnequip" + toString(o),
181                                              true);
182     }
183     mAwayOutfit = cfg->getValue("OutfitAwayIndex", OUTFITS_COUNT - 1);
184     if (mAwayOutfit >= CAST_S32(OUTFITS_COUNT))
185         mAwayOutfit = CAST_S32(OUTFITS_COUNT) - 1;
186 
187     if (mAwayOutfitCheck != nullptr)
188         mAwayOutfitCheck->setSelected(mAwayOutfit == mCurrentOutfit);
189 }
190 
save() const191 void OutfitWindow::save() const
192 {
193     std::string outfitStr;
194     std::string outfitColorsStr;
195     for (unsigned o = 0; o < OUTFITS_COUNT; o++)
196     {
197         bool good = false;
198         for (unsigned i = 0; i < OUTFIT_ITEM_COUNT; i++)
199         {
200             const int val = mItems[o][i];
201             const int res = val != 0 ? val : -1;
202             if (res != -1)
203                 good = true;
204             outfitStr.append(toString(res));
205             if (i < OUTFIT_ITEM_COUNT - 1)
206                 outfitStr.append(" ");
207             outfitColorsStr.append(toString(CAST_S32(
208                 toInt(mItemColors[o][i], int))));
209             if (i < OUTFIT_ITEM_COUNT - 1)
210                 outfitColorsStr.append(" ");
211         }
212         if (good)
213         {
214             serverConfig.setValue("Outfit" + toString(o), outfitStr);
215             serverConfig.setValue("OutfitColor" + toString(o),
216                 outfitColorsStr);
217         }
218         else
219         {
220             serverConfig.deleteKey("Outfit" + toString(o));
221             serverConfig.deleteKey("OutfitColor" + toString(o));
222         }
223 
224         if (mItemsUnequip[o])
225         {
226             serverConfig.deleteKey("OutfitUnequip" + toString(o));
227         }
228         else
229         {
230             serverConfig.setValue("OutfitUnequip" + toString(o),
231                 mItemsUnequip[o]);
232         }
233         outfitStr.clear();
234         outfitColorsStr.clear();
235     }
236     serverConfig.setValue("OutfitAwayIndex", mAwayOutfit);
237 }
238 
action(const ActionEvent & event)239 void OutfitWindow::action(const ActionEvent &event)
240 {
241     const std::string &eventId = event.getId();
242     if (eventId == "next")
243     {
244         next();
245     }
246     else if (eventId == "previous")
247     {
248         previous();
249     }
250     else if (eventId == "unequip")
251     {
252         if (mCurrentOutfit >= 0 && mCurrentOutfit < CAST_S32(
253             OUTFITS_COUNT))
254         {
255             mItemsUnequip[mCurrentOutfit] = mUnequipCheck->isSelected();
256         }
257     }
258     else if (eventId == "equip")
259     {
260         wearOutfit(mCurrentOutfit,
261             true,
262             false);
263         if (Game::instance() != nullptr)
264             Game::instance()->setValidSpeed();
265     }
266     else if (eventId == "away")
267     {
268         mAwayOutfit = mCurrentOutfit;
269         if (!mAwayOutfitCheck->isSelected())
270             mAwayOutfitCheck->setSelected(true);
271     }
272 }
273 
wearOutfit(const int outfit,const bool unwearEmpty,const bool select)274 void OutfitWindow::wearOutfit(const int outfit,
275                               const bool unwearEmpty,
276                               const bool select)
277 {
278     bool isEmpty = true;
279 
280     if (outfit < 0 || outfit > CAST_S32(OUTFITS_COUNT))
281         return;
282 
283     for (unsigned i = 0; i < OUTFIT_ITEM_COUNT; i++)
284     {
285         const Item *const item = PlayerInfo::getInventory()->findItem(
286             mItems[outfit][i],
287             mItemColors[outfit][i]);
288         if ((item != nullptr)
289             && item->isEquipped() == Equipped_false
290             && (item->getQuantity() != 0))
291         {
292             if (item->isEquipment() == Equipm_true)
293             {
294                 PlayerInfo::equipItem(item, Sfx_false);
295                 isEmpty = false;
296             }
297         }
298     }
299 
300     if ((!isEmpty || unwearEmpty) && outfit < CAST_S32(OUTFITS_COUNT)
301         && mItemsUnequip[outfit])
302     {
303         unequipNotInOutfit(outfit);
304     }
305     if (select)
306     {
307         mCurrentOutfit = outfit;
308         showCurrentOutfit();
309     }
310 }
311 
copyOutfit(const int outfit)312 void OutfitWindow::copyOutfit(const int outfit)
313 {
314     copyOutfit(outfit, mCurrentOutfit);
315 }
316 
copyOutfit(const int src,const int dst)317 void OutfitWindow::copyOutfit(const int src, const int dst)
318 {
319     if (src < 0 || src > CAST_S32(OUTFITS_COUNT)
320         || dst < 0 || dst > CAST_S32(OUTFITS_COUNT))
321     {
322         return;
323     }
324 
325     for (unsigned int i = 0; i < OUTFIT_ITEM_COUNT; i++)
326         mItems[dst][i] = mItems[src][i];
327     save();
328 }
329 
draw(Graphics * const graphics)330 void OutfitWindow::draw(Graphics *const graphics)
331 {
332     BLOCK_START("OutfitWindow::draw")
333     Window::draw(graphics);
334 
335     if (mCurrentOutfit < 0 || mCurrentOutfit
336         >= static_cast<signed int>(OUTFITS_COUNT))
337     {
338         return;
339     }
340 
341     for (unsigned int i = 0; i < OUTFIT_ITEM_COUNT; i++)
342     {
343         const int itemX = mPadding + ((i % mGridWidth) * mBoxWidth);
344         const int itemY = mPadding + mTitleBarHeight
345             + ((i / CAST_U32(mGridWidth)) * mBoxHeight);
346 
347         graphics->setColor(mBorderColor);
348         graphics->drawRectangle(Rect(itemX, itemY, 32, 32));
349         graphics->setColor(mBackgroundColor);
350         graphics->fillRectangle(Rect(itemX, itemY, 32, 32));
351 
352         if (mItems[mCurrentOutfit][i] < 0)
353             continue;
354 
355         bool foundItem = false;
356         const Inventory *const inv = PlayerInfo::getInventory();
357         if (inv != nullptr)
358         {
359             const Item *const item = inv->findItem(mItems[mCurrentOutfit][i],
360                 mItemColors[mCurrentOutfit][i]);
361             if (item != nullptr)
362             {
363                 // Draw item icon.
364                 const Image *const image = item->getImage();
365                 if (image != nullptr)
366                 {
367                     graphics->drawImage(image, itemX, itemY);
368                     foundItem = true;
369                 }
370             }
371         }
372         if (!foundItem)
373         {
374             Image *const image = Item::getImage(mItems[mCurrentOutfit][i],
375                 mItemColors[mCurrentOutfit][i]);
376             if (image != nullptr)
377             {
378                 graphics->drawImage(image, itemX, itemY);
379                 image->decRef();
380             }
381         }
382     }
383     BLOCK_END("OutfitWindow::draw")
384 }
385 
safeDraw(Graphics * const graphics)386 void OutfitWindow::safeDraw(Graphics *const graphics)
387 {
388     BLOCK_START("OutfitWindow::draw")
389     Window::safeDraw(graphics);
390 
391     if (mCurrentOutfit < 0 || mCurrentOutfit
392         >= static_cast<signed int>(OUTFITS_COUNT))
393     {
394         return;
395     }
396 
397     for (unsigned int i = 0; i < OUTFIT_ITEM_COUNT; i++)
398     {
399         const int itemX = mPadding + ((i % mGridWidth) * mBoxWidth);
400         const int itemY = mPadding + mTitleBarHeight
401             + ((i / CAST_U32(mGridWidth)) * mBoxHeight);
402 
403         graphics->setColor(mBorderColor);
404         graphics->drawRectangle(Rect(itemX, itemY, 32, 32));
405         graphics->setColor(mBackgroundColor);
406         graphics->fillRectangle(Rect(itemX, itemY, 32, 32));
407 
408         if (mItems[mCurrentOutfit][i] < 0)
409             continue;
410 
411         bool foundItem = false;
412         const Inventory *const inv = PlayerInfo::getInventory();
413         if (inv != nullptr)
414         {
415             const Item *const item = inv->findItem(mItems[mCurrentOutfit][i],
416                 mItemColors[mCurrentOutfit][i]);
417             if (item != nullptr)
418             {
419                 // Draw item icon.
420                 const Image *const image = item->getImage();
421                 if (image != nullptr)
422                 {
423                     graphics->drawImage(image, itemX, itemY);
424                     foundItem = true;
425                 }
426             }
427         }
428         if (!foundItem)
429         {
430             Image *const image = Item::getImage(mItems[mCurrentOutfit][i],
431                 mItemColors[mCurrentOutfit][i]);
432             if (image != nullptr)
433             {
434                 graphics->drawImage(image, itemX, itemY);
435                 image->decRef();
436             }
437         }
438     }
439     BLOCK_END("OutfitWindow::draw")
440 }
441 
mouseDragged(MouseEvent & event)442 void OutfitWindow::mouseDragged(MouseEvent &event)
443 {
444     if (event.getButton() == MouseButton::LEFT)
445     {
446         if (dragDrop.isEmpty() && mItemClicked)
447         {
448             if (mCurrentOutfit < 0 || mCurrentOutfit
449                 >= static_cast<signed int>(OUTFITS_COUNT))
450             {
451                 Window::mouseDragged(event);
452                 return;
453             }
454 
455             const int index = getIndexFromGrid(event.getX(), event.getY());
456             if (index == -1)
457             {
458                 Window::mouseDragged(event);
459                 return;
460             }
461             const int itemId = mItems[mCurrentOutfit][index];
462             const ItemColor itemColor = mItemColors[mCurrentOutfit][index];
463             if (itemId < 0)
464             {
465                 Window::mouseDragged(event);
466                 return;
467             }
468             mMoved = false;
469             event.consume();
470             const Inventory *const inv = PlayerInfo::getInventory();
471             if (inv != nullptr)
472             {
473                 Item *const item = inv->findItem(itemId, itemColor);
474                 if (item != nullptr)
475                     dragDrop.dragItem(item, DragDropSource::Outfit, 0);
476                 else
477                     dragDrop.clear();
478                 mItems[mCurrentOutfit][index] = -1;
479             }
480         }
481     }
482     Window::mouseDragged(event);
483 }
484 
mousePressed(MouseEvent & event)485 void OutfitWindow::mousePressed(MouseEvent &event)
486 {
487     const int index = getIndexFromGrid(event.getX(), event.getY());
488     if (event.getButton() == MouseButton::RIGHT && (popupMenu != nullptr))
489     {
490         popupMenu->showOutfitsWindowPopup(viewport->mMouseX,
491             viewport->mMouseY);
492         event.consume();
493         return;
494     }
495     else if (index == -1)
496     {
497         Window::mousePressed(event);
498         return;
499     }
500     mMoved = false;
501     event.consume();
502 
503     if (mItems[mCurrentOutfit][index] > 0)
504     {
505         mItemClicked = true;
506     }
507     else
508     {
509         if (dragDrop.isSelected())
510         {
511             mItems[mCurrentOutfit][index] = dragDrop.getSelected();
512             mItemColors[mCurrentOutfit][index] = dragDrop.getSelectedColor();
513             dragDrop.deselect();
514             save();
515         }
516     }
517 
518     Window::mousePressed(event);
519 }
520 
mouseReleased(MouseEvent & event)521 void OutfitWindow::mouseReleased(MouseEvent &event)
522 {
523     if (event.getButton() == MouseButton::LEFT)
524     {
525         if (mCurrentOutfit < 0 || mCurrentOutfit
526             >= static_cast<signed int>(OUTFITS_COUNT))
527         {
528             return;
529         }
530         const int index = getIndexFromGrid(event.getX(), event.getY());
531         if (index == -1)
532         {
533             dragDrop.clear();
534             Window::mouseReleased(event);
535             return;
536         }
537         mMoved = false;
538         event.consume();
539         if (!dragDrop.isEmpty())
540         {
541             if (dragDrop.isSourceItemContainer())
542             {
543                 mItems[mCurrentOutfit][index] = dragDrop.getItem();
544                 mItemColors[mCurrentOutfit][index] = dragDrop.getItemColor();
545                 dragDrop.clear();
546                 dragDrop.deselect();
547                 save();
548             }
549         }
550         if (mItemClicked)
551             mItemClicked = false;
552     }
553     Window::mouseReleased(event);
554 }
555 
getIndexFromGrid(const int pointX,const int pointY) const556 int OutfitWindow::getIndexFromGrid(const int pointX, const int pointY) const
557 {
558     const Rect tRect = Rect(mPadding, mTitleBarHeight,
559         mGridWidth * mBoxWidth, mGridHeight * mBoxHeight);
560     if (!tRect.isPointInRect(pointX, pointY))
561         return -1;
562     const int index = (((pointY - mTitleBarHeight) / mBoxHeight) * mGridWidth)
563         + (pointX - mPadding) / mBoxWidth;
564     if (index >= CAST_S32(OUTFIT_ITEM_COUNT) || index < 0)
565         return -1;
566     return index;
567 }
568 
unequipNotInOutfit(const int outfit) const569 void OutfitWindow::unequipNotInOutfit(const int outfit) const
570 {
571     // here we think that outfit is correct index
572 
573     const Inventory *const inventory = PlayerInfo::getInventory();
574     if (inventory == nullptr)
575         return;
576 
577     const unsigned int invSize = inventory->getSize();
578     for (unsigned i = 0; i < invSize; i++)
579     {
580         const Item *const item = inventory->getItem(i);
581         if ((item != nullptr) && item->isEquipped() == Equipped_true)
582         {
583             bool found = false;
584             for (unsigned f = 0; f < OUTFIT_ITEM_COUNT; f++)
585             {
586                 if (item->getId() == mItems[outfit][f])
587                 {
588                     found = true;
589                     break;
590                 }
591             }
592             if (!found)
593                 PlayerInfo::unequipItem(item, Sfx_false);
594         }
595     }
596 }
597 
keyName(const int number)598 std::string OutfitWindow::keyName(const int number)
599 {
600     if (number < 0 || number >= SHORTCUT_EMOTES)
601         return "";
602     return inputManager.getKeyStringLong(InputAction::OUTFIT_1 + number);
603 }
604 
next()605 void OutfitWindow::next()
606 {
607     if (mCurrentOutfit < (CAST_S32(OUTFITS_COUNT) - 1))
608         mCurrentOutfit++;
609     else
610         mCurrentOutfit = 0;
611     showCurrentOutfit();
612 }
613 
previous()614 void OutfitWindow::previous()
615 {
616     if (mCurrentOutfit > 0)
617         mCurrentOutfit--;
618     else
619         mCurrentOutfit = OUTFITS_COUNT - 1;
620     showCurrentOutfit();
621 }
622 
showCurrentOutfit()623 void OutfitWindow::showCurrentOutfit()
624 {
625     // TRANSLATORS: outfits window label
626     mCurrentLabel->setCaption(strprintf(_("Outfit: %d"), mCurrentOutfit + 1));
627     if (mCurrentOutfit < CAST_S32(OUTFITS_COUNT))
628         mUnequipCheck->setSelected(mItemsUnequip[mCurrentOutfit]);
629     else
630         mUnequipCheck->setSelected(false);
631     // TRANSLATORS: outfits window label
632     mKeyLabel->setCaption(strprintf(_("Key: %s"),
633         keyName(mCurrentOutfit).c_str()));
634     mAwayOutfitCheck->setSelected(mAwayOutfit == mCurrentOutfit);
635 }
636 
wearNextOutfit(const bool all)637 void OutfitWindow::wearNextOutfit(const bool all)
638 {
639     next();
640     if (!all && mCurrentOutfit >= 0 && mCurrentOutfit
641         < CAST_S32(OUTFITS_COUNT))
642     {
643         bool fromStart = false;
644         while (!mItemsUnequip[mCurrentOutfit])
645         {
646             next();
647             if (mCurrentOutfit == 0)
648             {
649                 if (!fromStart)
650                     fromStart = true;
651                 else
652                     return;
653             }
654         }
655     }
656     wearOutfit(mCurrentOutfit,
657         true,
658         false);
659 }
660 
wearPreviousOutfit(const bool all)661 void OutfitWindow::wearPreviousOutfit(const bool all)
662 {
663     previous();
664     if (!all && mCurrentOutfit >= 0 && mCurrentOutfit
665         < CAST_S32(OUTFITS_COUNT))
666     {
667         bool fromStart = false;
668         while (!mItemsUnequip[mCurrentOutfit])
669         {
670             previous();
671             if (mCurrentOutfit == 0)
672             {
673                 if (!fromStart)
674                     fromStart = true;
675                 else
676                     return;
677             }
678         }
679     }
680     wearOutfit(mCurrentOutfit,
681         true,
682         false);
683 }
684 
copyFromEquiped()685 void OutfitWindow::copyFromEquiped()
686 {
687     copyFromEquiped(mCurrentOutfit);
688 }
689 
copyFromEquiped(const int dst)690 void OutfitWindow::copyFromEquiped(const int dst)
691 {
692     const Inventory *const inventory = PlayerInfo::getInventory();
693     if (inventory == nullptr)
694         return;
695 
696     int outfitCell = 0;
697     for (unsigned i = 0, sz = inventory->getSize(); i < sz; i++)
698     {
699         const Item *const item = inventory->getItem(i);
700         if ((item != nullptr) && item->isEquipped() == Equipped_true)
701         {
702             mItems[dst][outfitCell] = item->getId();
703             mItemColors[dst][outfitCell++] = item->getColor();
704             if (outfitCell >= CAST_S32(OUTFIT_ITEM_COUNT))
705                 break;
706         }
707     }
708     save();
709 }
710 
wearAwayOutfit()711 void OutfitWindow::wearAwayOutfit()
712 {
713     copyFromEquiped(OUTFITS_COUNT);
714     wearOutfit(mAwayOutfit,
715         false,
716         false);
717 }
718 
unwearAwayOutfit()719 void OutfitWindow::unwearAwayOutfit()
720 {
721     wearOutfit(OUTFITS_COUNT,
722         true,
723         false);
724 }
725 
clearCurrentOutfit()726 void OutfitWindow::clearCurrentOutfit()
727 {
728     if (mCurrentOutfit < 0 || mCurrentOutfit
729         >= static_cast<signed int>(OUTFITS_COUNT))
730     {
731         return;
732     }
733     for (unsigned f = 0; f < OUTFIT_ITEM_COUNT; f++)
734     {
735         mItems[mCurrentOutfit][f] = -1;
736         mItemColors[mCurrentOutfit][f] = ItemColor_one;
737     }
738     save();
739 }
740 
getOutfitString() const741 std::string OutfitWindow::getOutfitString() const
742 {
743     std::string str;
744     for (unsigned int i = 0; i < OUTFIT_ITEM_COUNT; i++)
745     {
746         const int id = mItems[mCurrentOutfit][i];
747         if (id < 0)
748             continue;
749 
750         const ItemColor color = mItemColors[mCurrentOutfit][i];
751         STD_VECTOR<int> ids;
752         ids.push_back(id);
753         ids.push_back(CAST_S32(color));
754 
755         const std::string name = ItemDB::getNamesStr(ids);
756         if (name.empty())
757             continue;
758         str.append("[");
759         str.append(name);
760         str.append("] ");
761     }
762     return str;
763 }
764