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

3
}  // namespace EAthena