GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/resources/inventory/inventory.cpp Lines: 35 206 17.0 %
Date: 2017-11-29 Branches: 19 253 7.5 %

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












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



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

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

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