GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/shopwindow.cpp Lines: 129 540 23.9 %
Date: 2018-07-14 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-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/shopwindow.h"
24
25
#include "const/resources/currency.h"
26
27
#ifdef TMWA_SUPPORT
28
#include "gui/windows/buydialog.h"
29
#include "gui/windows/chatwindow.h"
30
#include "gui/windows/confirmdialog.h"
31
#include "gui/windows/shopselldialog.h"
32
#include "gui/windows/tradewindow.h"
33
34
#include "const/sound.h"
35
36
#include "const/gui/chat.h"
37
#endif  // TMWA_SUPPORT
38
39
#include "enums/gui/layouttype.h"
40
41
#include "gui/windows/editdialog.h"
42
43
#include "gui/windows/itemamountwindow.h"
44
#include "gui/windows/setupwindow.h"
45
46
#include "gui/models/shopitems.h"
47
48
#include "gui/widgets/button.h"
49
#include "gui/widgets/checkbox.h"
50
#include "gui/widgets/containerplacer.h"
51
#include "gui/widgets/createwidget.h"
52
#include "gui/widgets/layout.h"
53
#include "gui/widgets/scrollarea.h"
54
#include "gui/widgets/shoplistbox.h"
55
#include "gui/widgets/tabstrip.h"
56
57
#include "listeners/shoprenamelistener.h"
58
59
#ifdef TMWA_SUPPORT
60
#include "actormanager.h"
61
#include "soundmanager.h"
62
#endif  // TMWA_SUPPORT
63
#include "configuration.h"
64
#include "settings.h"
65
66
#include "being/localplayer.h"
67
#include "being/playerinfo.h"
68
69
#ifdef TMWA_SUPPORT
70
#include "being/playerrelations.h"
71
#include "net/chathandler.h"
72
#endif  // TMWA_SUPPORT
73
#include "net/buyingstorehandler.h"
74
#include "net/vendinghandler.h"
75
#include "net/net.h"
76
#ifdef TMWA_SUPPORT
77
#include "net/tradehandler.h"
78
#endif  // TMWA_SUPPORT
79
80
#include "utils/checkutils.h"
81
#include "utils/delete2.h"
82
#include "utils/foreach.h"
83
#include "utils/gettext.h"
84
85
#ifdef TMWA_SUPPORT
86
#include "resources/iteminfo.h"
87
#endif  // TMWA_SUPPORT
88
89
#include "resources/inventory/inventory.h"
90
91
#include "resources/item/shopitem.h"
92
93
#include <sys/stat.h>
94
95
#include <fstream>
96
#include <sstream>
97
98
#include "debug.h"
99
100
ShopWindow *shopWindow = nullptr;
101
extern std::string tradePartnerName;
102
1
ShopWindow::DialogList ShopWindow::instances;
103
104
1
ShopWindow::ShopWindow() :
105
    // TRANSLATORS: shop window name
106
1
    Window(_("Personal Shop"), Modal_false, nullptr, "shop.xml"),
107
    VendingModeListener(),
108
    VendingSlotsListener(),
109
    BuyingStoreModeListener(),
110
    BuyingStoreSlotsListener(),
111
    ActionListener(),
112
    SelectionListener(),
113
    // TRANSLATORS: shop window button
114

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

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

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

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

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

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

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

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

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







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

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

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

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

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

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


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


7
            this);
206
1
        placer(2, 6, mPublishButton, 1, 1);
207
1
        placer(3, 6, mRenameButton, 1, 1);
208
    }
209
    else
210
    {
211
        mAnnounceButton = new Button(this,
212
            // TRANSLATORS: shop window button
213
            _("Announce"),
214
            "announce",
215
            BUTTON_SKIN,
216
            this);
217
        // TRANSLATORS: shop window checkbox
218
        mAnnounceLinks = new CheckBox(this, _("Show links in announce"), false,
219
            this, "link announce");
220
221
        placer(2, 6, mAnnounceButton, 1, 1);
222
        placer(0, 7, mAnnounceLinks, 7, 1);
223
    }
224
225
2
    placer(0, 1, mScrollArea, 8, 5).setPadding(3);
226
1
    placer(0, 6, mAddButton, 1, 1);
227
1
    placer(1, 6, mDeleteButton, 1, 1);
228
1
    placer(7, 6, mCloseButton, 1, 1);
229
230
1
    Layout &layout = getLayout();
231
1
    layout.setRowHeight(0, LayoutType::SET);
232
233
1
    center();
234
1
    loadWindowState();
235
1
    updateShopName();
236
2
    instances.push_back(this);
237
1
}
238
239
1
void ShopWindow::postInit()
240
{
241
1
    Window::postInit();
242
1
    setVisible(Visible_false);
243
2
    enableVisibleSound(true);
244
1
    updateSelection();
245
1
}
246
247
8
ShopWindow::~ShopWindow()
248
{
249
1
    saveList();
250
251
1
    delete2(mBuyShopItemList);
252
1
    delete2(mSellShopItemList);
253
1
    delete2(mBuyShopItems);
254
1
    delete2(mSellShopItems);
255
1
    delete2(mTradeItem);
256
257
1
    instances.remove(this);
258
2
}
259
260
void ShopWindow::action(const ActionEvent &event)
261
{
262
    const std::string &eventId = event.getId();
263
    if (eventId == "close")
264
    {
265
        close();
266
        return;
267
    }
268
#ifdef TMWA_SUPPORT
269
    else if (eventId == "yes")
270
    {
271
        startTrade();
272
    }
273
    else if (eventId == "no")
274
    {
275
        mTradeNick.clear();
276
    }
277
    else if (eventId == "ignore")
278
    {
279
        playerRelations.ignoreTrade(mTradeNick);
280
        mTradeNick.clear();
281
    }
282
    else if (eventId == "announce")
283
    {
284
        if (isBuySelected)
285
        {
286
            if (mBuyShopItems->getNumberOfElements() > 0)
287
                announce(mBuyShopItems, BUY);
288
        }
289
        else if (mSellShopItems->getNumberOfElements() > 0)
290
        {
291
            announce(mSellShopItems, SELL);
292
        }
293
    }
294
#endif  // TMWA_SUPPORT
295
    else if (eventId == "delete")
296
    {
297
        if (isBuySelected)
298
        {
299
            if (mBuyShopItemList != nullptr &&
300
                mBuyShopItemList->getSelected() >= 0)
301
            {
302
                mBuyShopItems->del(mBuyShopItemList->getSelected());
303
                if (isShopEmpty() && (localPlayer != nullptr))
304
                    localPlayer->updateStatus();
305
            }
306
        }
307
        else if ((mSellShopItemList != nullptr)
308
                 && mSellShopItemList->getSelected() >= 0)
309
        {
310
            mSellShopItems->del(mSellShopItemList->getSelected());
311
            if (isShopEmpty() && (localPlayer != nullptr))
312
                localPlayer->updateStatus();
313
        }
314
    }
315
    else if (eventId == "tab_buy")
316
    {
317
        isBuySelected = true;
318
        updateSelection();
319
    }
320
    else if (eventId == "tab_sell")
321
    {
322
        isBuySelected = false;
323
        updateSelection();
324
    }
325
    else if (eventId == "publish")
326
    {
327
        if (isBuySelected)
328
        {
329
            if (mEnableBuyingStore)
330
            {
331
                buyingStoreHandler->close();
332
                BuyingStoreModeListener::distributeEvent(false);
333
            }
334
            else
335
            {
336
                STD_VECTOR<ShopItem*> &items = mBuyShopItems->items();
337
                if (!items.empty())
338
                {
339
                    buyingStoreHandler->create(mSellShopName,
340
                        PlayerInfo::getAttribute(Attributes::MONEY),
341
                        true,
342
                        items);
343
                }
344
            }
345
        }
346
        else
347
        {
348
            if (mEnableVending)
349
            {
350
                vendingHandler->close();
351
                VendingModeListener::distributeEvent(false);
352
            }
353
            else
354
            {
355
                STD_VECTOR<ShopItem*> &oldItems = mSellShopItems->items();
356
                STD_VECTOR<ShopItem*> items;
357
                const Inventory *const inv = PlayerInfo::getCartInventory();
358
                if (inv == nullptr)
359
                    return;
360
                FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, oldItems)
361
                {
362
                    ShopItem *const item = *it;
363
                    if (item == nullptr)
364
                        continue;
365
                    const Item *const cartItem = inv->findItem(item->getId(),
366
                        item->getColor());
367
                    if (cartItem == nullptr)
368
                        continue;
369
                    item->setInvIndex(cartItem->getInvIndex());
370
                    const int amount = cartItem->getQuantity();
371
                    if (amount == 0)
372
                        continue;
373
                    if (item->getQuantity() > amount)
374
                        item->setQuantity(amount);
375
                    items.push_back(item);
376
                    if (static_cast<signed int>(items.size()) >= mSellShopSize)
377
                        break;
378
                }
379
                if (!items.empty())
380
                    vendingHandler->createShop(mSellShopName, true, items);
381
            }
382
        }
383
    }
384
    else if (eventId == "rename")
385
    {
386
        EditDialog *const dialog = CREATEWIDGETR(EditDialog,
387
            // TRANSLATORS: shop rename dialog title
388
            _("Please enter new shop name"),
389
            mSellShopName,
390
            "OK",
391
            300,
392
            nullptr,
393
            Modal_true);
394
        shopRenameListener.setDialog(dialog);
395
        dialog->addActionListener(&shopRenameListener);
396
    }
397
398
    if (mSelectedItem < 1)
399
        return;
400
401
    const Inventory *const inv = mHaveVending && !isBuySelected
402
        ? PlayerInfo::getCartInventory() : PlayerInfo::getInventory();
403
    if (inv == nullptr)
404
        return;
405
406
    // +++ need support for colors
407
    Item *const item = inv->findItem(mSelectedItem, ItemColor_zero);
408
    if (item != nullptr)
409
    {
410
        if (eventId == "add")
411
        {
412
            if (isBuySelected)
413
            {
414
                ItemAmountWindow::showWindow(ItemAmountWindowUsage::ShopBuyAdd,
415
                    this,
416
                    item,
417
                    sumAmount(item),
418
                    0);
419
            }
420
            else
421
            {
422
                ItemAmountWindow::showWindow(
423
                    ItemAmountWindowUsage::ShopSellAdd,
424
                    this,
425
                    item,
426
                    sumAmount(item),
427
                    0);
428
            }
429
        }
430
    }
431
}
432
433
void ShopWindow::valueChanged(const SelectionEvent &event A_UNUSED)
434
{
435
    updateButtonsAndLabels();
436
}
437
438
1
void ShopWindow::updateButtonsAndLabels()
439
{
440
1
    bool allowDel(false);
441
1
    bool allowAdd(false);
442
2
    const bool sellNotEmpty = mSellShopItems->getNumberOfElements() > 0;
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
        allowAdd = !mEnableVending && mSelectedItem != -1;
471
        allowDel = !mEnableVending
472
            && mSellShopItemList->getSelected() != -1
473
            && sellNotEmpty;
474
        if (mPublishButton != nullptr)
475
        {
476
            if (mEnableVending)
477
            {
478
                // TRANSLATORS: unpublish shop button
479
                mPublishButton->setCaption(_("Unpublish"));
480
            }
481
            else
482
            {
483
                // TRANSLATORS: publish shop button
484
                mPublishButton->setCaption(_("Publish"));
485
            }
486
            mPublishButton->adjustSize();
487
            if (sellNotEmpty
488
                && mSellShopSize > 0
489
                && (localPlayer != nullptr)
490
                && localPlayer->getHaveCart())
491
            {
492
                mPublishButton->setEnabled(true);
493
            }
494
            else
495
            {
496
                mPublishButton->setEnabled(false);
497
            }
498
        }
499
    }
500
2
    mAddButton->setEnabled(allowAdd);
501
2
    mDeleteButton->setEnabled(allowDel);
502
1
    if (mRenameButton != nullptr)
503
1
        mRenameButton->setEnabled(!mEnableVending);
504
1
}
505
506
void ShopWindow::setVisible(Visible visible)
507
{
508
1
    Window::setVisible(visible);
509
}
510
511
void ShopWindow::addBuyItem(const Item *const item, const int amount,
512
                            const int price)
513
{
514
    if (item == nullptr)
515
        return;
516
    const bool emp = isShopEmpty();
517
    mBuyShopItems->addItemNoDup(item->getId(),
518
        item->getType(),
519
        item->getColor(),
520
        amount,
521
        price);
522
    if (emp && (localPlayer != nullptr))
523
        localPlayer->updateStatus();
524
525
    updateButtonsAndLabels();
526
}
527
528
void ShopWindow::addSellItem(const Item *const item, const int amount,
529
                             const int price)
530
{
531
    if (item == nullptr)
532
        return;
533
    const bool emp = isShopEmpty();
534
    mSellShopItems->addItemNoDup(item->getId(),
535
        item->getType(),
536
        item->getColor(),
537
        amount,
538
        price);
539
    if (emp && (localPlayer != nullptr))
540
        localPlayer->updateStatus();
541
542
    updateButtonsAndLabels();
543
}
544
545
1
void ShopWindow::loadList()
546
{
547
2
    std::ifstream shopFile;
548
    struct stat statbuf;
549
550
1
    mBuyShopItems->clear();
551
1
    mSellShopItems->clear();
552
553
    const std::string shopListName = settings.serverConfigDir
554
2
        + "/shoplist.txt";
555
556

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

3
}