GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/inventoryrecv.cpp Lines: 2 732 0.3 %
Date: 2018-09-20 Branches: 2 512 0.4 %

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 "net/eathena/inventoryrecv.h"
24
25
#include "actormanager.h"
26
#include "notifymanager.h"
27
#include "itemcolormanager.h"
28
#include "itemsoundmanager.h"
29
30
#include "being/localplayer.h"
31
32
#include "const/net/inventory.h"
33
34
#include "enums/equipslot.h"
35
36
#include "enums/resources/notifytypes.h"
37
38
#include "enums/net/deleteitemreason.h"
39
40
#include "gui/popups/itempopup.h"
41
42
#include "gui/widgets/createwidget.h"
43
44
#include "gui/windows/insertcarddialog.h"
45
46
#include "listeners/arrowslistener.h"
47
48
#include "net/inventoryhandler.h"
49
#include "net/messagein.h"
50
51
#include "net/eathena/itemflags.h"
52
#include "net/eathena/menu.h"
53
54
#include "net/ea/equipbackend.h"
55
#include "net/ea/inventoryrecv.h"
56
57
#include "resources/iteminfo.h"
58
59
#include "utils/gettext.h"
60
#include "utils/foreach.h"
61
#include "utils/stringutils.h"
62
63
#include "debug.h"
64
65
extern int serverVersion;
66
extern int packetVersion;
67
extern int itemIdLen;
68
69
namespace EAthena
70
{
71
72
namespace InventoryRecv
73
{
74
    // EQP_* to EquipSlot
75
    const EquipSlot::Type EQUIP_POINTS[EquipSlot::VECTOREND] =
76
    {
77
        EquipSlot::LEGS_SLOT,               // 0  1       EQP_HEAD_LOW
78
        EquipSlot::FIGHT1_SLOT,             // 1  2       EQP_HAND_R
79
        EquipSlot::GLOVES_SLOT,             // 2  4       EQP_GARMENT
80
        EquipSlot::RING2_SLOT,              // 3  8       EQP_ACC_L
81
        EquipSlot::RING1_SLOT,              // 4  16      EQP_ARMOR
82
        EquipSlot::FIGHT2_SLOT,             // 5  32      EQP_HAND_L
83
        EquipSlot::FEET_SLOT,               // 6  64      EQP_SHOES
84
        EquipSlot::NECK_SLOT,               // 7  128     EQP_ACC_R
85
        EquipSlot::HEAD_SLOT,               // 8  256     EQP_HEAD_TOP
86
        EquipSlot::TORSO_SLOT,              // 9  512     EQP_HEAD_MID
87
        EquipSlot::EVOL_RING1_SLOT,         // 10 1024    EQP_COSTUME_HEAD_TOP
88
        EquipSlot::EVOL_RING2_SLOT,         // 11 2048    EQP_COSTUME_HEAD_MID
89
        EquipSlot::PROJECTILE_SLOT,         // 12 4096    EQP_COSTUME_HEAD_LOW
90
        EquipSlot::COSTUME_ROBE_SLOT,       // 13 8192    EQP_COSTUME_GARMENT
91
        EquipSlot::PROJECTILE_SLOT,         // 14 16384   UNUSED_COSTUME_FLOOR
92
        EquipSlot::PROJECTILE_SLOT,         // 15 32768   EQP_AMMO
93
        EquipSlot::SHADOW_ARMOR_SLOT,       // 16 65536   EQP_SHADOW_ARMOR
94
        EquipSlot::SHADOW_WEAPON_SLOT,      // 17 131072  EQP_SHADOW_WEAPON
95
        EquipSlot::SHADOW_SHIELD_SLOT,      // 18 262144  EQP_SHADOW_SHIELD
96
        EquipSlot::SHADOW_SHOES_SLOT,       // 19 524288  EQP_SHADOW_SHOES
97
        EquipSlot::SHADOW_ACCESSORY2_SLOT,  // 20 1048576 EQP_SHADOW_ACC_R
98
        EquipSlot::SHADOW_ACCESSORY1_SLOT,  // 21 2097152 EQP_SHADOW_ACC_L
99
    };
100
101
1
    Ea::InventoryItems mCartItems;
102
}  // namespace InventoryRecv
103
104
void InventoryRecv::processPlayerEquipment(Net::MessageIn &msg)
105
{
106
    BLOCK_START("InventoryRecv::processPlayerEquipment")
107
    Inventory *const inventory = localPlayer != nullptr
108
        ? PlayerInfo::getInventory() : nullptr;
109
110
    msg.readInt16("len");
111
    Equipment *const equipment = PlayerInfo::getEquipment();
112
    if ((equipment != nullptr) && (equipment->getBackend() == nullptr))
113
    {   // look like SMSG_PLAYER_INVENTORY was not received
114
        Ea::InventoryRecv::mEquips.clear();
115
        equipment->setBackend(&Ea::InventoryRecv::mEquips);
116
    }
117
118
    int packetLen = 2 + 2 + 1 + 1 + 8;
119
    if (msg.getVersion() >= 20120925)
120
        packetLen += 4 + 4 + 1;
121
    else
122
        packetLen += 1 + 2 + 2 + 1;
123
    if (msg.getVersion() >= 20071002)
124
        packetLen += 4;
125
    if (msg.getVersion() >= 20080102)
126
        packetLen += 2;
127
    if (msg.getVersion() >= 20100629)
128
        packetLen += 2;
129
    if (msg.getVersion() >= 20150226)
130
        packetLen += 26;
131
    packetLen += itemIdLen * 5 - 2 * 5;   // - 5 items by 2 bytes. + 5 items
132
133
    const int number = (msg.getLength() - 4) / packetLen;
134
135
    for (int loop = 0; loop < number; loop++)
136
    {
137
        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
138
        const int itemId = msg.readItemId("item id");
139
        const ItemTypeT itemType = static_cast<ItemTypeT>(
140
            msg.readUInt8("item type"));
141
        int equipType;
142
        if (msg.getVersion() >= 20120925)
143
        {
144
            msg.readInt32("location");
145
            equipType = msg.readInt32("wear state");
146
        }
147
        else
148
        {
149
            msg.readUInt8("identified");
150
            msg.readInt16("location");
151
            equipType = msg.readInt16("wear state");
152
            msg.readUInt8("is damaged");
153
        }
154
        const uint8_t refine = CAST_U8(msg.readInt8("refine"));
155
        int cards[maxCards];
156
        for (int f = 0; f < maxCards; f++)
157
            cards[f] = msg.readItemId("card");
158
        if (msg.getVersion() >= 20071002)
159
            msg.readInt32("hire expire date (?)");
160
        if (msg.getVersion() >= 20080102)
161
            msg.readInt16("equip type");
162
        if (msg.getVersion() >= 20100629)
163
            msg.readInt16("item sprite number");
164
        ItemOptionsList *options = nullptr;
165
        if (msg.getVersion() >= 20150226)
166
        {
167
            options = new ItemOptionsList(msg.readUInt8("option count"));
168
            for (int f = 0; f < 5; f ++)
169
            {
170
                const uint16_t idx = msg.readInt16("option index");
171
                const uint16_t val = msg.readInt16("option value");
172
                msg.readUInt8("option param");
173
                options->add(idx, val);
174
            }
175
        }
176
        ItemFlags flags;
177
        if (msg.getVersion() >= 20120925)
178
            flags.byte = msg.readUInt8("flags");
179
        else
180
            flags.byte = 0;
181
        if (inventory != nullptr)
182
        {
183
            inventory->setItem(index,
184
                itemId,
185
                itemType,
186
                1,
187
                refine,
188
                ItemColorManager::getColorFromCards(&cards[0]),
189
                fromBool(flags.bits.isIdentified, Identified),
190
                fromBool(flags.bits.isDamaged, Damaged),
191
                fromBool(flags.bits.isFavorite, Favorite),
192
                Equipm_true,
193
                Equipped_false);
194
            inventory->setCards(index, cards, maxCards);
195
            inventory->setOptions(index, options);
196
        }
197
        delete options;
198
199
        if (equipType != 0)
200
        {
201
            Ea::InventoryRecv::mEquips.setEquipment(
202
                InventoryRecv::getSlot(equipType),
203
                index);
204
        }
205
    }
206
    BLOCK_END("InventoryRecv::processPlayerEquipment")
207
}
208
209
void InventoryRecv::processPlayerInventoryAdd(Net::MessageIn &msg)
210
{
211
    BLOCK_START("InventoryRecv::processPlayerInventoryAdd")
212
    Inventory *const inventory = localPlayer != nullptr
213
        ? PlayerInfo::getInventory() : nullptr;
214
215
    if ((PlayerInfo::getEquipment() != nullptr)
216
        && (PlayerInfo::getEquipment()->getBackend() == nullptr))
217
    {   // look like SMSG_PLAYER_INVENTORY was not received
218
        Ea::InventoryRecv::mEquips.clear();
219
        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
220
    }
221
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
222
    int amount = msg.readInt16("count");
223
    const int itemId = msg.readItemId("item id");
224
    const uint8_t identified = msg.readUInt8("identified");
225
    const uint8_t damaged = msg.readUInt8("is damaged");
226
    const uint8_t refine = msg.readUInt8("refine");
227
    Favorite favorite = Favorite_false;
228
    int cards[maxCards];
229
    for (int f = 0; f < maxCards; f++)
230
        cards[f] = msg.readItemId("card");
231
    int equipType;
232
    if (msg.getVersion() >= 20120925)
233
        equipType = msg.readInt32("location");
234
    else
235
        equipType = msg.readInt16("location");
236
    const ItemTypeT itemType = static_cast<ItemTypeT>(
237
        msg.readUInt8("item type"));
238
    const unsigned char err = msg.readUInt8("result");
239
    if (msg.getVersion() >= 20061218)
240
        msg.readInt32("hire expire date");
241
    if (msg.getVersion() >= 20071002)
242
        msg.readInt16("bind on equip");
243
    ItemOptionsList *options = nullptr;
244
    if (msg.getVersion() >= 20150226)
245
    {
246
        options = new ItemOptionsList;
247
        for (int f = 0; f < 5; f ++)
248
        {
249
            const uint16_t idx = msg.readInt16("option index");
250
            const uint16_t val = msg.readInt16("option value");
251
            msg.readUInt8("option param");
252
            options->add(idx, val);
253
        }
254
    }
255
    if (msg.getVersion() >= 20160921)
256
    {
257
        favorite = fromBool(msg.readUInt8("favorite"), Favorite);
258
        msg.readInt16("look");
259
    }
260
261
    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
262
    BeingId floorId;
263
    if (Ea::InventoryRecv::mSentPickups.empty())
264
    {
265
        floorId = BeingId_zero;
266
    }
267
    else
268
    {
269
        floorId = Ea::InventoryRecv::mSentPickups.front();
270
        Ea::InventoryRecv::mSentPickups.pop();
271
    }
272
273
    if (err != 0u)
274
    {
275
        PickupT pickup;
276
        switch (err)
277
        {
278
            case 1:
279
                pickup = Pickup::BAD_ITEM;
280
                break;
281
            case 2:
282
                pickup = Pickup::TOO_HEAVY;
283
                break;
284
            case 4:
285
                pickup = Pickup::INV_FULL;
286
                break;
287
            case 5:
288
                pickup = Pickup::MAX_AMOUNT;
289
                break;
290
            case 6:
291
                pickup = Pickup::TOO_FAR;
292
                break;
293
            case 7:
294
                pickup = Pickup::STACK_AMOUNT;
295
                break;
296
            default:
297
                pickup = Pickup::UNKNOWN;
298
                UNIMPLEMENTEDPACKETFIELD(err);
299
                break;
300
        }
301
        if (localPlayer != nullptr)
302
        {
303
            if (itemId == 0)
304
            {
305
                localPlayer->pickedUp(ItemDB::getEmpty(),
306
                    0,
307
                    color,
308
                    floorId,
309
                    pickup);
310
            }
311
            else
312
            {
313
                localPlayer->pickedUp(ItemDB::get(itemId),
314
                    0,
315
                    color,
316
                    floorId,
317
                    pickup);
318
            }
319
        }
320
    }
321
    else
322
    {
323
        if (localPlayer != nullptr)
324
        {
325
            if (itemId == 0)
326
            {
327
                localPlayer->pickedUp(ItemDB::getEmpty(),
328
                    amount,
329
                    color,
330
                    floorId,
331
                    Pickup::OKAY);
332
            }
333
            else
334
            {
335
                localPlayer->pickedUp(ItemDB::get(itemId),
336
                    amount,
337
                    color,
338
                    floorId,
339
                    Pickup::OKAY);
340
            }
341
        }
342
343
        if (inventory != nullptr)
344
        {
345
            const Item *const item = inventory->getItem(index);
346
347
            if ((item != nullptr) && item->getId() == itemId)
348
                amount += item->getQuantity();
349
350
            inventory->setItem(index,
351
                itemId,
352
                itemType,
353
                amount,
354
                refine,
355
                color,
356
                fromBool(identified, Identified),
357
                fromBool(damaged, Damaged),
358
                favorite,
359
                fromBool(equipType, Equipm),
360
                Equipped_false);
361
            inventory->setCards(index, cards, maxCards);
362
            inventory->setOptions(index, options);
363
        }
364
        ArrowsListener::distributeEvent();
365
    }
366
    delete options;
367
    BLOCK_END("InventoryRecv::processPlayerInventoryAdd")
368
}
369
370
void InventoryRecv::processPlayerInventory(Net::MessageIn &msg)
371
{
372
    BLOCK_START("InventoryRecv::processPlayerInventory")
373
    Inventory *const inventory = localPlayer != nullptr
374
        ? PlayerInfo::getInventory() : nullptr;
375
    if (PlayerInfo::getEquipment() != nullptr)
376
    {
377
        // Clear inventory - this will be a complete refresh
378
        Ea::InventoryRecv::mEquips.clear();
379
        PlayerInfo::getEquipment()->setBackend(&Ea::InventoryRecv::mEquips);
380
    }
381
382
    if (inventory != nullptr)
383
        inventory->clear();
384
385
    msg.readInt16("len");
386
387
    int packetLen = 7;
388
    if (msg.getVersion() >= 20120925)
389
        packetLen += 4 + 1;
390
    else
391
        packetLen += 1 + 2;
392
    if (packetVersion >= 5)
393
        packetLen += 8;
394
    if (msg.getVersion() >= 20080102)
395
        packetLen += 4;
396
    packetLen += itemIdLen * 5 - 10;
397
398
    const int number = (msg.getLength() - 4) / packetLen;
399
400
    for (int loop = 0; loop < number; loop++)
401
    {
402
        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
403
        const int itemId = msg.readItemId("item id");
404
        const ItemTypeT itemType = static_cast<ItemTypeT>(
405
            msg.readUInt8("item type"));
406
        if (msg.getVersion() < 20120925)
407
            msg.readUInt8("identified");
408
        const int amount = msg.readInt16("count");
409
        if (msg.getVersion() >= 20120925)
410
            msg.readInt32("wear state / equip");
411
        else
412
            msg.readInt16("wear state / equip");
413
        int cards[maxCards];
414
        if (packetVersion >= 5)
415
        {
416
            for (int f = 0; f < maxCards; f++)
417
                cards[f] = msg.readItemId("card");
418
        }
419
        else
420
        {
421
            for (int f = 0; f < maxCards; f++)
422
                cards[f] = 0;
423
        }
424
        if (msg.getVersion() >= 20080102)
425
            msg.readInt32("hire expire date (?)");
426
        ItemFlags flags;
427
        if (msg.getVersion() >= 20120925)
428
            flags.byte = msg.readUInt8("flags");
429
        else
430
            flags.byte = 0;
431
432
        if (inventory != nullptr)
433
        {
434
            inventory->setItem(index,
435
                itemId,
436
                itemType,
437
                amount,
438
                0,
439
                ItemColorManager::getColorFromCards(&cards[0]),
440
                fromBool(flags.bits.isIdentified, Identified),
441
                fromBool(flags.bits.isDamaged, Damaged),
442
                fromBool(flags.bits.isFavorite, Favorite),
443
                Equipm_false,
444
                Equipped_false);
445
            inventory->setCards(index, cards, maxCards);
446
        }
447
    }
448
    BLOCK_END("InventoryRecv::processPlayerInventory")
449
}
450
451
void InventoryRecv::processPlayerStorage(Net::MessageIn &msg)
452
{
453
    BLOCK_START("InventoryRecv::processPlayerInventory")
454
    Ea::InventoryRecv::mInventoryItems.clear();
455
456
    msg.readInt16("len");
457
458
    int packetLen = 7;
459
    if (msg.getVersion() >= 20120925)
460
        packetLen += 4 + 1;
461
    else
462
        packetLen += 1 + 2;
463
    if (packetVersion >= 5)
464
        packetLen += 8;
465
    if (msg.getVersion() >= 20080102)
466
        packetLen += 4;
467
    packetLen += itemIdLen * 5 - 10;
468
469
    int number;
470
    if (msg.getVersion() >= 20120925)
471
    {
472
        msg.readString(24, "storage name");
473
        number = (msg.getLength() - 4 - 24) / packetLen;
474
    }
475
    else
476
    {
477
        number = (msg.getLength() - 4) / packetLen;
478
    }
479
480
    for (int loop = 0; loop < number; loop++)
481
    {
482
        const int index = msg.readInt16("item index") - STORAGE_OFFSET;
483
        const int itemId = msg.readItemId("item id");
484
        const ItemTypeT itemType = static_cast<ItemTypeT>(
485
            msg.readUInt8("item type"));
486
        if (msg.getVersion() < 20120925)
487
            msg.readUInt8("identified");
488
        const int amount = msg.readInt16("count");
489
        if (msg.getVersion() >= 20120925)
490
            msg.readInt32("wear state / equip");
491
        else
492
            msg.readInt16("wear state / equip");
493
        int cards[maxCards];
494
        if (msg.getVersion() >= 5)
495
        {
496
            for (int f = 0; f < maxCards; f++)
497
                cards[f] = msg.readItemId("card");
498
        }
499
        else
500
        {
501
            for (int f = 0; f < maxCards; f++)
502
                cards[f] = 0;
503
        }
504
        if (msg.getVersion() >= 20080102)
505
            msg.readInt32("hire expire date (?)");
506
        ItemFlags flags;
507
        if (msg.getVersion() >= 20120925)
508
            flags.byte = msg.readUInt8("flags");
509
        else
510
            flags.byte = 0;
511
512
        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
513
            index,
514
            itemId,
515
            itemType,
516
            cards,
517
            nullptr,
518
            amount,
519
            0,
520
            ItemColorManager::getColorFromCards(&cards[0]),
521
            fromBool(flags.bits.isIdentified, Identified),
522
            fromBool(flags.bits.isDamaged, Damaged),
523
            fromBool(flags.bits.isFavorite, Favorite),
524
            Equipm_false));
525
    }
526
    BLOCK_END("InventoryRecv::processPlayerInventory")
527
}
528
529
void InventoryRecv::processPlayerEquip(Net::MessageIn &msg)
530
{
531
    BLOCK_START("InventoryRecv::processPlayerEquip")
532
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
533
    int equipType;
534
    if (msg.getVersion() >= 20120925)
535
        equipType = msg.readInt32("wear location");
536
    else
537
        equipType = msg.readInt16("wear location");
538
    if (msg.getVersion() >= 20100629)
539
        msg.readInt16("sprite");
540
    const uint8_t flag = msg.readUInt8("result");
541
542
    switch (flag)
543
    {
544
        case 0:
545
            Ea::InventoryRecv::mEquips.setEquipment(
546
                InventoryRecv::getSlot(equipType),
547
                index);
548
            break;
549
        case 1:
550
            NotifyManager::notify(NotifyTypes::EQUIP_FAILED_LEVEL);
551
            break;
552
553
        case 2:
554
        default:
555
            NotifyManager::notify(NotifyTypes::EQUIP_FAILED);
556
            break;
557
    }
558
    BLOCK_END("InventoryRecv::processPlayerEquip")
559
}
560
561
void InventoryRecv::processPlayerUnEquip(Net::MessageIn &msg)
562
{
563
    BLOCK_START("InventoryRecv::processPlayerUnEquip")
564
    msg.readInt16("index");
565
    int equipType;
566
    if (msg.getVersion() >= 20120925)
567
        equipType = msg.readInt32("wear location");
568
    else
569
        equipType = msg.readInt16("wear location");
570
    const uint8_t flag = msg.readUInt8("result");
571
572
    if (flag != 0u)
573
    {
574
        NotifyManager::notify(NotifyTypes::UNEQUIP_FAILED);
575
    }
576
    else
577
    {
578
        Ea::InventoryRecv::mEquips.setEquipment(
579
            InventoryRecv::getSlot(equipType),
580
            -1);
581
    }
582
    if ((equipType & 0x8000) != 0)
583
        ArrowsListener::distributeEvent();
584
    BLOCK_END("InventoryRecv::processPlayerUnEquip")
585
}
586
587
void InventoryRecv::processPlayerInventoryRemove2(Net::MessageIn &msg)
588
{
589
    BLOCK_START("InventoryRecv::processPlayerInventoryRemove2")
590
    Inventory *const inventory = localPlayer != nullptr
591
        ? PlayerInfo::getInventory() : nullptr;
592
593
    const DeleteItemReasonT reason = static_cast<DeleteItemReasonT>(
594
        msg.readInt16("reason"));
595
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
596
    const int amount = msg.readInt16("amount");
597
598
    if (inventory != nullptr)
599
    {
600
        if (Item *const item = inventory->getItem(index))
601
        {
602
            switch (reason)
603
            {
604
                case DeleteItemReason::Normal:
605
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_NORMAL,
606
                        item->getName());
607
                    break;
608
                case DeleteItemReason::SkillUse:
609
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_SKILL_USE,
610
                        item->getName());
611
                    break;
612
                case DeleteItemReason::FailRefine:
613
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_FAIL_REFINE,
614
                        item->getName());
615
                    break;
616
                case DeleteItemReason::MaterialChange:
617
                    NotifyManager::notify(
618
                        NotifyTypes::DELETE_ITEM_MATERIAL_CHANGE,
619
                        item->getName());
620
                    break;
621
                case DeleteItemReason::ToStorage:
622
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_TO_STORAGE,
623
                        item->getName());
624
                    break;
625
                case DeleteItemReason::ToCart:
626
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_TO_CART,
627
                        item->getName());
628
                    break;
629
                case DeleteItemReason::Sold:
630
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_SOLD,
631
                        item->getName());
632
                    break;
633
                case DeleteItemReason::Analysis:
634
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_ANALYSIS,
635
                        item->getName());
636
                    break;
637
                default:
638
                    NotifyManager::notify(NotifyTypes::DELETE_ITEM_UNKNOWN,
639
                        item->getName());
640
                    break;
641
            }
642
643
            item->increaseQuantity(-amount);
644
            if (item->getQuantity() == 0)
645
                inventory->removeItemAt(index);
646
            ArrowsListener::distributeEvent();
647
        }
648
    }
649
    BLOCK_END("InventoryRecv::processPlayerInventoryRemove2")
650
}
651
652
void InventoryRecv::processPlayerStorageEquip(Net::MessageIn &msg)
653
{
654
    BLOCK_START("InventoryRecv::processPlayerStorageEquip")
655
    msg.readInt16("len");
656
657
    int packetLen = 2 + 2 + 1 + 1 + 8;
658
    if (msg.getVersion() >= 20120925)
659
        packetLen += 4 + 4 + 1;
660
    else
661
        packetLen += 1 + 2 + 2 + 1;
662
    if (msg.getVersion() >= 20071002)
663
        packetLen += 4;
664
    if (msg.getVersion() >= 20080102)
665
        packetLen += 2;
666
    if (msg.getVersion() >= 20100629)
667
        packetLen += 2;
668
    if (msg.getVersion() >= 20150226)
669
        packetLen += 26;
670
    packetLen += itemIdLen * 5 - 10;
671
672
    int number;
673
    if (msg.getVersion() >= 20120925)
674
    {
675
        msg.readString(24, "storage name");
676
        number = (msg.getLength() - 4 - 24) / packetLen;
677
    }
678
    else
679
    {
680
        number = (msg.getLength() - 4) / packetLen;
681
    }
682
683
    for (int loop = 0; loop < number; loop++)
684
    {
685
        const int index = msg.readInt16("index") - STORAGE_OFFSET;
686
        const int itemId = msg.readItemId("item id");
687
        const ItemTypeT itemType = static_cast<ItemTypeT>(
688
            msg.readUInt8("item type"));
689
        const int amount = 1;
690
        if (msg.getVersion() >= 20120925)
691
        {
692
            msg.readInt32("location");
693
            msg.readInt32("wear state");
694
        }
695
        else
696
        {
697
            msg.readUInt8("identified");
698
            msg.readInt16("location");
699
            msg.readInt16("wear state");
700
            msg.readUInt8("is damaged");
701
        }
702
        const uint8_t refine = msg.readUInt8("refine level");
703
        int cards[maxCards];
704
        for (int f = 0; f < maxCards; f++)
705
            cards[f] = msg.readItemId("card");
706
        if (msg.getVersion() >= 20071002)
707
            msg.readInt32("hire expire date");
708
        if (msg.getVersion() >= 20080102)
709
            msg.readInt16("bind on equip");
710
        if (msg.getVersion() >= 20100629)
711
            msg.readInt16("sprite");
712
        ItemOptionsList *options = nullptr;
713
        if (msg.getVersion() >= 20150226)
714
        {
715
            options = new ItemOptionsList(msg.readUInt8("option count"));
716
            for (int f = 0; f < 5; f ++)
717
            {
718
                const uint16_t idx = msg.readInt16("option index");
719
                const uint16_t val = msg.readInt16("option value");
720
                msg.readUInt8("option param");
721
                options->add(idx, val);
722
            }
723
        }
724
725
        ItemFlags flags;
726
        if (msg.getVersion() >= 20120925)
727
            flags.byte = msg.readUInt8("flags");
728
        else
729
            flags.byte = 0;
730
731
        Ea::InventoryRecv::mInventoryItems.push_back(Ea::InventoryItem(
732
            index,
733
            itemId,
734
            itemType,
735
            cards,
736
            options,
737
            amount,
738
            refine,
739
            ItemColorManager::getColorFromCards(&cards[0]),
740
            fromBool(flags.bits.isIdentified, Identified),
741
            fromBool(flags.bits.isDamaged, Damaged),
742
            fromBool(flags.bits.isFavorite, Favorite),
743
            Equipm_false));
744
        delete options;
745
    }
746
    BLOCK_END("InventoryRecv::processPlayerStorageEquip")
747
}
748
749
void InventoryRecv::processPlayerStorageAdd(Net::MessageIn &msg)
750
{
751
    BLOCK_START("InventoryRecv::processPlayerStorageAdd")
752
    // Move an item into storage
753
    const int index = msg.readInt16("index") - STORAGE_OFFSET;
754
    const int amount = msg.readInt32("amount");
755
    const int itemId = msg.readItemId("item id");
756
    ItemTypeT itemType;
757
    if (msg.getVersion() >= 5)
758
        itemType = static_cast<ItemTypeT>(msg.readUInt8("type"));
759
    else
760
        itemType = ItemType::Unknown;
761
    const unsigned char identified = msg.readUInt8("identify");
762
    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
763
    const uint8_t refine = msg.readUInt8("refine");
764
    int cards[maxCards];
765
    for (int f = 0; f < maxCards; f++)
766
        cards[f] = msg.readItemId("card");
767
    ItemOptionsList *options = nullptr;
768
    if (msg.getVersion() >= 20150226)
769
    {
770
        options = new ItemOptionsList;
771
        for (int f = 0; f < 5; f ++)
772
        {
773
            const uint16_t idx = msg.readInt16("option index");
774
            const uint16_t val = msg.readInt16("option value");
775
            msg.readUInt8("option param");
776
            options->add(idx, val);
777
        }
778
    }
779
780
    const ItemColor color = ItemColorManager::getColorFromCards(&cards[0]);
781
    if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
782
    {
783
        item->setId(itemId, color);
784
        item->increaseQuantity(amount);
785
    }
786
    else
787
    {
788
        if (Ea::InventoryRecv::mStorage != nullptr)
789
        {
790
            Ea::InventoryRecv::mStorage->setItem(index,
791
                itemId,
792
                itemType,
793
                amount,
794
                refine,
795
                color,
796
                fromBool(identified, Identified),
797
                damaged,
798
                Favorite_false,
799
                Equipm_false,
800
                Equipped_false);
801
            Ea::InventoryRecv::mStorage->setCards(index, cards, maxCards);
802
            Ea::InventoryRecv::mStorage->setOptions(index, options);
803
        }
804
    }
805
    delete options;
806
    BLOCK_END("InventoryRecv::processPlayerStorageAdd")
807
}
808
809
void InventoryRecv::processPlayerUseCard(Net::MessageIn &msg)
810
{
811
    const Inventory *const inv = PlayerInfo::getInventory();
812
    const int index = inventoryHandler->getItemIndex();
813
    const Item *item1 = nullptr;
814
    if (inv != nullptr)
815
        item1 = inv->getItem(index);
816
    SellDialog *const dialog = CREATEWIDGETR(InsertCardDialog,
817
        index, item1);
818
819
    const int count = (msg.readInt16("len") - 4) / 2;
820
    for (int f = 0; f < count; f ++)
821
    {
822
        const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
823
        if (inv == nullptr)
824
            continue;
825
        const Item *const item = inv->getItem(itemIndex);
826
        if (item == nullptr)
827
            continue;
828
        dialog->addItem(item, 0);
829
    }
830
}
831
832
void InventoryRecv::processPlayerInsertCard(Net::MessageIn &msg)
833
{
834
    const int itemIndex = msg.readInt16("item index") - INVENTORY_OFFSET;
835
    const int cardIndex = msg.readInt16("card index") - INVENTORY_OFFSET;
836
    if (msg.readUInt8("flag") != 0u)
837
    {
838
        NotifyManager::notify(NotifyTypes::CARD_INSERT_FAILED);
839
    }
840
    else
841
    {
842
        NotifyManager::notify(NotifyTypes::CARD_INSERT_SUCCESS);
843
        Inventory *const inv = PlayerInfo::getInventory();
844
        if (inv == nullptr)
845
            return;
846
        Item *const card = inv->getItem(cardIndex);
847
        int cardId = 0;
848
        if (card != nullptr)
849
        {
850
            cardId = card->getId();
851
            card->increaseQuantity(-1);
852
            if (card->getQuantity() == 0)
853
                inv->removeItemAt(cardIndex);
854
        }
855
        Item *const item = inv->getItem(itemIndex);
856
        if (item != nullptr)
857
        {
858
            item->addCard(cardId);
859
            item->updateColor();
860
            itemPopup->resetPopup();
861
        }
862
    }
863
}
864
865
void InventoryRecv::processPlayerItemRentalTime(Net::MessageIn &msg)
866
{
867
    const int id = msg.readItemId("item id");
868
    const int seconds = msg.readInt32("seconds");
869
    const ItemInfo &info = ItemDB::get(id);
870
    const std::string timeStr = timeDiffToString(seconds);
871
    NotifyManager::notify(NotifyTypes::RENTAL_TIME_LEFT,
872
        // TRANSLATORS: notification message
873
        strprintf(_("Left %s rental time for item %s."),
874
        timeStr.c_str(), info.getName().c_str()));
875
}
876
877
void InventoryRecv::processPlayerItemRentalExpired(Net::MessageIn &msg)
878
{
879
    Inventory *const inventory = localPlayer != nullptr
880
        ? PlayerInfo::getInventory() : nullptr;
881
882
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
883
    const int id = msg.readItemId("item id");
884
    const ItemInfo &info = ItemDB::get(id);
885
886
    NotifyManager::notify(NotifyTypes::RENTAL_TIME_EXPIRED,
887
        info.getName());
888
    if (inventory != nullptr)
889
    {
890
        if (Item *const item = inventory->getItem(index))
891
        {
892
            item->increaseQuantity(-item->getQuantity());
893
            inventory->removeItemAt(index);
894
            ArrowsListener::distributeEvent();
895
        }
896
    }
897
}
898
899
void InventoryRecv::processPlayerStorageRemove(Net::MessageIn &msg)
900
{
901
    BLOCK_START("InventoryRecv::processPlayerStorageRemove")
902
    // Move an item out of storage
903
    const int index = msg.readInt16("index") - STORAGE_OFFSET;
904
    const int amount = msg.readInt32("amount");
905
    if (Ea::InventoryRecv::mStorage != nullptr)
906
    {
907
        if (Item *const item = Ea::InventoryRecv::mStorage->getItem(index))
908
        {
909
            item->increaseQuantity(-amount);
910
            if (item->getQuantity() == 0)
911
                Ea::InventoryRecv::mStorage->removeItemAt(index);
912
        }
913
    }
914
    BLOCK_END("InventoryRecv::processPlayerStorageRemove")
915
}
916
917
void InventoryRecv::processCartInfo(Net::MessageIn &msg)
918
{
919
    msg.readInt16("cart items used");
920
    const int size = msg.readInt16("max cart items");
921
    PlayerInfo::setAttribute(Attributes::CART_TOTAL_WEIGHT,
922
        msg.readInt32("cart weight"),
923
        Notify_true);
924
    PlayerInfo::setAttribute(Attributes::CART_MAX_WEIGHT,
925
        msg.readInt32("max cart weight"),
926
        Notify_true);
927
    if (mCartItems.empty())
928
        return;
929
930
    Inventory *const inv = PlayerInfo::getCartInventory();
931
    if (inv == nullptr)
932
        return;
933
934
    inv->resize(size);
935
936
    FOR_EACH (Ea::InventoryItems::const_iterator, it, mCartItems)
937
    {
938
        inv->setItem((*it).slot,
939
            (*it).id,
940
            (*it).type,
941
            (*it).quantity,
942
            (*it).refine,
943
            (*it).color,
944
            (*it).identified,
945
            (*it).damaged,
946
            (*it).favorite,
947
            (*it).equip,
948
            Equipped_false);
949
    }
950
    mCartItems.clear();
951
}
952
953
void InventoryRecv::processCartRemove(Net::MessageIn &msg)
954
{
955
    UNIMPLEMENTEDPACKET;
956
    // +++ need close or clear cart?
957
}
958
959
void InventoryRecv::processPlayerCartAdd(Net::MessageIn &msg)
960
{
961
    BLOCK_START("InventoryRecv::processPlayerCartAdd")
962
    Inventory *const inventory = localPlayer != nullptr
963
        ? PlayerInfo::getCartInventory() : nullptr;
964
965
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
966
    int amount = msg.readInt32("count");
967
    const int itemId = msg.readItemId("item id");
968
    ItemTypeT itemType = ItemType::Unknown;
969
    if (msg.getVersion() >= 5)
970
    {
971
        itemType = static_cast<ItemTypeT>(
972
            msg.readUInt8("item type"));
973
    }
974
    const uint8_t identified = msg.readUInt8("identified");
975
    const Damaged damaged = fromBool(msg.readUInt8("attribute"), Damaged);
976
    const uint8_t refine = msg.readUInt8("refine");
977
    int cards[maxCards];
978
    for (int f = 0; f < maxCards; f++)
979
        cards[f] = msg.readItemId("card");
980
    ItemOptionsList *options = nullptr;
981
    if (msg.getVersion() >= 20150226)
982
    {
983
        options = new ItemOptionsList;
984
        for (int f = 0; f < 5; f ++)
985
        {
986
            const uint16_t idx = msg.readInt16("option index");
987
            const uint16_t val = msg.readInt16("option value");
988
            msg.readUInt8("option param");
989
            options->add(idx, val);
990
        }
991
    }
992
993
    // check what cart was created, if not add delayed items
994
    if ((inventory != nullptr) && inventory->getSize() > 0)
995
    {
996
        const Item *const item = inventory->getItem(index);
997
998
        if ((item != nullptr) && item->getId() == itemId)
999
            amount += item->getQuantity();
1000
1001
        inventory->setItem(index,
1002
            itemId,
1003
            itemType,
1004
            amount,
1005
            refine,
1006
            ItemColorManager::getColorFromCards(&cards[0]),
1007
            fromBool(identified, Identified),
1008
            damaged,
1009
            Favorite_false,
1010
            Equipm_false,
1011
            Equipped_false);
1012
        inventory->setCards(index, cards, maxCards);
1013
        inventory->setOptions(index, options);
1014
    }
1015
    else
1016
    {
1017
        mCartItems.push_back(Ea::InventoryItem(index,
1018
            itemId,
1019
            itemType,
1020
            cards,
1021
            options,
1022
            amount,
1023
            refine,
1024
            ItemColorManager::getColorFromCards(&cards[0]),
1025
            fromBool(identified, Identified),
1026
            damaged,
1027
            Favorite_false,
1028
            Equipm_false));
1029
    }
1030
    delete options;
1031
    BLOCK_END("InventoryRecv::processPlayerCartAdd")
1032
}
1033
1034
void InventoryRecv::processPlayerCartEquip(Net::MessageIn &msg)
1035
{
1036
    BLOCK_START("InventoryRecv::processPlayerCartEquip")
1037
    msg.readInt16("len");
1038
1039
    int packetLen = 2 + 2 + 1 + 1 + 8;
1040
    if (msg.getVersion() >= 20120925)
1041
        packetLen += 4 + 4 + 1;
1042
    else
1043
        packetLen += 1 + 2 + 2 + 1;
1044
    if (msg.getVersion() >= 20071002)
1045
        packetLen += 4;
1046
    if (msg.getVersion() >= 20080102)
1047
        packetLen += 2;
1048
    if (msg.getVersion() >= 20100629)
1049
        packetLen += 2;
1050
    if (msg.getVersion() >= 20150226)
1051
        packetLen += 26;
1052
    packetLen += itemIdLen * 5 - 10;
1053
1054
    const int number = (msg.getLength() - 4) / packetLen;
1055
    for (int loop = 0; loop < number; loop++)
1056
    {
1057
        const int index = msg.readInt16("index") - INVENTORY_OFFSET;
1058
        const int itemId = msg.readItemId("item id");
1059
        const ItemTypeT itemType = static_cast<ItemTypeT>(
1060
            msg.readUInt8("item type"));
1061
        const int amount = 1;
1062
        if (msg.getVersion() >= 20120925)
1063
        {
1064
            msg.readInt32("location");
1065
            msg.readInt32("wear state");
1066
        }
1067
        else
1068
        {
1069
            msg.readUInt8("identified");
1070
            msg.readInt16("location");
1071
            msg.readInt16("wear state");
1072
            msg.readUInt8("is damaged");
1073
        }
1074
        const uint8_t refine = msg.readUInt8("refine level");
1075
        int cards[maxCards];
1076
        for (int f = 0; f < maxCards; f++)
1077
            cards[f] = msg.readItemId("card");
1078
        if (msg.getVersion() >= 20071002)
1079
            msg.readInt32("hire expire date");
1080
        if (msg.getVersion() >= 20080102)
1081
            msg.readInt16("bind on equip");
1082
        if (msg.getVersion() >= 20100629)
1083
            msg.readInt16("sprite");
1084
        ItemOptionsList *options = nullptr;
1085
        if (msg.getVersion() >= 20150226)
1086
        {
1087
            options = new ItemOptionsList(msg.readUInt8("option count"));
1088
            for (int f = 0; f < 5; f ++)
1089
            {
1090
                const uint16_t idx = msg.readInt16("option index");
1091
                const uint16_t val = msg.readInt16("option value");
1092
                msg.readUInt8("option param");
1093
                options->add(idx, val);
1094
            }
1095
        }
1096
        ItemFlags flags;
1097
        if (msg.getVersion() >= 20120925)
1098
            flags.byte = msg.readUInt8("flags");
1099
        else
1100
            flags.byte = 0;
1101
1102
        mCartItems.push_back(Ea::InventoryItem(index,
1103
            itemId,
1104
            itemType,
1105
            cards,
1106
            options,
1107
            amount,
1108
            refine,
1109
            ItemColorManager::getColorFromCards(&cards[0]),
1110
            fromBool(flags.bits.isIdentified, Identified),
1111
            fromBool(flags.bits.isDamaged, Damaged),
1112
            fromBool(flags.bits.isFavorite, Favorite),
1113
            Equipm_false));
1114
        delete options;
1115
    }
1116
    BLOCK_END("InventoryRecv::processPlayerCartEquip")
1117
}
1118
1119
void InventoryRecv::processPlayerCartItems(Net::MessageIn &msg)
1120
{
1121
    BLOCK_START("InventoryRecv::processPlayerCartItems")
1122
    Ea::InventoryRecv::mInventoryItems.clear();
1123
1124
    msg.readInt16("len");
1125
1126
    int packetLen = 7;
1127
    if (msg.getVersion() >= 20120925)
1128
        packetLen += 4 + 1;
1129
    else
1130
        packetLen += 1 + 2;
1131
    if (packetVersion >= 5)
1132
        packetLen += 8;
1133
    if (msg.getVersion() >= 20080102)
1134
        packetLen += 4;
1135
    packetLen += itemIdLen * 5 - 10;
1136
1137
    const int number = (msg.getLength() - 4) / packetLen;
1138
    for (int loop = 0; loop < number; loop++)
1139
    {
1140
        const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
1141
        const int itemId = msg.readItemId("item id");
1142
        const ItemTypeT itemType = static_cast<ItemTypeT>(
1143
            msg.readUInt8("item type"));
1144
        if (msg.getVersion() < 20120925)
1145
            msg.readUInt8("identified");
1146
        const int amount = msg.readInt16("count");
1147
        if (msg.getVersion() >= 20120925)
1148
            msg.readInt32("wear state / equip");
1149
        int cards[maxCards];
1150
        if (msg.getVersion() >= 5)
1151
        {
1152
            for (int f = 0; f < maxCards; f++)
1153
                cards[f] = msg.readItemId("card");
1154
        }
1155
        else
1156
        {
1157
            for (int f = 0; f < maxCards; f++)
1158
                cards[f] = 0;
1159
        }
1160
        if (msg.getVersion() >= 20080102)
1161
            msg.readInt32("hire expire date (?)");
1162
        ItemFlags flags;
1163
        if (msg.getVersion() >= 20120925)
1164
            flags.byte = msg.readUInt8("flags");
1165
        else
1166
            flags.byte = 0;
1167
1168
        mCartItems.push_back(Ea::InventoryItem(index,
1169
            itemId,
1170
            itemType,
1171
            cards,
1172
            nullptr,
1173
            amount,
1174
            0,
1175
            ItemColorManager::getColorFromCards(&cards[0]),
1176
            fromBool(flags.bits.isIdentified, Identified),
1177
            fromBool(flags.bits.isDamaged, Damaged),
1178
            fromBool(flags.bits.isFavorite, Favorite),
1179
            Equipm_false));
1180
    }
1181
    BLOCK_END("InventoryRecv::processPlayerCartItems")
1182
}
1183
1184
void InventoryRecv::processPlayerCartRemove(Net::MessageIn &msg)
1185
{
1186
    BLOCK_START("InventoryRecv::processPlayerCartRemove")
1187
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
1188
    const int amount = msg.readInt32("amount");
1189
1190
    Inventory *const inv = PlayerInfo::getCartInventory();
1191
    if (inv == nullptr)
1192
        return;
1193
1194
    if (Item *const item = inv->getItem(index))
1195
    {
1196
        item->increaseQuantity(-amount);
1197
        if (item->getQuantity() == 0)
1198
            inv->removeItemAt(index);
1199
    }
1200
    BLOCK_END("InventoryRecv::processPlayerCartRemove")
1201
}
1202
1203
void InventoryRecv::processPlayerIdentifyList(Net::MessageIn &msg)
1204
{
1205
    UNIMPLEMENTEDPACKET;
1206
1207
    menu = MenuType::Identify;
1208
    const int count = msg.readInt16("len") - 4;
1209
    for (int f = 0; f < count; f ++)
1210
        msg.readInt16("inv index");
1211
}
1212
1213
void InventoryRecv::processPlayerIdentified(Net::MessageIn &msg)
1214
{
1215
    UNIMPLEMENTEDPACKET;
1216
1217
    msg.readInt16("inv index");
1218
    msg.readUInt8("flag");
1219
}
1220
1221
void InventoryRecv::processPlayerRefine(Net::MessageIn &msg)
1222
{
1223
    const int flag = msg.readInt16("flag");
1224
    const int index = msg.readInt16("inv index") - INVENTORY_OFFSET;
1225
    msg.readInt16("refine");
1226
    const Inventory *const inv = PlayerInfo::getInventory();
1227
    const Item *item = nullptr;
1228
    int notifyType;
1229
    std::string itemName;
1230
    if (inv != nullptr)
1231
        item = inv->getItem(index);
1232
    if (item != nullptr)
1233
    {
1234
        itemName = item->getName();
1235
    }
1236
    else
1237
    {
1238
        // TRANSLATORS: unknown item
1239
        itemName = _("Unknown item");
1240
    }
1241
    switch (flag)
1242
    {
1243
        case 0:
1244
            notifyType = NotifyTypes::REFINE_SUCCESS;
1245
            break;
1246
        case 1:
1247
            notifyType = NotifyTypes::REFINE_FAILURE;
1248
            break;
1249
        case 2:
1250
            notifyType = NotifyTypes::REFINE_DOWNGRADE;
1251
            break;
1252
        default:
1253
            UNIMPLEMENTEDPACKETFIELD(flag);
1254
            notifyType = NotifyTypes::REFINE_UNKNOWN;
1255
            break;
1256
    }
1257
    NotifyManager::notify(notifyType, itemName);
1258
}
1259
1260
void InventoryRecv::processPlayerRepairList(Net::MessageIn &msg)
1261
{
1262
    UNIMPLEMENTEDPACKET;
1263
1264
    const int count = (msg.readInt16("len") - 4) /
1265
        (3 + (1 + maxCards) * itemIdLen);
1266
    for (int f = 0; f < count; f ++)
1267
    {
1268
        msg.readInt16("index");
1269
        msg.readItemId("item id");
1270
        msg.readUInt8("refine");
1271
        for (int d = 0; d < maxCards; d ++)
1272
            msg.readItemId("card");
1273
    }
1274
    menu = MenuType::RepairWespon;
1275
}
1276
1277
void InventoryRecv::processPlayerRepairEffect(Net::MessageIn &msg)
1278
{
1279
    UNIMPLEMENTEDPACKET;
1280
1281
    msg.readInt16("item index");
1282
    msg.readUInt8("flag");
1283
}
1284
1285
void InventoryRecv::processPlayerRefineList(Net::MessageIn &msg)
1286
{
1287
    UNIMPLEMENTEDPACKET;
1288
1289
    const int count = (msg.readInt16("len") - 4) /
1290
        (3 + (1 + maxCards) * itemIdLen);
1291
1292
    for (int f = 0; f < count; f ++)
1293
    {
1294
        msg.readInt16("item index");
1295
        msg.readItemId("item id");
1296
        msg.readUInt8("refine");
1297
        for (int d = 0; d < maxCards; d ++)
1298
            msg.readItemId("card");
1299
    }
1300
    menu = MenuType::WeaponeRefine;
1301
}
1302
1303
void InventoryRecv::processPlayerStoragePassword(Net::MessageIn &msg)
1304
{
1305
    UNIMPLEMENTEDPACKET;
1306
1307
    msg.readInt16("info");
1308
}
1309
1310
void InventoryRecv::processPlayerStoragePasswordResult(Net::MessageIn &msg)
1311
{
1312
    UNIMPLEMENTEDPACKET;
1313
1314
    msg.readInt16("result");
1315
    msg.readInt16("error count");
1316
}
1317
1318
void InventoryRecv::processPlayerCookingList(Net::MessageIn &msg)
1319
{
1320
    UNIMPLEMENTEDPACKET;
1321
1322
    const int count = (msg.readInt16("len") - 6) / itemIdLen;
1323
    msg.readInt16("list type");
1324
    for (int f = 0; f < count; f ++)
1325
        msg.readItemId("item id");
1326
}
1327
1328
void InventoryRecv::processItemDamaged(Net::MessageIn &msg)
1329
{
1330
    UNIMPLEMENTEDPACKET;
1331
1332
    msg.readInt16("position");
1333
    msg.readBeingId("account id");
1334
}
1335
1336
void InventoryRecv::processFavoriteItem(Net::MessageIn &msg)
1337
{
1338
    UNIMPLEMENTEDPACKET;
1339
1340
    msg.readInt16("item index");
1341
    msg.readUInt8("favorite (0 - favorite)");
1342
}
1343
1344
void InventoryRecv::processCartAddError(Net::MessageIn &msg)
1345
{
1346
    switch (msg.readUInt8("flag"))
1347
    {
1348
        case 0:
1349
            NotifyManager::notify(NotifyTypes::CART_ADD_WEIGHT_ERROR);
1350
            break;
1351
        case 1:
1352
            NotifyManager::notify(NotifyTypes::CART_ADD_COUNT_ERROR);
1353
            break;
1354
        default:
1355
            break;
1356
    }
1357
}
1358
1359
void InventoryRecv::processBindItem(Net::MessageIn &msg)
1360
{
1361
    const int index = msg.readInt16("item index") - INVENTORY_OFFSET;
1362
    const Inventory *const inv = PlayerInfo::getInventory();
1363
    if (inv != nullptr)
1364
    {
1365
        std::string itemName;
1366
        const Item *const item = inv->getItem(index);
1367
        if (item != nullptr)
1368
        {
1369
            itemName = item->getName();
1370
        }
1371
        else
1372
        {
1373
            // TRANSLATORS: unknown item message
1374
            itemName = _("Unknown item");
1375
        }
1376
        NotifyManager::notify(NotifyTypes::BOUND_ITEM, itemName);
1377
    }
1378
}
1379
1380
void InventoryRecv::processPlayerInventoryRemove(Net::MessageIn &msg)
1381
{
1382
    BLOCK_START("InventoryRecv::processPlayerInventoryRemove")
1383
    Inventory *const inventory = localPlayer != nullptr
1384
        ? PlayerInfo::getInventory() : nullptr;
1385
1386
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
1387
    const int amount = msg.readInt16("amount");
1388
    if (inventory != nullptr)
1389
    {
1390
        if (Item *const item = inventory->getItem(index))
1391
        {
1392
            if (amount != 0)
1393
            {
1394
                NotifyManager::notify(NotifyTypes::DELETE_ITEM_DROPPED,
1395
                    item->getName());
1396
            }
1397
            item->increaseQuantity(-amount);
1398
            if (item->getQuantity() == 0)
1399
                inventory->removeItemAt(index);
1400
            ArrowsListener::distributeEvent();
1401
        }
1402
    }
1403
    BLOCK_END("InventoryRecv::processPlayerInventoryRemove")
1404
}
1405
1406
void InventoryRecv::processSelectCart(Net::MessageIn &msg)
1407
{
1408
    UNIMPLEMENTEDPACKET;
1409
1410
    const int count = msg.readInt16("len") - 8;
1411
    msg.readBeingId("account id");
1412
    for (int f = 0; f < count; f ++)
1413
        msg.readUInt8("cart type");
1414
}
1415
1416
int InventoryRecv::getSlot(const int eAthenaSlot)
1417
{
1418
    if (eAthenaSlot == 0)
1419
        return EquipSlot::VECTOREND;
1420
1421
    if ((eAthenaSlot & 0x8000) != 0)
1422
        return inventoryHandler->getProjectileSlot();
1423
1424
    unsigned int mask = 1;
1425
    int position = 0;
1426
    while ((eAthenaSlot & mask) == 0u)
1427
    {
1428
        mask <<= 1;
1429
        position++;
1430
    }
1431
    if (position >= EquipSlot::VECTOREND)
1432
        return EquipSlot::VECTOREND;
1433
    return CAST_S32(EQUIP_POINTS[position]);
1434
}
1435
1436
void InventoryRecv::processMergeItem(Net::MessageIn &msg)
1437
{
1438
    UNIMPLEMENTEDPACKET;
1439
1440
    const int count = (msg.readInt16("len") - 4) / 2;
1441
    for (int f = 0; f < count; f ++)
1442
        msg.readInt16("inv index");
1443
}
1444
1445
void InventoryRecv::processMergeItemResponse(Net::MessageIn &msg)
1446
{
1447
    UNIMPLEMENTEDPACKET;
1448
1449
    msg.readInt16("inv index");
1450
    msg.readInt16("amount");
1451
    msg.readUInt8("result");
1452
}
1453
1454
void InventoryRecv::processPlayerInventoryUse(Net::MessageIn &msg)
1455
{
1456
    BLOCK_START("InventoryRecv::processPlayerInventoryUse")
1457
    Inventory *const inventory = localPlayer != nullptr
1458
        ? PlayerInfo::getInventory() : nullptr;
1459
1460
    const int index = msg.readInt16("index") - INVENTORY_OFFSET;
1461
    const int itemId = msg.readItemId("item id");
1462
    const BeingId id = msg.readBeingId("account id");
1463
    const int amount = msg.readInt16("amount");
1464
    const uint8_t flag = msg.readUInt8("type");
1465
    Being *const dstBeing = actorManager->findBeing(id);
1466
1467
    if (dstBeing == localPlayer)
1468
    {
1469
        if (flag == 0)
1470
        {
1471
            NotifyManager::notify(NotifyTypes::USE_FAILED);
1472
            return;
1473
        }
1474
        if (inventory != nullptr)
1475
        {
1476
            if (Item *const item = inventory->getItem(index))
1477
            {
1478
                if (amount != 0)
1479
                    item->setQuantity(amount);
1480
                else
1481
                    inventory->removeItemAt(index);
1482
            }
1483
        }
1484
    }
1485
    else
1486
    {
1487
        // +++ here can count left items in other player slot + id + amount
1488
        ItemSoundManager::playSfx(dstBeing, itemId, ItemSoundEvent::USE);
1489
    }
1490
    BLOCK_END("InventoryRecv::processPlayerInventoryUse")
1491
}
1492
1493

3
}  // namespace EAthena