GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/shopwindow.cpp Lines: 128 540 23.7 %
Date: 2021-03-17 Branches: 119 838 14.2 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-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/shopwindow.h"
25
26
#include "const/resources/currency.h"
27
28
#ifdef TMWA_SUPPORT
29
#include "gui/windows/buydialog.h"
30
#include "gui/windows/chatwindow.h"
31
#include "gui/windows/confirmdialog.h"
32
#include "gui/windows/shopselldialog.h"
33
#include "gui/windows/tradewindow.h"
34
35
#include "const/sound.h"
36
37
#include "const/gui/chat.h"
38
#endif  // TMWA_SUPPORT
39
40
#include "enums/gui/layouttype.h"
41
42
#include "gui/windows/editdialog.h"
43
44
#include "gui/windows/itemamountwindow.h"
45
#include "gui/windows/setupwindow.h"
46
47
#include "gui/models/shopitems.h"
48
49
#include "gui/widgets/button.h"
50
#include "gui/widgets/checkbox.h"
51
#include "gui/widgets/containerplacer.h"
52
#include "gui/widgets/createwidget.h"
53
#include "gui/widgets/layout.h"
54
#include "gui/widgets/scrollarea.h"
55
#include "gui/widgets/shoplistbox.h"
56
#include "gui/widgets/tabstrip.h"
57
58
#include "listeners/shoprenamelistener.h"
59
60
#ifdef TMWA_SUPPORT
61
#include "actormanager.h"
62
#include "soundmanager.h"
63
#endif  // TMWA_SUPPORT
64
#include "configuration.h"
65
#include "settings.h"
66
67
#include "being/localplayer.h"
68
#include "being/playerinfo.h"
69
70
#ifdef TMWA_SUPPORT
71
#include "being/playerrelations.h"
72
#include "net/chathandler.h"
73
#endif  // TMWA_SUPPORT
74
#include "net/buyingstorehandler.h"
75
#include "net/vendinghandler.h"
76
#include "net/net.h"
77
#ifdef TMWA_SUPPORT
78
#include "net/tradehandler.h"
79
#endif  // TMWA_SUPPORT
80
81
#include "utils/checkutils.h"
82
#include "utils/delete2.h"
83
#include "utils/foreach.h"
84
#include "utils/gettext.h"
85
86
#ifdef TMWA_SUPPORT
87
#include "resources/iteminfo.h"
88
#endif  // TMWA_SUPPORT
89
90
#include "resources/inventory/inventory.h"
91
92
#include "resources/item/shopitem.h"
93
94
#include <sys/stat.h>
95
96
#include <fstream>
97
#include <sstream>
98
99
#include "debug.h"
100
101
ShopWindow *shopWindow = nullptr;
102
extern std::string tradePartnerName;
103
1
ShopWindow::DialogList ShopWindow::instances;
104
105
1
ShopWindow::ShopWindow() :
106
    // TRANSLATORS: shop window name
107
1
    Window(_("Personal Shop"), Modal_false, nullptr, "shop.xml"),
108
    VendingModeListener(),
109
    VendingSlotsListener(),
110
    BuyingStoreModeListener(),
111
    BuyingStoreSlotsListener(),
112
    ActionListener(),
113
    SelectionListener(),
114
    // TRANSLATORS: shop window button
115

2
    mCloseButton(new Button(this, _("Close"), "close", BUTTON_SKIN, this)),
116

1
    mBuyShopItems(new ShopItems(false, DEFAULT_CURRENCY)),
117

1
    mSellShopItems(new ShopItems(false, DEFAULT_CURRENCY)),
118
    mTradeItem(nullptr),
119

1
    mBuyShopItemList(CREATEWIDGETR(ShopListBox,
120
        this, mBuyShopItems, mBuyShopItems, ShopListBoxType::BuyShop)),
121

1
    mSellShopItemList(CREATEWIDGETR(ShopListBox,
122
        this, mSellShopItems, mSellShopItems, ShopListBoxType::SellShop)),
123
    mCurrentShopItemList(nullptr),
124
1
    mScrollArea(new ScrollArea(this, mBuyShopItemList,
125

4
        fromBool(getOptionBool("showbuybackground", false), Opaque),
126

2
        "shop_buy_background.xml")),
127
    // TRANSLATORS: shop window label
128

2
    mAddButton(new Button(this, _("Add"), "add", BUTTON_SKIN, this)),
129
    // TRANSLATORS: shop window label
130

2
    mDeleteButton(new Button(this, _("Delete"), "delete", BUTTON_SKIN, this)),
131
    mAnnounceButton(nullptr),
132
    mPublishButton(nullptr),
133
    mRenameButton(nullptr),
134
    mAnnounceLinks(nullptr),
135
    mTabs(nullptr),
136
    mAcceptPlayer(),
137
    mTradeNick(),
138
    mSellShopName(serverConfig.getStringValue("sellShopName")),
139
    mSelectedItem(-1),
140
    mAnnonceTime(0),
141
    mLastRequestTimeList(0),
142
    mLastRequestTimeItem(0),
143
    mRandCounter(0),
144
    mTradeMoney(0),
145
    mSellShopSize(0),
146
    mBuyShopSize(0),
147
    isBuySelected(true),
148
1
    mHaveVending(Net::getNetworkType() != ServerType::TMWATHENA),
149
    mEnableBuyingStore(false),
150







46
    mEnableVending(false)
151
{
152
5
    setWindowName("Personal Shop");
153
1
    setResizable(true);
154
1
    setCloseButton(true);
155
1
    setStickyButtonLock(true);
156
1
    setMinWidth(300);
157
1
    setMinHeight(220);
158
1
    if (mainGraphics->mWidth > 600)
159
1
        setDefaultSize(500, 300, ImagePosition::CENTER, 0, 0);
160
    else
161
        setDefaultSize(380, 300, ImagePosition::CENTER, 0, 0);
162
163
1
    if (setupWindow != nullptr)
164
        setupWindow->registerWindowForReset(this);
165
166

4
    const int size = config.getIntValue("fontSize")
167

4
        + getOption("tabHeightAdjust", 16);
168

4
    mTabs = new TabStrip(this, "shop", size, 0);
169
1
    mTabs->addActionListener(this);
170
5
    mTabs->setActionEventId("tab_");
171
    // TRANSLATORS: shop window tab name
172

7
    mTabs->addButton(_("Buy"), "buy", true);
173
    // TRANSLATORS: shop window tab name
174

7
    mTabs->addButton(_("Sell"), "sell", false);
175
2
    mTabs->setSelectable(false);
176
2
    mScrollArea->setSelectable(false);
177
178
1
    loadList();
179
180
1
    mBuyShopItemList->setPriceCheck(false);
181
1
    mSellShopItemList->setPriceCheck(false);
182
183
1
    mScrollArea->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
184
185
1
    mBuyShopItemList->addSelectionListener(this);
186
1
    mSellShopItemList->addSelectionListener(this);
187
188
1
    ContainerPlacer placer(nullptr, nullptr);
189
1
    placer = getPlacer(0, 0);
190
191
2
    placer(0, 0, mTabs, 8, 1).setPadding(3);
192
193
1
    if (mHaveVending)
194
    {
195
1
        mPublishButton = new Button(this,
196
            // TRANSLATORS: shop window button
197
1
            _("Publish"),
198
            "publish",
199
            BUTTON_SKIN,
200


7
            this);
201
1
        mRenameButton = new Button(this,
202
            // TRANSLATORS: shop window button
203
1
            _("Rename"),
204
            "rename",
205
            BUTTON_SKIN,
206


7
            this);
207
1
        placer(2, 6, mPublishButton, 1, 1);
208
1
        placer(3, 6, mRenameButton, 1, 1);
209
    }
210
    else
211
    {
212
        mAnnounceButton = new Button(this,
213
            // TRANSLATORS: shop window button
214
            _("Announce"),
215
            "announce",
216
            BUTTON_SKIN,
217
            this);
218
        // TRANSLATORS: shop window checkbox
219
        mAnnounceLinks = new CheckBox(this, _("Show links in announce"), false,
220
            this, "link announce");
221
222
        placer(2, 6, mAnnounceButton, 1, 1);
223
        placer(0, 7, mAnnounceLinks, 7, 1);
224
    }
225
226
2
    placer(0, 1, mScrollArea, 8, 5).setPadding(3);
227
1
    placer(0, 6, mAddButton, 1, 1);
228
1
    placer(1, 6, mDeleteButton, 1, 1);
229
1
    placer(7, 6, mCloseButton, 1, 1);
230
231
1
    Layout &layout = getLayout();
232
1
    layout.setRowHeight(0, LayoutType::SET);
233
234
1
    center();
235
1
    loadWindowState();
236
1
    updateShopName();
237
2
    instances.push_back(this);
238
1
}
239
240
1
void ShopWindow::postInit()
241
{
242
1
    Window::postInit();
243
1
    setVisible(Visible_false);
244
2
    enableVisibleSound(true);
245
1
    updateSelection();
246
1
}
247
248
8
ShopWindow::~ShopWindow()
249
{
250
1
    saveList();
251
252
1
    delete2(mBuyShopItemList)
253
1
    delete2(mSellShopItemList)
254
1
    delete2(mBuyShopItems)
255
1
    delete2(mSellShopItems)
256
1
    delete2(mTradeItem)
257
258
1
    instances.remove(this);
259
2
}
260
261
void ShopWindow::action(const ActionEvent &event)
262
{
263
    const std::string &eventId = event.getId();
264
    if (eventId == "close")
265
    {
266
        close();
267
        return;
268
    }
269
#ifdef TMWA_SUPPORT
270
    else if (eventId == "yes")
271
    {
272
        startTrade();
273
    }
274
    else if (eventId == "no")
275
    {
276
        mTradeNick.clear();
277
    }
278
    else if (eventId == "ignore")
279
    {
280
        playerRelations.ignoreTrade(mTradeNick);
281
        mTradeNick.clear();
282
    }
283
    else if (eventId == "announce")
284
    {
285
        if (isBuySelected)
286
        {
287
            if (mBuyShopItems->getNumberOfElements() > 0)
288
                announce(mBuyShopItems, BUY);
289
        }
290
        else if (mSellShopItems->getNumberOfElements() > 0)
291
        {
292
            announce(mSellShopItems, SELL);
293
        }
294
    }
295
#endif  // TMWA_SUPPORT
296
    else if (eventId == "delete")
297
    {
298
        if (isBuySelected)
299
        {
300
            if (mBuyShopItemList != nullptr &&
301
                mBuyShopItemList->getSelected() >= 0)
302
            {
303
                mBuyShopItems->del(mBuyShopItemList->getSelected());
304
                if (isShopEmpty() && (localPlayer != nullptr))
305
                    localPlayer->updateStatus();
306
            }
307
        }
308
        else if ((mSellShopItemList != nullptr)
309
                 && mSellShopItemList->getSelected() >= 0)
310
        {
311
            mSellShopItems->del(mSellShopItemList->getSelected());
312
            if (isShopEmpty() && (localPlayer != nullptr))
313
                localPlayer->updateStatus();
314
        }
315
    }
316
    else if (eventId == "tab_buy")
317
    {
318
        isBuySelected = true;
319
        updateSelection();
320
    }
321
    else if (eventId == "tab_sell")
322
    {
323
        isBuySelected = false;
324
        updateSelection();
325
    }
326
    else if (eventId == "publish")
327
    {
328
        if (isBuySelected)
329
        {
330
            if (mEnableBuyingStore)
331
            {
332
                buyingStoreHandler->close();
333
                BuyingStoreModeListener::distributeEvent(false);
334
            }
335
            else
336
            {
337
                STD_VECTOR<ShopItem*> &items = mBuyShopItems->items();
338
                if (!items.empty())
339
                {
340
                    buyingStoreHandler->create(mSellShopName,
341
                        PlayerInfo::getAttribute(Attributes::MONEY),
342
                        true,
343
                        items);
344
                }
345
            }
346
        }
347
        else
348
        {
349
            if (mEnableVending)
350
            {
351
                vendingHandler->close();
352
                VendingModeListener::distributeEvent(false);
353
            }
354
            else
355
            {
356
                STD_VECTOR<ShopItem*> &oldItems = mSellShopItems->items();
357
                STD_VECTOR<ShopItem*> items;
358
                const Inventory *const inv = PlayerInfo::getCartInventory();
359
                if (inv == nullptr)
360
                    return;
361
                FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, oldItems)
362
                {
363
                    ShopItem *const item = *it;
364
                    if (item == nullptr)
365
                        continue;
366
                    const Item *const cartItem = inv->findItem(item->getId(),
367
                        item->getColor());
368
                    if (cartItem == nullptr)
369
                        continue;
370
                    item->setInvIndex(cartItem->getInvIndex());
371
                    const int amount = cartItem->getQuantity();
372
                    if (amount == 0)
373
                        continue;
374
                    if (item->getQuantity() > amount)
375
                        item->setQuantity(amount);
376
                    items.push_back(item);
377
                    if (static_cast<signed int>(items.size()) >= mSellShopSize)
378
                        break;
379
                }
380
                if (!items.empty())
381
                    vendingHandler->createShop(mSellShopName, true, items);
382
            }
383
        }
384
    }
385
    else if (eventId == "rename")
386
    {
387
        EditDialog *const dialog = CREATEWIDGETR(EditDialog,
388
            // TRANSLATORS: shop rename dialog title
389
            _("Please enter new shop name"),
390
            mSellShopName,
391
            "OK",
392
            300,
393
            nullptr,
394
            Modal_true);
395
        shopRenameListener.setDialog(dialog);
396
        dialog->addActionListener(&shopRenameListener);
397
    }
398
399
    if (mSelectedItem < 1)
400
        return;
401
402
    const Inventory *const inv = mHaveVending && !isBuySelected
403
        ? PlayerInfo::getCartInventory() : PlayerInfo::getInventory();
404
    if (inv == nullptr)
405
        return;
406
407
    // +++ need support for colors
408
    Item *const item = inv->findItem(mSelectedItem, ItemColor_zero);
409
    if (item != nullptr)
410
    {
411
        if (eventId == "add")
412
        {
413
            if (isBuySelected)
414
            {
415
                ItemAmountWindow::showWindow(ItemAmountWindowUsage::ShopBuyAdd,
416
                    this,
417
                    item,
418
                    sumAmount(item),
419
                    0);
420
            }
421
            else
422
            {
423
                ItemAmountWindow::showWindow(
424
                    ItemAmountWindowUsage::ShopSellAdd,
425
                    this,
426
                    item,
427
                    sumAmount(item),
428
                    0);
429
            }
430
        }
431
    }
432
}
433
434
void ShopWindow::valueChanged(const SelectionEvent &event A_UNUSED)
435
{
436
    updateButtonsAndLabels();
437
}
438
439
1
void ShopWindow::updateButtonsAndLabels()
440
{
441
1
    bool allowDel(false);
442
1
    bool allowAdd(false);
443
1
    if (isBuySelected)
444
    {
445
1
        allowAdd = !mEnableBuyingStore;
446
1
        allowDel = !mEnableBuyingStore
447
2
            && mBuyShopItemList->getSelected() != -1
448

1
            && mBuyShopItems->getNumberOfElements() > 0;
449
1
        if (mPublishButton != nullptr)
450
        {
451
1
            if (mEnableBuyingStore)
452
            {
453
                // TRANSLATORS: unpublish shop button
454
                mPublishButton->setCaption(_("Unpublish"));
455
            }
456
            else
457
            {
458
                // TRANSLATORS: publish shop button
459
5
                mPublishButton->setCaption(_("Publish"));
460
            }
461
1
            mPublishButton->adjustSize();
462
1
            if (mBuyShopSize > 0)
463
                mPublishButton->setEnabled(true);
464
            else
465
1
                mPublishButton->setEnabled(false);
466
        }
467
    }
468
    else
469
    {
470
        const bool sellNotEmpty = mSellShopItems->getNumberOfElements() > 0;
471
        allowAdd = !mEnableVending && mSelectedItem != -1;
472
        allowDel = !mEnableVending
473
            && mSellShopItemList->getSelected() != -1
474
            && sellNotEmpty;
475
        if (mPublishButton != nullptr)
476
        {
477
            if (mEnableVending)
478
            {
479
                // TRANSLATORS: unpublish shop button
480
                mPublishButton->setCaption(_("Unpublish"));
481
            }
482
            else
483
            {
484
                // TRANSLATORS: publish shop button
485
                mPublishButton->setCaption(_("Publish"));
486
            }
487
            mPublishButton->adjustSize();
488
            if (sellNotEmpty
489
                && mSellShopSize > 0
490
                && (localPlayer != nullptr)
491
                && localPlayer->getHaveCart())
492
            {
493
                mPublishButton->setEnabled(true);
494
            }
495
            else
496
            {
497
                mPublishButton->setEnabled(false);
498
            }
499
        }
500
    }
501
2
    mAddButton->setEnabled(allowAdd);
502
2
    mDeleteButton->setEnabled(allowDel);
503
1
    if (mRenameButton != nullptr)
504
1
        mRenameButton->setEnabled(!mEnableVending);
505
1
}
506
507
void ShopWindow::setVisible(Visible visible)
508
{
509
1
    Window::setVisible(visible);
510
}
511
512
void ShopWindow::addBuyItem(const Item *const item, const int amount,
513
                            const int price)
514
{
515
    if (item == nullptr)
516
        return;
517
    const bool emp = isShopEmpty();
518
    mBuyShopItems->addItemNoDup(item->getId(),
519
        item->getType(),
520
        item->getColor(),
521
        amount,
522
        price);
523
    if (emp && (localPlayer != nullptr))
524
        localPlayer->updateStatus();
525
526
    updateButtonsAndLabels();
527
}
528
529
void ShopWindow::addSellItem(const Item *const item, const int amount,
530
                             const int price)
531
{
532
    if (item == nullptr)
533
        return;
534
    const bool emp = isShopEmpty();
535
    mSellShopItems->addItemNoDup(item->getId(),
536
        item->getType(),
537
        item->getColor(),
538
        amount,
539
        price);
540
    if (emp && (localPlayer != nullptr))
541
        localPlayer->updateStatus();
542
543
    updateButtonsAndLabels();
544
}
545
546
1
void ShopWindow::loadList()
547
{
548
2
    std::ifstream shopFile;
549
    struct stat statbuf;
550
551
1
    mBuyShopItems->clear();
552
1
    mSellShopItems->clear();
553
554
    const std::string shopListName = settings.serverConfigDir
555
2
        + "/shoplist.txt";
556
557

2
    if (stat(shopListName.c_str(), &statbuf) == 0 &&
558
        S_ISREG(statbuf.st_mode))
559
    {
560
        shopFile.open(shopListName.c_str(), std::ios::in);
561
        if (!shopFile.is_open())
562
        {
563
            reportAlways("Error opening file for reading: %s",
564
                shopListName.c_str())
565
            shopFile.close();
566
            return;
567
        }
568
        char line[101];
569
        while (shopFile.getline(line, 100))
570
        {
571
            std::string buf;
572
            const std::string str = line;
573
            if (!str.empty())
574
            {
575
                STD_VECTOR<int> tokens;
576
                std::stringstream ss(str);
577
578
                while (ss >> buf)
579
                    tokens.push_back(atoi(buf.c_str()));
580
581
                if (tokens.size() == 5 && (tokens[0] != 0))
582
                {
583
                    // +++ need impliment colors?
584
                    if ((tokens[1] != 0) && (tokens[2] != 0))
585
                    {
586
                        mBuyShopItems->addItem(
587
                            tokens[0],
588
                            ItemType::Unknown,
589
                            ItemColor_one,
590
                            tokens[1],
591
                            tokens[2]);
592
                    }
593
                    if ((tokens[3] != 0) && (tokens[4] != 0))
594
                    {
595
                        mSellShopItems->addItem(
596
                            tokens[0],
597
                            ItemType::Unknown,
598
                            ItemColor_one,
599
                            tokens[3],
600
                            tokens[4]);
601
                    }
602
                }
603
            }
604
        }
605
        shopFile.close();
606
    }
607
}
608
609
1
void ShopWindow::saveList() const
610
{
611
2
    std::ofstream shopFile;
612
    const std::string shopListName = settings.serverConfigDir
613
2
        + "/shoplist.txt";
614
2
    std::map<int, ShopItem*> mapItems;
615
616
1
    shopFile.open(shopListName.c_str(), std::ios::binary);
617
1
    if (!shopFile.is_open())
618
    {
619
        reportAlways("Error opening file writing: %s",
620
            shopListName.c_str())
621
        return;
622
    }
623
624
3
    STD_VECTOR<ShopItem*> items = mBuyShopItems->items();
625
5
    FOR_EACH (STD_VECTOR<ShopItem*>::const_iterator, it, items)
626
    {
627
        ShopItem *const item = *(it);
628
        if (item != nullptr)
629
            mapItems[item->getId()] = item;
630
    }
631
632
2
    items = mSellShopItems->items();
633
5
    FOR_EACH (STD_VECTOR<ShopItem*>::const_iterator, it, items)
634
    {
635
        if ((*it) == nullptr)
636
            continue;
637
        const ShopItem *const sellItem = *(it);
638
        const ShopItem *const buyItem = mapItems[sellItem->getId()];
639
640
        shopFile << sellItem->getId();
641
        if (buyItem != nullptr)
642
        {
643
            shopFile << strprintf(" %d %d ", buyItem->getQuantity(),
644
                buyItem->getPrice());
645
            mapItems.erase(sellItem->getId());
646
        }
647
        else
648
        {
649
            shopFile << " 0 0 ";
650
        }
651
652
        shopFile << strprintf("%d %d", sellItem->getQuantity(),
653
            sellItem->getPrice()) << std::endl;
654
    }
655
656
3
    for (std::map<int, ShopItem*>::const_iterator mapIt = mapItems.begin(),
657
2
         mapIt_fend = mapItems.end();
658
         mapIt != mapIt_fend;
659
         ++mapIt)
660
    {
661
        const ShopItem *const buyItem = (*mapIt).second;
662
        if (buyItem != nullptr)
663
        {
664
            shopFile << buyItem->getId();
665
            shopFile << strprintf(" %d %d ", buyItem->getQuantity(),
666
                                  buyItem->getPrice());
667
            shopFile << "0 0" << std::endl;
668
        }
669
    }
670
671
1
    shopFile.close();
672
1
}
673
674
#ifdef TMWA_SUPPORT
675
void ShopWindow::announce(ShopItems *const list, const int mode)
676
{
677
    if (list == nullptr)
678
        return;
679
680
    std::string data;
681
    if (mode == BUY)
682
        data.append("Buy ");
683
    else
684
        data.append("Sell ");
685
686
    if (mAnnonceTime != 0 &&
687
        (mAnnonceTime + (2 * 60) > cur_time || mAnnonceTime > cur_time))
688
    {
689
        return;
690
    }
691
692
    mAnnonceTime = cur_time;
693
    if (mAnnounceButton != nullptr)
694
        mAnnounceButton->setEnabled(false);
695
696
    STD_VECTOR<ShopItem*> items = list->items();
697
698
    FOR_EACH (STD_VECTOR<ShopItem*>::const_iterator, it, items)
699
    {
700
        const ShopItem *const item = *(it);
701
        if (item->getQuantity() > 1)
702
        {
703
            if (mAnnounceLinks->isSelected())
704
            {
705
                data.append(strprintf("[@@%d|%s@@] (%dGP) %d, ", item->getId(),
706
                    item->getInfo().getName().c_str(),
707
                    item->getPrice(), item->getQuantity()));
708
            }
709
            else
710
            {
711
                data.append(strprintf("%s (%dGP) %d, ",
712
                    item->getInfo().getName().c_str(),
713
                    item->getPrice(), item->getQuantity()));
714
            }
715
        }
716
        else
717
        {
718
            if (mAnnounceLinks->isSelected())
719
            {
720
                data.append(strprintf("[@@%d|%s@@] (%dGP), ", item->getId(),
721
                    item->getInfo().getName().c_str(), item->getPrice()));
722
            }
723
            else
724
            {
725
                data.append(strprintf("%s (%dGP), ",
726
                    item->getInfo().getName().c_str(), item->getPrice()));
727
            }
728
        }
729
    }
730
731
    chatHandler->channelMessage(TRADE_CHANNEL, data);
732
}
733
734
void ShopWindow::startTrade()
735
{
736
    if (actorManager == nullptr ||
737
        tradeWindow == nullptr)
738
    {
739
        return;
740
    }
741
742
    const Being *const being = actorManager->findBeingByName(
743
        mTradeNick, ActorType::Player);
744
    tradeWindow->clear();
745
    if (mTradeMoney != 0)
746
    {
747
        tradeWindow->addAutoMoney(mTradeNick, mTradeMoney);
748
    }
749
    else
750
    {
751
        tradeWindow->addAutoItem(mTradeNick, mTradeItem,
752
            mTradeItem->getQuantity());
753
    }
754
    tradeHandler->request(being);
755
    tradePartnerName = mTradeNick;
756
    mTradeNick.clear();
757
}
758
759
void ShopWindow::giveList(const std::string &nick, const int mode)
760
{
761
    if (!checkFloodCounter(mLastRequestTimeList))
762
        return;
763
764
    std::string data("\302\202");
765
766
    ShopItems *list;
767
    if (mode == BUY)
768
    {
769
        list = mBuyShopItems;
770
        data.append("S1");
771
    }
772
    else
773
    {
774
        list = mSellShopItems;
775
        data.append("B1");
776
    }
777
    if (list == nullptr)
778
        return;
779
780
    const Inventory *const inv = PlayerInfo::getInventory();
781
    if (inv == nullptr)
782
        return;
783
784
    STD_VECTOR<ShopItem*> items = list->items();
785
786
    FOR_EACH (STD_VECTOR<ShopItem*>::const_iterator, it, items)
787
    {
788
        const ShopItem *const item = *(it);
789
        if (item == nullptr)
790
            continue;
791
792
        if (mode == SELL)
793
        {
794
            const Item *const item2 = inv->findItem(item->getId(),
795
                ItemColor_zero);
796
            if (item2 != nullptr)
797
            {
798
                int amount = item->getQuantity();
799
                if (item2->getQuantity() < amount)
800
                    amount = item2->getQuantity();
801
802
                if (amount != 0)
803
                {
804
                    data.append(strprintf("%s%s%s",
805
                        encodeStr(item->getId(), 2).c_str(),
806
                        encodeStr(item->getPrice(), 4).c_str(),
807
                        encodeStr(amount, 3).c_str()));
808
                }
809
            }
810
        }
811
        else
812
        {
813
            int amount = item->getQuantity();
814
            if (item->getPrice() * amount > PlayerInfo::getAttribute(
815
                Attributes::MONEY))
816
            {
817
                amount = PlayerInfo::getAttribute(Attributes::MONEY)
818
                    / item->getPrice();
819
            }
820
821
            if (amount > 0)
822
            {
823
                data.append(strprintf("%s%s%s",
824
                    encodeStr(item->getId(), 2).c_str(),
825
                    encodeStr(item->getPrice(), 4).c_str(),
826
                    encodeStr(amount, 3).c_str()));
827
            }
828
        }
829
    }
830
    sendMessage(nick, data, true);
831
}
832
833
void ShopWindow::sendMessage(const std::string &nick,
834
                             std::string data,
835
                             const bool random)
836
{
837
    if (chatWindow == nullptr)
838
        return;
839
840
    if (random)
841
    {
842
        mRandCounter ++;
843
        if (mRandCounter > 200)
844
            mRandCounter = 0;
845
        data.append(encodeStr(mRandCounter, 2));
846
    }
847
848
    if (config.getBoolValue("hideShopMessages"))
849
        chatHandler->privateMessage(nick, data);
850
    else
851
        chatWindow->addWhisper(nick, data, ChatMsgType::BY_PLAYER);
852
}
853
854
void ShopWindow::showList(const std::string &nick, std::string data)
855
{
856
    const Inventory *const inv = PlayerInfo::getInventory();
857
    if (inv == nullptr)
858
        return;
859
860
    BuyDialog *buyDialog = nullptr;
861
    SellDialog *sellDialog = nullptr;
862
    if (data.find("B1") == 0)
863
    {
864
        data = data.substr(2);
865
        CREATEWIDGETV(buyDialog, BuyDialog, nick, DEFAULT_CURRENCY);
866
    }
867
    else if (data.find("S1") == 0)
868
    {
869
        data = data.substr(2);
870
        CREATEWIDGETV(sellDialog, ShopSellDialog, nick);
871
    }
872
    else
873
    {
874
        return;
875
    }
876
877
    if (buyDialog != nullptr)
878
        buyDialog->setMoney(PlayerInfo::getAttribute(Attributes::MONEY));
879
    if (sellDialog != nullptr)
880
        sellDialog->setMoney(PlayerInfo::getAttribute(Attributes::MONEY));
881
882
    for (size_t f = 0; f < data.length(); f += 9)
883
    {
884
        if (f + 9 > data.length())
885
            break;
886
887
        const int id = decodeStr(data.substr(f, 2));
888
        const int price = decodeStr(data.substr(f + 2, 4));
889
        int amount = decodeStr(data.substr(f + 6, 3));
890
        if (buyDialog != nullptr && amount > 0)
891
        {
892
            buyDialog->addItem(id,
893
                ItemType::Unknown,
894
                ItemColor_one,
895
                amount,
896
                price);
897
        }
898
        if (sellDialog != nullptr)
899
        {
900
            const Item *const item = inv->findItem(id, ItemColor_zero);
901
            if (item != nullptr)
902
            {
903
                if (item->getQuantity() < amount)
904
                    amount = item->getQuantity();
905
            }
906
            else
907
            {
908
                amount = 0;
909
            }
910
            ShopItem *const shopItem = sellDialog->addItem(id,
911
                ItemType::Unknown,
912
                ItemColor_one,
913
                amount,
914
                price);
915
916
            if (shopItem != nullptr && amount <= 0)
917
                shopItem->setDisabled(true);
918
        }
919
    }
920
    if (buyDialog != nullptr)
921
        buyDialog->sort();
922
}
923
924
void ShopWindow::processRequest(const std::string &nick,
925
                                std::string data,
926
                                const int mode)
927
{
928
    if (localPlayer == nullptr ||
929
        !mTradeNick.empty() ||
930
        PlayerInfo::isTrading() == Trading_true ||
931
        actorManager == nullptr ||
932
        actorManager->findBeingByName(nick, ActorType::Player) == nullptr)
933
    {
934
        return;
935
    }
936
937
    const Inventory *const inv = PlayerInfo::getInventory();
938
    if (inv == nullptr)
939
        return;
940
941
    const size_t idx = data.find(' ');
942
    if (idx == std::string::npos)
943
        return;
944
945
    if (!checkFloodCounter(mLastRequestTimeItem))
946
        return;
947
948
    if (!mTradeNick.empty())
949
    {
950
        sendMessage(nick,
951
            // TRANSLATORS: error buy/sell shop request
952
            _("error: player busy") + std::string(" "),
953
            true);
954
        return;
955
    }
956
957
    data = data.substr(idx + 1);
958
959
    std::string part1;
960
    std::string part2;
961
    std::string part3;
962
    std::stringstream ss(data);
963
    int id;
964
    int price;
965
    int amount;
966
967
    if (!(ss >> part1))
968
        return;
969
970
    if (!(ss >> part2))
971
        return;
972
973
    if (!(ss >> part3))
974
        return;
975
976
    id = atoi(part1.c_str());
977
    price = atoi(part2.c_str());
978
    amount = atoi(part3.c_str());
979
980
    delete mTradeItem;
981
    // +++ need impliment colors?
982
    mTradeItem = new ShopItem(-1,
983
        id,
984
        ItemType::Unknown,
985
        ItemColor_one,
986
        amount,
987
        price,
988
        DEFAULT_CURRENCY);
989
990
    if (mode == BUY)
991
    {
992
        // +++ need support for colors
993
        const Item *const item2 = inv->findItem(mTradeItem->getId(),
994
            ItemColor_zero);
995
        if (item2 == nullptr ||
996
            item2->getQuantity() < amount ||
997
            !findShopItem(mTradeItem, SELL))
998
        {
999
            sendMessage(nick,
1000
                // TRANSLATORS: error buy/sell shop request
1001
                _("error: Can't sell this item") + std::string(" "),
1002
                true);
1003
            return;
1004
        }
1005
        mTradeMoney = 0;
1006
    }
1007
    else
1008
    {
1009
        if (!findShopItem(mTradeItem, BUY))
1010
        {
1011
            sendMessage(nick,
1012
                // TRANSLATORS: error buy/sell shop request
1013
                _("error: Can't buy this item") + std::string(" "),
1014
                true);
1015
            return;
1016
        }
1017
        mTradeMoney = mTradeItem->getPrice() * mTradeItem->getQuantity();
1018
    }
1019
1020
    mTradeNick = nick;
1021
1022
    if (config.getBoolValue("autoShop"))
1023
    {
1024
        soundManager.playGuiSound(SOUND_TRADE);
1025
        startTrade();
1026
    }
1027
    else
1028
    {
1029
        std::string msg;
1030
        if (mode == BUY)
1031
        {
1032
            // TRANSLATORS: buy shop request (nick, item)
1033
            msg = strprintf(_("%s wants to buy %s do you accept?"),
1034
                nick.c_str(),
1035
                mTradeItem->getInfo().getName().c_str());
1036
        }
1037
        else
1038
        {
1039
            // TRANSLATORS: sell shop request (nick, item)
1040
            msg = strprintf(_("%s wants to sell %s do you accept?"),
1041
                nick.c_str(),
1042
                mTradeItem->getInfo().getName().c_str());
1043
        }
1044
1045
        ConfirmDialog *const confirmDlg = CREATEWIDGETR(ConfirmDialog,
1046
            // TRANSLATORS: shop window dialog
1047
            _("Request for Trade"),
1048
            msg,
1049
            SOUND_REQUEST,
1050
            true,
1051
            Modal_false,
1052
            nullptr);
1053
        confirmDlg->addActionListener(this);
1054
    }
1055
}
1056
1057
void ShopWindow::updateTimes()
1058
{
1059
    BLOCK_START("ShopWindow::updateTimes")
1060
    if (mAnnounceButton == nullptr)
1061
    {
1062
        BLOCK_END("ShopWindow::updateTimes")
1063
        return;
1064
    }
1065
    if (mAnnonceTime + (2 * 60) < cur_time ||
1066
        mAnnonceTime > cur_time)
1067
    {
1068
        mAnnounceButton->setEnabled(true);
1069
    }
1070
    BLOCK_END("ShopWindow::updateTimes")
1071
}
1072
1073
bool ShopWindow::checkFloodCounter(time_t &counterTime)
1074
{
1075
    if (counterTime == 0 || counterTime > cur_time)
1076
        counterTime = cur_time;
1077
    else if (counterTime + 10 > cur_time)
1078
        return false;
1079
    else
1080
        counterTime = cur_time;
1081
    return true;
1082
}
1083
1084
bool ShopWindow::findShopItem(const ShopItem *const shopItem,
1085
                              const int mode) const
1086
{
1087
    if (shopItem == nullptr)
1088
        return false;
1089
1090
    STD_VECTOR<ShopItem*> items;
1091
    if (mode == SELL)
1092
        items = mSellShopItems->items();
1093
    else
1094
        items = mBuyShopItems->items();
1095
1096
    FOR_EACH (STD_VECTOR<ShopItem*>::const_iterator, it, items)
1097
    {
1098
        const ShopItem *const item = *(it);
1099
        if (item == nullptr)
1100
            continue;
1101
1102
        if (item->getId() == shopItem->getId()
1103
            && item->getPrice() == shopItem->getPrice()
1104
            && item->getQuantity() >= shopItem->getQuantity())
1105
        {
1106
            return true;
1107
        }
1108
    }
1109
    return false;
1110
}
1111
#endif  // TMWA_SUPPORT
1112
1113
int ShopWindow::sumAmount(const Item *const shopItem)
1114
{
1115
    if ((localPlayer == nullptr) || (shopItem == nullptr))
1116
        return 0;
1117
1118
    const Inventory *const inv = PlayerInfo::getInventory();
1119
    if (inv == nullptr)
1120
        return 0;
1121
    int sum = 0;
1122
1123
    for (unsigned f = 0; f < inv->getSize(); f ++)
1124
    {
1125
        const Item *const item = inv->getItem(f);
1126
        if ((item != nullptr) && item->getId() == shopItem->getId())
1127
            sum += item->getQuantity();
1128
    }
1129
    return sum;
1130
}
1131
1132
bool ShopWindow::isShopEmpty() const
1133
{
1134
    if (mBuyShopItems->empty() && mSellShopItems->empty())
1135
        return true;
1136
    return false;
1137
}
1138
1139
1
void ShopWindow::updateSelection()
1140
{
1141
1
    if (isBuySelected)
1142
1
        mCurrentShopItemList = mBuyShopItemList;
1143
    else
1144
        mCurrentShopItemList = mSellShopItemList;
1145
1
    mScrollArea->setContent(mCurrentShopItemList);
1146
1
    updateButtonsAndLabels();
1147
1
}
1148
1149
1
void ShopWindow::updateShopName()
1150
{
1151
2
    if (mSellShopName.empty())
1152
    {
1153
        // TRANSLATORS: shop window name
1154
        setCaption(_("Personal Shop"));
1155
    }
1156
    else
1157
    {
1158
        // TRANSLATORS: shop window name
1159
4
        setCaption(strprintf(_("Personal Shop - %s"), mSellShopName.c_str()));
1160
    }
1161
1
}
1162
1163
void ShopWindow::setShopName(const std::string &name)
1164
{
1165
    mSellShopName = name;
1166
    serverConfig.setValue("sellShopName", mSellShopName);
1167
    updateShopName();
1168
}
1169
1170
void ShopWindow::vendingSlotsChanged(const int slots)
1171
{
1172
    mSellShopSize = slots;
1173
    updateButtonsAndLabels();
1174
}
1175
1176
void ShopWindow::vendingEnabled(const bool b)
1177
{
1178
    mEnableVending = b;
1179
    localPlayer->enableShop(b);
1180
    if (!b)
1181
        mSellShopSize = 0;
1182
    updateButtonsAndLabels();
1183
}
1184
1185
void ShopWindow::buyingStoreSlotsChanged(const int slots)
1186
{
1187
    mBuyShopSize = slots;
1188
    updateButtonsAndLabels();
1189
}
1190
1191
void ShopWindow::buyingStoreEnabled(const bool b)
1192
{
1193
    mEnableBuyingStore = b;
1194
    localPlayer->enableShop(b);
1195
    if (!b)
1196
        mBuyShopSize = 0;
1197
    updateButtonsAndLabels();
1198

3
}