GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/inventory/inventory.cpp Lines: 35 206 17.0 %
Date: 2021-03-17 Branches: 20 263 7.6 %

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 "resources/inventory/inventory.h"
25
26
#include "being/playerinfo.h"
27
28
#include "net/inventoryhandler.h"
29
30
#include "resources/iteminfo.h"
31
32
#include "resources/item/item.h"
33
34
#include "listeners/inventorylistener.h"
35
36
#include "utils/checkutils.h"
37
#include "utils/delete2.h"
38
#include "utils/foreach.h"
39
#include "utils/gettext.h"
40
#include "utils/stringutils.h"
41
42
#include <algorithm>
43
44
#include "debug.h"
45
46
namespace
47
{
48
    struct SlotUsed final
49
    {
50
        A_DEFAULT_COPY(SlotUsed)
51
52
        bool operator()(const Item *const item) const
53
        {
54












1
            return (item != nullptr) && item->mId >= 0 && item->mQuantity > 0;
55
        }
56
        typedef Item *argument_type;
57
    };
58
}  // namespace
59
60
7
Inventory::Inventory(const InventoryTypeT type,
61
7
                     const int size1) :
62
    mInventoryListeners(),
63
    mVirtualRemove(),
64
    mType(type),
65
11
    mSize(size1 == -1 ? CAST_U32(
66
4
          inventoryHandler->getSize(type))
67
          : CAST_U32(size1)),
68
7
    mItems(new Item*[mSize]),
69
35
    mUsed(0)
70
{
71
14
    std::fill_n(mItems, mSize, static_cast<Item*>(nullptr));
72
7
}
73
74
34
Inventory::~Inventory()
75
{
76
234
    for (unsigned i = 0; i < mSize; i++)
77
227
        delete mItems[i];
78
79
7
    delete [] mItems;
80
7
    mItems = nullptr;
81
13
}
82
83
301
Item *Inventory::getItem(const int index) const
84
{
85



301
    if (index < 0 || index >= CAST_S32(mSize) || (mItems[index] == nullptr)
86
        || mItems[index]->mQuantity <= 0)
87
    {
88
        return nullptr;
89
    }
90
91
    return mItems[index];
92
}
93
94
Item *Inventory::findItem(const int itemId,
95
                          const ItemColor color) const
96
{
97
    for (unsigned i = 0; i < mSize; i++)
98
    {
99
        Item *const item = mItems[i];
100
        if ((item != nullptr) && item->mId == itemId)
101
        {
102
            if (color == ItemColor_zero ||
103
                item->mColor == color ||
104
                (color == ItemColor_one &&
105
                item->mColor <= ItemColor_one))
106
            {
107
                return item;
108
            }
109
        }
110
    }
111
112
    return nullptr;
113
}
114
115
int Inventory::addItem(const int id,
116
                       const ItemTypeT type,
117
                       const int quantity,
118
                       const uint8_t refine,
119
                       const ItemColor color,
120
                       const Identified identified,
121
                       const Damaged damaged,
122
                       const Favorite favorite,
123
                       const Equipm equipment,
124
                       const Equipped equipped)
125
{
126
    const int slot = getFreeSlot();
127
    setItem(slot,
128
        id,
129
        type,
130
        quantity,
131
        refine,
132
        color,
133
        identified,
134
        damaged,
135
        favorite,
136
        equipment,
137
        equipped);
138
    return slot;
139
}
140
141
void Inventory::setItem(const int index,
142
                        const int id,
143
                        const ItemTypeT type,
144
                        const int quantity,
145
                        const uint8_t refine,
146
                        const ItemColor color,
147
                        const Identified identified,
148
                        const Damaged damaged,
149
                        const Favorite favorite,
150
                        const Equipm equipment,
151
                        const Equipped equipped)
152
{
153
    if (index < 0 || index >= CAST_S32(mSize))
154
    {
155
        reportAlways("Warning: invalid inventory index: %d",
156
            index)
157
        return;
158
    }
159
160
    Item *const item1 = mItems[index];
161
    if ((item1 == nullptr) && id > 0)
162
    {
163
        Item *const item = new Item(id,
164
            type,
165
            quantity,
166
            refine,
167
            color,
168
            identified,
169
            damaged,
170
            favorite,
171
            equipment,
172
            equipped);
173
        item->setInvIndex(index);
174
        mItems[index] = item;
175
        mUsed++;
176
        distributeSlotsChangedEvent();
177
    }
178
    else if (id > 0 && (item1 != nullptr))
179
    {
180
        item1->setId(id, color);
181
        item1->setQuantity(quantity);
182
        item1->setRefine(refine);
183
        item1->setEquipment(equipment);
184
        item1->setIdentified(identified);
185
        item1->setDamaged(damaged);
186
        item1->setFavorite(favorite);
187
    }
188
    else if (item1 != nullptr)
189
    {
190
        removeItemAt(index);
191
    }
192
}
193
194
void Inventory::setCards(const int index,
195
                         const int *const cards,
196
                         const int size) const
197
{
198
    if (index < 0 || index >= CAST_S32(mSize))
199
    {
200
        reportAlways("Warning: invalid inventory index: %d",
201
            index)
202
        return;
203
    }
204
205
    Item *const item1 = mItems[index];
206
    if (item1 != nullptr)
207
        item1->setCards(cards, size);
208
}
209
210
void Inventory::setOptions(const int index,
211
                           const ItemOptionsList *const options)
212
{
213
    if (index < 0 || index >= CAST_S32(mSize))
214
    {
215
        reportAlways("Warning: invalid inventory index: %d",
216
            index)
217
        return;
218
    }
219
    Item *const item1 = mItems[index];
220
    if (item1 != nullptr)
221
        item1->setOptions(options);
222
}
223
224
void Inventory::setTag(const int index,
225
                       const int tag)
226
{
227
    if (index < 0 || index >= CAST_S32(mSize))
228
    {
229
        reportAlways("Warning: invalid inventory index: %d",
230
            index)
231
        return;
232
    }
233
    Item *const item1 = mItems[index];
234
    if (item1 != nullptr)
235
        item1->setTag(tag);
236
}
237
238
2
void Inventory::clear()
239
{
240

26
    for (unsigned i = 0; i < mSize; i++)
241
24
        removeItemAt(i);
242
2
}
243
244
void Inventory::removeItem(const int id)
245
{
246
    for (unsigned i = 0; i < mSize; i++)
247
    {
248
        const Item *const item = mItems[i];
249
        if ((item != nullptr) && item->mId == id)
250
            removeItemAt(i);
251
    }
252
}
253
254
24
void Inventory::removeItemAt(const int index)
255
{
256
24
    if (mItems[index] == nullptr)
257
        return;
258
    delete2(mItems[index])
259
    mUsed--;
260
    if (mUsed < 0)  // Already at 0, no need to distribute event
261
        mUsed = 0;
262
    else
263
        distributeSlotsChangedEvent();
264
}
265
266
bool Inventory::contains(const Item *const item) const
267
{
268
    if (item == nullptr)
269
        return false;
270
271
    const int id = item->mId;
272
    for (unsigned i = 0; i < mSize; i++)
273
    {
274
        const Item *const item1 = mItems[i];
275
        if ((item1 != nullptr) && item1->mId == id)
276
            return true;
277
    }
278
279
    return false;
280
}
281
282
int Inventory::getFreeSlot() const
283
{
284
    Item *const *const i = std::find_if(mItems,
285
        mItems + mSize,
286
        std::not1(SlotUsed()));
287
    return (i == mItems + mSize) ? -1
288
        : CAST_S32(i - mItems);
289
}
290
291
1
int Inventory::getLastUsedSlot() const
292
{
293
2
    for (int i = mSize - 1; i >= 0; i--)
294
    {
295
2
        if (SlotUsed()(mItems[i]))
296
            return i;
297
    }
298
299
    return -1;
300
}
301
302
1
void Inventory::addInventoyListener(InventoryListener* const listener)
303
{
304
2
    mInventoryListeners.push_back(listener);
305
1
}
306
307
1
void Inventory::removeInventoyListener(InventoryListener* const listener)
308
{
309
1
    mInventoryListeners.remove(listener);
310
1
}
311
312
void Inventory::distributeSlotsChangedEvent()
313
{
314
    FOR_EACH (InventoryListenerList::const_iterator, i, mInventoryListeners)
315
        (*i)->slotsChanged(this);
316
}
317
318
const Item *Inventory::findItemBySprite(std::string spritePath,
319
                                        const GenderT gender,
320
                                        const BeingTypeId race) const
321
{
322
    spritePath = removeSpriteIndex(spritePath);
323
324
    const std::string spritePathShort = extractNameFromSprite(spritePath);
325
    int partialIndex = -1;
326
327
    for (unsigned i = 0; i < mSize; i++)
328
    {
329
        const Item *const item = mItems[i];
330
        if (item != nullptr)
331
        {
332
            std::string path = item->getInfo().getSprite(gender, race);
333
            if (!path.empty())
334
            {
335
                path = removeSpriteIndex(path);
336
                if (spritePath == path)
337
                    return item;
338
339
                path = extractNameFromSprite(path);
340
                if (spritePathShort == path)
341
                    partialIndex = i;
342
            }
343
        }
344
    }
345
    if (partialIndex != -1)
346
        return mItems[partialIndex];
347
348
    return nullptr;
349
}
350
351
2
std::string Inventory::getName() const
352
{
353

2
    switch (mType)
354
    {
355
        case InventoryType::Inventory:
356
        case InventoryType::Vending:
357
        case InventoryType::TypeEnd:
358
        default:
359
        {
360
            // TRANSLATORS: inventory type name
361
6
            return N_("Inventory");
362
        }
363
        case InventoryType::Storage:
364
        {
365
            // TRANSLATORS: inventory type name
366
            return N_("Storage");
367
        }
368
        case InventoryType::Npc:
369
        {
370
            // TRANSLATORS: inventory type name
371
            return N_("Npc");
372
        }
373
        case InventoryType::Cart:
374
        {
375
            // TRANSLATORS: inventory type name
376
            return N_("Cart");
377
        }
378
        case InventoryType::MailEdit:
379
        case InventoryType::MailView:
380
        {
381
            // TRANSLATORS: inventory type name
382
            return N_("Mail");
383
        }
384
        case InventoryType::Craft:
385
        {
386
            // TRANSLATORS: inventory type name
387
            return N_("Craft");
388
        }
389
        case InventoryType::Trade:
390
        {
391
            // TRANSLATORS: inventory type name
392
            return N_("Trade");
393
        }
394
    }
395
}
396
397
void Inventory::resize(const unsigned int newSize)
398
{
399
    clear();
400
    if (mSize == newSize)
401
        return;
402
403
    for (unsigned i = 0; i < mSize; i++)
404
        delete mItems[i];
405
    delete [] mItems;
406
407
    mSize = newSize;
408
    mItems = new Item*[CAST_SIZE(mSize)];
409
    std::fill_n(mItems, mSize, static_cast<Item*>(nullptr));
410
}
411
412
int Inventory::findIndexByTag(const int tag) const
413
{
414
    for (unsigned i = 0; i < mSize; i++)
415
    {
416
        const Item *const item = mItems[i];
417
        if ((item != nullptr) && item->mTag == tag)
418
            return i;
419
    }
420
421
    return -1;
422
}
423
424
Item *Inventory::findItemByTag(const int tag) const
425
{
426
    for (unsigned i = 0; i < mSize; i++)
427
    {
428
        Item *const item = mItems[i];
429
        if (item != nullptr &&
430
            item->mTag == tag)
431
        {
432
            return item;
433
        }
434
    }
435
436
    return nullptr;
437
}
438
439
bool Inventory::addVirtualItem(const Item *const item,
440
                               int index,
441
                               const int amount)
442
{
443
    if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId()))
444
    {
445
        if (index >= 0 && index < CAST_S32(mSize))
446
        {
447
            if (mItems[index] != nullptr)
448
                return false;
449
            setItem(index,
450
                item->getId(),
451
                item->getType(),
452
                amount,
453
                1,
454
                item->getColor(),
455
                item->getIdentified(),
456
                item->getDamaged(),
457
                item->getFavorite(),
458
                Equipm_false,
459
                Equipped_false);
460
        }
461
        else
462
        {
463
            index = addItem(item->getId(),
464
                item->getType(),
465
                amount,
466
                1,
467
                item->getColor(),
468
                item->getIdentified(),
469
                item->getDamaged(),
470
                item->getFavorite(),
471
                Equipm_false,
472
                Equipped_false);
473
        }
474
        if (index == -1)
475
            return false;
476
477
        Item *const item2 = getItem(index);
478
        if (item2 != nullptr)
479
            item2->setTag(item->getInvIndex());
480
        return true;
481
    }
482
    return false;
483
}
484
485
void Inventory::virtualRemove(Item *const item,
486
                              const int amount)
487
{
488
    if ((item == nullptr) || item->mQuantity < amount)
489
        return;
490
491
    const int index = item->getInvIndex();
492
    const IntMapCIter it = mVirtualRemove.find(index);
493
    if (it == mVirtualRemove.end())
494
        mVirtualRemove[index] = amount;
495
    else
496
        mVirtualRemove[index] += amount;
497
    item->mQuantity -= amount;
498
}
499
500
void Inventory::restoreVirtuals()
501
{
502
    const int sz = CAST_S32(mSize);
503
504
    FOR_EACH (IntMapCIter, it, mVirtualRemove)
505
    {
506
        const int index = (*it).first;
507
        if (index < 0 || index >= sz)
508
            continue;
509
        Item *const item = mItems[index];
510
        if (item == nullptr)
511
            continue;
512
        item->mQuantity += (*it).second;
513
    }
514
    mVirtualRemove.clear();
515
}
516
517
void Inventory::virtualRestore(const Item *const item,
518
                               const int amount)
519
{
520
    const int index = item->getTag();
521
    const IntMapCIter it = mVirtualRemove.find(index);
522
    if (it != mVirtualRemove.end())
523
    {
524
        mVirtualRemove[index] -= amount;
525
        if (mVirtualRemove[index] < 0)
526
            mVirtualRemove.erase(index);
527
        if (index < 0 ||
528
            index >= CAST_S32(mSize) ||
529
            mItems[index] == nullptr)
530
        {
531
            return;
532
        }
533
        mItems[index]->mQuantity += amount;
534
    }
535
}
536
537
void Inventory::moveItem(const int index1,
538
                         const int index2)
539
{
540
    if (index1 < 0 ||
541
        index1 >= CAST_S32(mSize) ||
542
        index2 < 0 ||
543
        index2 >= CAST_S32(mSize))
544
    {
545
        return;
546
    }
547
548
    Item *const item1 = mItems[index1];
549
    Item *const item2 = mItems[index2];
550
    if (item1 != nullptr)
551
        item1->setInvIndex(index2);
552
    if (item2 != nullptr)
553
        item2->setInvIndex(index1);
554
    mItems[index1] = item2;
555
    mItems[index2] = item1;
556
    distributeSlotsChangedEvent();
557
}