GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/tmwa/inventoryrecv.cpp Lines: 1 223 0.4 %
Date: 2021-03-17 Branches: 0 139 0.0 %

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 "net/tmwa/inventoryrecv.h"
25
26
#include "notifymanager.h"
27
28
#include "being/localplayer.h"
29
30
#include "const/net/inventory.h"
31
32
#include "enums/equipslot.h"
33
34
#include "enums/resources/notifytypes.h"
35
36
#include "listeners/arrowslistener.h"
37
38
#include "net/inventoryhandler.h"
39
#include "net/messagein.h"
40
41
#include "net/ea/equipbackend.h"
42
#include "net/ea/inventoryrecv.h"
43
44
#include "debug.h"
45
46
namespace TmwAthena
47
{
48
49
namespace InventoryRecv
50
{
51
    const EquipSlot::Type EQUIP_POINTS[EquipSlot::VECTOREND] =
52
    {
53
        EquipSlot::LEGS_SLOT,               // Lower Headgear
54
        EquipSlot::FIGHT1_SLOT,             // Weapon
55
        EquipSlot::GLOVES_SLOT,             // Garment
56
        EquipSlot::RING2_SLOT,              // Accessory 1
57
        EquipSlot::RING1_SLOT,              // Armor
58
        EquipSlot::FIGHT2_SLOT,             // Shield
59
        EquipSlot::FEET_SLOT,               // Footgear
60
        EquipSlot::NECK_SLOT,               // Accessory 2
61
        EquipSlot::HEAD_SLOT,               // Upper Headgear
62
        EquipSlot::TORSO_SLOT,              // Middle Headgear
63
        EquipSlot::EVOL_RING1_SLOT,         // Costume Top Headgear
64
        EquipSlot::EVOL_RING2_SLOT,         // Costume Mid Headgear
65
        EquipSlot::PROJECTILE_SLOT,         // Costume Low Headgear
66
        EquipSlot::COSTUME_ROBE_SLOT,       // Costume Garment/Robe
67
        EquipSlot::MISSING1_SLOT,           // Missing slot 1
68
        EquipSlot::MISSING2_SLOT,           // Missing slot 2
69
        EquipSlot::SHADOW_ARMOR_SLOT,       // Shadow Armor
70
        EquipSlot::SHADOW_WEAPON_SLOT,      // Shadow Weapon
71
        EquipSlot::SHADOW_SHIELD_SLOT,      // Shadow Shield
72
        EquipSlot::SHADOW_SHOES_SLOT,       // Shadow Shoes
73
        EquipSlot::SHADOW_ACCESSORY2_SLOT,  // Shadow Accessory 2
74
        EquipSlot::SHADOW_ACCESSORY1_SLOT,  // Shadow Accessory 1
75
    };
76
}  // namespace InventoryRecv
77
78
void InventoryRecv::processPlayerEquipment(Net::MessageIn &msg)
79
{
80
    BLOCK_START("InventoryRecv::processPlayerEquipment")
81
    Inventory *const inventory = localPlayer != nullptr
82
        ? PlayerInfo::getInventory() : nullptr;
83
84
    msg.readInt16("len");
85
    Equipment *const equipment = PlayerInfo::getEquipment();
86
    if ((equipment != nullptr) && (equipment->getBackend() == nullptr))
87
    {   // look like SMSG_PLAYER_INVENTORY was not received
88
        Ea::InventoryRecv::mEquips.clear();
89
        equipment->setBackend(&Ea::InventoryRecv::mEquips);
90
    }
91
    const int number = (msg.getLength() - 4) / 20;
92
93
    for (int loop = 0; loop < number; loop++)
94
    {
95
        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
96
        const int itemId = msg.readInt16("item id");
97
        const ItemTypeT itemType = static_cast<ItemTypeT>(
98
            msg.readUInt8("item type"));
99
        const uint8_t identified = msg.readUInt8("identify");
100
        msg.readInt16("equip type?");
101
        const int equipType = msg.readInt16("equip type");
102
        msg.readUInt8("attribute");
103
        const uint8_t refine = msg.readUInt8("refine");
104
        int cards[maxCards];
105
        for (int f = 0; f < maxCards; f++)
106
            cards[f] = msg.readUInt16("card");
107
108
        if (Ea::InventoryRecv::mDebugInventory)
109
        {
110
            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d",
111
                        index, itemId, CAST_S32(itemType), identified);
112
        }
113
114
        if (inventory != nullptr)
115
        {
116
            inventory->setItem(index,
117
                itemId,
118
                itemType,
119
                1,
120
                refine,
121
                ItemColor_one,
122
                fromBool(identified, Identified),
123
                Damaged_false,
124
                Favorite_false,
125
                Equipm_true,
126
                Equipped_false);
127
            inventory->setCards(index, cards, maxCards);
128
        }
129
130
        if (equipType != 0)
131
        {
132
            Ea::InventoryRecv::mEquips.setEquipment(
133
                InventoryRecv::getSlot(equipType),
134
                index);
135
        }
136
    }
137
    BLOCK_END("InventoryRecv::processPlayerEquipment")
138
}
139
140
void InventoryRecv::processPlayerInventoryAdd(Net::MessageIn &msg)
141
{
142
    BLOCK_START("InventoryRecv::processPlayerInventoryAdd")
143
    Inventory *const inventory = localPlayer != nullptr
144
        ? PlayerInfo::getInventory() : nullptr;
145
146
    if ((PlayerInfo::getEquipment() != nullptr)
147
        && (PlayerInfo::getEquipment()->getBackend() == nullptr))
148
    {   // look like SMSG_PLAYER_INVENTORY was not received
149
        Ea::InventoryRecv::mEquips.clear();
150
        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
151
    }
152
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
153
    int amount = msg.readInt16("amount");
154
    const int itemId = msg.readInt16("item id");
155
    const uint8_t identified = msg.readUInt8("identified");
156
    msg.readUInt8("attribute");
157
    const uint8_t refine = msg.readUInt8("refine");
158
    int cards[maxCards];
159
    for (int f = 0; f < maxCards; f++)
160
        cards[f] = msg.readUInt16("card");
161
    const int equipType = msg.readInt16("equip type");
162
    const ItemTypeT type = static_cast<ItemTypeT>(msg.readUInt8("item type"));
163
    const unsigned char err = msg.readUInt8("status");
164
    BeingId floorId;
165
    if (Ea::InventoryRecv::mSentPickups.empty())
166
    {
167
        floorId = BeingId_zero;
168
    }
169
    else
170
    {
171
        floorId = Ea::InventoryRecv::mSentPickups.front();
172
        Ea::InventoryRecv::mSentPickups.pop();
173
    }
174
175
    if (err != 0U)
176
    {
177
        PickupT pickup;
178
        switch (err)
179
        {
180
            case 1:
181
                pickup = Pickup::BAD_ITEM;
182
                break;
183
            case 2:
184
                pickup = Pickup::TOO_HEAVY;
185
                break;
186
            case 3:
187
                pickup = Pickup::TOO_FAR;
188
                break;
189
            case 4:
190
                pickup = Pickup::INV_FULL;
191
                break;
192
            case 5:
193
                pickup = Pickup::STACK_FULL;
194
                break;
195
            case 6:
196
                pickup = Pickup::DROP_STEAL;
197
                break;
198
            default:
199
                pickup = Pickup::UNKNOWN;
200
                UNIMPLEMENTEDPACKETFIELD(err);
201
                break;
202
        }
203
        if (localPlayer != nullptr)
204
        {
205
            if (itemId == 0)
206
            {
207
                localPlayer->pickedUp(ItemDB::getEmpty(),
208
                    0,
209
                    ItemColor_one,
210
                    floorId,
211
                    pickup);
212
            }
213
            else
214
            {
215
                localPlayer->pickedUp(ItemDB::get(itemId),
216
                    0,
217
                    ItemColor_one,
218
                    floorId,
219
                    pickup);
220
            }
221
        }
222
    }
223
    else
224
    {
225
        if (localPlayer != nullptr)
226
        {
227
            if (itemId == 0)
228
            {
229
                localPlayer->pickedUp(ItemDB::getEmpty(),
230
                    amount,
231
                    ItemColor_one,
232
                    floorId,
233
                    Pickup::OKAY);
234
            }
235
            else
236
            {
237
                localPlayer->pickedUp(ItemDB::get(itemId),
238
                    amount,
239
                    ItemColor_one,
240
                    floorId,
241
                    Pickup::OKAY);
242
            }
243
        }
244
245
        if (inventory != nullptr)
246
        {
247
            const Item *const item = inventory->getItem(index);
248
249
            if ((item != nullptr) && item->getId() == itemId)
250
                amount += item->getQuantity();
251
252
            inventory->setItem(index,
253
                itemId,
254
                type,
255
                amount,
256
                refine,
257
                ItemColor_one,
258
                fromBool(identified, Identified),
259
                Damaged_false,
260
                Favorite_false,
261
                fromBool(equipType, Equipm),
262
                Equipped_false);
263
            inventory->setCards(index, cards, maxCards);
264
        }
265
        ArrowsListener::distributeEvent();
266
    }
267
    BLOCK_END("InventoryRecv::processPlayerInventoryAdd")
268
}
269
270
void InventoryRecv::processPlayerInventory(Net::MessageIn &msg)
271
{
272
    BLOCK_START("InventoryRecv::processPlayerInventory")
273
    Inventory *const inventory = localPlayer != nullptr
274
        ? PlayerInfo::getInventory() : nullptr;
275
276
    if (PlayerInfo::getEquipment() != nullptr)
277
    {
278
        // Clear inventory - this will be a complete refresh
279
        Ea::InventoryRecv::mEquips.clear();
280
        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
281
    }
282
283
    if (inventory != nullptr)
284
        inventory->clear();
285
286
    msg.readInt16("len");
287
    const int number = (msg.getLength() - 4) / 18;
288
289
    for (int loop = 0; loop < number; loop++)
290
    {
291
        int cards[maxCards];
292
        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
293
        const int itemId = msg.readInt16("item id");
294
        const ItemTypeT itemType = static_cast<ItemTypeT>(
295
            msg.readUInt8("item type"));
296
        const uint8_t identified = msg.readUInt8("identified");
297
        const int amount = msg.readInt16("amount");
298
        const int arrow = msg.readInt16("arrow");
299
        for (int i = 0; i < maxCards; i++)
300
            cards[i] = msg.readUInt16("card");
301
302
        if (Ea::InventoryRecv::mDebugInventory)
303
        {
304
            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
305
                        "Qty: %d, Cards: %d, %d, %d, %d",
306
                        index, itemId, CAST_S32(itemType), identified, amount,
307
                        cards[0], cards[1], cards[2], cards[3]);
308
        }
309
310
        // Trick because arrows are not considered equipment
311
        const bool isEquipment = (arrow & 0x8000) != 0;
312
313
        if (inventory != nullptr)
314
        {
315
            inventory->setItem(index,
316
                itemId,
317
                itemType,
318
                amount,
319
                0,
320
                ItemColor_one,
321
                fromBool(identified, Identified),
322
                Damaged_false,
323
                Favorite_false,
324
                fromBool(isEquipment, Equipm),
325
                Equipped_false);
326
            inventory->setCards(index, cards, maxCards);
327
        }
328
    }
329
    BLOCK_END("InventoryRecv::processPlayerInventory")
330
}
331
332
void InventoryRecv::processPlayerStorage(Net::MessageIn &msg)
333
{
334
    BLOCK_START("InventoryRecv::processPlayerInventory")
335
    Ea::InventoryRecv::mStorageItems.clear();
336
337
    msg.readInt16("len");
338
    const int number = (msg.getLength() - 4) / 18;
339
340
    for (int loop = 0; loop < number; loop++)
341
    {
342
        int cards[maxCards];
343
        const int index = msg.readInt16("index") - STORAGE_OFFSET;
344
        const int itemId = msg.readInt16("item id");
345
        const ItemTypeT itemType = static_cast<ItemTypeT>(
346
            msg.readUInt8("item type"));
347
        const uint8_t identified = msg.readUInt8("identified");
348
        const int amount = msg.readInt16("amount");
349
        msg.readInt16("arrow");
350
        for (int i = 0; i < maxCards; i++)
351
            cards[i] = msg.readUInt16("card");
352
353
        if (Ea::InventoryRecv::mDebugInventory)
354
        {
355
            logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, "
356
                        "Qty: %d, Cards: %d, %d, %d, %d",
357
                        index, itemId, CAST_S32(itemType), identified, amount,
358
                        cards[0], cards[1], cards[2], cards[3]);
359
        }
360
361
        Ea::InventoryRecv::mStorageItems.push_back(Ea::InventoryItem(
362
            index,
363
            itemId,
364
            itemType,
365
            cards,
366
            nullptr,
367
            amount,
368
            0,
369
            ItemColor_one,
370
            fromBool(identified, Identified),
371
            Damaged_false,
372
            Favorite_false,
373
            Equipm_false,
374
            -1));
375
    }
376
    BLOCK_END("InventoryRecv::processPlayerInventory")
377
}
378
379
void InventoryRecv::processPlayerEquip(Net::MessageIn &msg)
380
{
381
    BLOCK_START("InventoryRecv::processPlayerEquip")
382
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
383
    const int equipType = msg.readInt16("equip type");
384
    const uint8_t flag = msg.readUInt8("flag");
385
386
    if (flag == 0U)
387
    {
388
        NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
389
    }
390
    else
391
    {
392
        Ea::InventoryRecv::mEquips.setEquipment(
393
            InventoryRecv::getSlot(equipType),
394
            index);
395
    }
396
    BLOCK_END("InventoryRecv::processPlayerEquip")
397
}
398
399
void InventoryRecv::processPlayerUnEquip(Net::MessageIn &msg)
400
{
401
    BLOCK_START("InventoryRecv::processPlayerUnEquip")
402
    msg.readInt16("index");
403
    const int equipType = msg.readInt16("equip type");
404
    const uint8_t flag = msg.readUInt8("flag");
405
406
    if (flag != 0U)
407
    {
408
        Ea::InventoryRecv::mEquips.setEquipment(
409
            InventoryRecv::getSlot(equipType),
410
            -1);
411
    }
412
    if ((equipType & 0x8000) != 0)
413
        ArrowsListener::distributeEvent();
414
    BLOCK_END("InventoryRecv::processPlayerUnEquip")
415
}
416
417
void InventoryRecv::processPlayerStorageEquip(Net::MessageIn &msg)
418
{
419
    BLOCK_START("InventoryRecv::processPlayerStorageEquip")
420
    msg.readInt16("len");
421
    const int number = (msg.getLength() - 4) / 20;
422
423
    for (int loop = 0; loop < number; loop++)
424
    {
425
        int cards[maxCards];
426
        const int index = msg.readInt16("index") - STORAGE_OFFSET;
427
        const int itemId = msg.readInt16("item id");
428
        const ItemTypeT itemType = static_cast<ItemTypeT>(
429
            msg.readUInt8("item type"));
430
        const uint8_t identified = msg.readUInt8("identified");
431
        const int amount = 1;
432
        msg.readInt16("equip point?");
433
        msg.readInt16("another equip point?");
434
        msg.readUInt8("attribute (broken)");
435
        const uint8_t refine = msg.readUInt8("refine");
436
        for (int i = 0; i < maxCards; i++)
437
            cards[i] = msg.readUInt16("card");
438
439
        if (Ea::InventoryRecv::mDebugInventory)
440
        {
441
            logger->log("Index: %d, ID: %d, Type: %d, Identified: %u, "
442
                "Qty: %d, Cards: %d, %d, %d, %d, Refine: %u",
443
                index, itemId, CAST_S32(itemType),
444
                CAST_U32(identified), amount,
445
                cards[0], cards[1], cards[2], cards[3],
446
                CAST_U32(refine));
447
        }
448
449
        Ea::InventoryRecv::mStorageItems.push_back(Ea::InventoryItem(
450
            index,
451
            itemId,
452
            itemType,
453
            cards,
454
            nullptr,
455
            amount,
456
            refine,
457
            ItemColor_one,
458
            fromBool(identified, Identified),
459
            Damaged_false,
460
            Favorite_false,
461
            Equipm_false,
462
            -1));
463
    }
464
    BLOCK_END("InventoryRecv::processPlayerStorageEquip")
465
}
466
467
void InventoryRecv::processPlayerStorageAdd(Net::MessageIn &msg)
468
{
469
    BLOCK_START("InventoryRecv::processPlayerStorageAdd")
470
    // Move an item into storage
471
    const int index = msg.readInt16("index") - STORAGE_OFFSET;
472
    const int amount = msg.readInt32("amount");
473
    const int itemId = msg.readInt16("item id");
474
    const unsigned char identified = msg.readUInt8("identified");
475
    msg.readUInt8("attribute");
476
    const uint8_t refine = msg.readUInt8("refine");
477
    int cards[maxCards];
478
    for (int f = 0; f < maxCards; f++)
479
        cards[f] = msg.readUInt16("card");
480
481
    if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
482
    {
483
        item->setId(itemId, ItemColor_one);
484
        item->increaseQuantity(amount);
485
    }
486
    else
487
    {
488
        if (Ea::InventoryRecv::mStorage != nullptr)
489
        {
490
            Ea::InventoryRecv::mStorage->setItem(index,
491
                itemId,
492
                ItemType::Unknown,
493
                amount,
494
                refine,
495
                ItemColor_one,
496
                fromBool(identified, Identified),
497
                Damaged_false,
498
                Favorite_false,
499
                Equipm_false,
500
                Equipped_false);
501
            Ea::InventoryRecv::mStorage->setCards(index, cards, maxCards);
502
        }
503
    }
504
    BLOCK_END("InventoryRecv::processPlayerStorageAdd")
505
}
506
507
void InventoryRecv::processPlayerStorageRemove(Net::MessageIn &msg)
508
{
509
    BLOCK_START("InventoryRecv::processPlayerStorageRemove")
510
    // Move an item out of storage
511
    const int index = msg.readInt16("index") - STORAGE_OFFSET;
512
    const int amount = msg.readInt32("amount");
513
    if (Ea::InventoryRecv::mStorage != nullptr)
514
    {
515
        if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
516
        {
517
            item->increaseQuantity(-amount);
518
            if (item->getQuantity() == 0)
519
                Ea::InventoryRecv::mStorage->removeItemAt(index);
520
        }
521
    }
522
    BLOCK_END("InventoryRecv::processPlayerStorageRemove")
523
}
524
525
void InventoryRecv::processPlayerInventoryRemove(Net::MessageIn &msg)
526
{
527
    BLOCK_START("InventoryRecv::processPlayerInventoryRemove")
528
    Inventory *const inventory = localPlayer != nullptr
529
        ? PlayerInfo::getInventory() : nullptr;
530
531
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
532
    const int amount = msg.readInt16("amount");
533
    if (inventory != nullptr)
534
    {
535
        if (Item *const item = inventory->getItem(index))
536
        {
537
            item->increaseQuantity(-amount);
538
            if (item->getQuantity() == 0)
539
                inventory->removeItemAt(index);
540
            ArrowsListener::distributeEvent();
541
        }
542
    }
543
    BLOCK_END("InventoryRecv::processPlayerInventoryRemove")
544
}
545
546
int InventoryRecv::getSlot(const int eAthenaSlot)
547
{
548
    if (eAthenaSlot == 0)
549
        return EquipSlot::VECTOREND;
550
551
    if ((eAthenaSlot & 0x8000) != 0)
552
        return inventoryHandler->getProjectileSlot();
553
554
    unsigned int mask = 1;
555
    int position = 0;
556
    while ((eAthenaSlot & mask) == 0U)
557
    {
558
        mask <<= 1;
559
        position++;
560
    }
561
    if (position >= EquipSlot::VECTOREND)
562
        return EquipSlot::VECTOREND;
563
    return CAST_S32(EQUIP_POINTS[position]);
564
}
565
566
void InventoryRecv::processPlayerInventoryUse(Net::MessageIn &msg)
567
{
568
    BLOCK_START("InventoryRecv::processPlayerInventoryUse")
569
    Inventory *const inventory = localPlayer != nullptr
570
        ? PlayerInfo::getInventory() : nullptr;
571
572
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
573
    msg.readItemId("item id");
574
    msg.readInt32("id?");
575
    const int amount = msg.readInt16("amount");
576
    msg.readUInt8("type");
577
578
    if (inventory != nullptr)
579
    {
580
        if (Item *const item = inventory->getItem(index))
581
        {
582
            if (amount != 0)
583
                item->setQuantity(amount);
584
            else
585
                inventory->removeItemAt(index);
586
        }
587
    }
588
    BLOCK_END("InventoryRecv::processPlayerInventoryUse")
589
}
590
591
2
}  // namespace TmwAthena