GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/inventoryrecv.cpp Lines: 3 936 0.3 %
Date: 2019-03-24 Branches: 2 606 0.3 %

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

3
}  // namespace EAthena