GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/beingrecv.cpp Lines: 1 1178 0.1 %
Date: 2018-11-12 Branches: 0 786 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "net/eathena/beingrecv.h"
24
25
#include "actormanager.h"
26
#include "effectmanager.h"
27
#include "game.h"
28
#include "notifymanager.h"
29
#include "party.h"
30
31
#include "being/mercenaryinfo.h"
32
33
#include "const/utils/timer.h"
34
35
#include "enums/resources/notifytypes.h"
36
37
#include "particle/particleengine.h"
38
39
#include "input/keyboardconfig.h"
40
41
#include "gui/viewport.h"
42
43
#include "gui/windows/skilldialog.h"
44
#include "gui/windows/socialwindow.h"
45
#include "gui/windows/outfitwindow.h"
46
47
#include "net/character.h"
48
#include "net/charserverhandler.h"
49
#include "net/messagein.h"
50
#include "net/serverfeatures.h"
51
52
#include "net/ea/beingrecv.h"
53
54
#include "net/eathena/maptypeproperty2.h"
55
#include "net/eathena/sp.h"
56
#include "net/eathena/sprite.h"
57
58
#include "resources/claninfo.h"
59
#include "resources/iteminfo.h"
60
61
#include "resources/db/clandb.h"
62
#include "resources/db/itemdb.h"
63
64
#include "resources/map/map.h"
65
66
#include "utils/checkutils.h"
67
#include "utils/foreach.h"
68
#include "utils/timer.h"
69
70
#include "debug.h"
71
72
extern int serverVersion;
73
extern Window *deathNotice;
74
extern bool packets_re;
75
extern bool packets_main;
76
extern bool packets_zero;
77
extern int itemIdLen;
78
79
namespace EAthena
80
{
81
82
static void setBasicFields(Being *restrict const dstBeing,
83
                           const uint8_t gender,
84
                           const int hairStyle,
85
                           const ItemColor hairColor,
86
                           const uint32_t weapon,
87
                           const uint16_t headBottom,
88
                           const uint16_t headMid,
89
                           const uint16_t headTop,
90
                           const uint16_t shoes,
91
                           const uint16_t gloves,
92
                           const bool notMove) A_NONNULL(1);
93
static void setBasicFields(Being *restrict const dstBeing,
94
                           const uint8_t gender,
95
                           const int hairStyle,
96
                           const ItemColor hairColor,
97
                           const uint32_t weapon,
98
                           const uint16_t headBottom,
99
                           const uint16_t headMid,
100
                           const uint16_t headTop,
101
                           const uint16_t shoes,
102
                           const uint16_t gloves,
103
                           const bool updateSlots)
104
{
105
    const ActorTypeT actorType = dstBeing->getType();
106
    switch (actorType)
107
    {
108
        case ActorType::Player:
109
            dstBeing->setGender(Being::intToGender(gender));
110
            dstBeing->setHairColor(hairColor);
111
            // Set these after the gender, as the sprites may be gender-specific
112
            if (hairStyle == 0)
113
            {
114
                dstBeing->updateSprite(SPRITE_HAIR_COLOR,
115
                    0,
116
                    std::string());
117
            }
118
            else
119
            {
120
                dstBeing->updateSprite(SPRITE_HAIR_COLOR,
121
                    hairStyle * -1,
122
                    ItemDB::get(-hairStyle).getDyeColorsString(hairColor));
123
            }
124
            if (updateSlots)
125
            {
126
                dstBeing->updateSprite(SPRITE_WEAPON,
127
                    headBottom,
128
                    std::string());
129
                dstBeing->updateSprite(SPRITE_HEAD_BOTTOM,
130
                    headMid,
131
                    std::string());
132
                dstBeing->updateSprite(SPRITE_CLOTHES_COLOR,
133
                    headTop,
134
                    std::string());
135
                dstBeing->updateSprite(SPRITE_HAIR,
136
                    shoes,
137
                    std::string());
138
                dstBeing->updateSprite(SPRITE_SHOES,
139
                    gloves,
140
                    std::string());
141
                dstBeing->updateSprite(SPRITE_BODY,
142
                    weapon,
143
                    std::string());
144
                dstBeing->setWeaponId(weapon);
145
            }
146
            break;
147
        case ActorType::Npc:
148
            if (serverFeatures->haveNpcGender())
149
            {
150
                dstBeing->setGender(Being::intToGender(gender));
151
            }
152
            if (dstBeing->getAllowNpcEquipment())
153
            {
154
                dstBeing->setHairColor(hairColor);
155
                dstBeing->setHairStyle(SPRITE_HAIR_COLOR, -hairStyle);
156
                // for npc not checking updateSlots flag,
157
                // probably because npc missing visible packet if moving
158
                dstBeing->updateSprite(SPRITE_WEAPON,
159
                    headBottom,
160
                    std::string());
161
                dstBeing->updateSprite(SPRITE_HEAD_BOTTOM,
162
                    headMid,
163
                    std::string());
164
                dstBeing->updateSprite(SPRITE_CLOTHES_COLOR,
165
                    headTop,
166
                    std::string());
167
                dstBeing->updateSprite(SPRITE_HAIR,
168
                    shoes,
169
                    std::string());
170
                dstBeing->updateSprite(SPRITE_SHOES,
171
                    gloves,
172
                    std::string());
173
                dstBeing->updateSprite(SPRITE_BODY,
174
                    weapon,
175
                    std::string());
176
                dstBeing->setWeaponId(weapon);
177
            }
178
            break;
179
        default:
180
        case ActorType::Monster:
181
        case ActorType::Portal:
182
        case ActorType::Pet:
183
        case ActorType::Mercenary:
184
        case ActorType::Homunculus:
185
        case ActorType::SkillUnit:
186
        case ActorType::Elemental:
187
            break;
188
        case ActorType::FloorItem:
189
        case ActorType::Avatar:
190
        case ActorType::Unknown:
191
            reportAlways("Wrong being type detected: %d",
192
                CAST_S32(actorType));
193
            break;
194
    }
195
}
196
197
void BeingRecv::processBeingChangeLook2(Net::MessageIn &msg)
198
{
199
    if (actorManager == nullptr)
200
        return;
201
202
    Being *const dstBeing = actorManager->findBeing(
203
        msg.readBeingId("being id"));
204
    const uint8_t type = msg.readUInt8("type");
205
206
    const int id = msg.readItemId("id1");
207
    unsigned int id2 = msg.readItemId("id2");
208
    if (type != 2)
209
        id2 = 1;
210
211
    if (localPlayer == nullptr || dstBeing == nullptr)
212
        return;
213
214
    processBeingChangeLookContinue(msg, dstBeing, type, id, id2, nullptr);
215
}
216
217
void BeingRecv::processBeingChangeLookCards(Net::MessageIn &msg)
218
{
219
    Being *dstBeing = nullptr;
220
    int cards[maxCards];
221
222
    if (actorManager == nullptr)
223
    {   // here can be look from char server
224
        Net::Characters &chars = Net::CharServerHandler::mCharacters;
225
        const BeingId id = msg.readBeingId("being id");
226
227
        FOR_EACH (Net::Characters::iterator, it, chars)
228
        {
229
            const Net::Character *const character = *it;
230
            if (character->dummy != nullptr &&
231
                character->dummy->getId() == id)
232
            {
233
                dstBeing = character->dummy;
234
                break;
235
            }
236
        }
237
    }
238
    else
239
    {
240
        dstBeing = actorManager->findBeing(
241
            msg.readBeingId("being id"));
242
    }
243
244
    const uint8_t type = msg.readUInt8("type");
245
246
    const int id = msg.readInt16("id1");
247
    unsigned int id2 = msg.readInt16("id2");
248
    if (type != 2)
249
        id2 = 1;
250
251
    for (int f = 0; f < maxCards; f ++)
252
        cards[f] = msg.readUInt16("card");  // +++ probably need use int32
253
254
    if (dstBeing == nullptr)
255
        return;
256
257
    processBeingChangeLookContinue(msg, dstBeing, type, id, id2, &cards[0]);
258
}
259
260
void BeingRecv::processBeingChangeLookContinue(const Net::MessageIn &msg,
261
                                               Being *const dstBeing,
262
                                               const uint8_t type,
263
                                               const int id,
264
                                               const int id2,
265
                                               const int *const cards)
266
{
267
    if (dstBeing->getType() == ActorType::Player)
268
        dstBeing->setOtherTime();
269
270
    switch (type)
271
    {
272
        // here should be used SPRITE_* constants
273
        // but for now they conflicting with sprites
274
        // SPRITE_* is same with server LOOK_*
275
        case 0:  // change race
276
            dstBeing->setSubtype(fromInt(id, BeingTypeId),
277
                dstBeing->getLook());
278
            break;
279
        case 1:  // eAthena LOOK_HAIR
280
            dstBeing->setHairColor(fromInt(id, ItemColor));
281
            dstBeing->setHairColorSpriteID(SPRITE_HAIR_COLOR,
282
                id * -1);
283
            break;
284
        case 2:  // LOOK_WEAPON Weapon ID in id, Shield ID in id2
285
            dstBeing->setSpriteCards(SPRITE_BODY,
286
                id,
287
                CardsList(cards));
288
            dstBeing->setWeaponId(id);
289
            dstBeing->setSpriteId(SPRITE_FLOOR,
290
                id2);
291
            if (localPlayer != nullptr)
292
                localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR);
293
            break;
294
        case 3:  // LOOK_HEAD_BOTTOM
295
            dstBeing->setSpriteCards(SPRITE_WEAPON,
296
                id,
297
                CardsList(cards));
298
            if (localPlayer != nullptr)
299
                localPlayer->imitateOutfit(dstBeing, SPRITE_WEAPON);
300
            break;
301
        case 4:  // LOOK_HEAD_TOP Change upper headgear for eAthena, hat for us
302
            dstBeing->setSpriteCards(SPRITE_CLOTHES_COLOR,
303
                id,
304
                CardsList(cards));
305
            if (localPlayer != nullptr)
306
                localPlayer->imitateOutfit(dstBeing, SPRITE_CLOTHES_COLOR);
307
            break;
308
        case 5:  // LOOK_HEAD_MID Change middle headgear for eathena,
309
                 // armor for us
310
            dstBeing->setSpriteCards(SPRITE_HEAD_BOTTOM,
311
                id,
312
                CardsList(cards));
313
            if (localPlayer != nullptr)
314
                localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_BOTTOM);
315
            break;
316
        case 6:  // eAthena LOOK_HAIR_COLOR
317
            dstBeing->setHairColor(fromInt(id, ItemColor));
318
            dstBeing->setSpriteColor(SPRITE_HAIR_COLOR,
319
                ItemDB::get(dstBeing->getSpriteID(
320
                SPRITE_HAIR_COLOR)).getDyeColorsString(
321
                fromInt(id, ItemColor)));
322
            break;
323
        case 7:  // Clothes color. Now used as look
324
            dstBeing->setLook(CAST_U8(id));
325
            break;
326
        case 8:  // eAthena LOOK_SHIELD
327
            dstBeing->setSpriteCards(SPRITE_FLOOR,
328
                id,
329
                CardsList(cards));
330
            if (localPlayer != nullptr)
331
                localPlayer->imitateOutfit(dstBeing, SPRITE_FLOOR);
332
            break;
333
        case 9:  // eAthena LOOK_SHOES
334
            dstBeing->setSpriteCards(SPRITE_HAIR,
335
                id,
336
                CardsList(cards));
337
            if (localPlayer != nullptr)
338
                localPlayer->imitateOutfit(dstBeing, SPRITE_HAIR);
339
            break;
340
        case 10:  // LOOK_GLOVES
341
            dstBeing->setSpriteCards(SPRITE_SHOES,
342
                id,
343
                CardsList(cards));
344
            if (localPlayer != nullptr)
345
                localPlayer->imitateOutfit(dstBeing, SPRITE_SHOES);
346
            break;
347
        case 11:  // LOOK_FLOOR
348
            dstBeing->setSpriteCards(SPRITE_SHIELD,
349
                id,
350
                CardsList(cards));
351
            if (localPlayer != nullptr)
352
                localPlayer->imitateOutfit(dstBeing, SPRITE_SHIELD);
353
            break;
354
        case 12:  // LOOK_ROBE
355
            dstBeing->setSpriteCards(SPRITE_HEAD_TOP,
356
                id,
357
                CardsList(cards));
358
            if (localPlayer != nullptr)
359
                localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_TOP);
360
            break;
361
        case 13:  // COSTUME_HEAD_TOP
362
            dstBeing->setSpriteCards(SPRITE_HEAD_MID,
363
                id,
364
                CardsList(cards));
365
            if (localPlayer != nullptr)
366
                localPlayer->imitateOutfit(dstBeing, SPRITE_HEAD_MID);
367
            break;
368
        case 14:  // COSTUME_HEAD_MID
369
            dstBeing->setSpriteCards(SPRITE_ROBE,
370
                id,
371
                CardsList(cards));
372
            if (localPlayer != nullptr)
373
                localPlayer->imitateOutfit(dstBeing, SPRITE_ROBE);
374
            break;
375
        case 15:  // COSTUME_HEAD_LOW
376
            dstBeing->setSpriteCards(SPRITE_EVOL2,
377
                id,
378
                CardsList(cards));
379
            if (localPlayer != nullptr)
380
                localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL2);
381
            break;
382
        case 16:  // COSTUME_GARMENT
383
            dstBeing->setSpriteCards(SPRITE_EVOL3,
384
                id,
385
                CardsList(cards));
386
            if (localPlayer != nullptr)
387
                localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL3);
388
            break;
389
        case 17:  // ARMOR
390
            dstBeing->setSpriteCards(SPRITE_EVOL4,
391
                id,
392
                CardsList(cards));
393
            if (localPlayer != nullptr)
394
                localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL4);
395
            break;
396
        case 18:
397
            dstBeing->setSpriteCards(SPRITE_EVOL5,
398
                id,
399
                CardsList(cards));
400
            if (localPlayer != nullptr)
401
                localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL5);
402
            break;
403
        case 19:
404
            dstBeing->setSpriteCards(SPRITE_EVOL6,
405
                id,
406
                CardsList(cards));
407
            if (localPlayer != nullptr)
408
                localPlayer->imitateOutfit(dstBeing, SPRITE_EVOL6);
409
            break;
410
        default:
411
            UNIMPLEMENTEDPACKETFIELD(type);
412
            break;
413
    }
414
}
415
416
void BeingRecv::processBeingVisible(Net::MessageIn &msg)
417
{
418
    if (actorManager == nullptr)
419
        return;
420
421
    // need set type based on id
422
    BeingTypeT type = BeingType::MONSTER;
423
    if (msg.getVersion() >= 20091103)
424
    {
425
        msg.readInt16("len");
426
        type = static_cast<BeingTypeT>(
427
            msg.readUInt8("object type"));
428
    }
429
430
    // Information about a being in range
431
    const BeingId id = msg.readBeingId("being id");
432
    if (msg.getVersion() >= 20131223)
433
        msg.readBeingId("char id");
434
    BeingId spawnId;
435
    if (id == Ea::BeingRecv::mSpawnId)
436
        spawnId = Ea::BeingRecv::mSpawnId;
437
    else
438
        spawnId = BeingId_zero;
439
    Ea::BeingRecv::mSpawnId = BeingId_zero;
440
441
    int16_t speed = msg.readInt16("speed");
442
    const uint32_t opt1 = msg.readInt16("opt1");
443
    // probably wrong effect usage
444
    const uint32_t opt2 = msg.readInt16("opt2");
445
    uint32_t option;
446
    if (msg.getVersion() >= 20080102)
447
        option = msg.readInt32("option");
448
    else
449
        option = msg.readInt16("option");
450
    const int16_t job = msg.readInt16("class");
451
452
    Being *dstBeing = actorManager->findBeing(id);
453
454
    if ((dstBeing != nullptr) && dstBeing->getType() == ActorType::Monster
455
        && !dstBeing->isAlive())
456
    {
457
        actorManager->destroy(dstBeing);
458
        actorManager->erase(dstBeing);
459
        dstBeing = nullptr;
460
    }
461
462
    if (dstBeing == nullptr)
463
    {
464
        if (actorManager->isBlocked(id) == true)
465
            return;
466
467
        dstBeing = createBeing2(msg, id, job, type);
468
        if (dstBeing == nullptr)
469
            return;
470
    }
471
    else
472
    {
473
        // undeleting marked for deletion being
474
        if (dstBeing->getType() == ActorType::Npc)
475
            actorManager->undelete(dstBeing);
476
    }
477
478
    if (dstBeing->getType() == ActorType::Player)
479
        dstBeing->setMoveTime();
480
481
    if (spawnId != BeingId_zero)
482
    {
483
        dstBeing->setAction(BeingAction::SPAWN, 0);
484
    }
485
    else
486
    {
487
        dstBeing->clearPath();
488
        dstBeing->setActionTime(tick_time);
489
        dstBeing->setAction(BeingAction::STAND, 0);
490
    }
491
492
    // Prevent division by 0 when calculating frame
493
    if (speed == 0)
494
        speed = 150;
495
496
    dstBeing->setWalkSpeed(speed);
497
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
498
    if (dstBeing->getType() == ActorType::Monster && (localPlayer != nullptr))
499
        localPlayer->checkNewName(dstBeing);
500
501
    const int hairStyle = msg.readInt16("hair style");
502
    uint32_t weapon;
503
    if (msg.getVersion() >= 7)
504
    {
505
        weapon = msg.readItemId("weapon");
506
        msg.readItemId("shield");
507
    }
508
    else
509
    {
510
        weapon = CAST_U32(msg.readInt16("weapon"));
511
    }
512
    const uint16_t headBottom = msg.readInt16("head bottom");
513
    if (msg.getVersion() < 7)
514
        msg.readInt16("shield");
515
    const uint16_t headTop = msg.readInt16("head top");
516
    const uint16_t headMid = msg.readInt16("head mid");
517
    const ItemColor hairColor = fromInt(msg.readInt16("hair color"),
518
        ItemColor);
519
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
520
521
    const uint16_t gloves = msg.readInt16("head dir / gloves");
522
    // may be use robe as gloves?
523
    if (msg.getVersion() >= 20101124)
524
        msg.readInt16("robe");
525
    msg.readInt32("guild id");
526
    msg.readInt16("guild emblem");
527
    dstBeing->setManner(msg.readInt16("manner"));
528
    uint32_t opt3;
529
    if (msg.getVersion() >= 7)
530
        opt3 = msg.readInt32("opt3");
531
    else
532
        opt3 = msg.readInt16("opt3");
533
    dstBeing->setKarma(msg.readUInt8("karma"));
534
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
535
536
    setBasicFields(dstBeing,
537
        gender,
538
        hairStyle,
539
        hairColor,
540
        weapon,
541
        headBottom,
542
        headMid,
543
        headTop,
544
        shoes,
545
        gloves,
546
        true);
547
548
    uint8_t dir;
549
    uint16_t x;
550
    uint16_t y;
551
    msg.readCoordinates(x, y, dir, "position");
552
    msg.readInt8("xs");
553
    msg.readInt8("ys");
554
    applyPlayerAction(msg, dstBeing, msg.readUInt8("action type"));
555
    dstBeing->setTileCoords(x, y);
556
557
    if (job == 45 && (socialWindow != nullptr) && (outfitWindow != nullptr))
558
    {
559
        const int num = socialWindow->getPortalIndex(x, y);
560
        if (num >= 0)
561
        {
562
            dstBeing->setName(KeyboardConfig::getKeyShortString(
563
                OutfitWindow::keyName(num)));
564
        }
565
        else
566
        {
567
            dstBeing->setName("");
568
        }
569
    }
570
571
    dstBeing->setDirection(dir);
572
573
    const int level = CAST_S32(msg.readInt16("level"));
574
    if (level != 0)
575
        dstBeing->setLevel(level);
576
    if (msg.getVersion() >= 20080102)
577
        msg.readInt16("font");
578
579
    if (msg.getVersion() >= 20120221)
580
    {
581
        const int maxHP = msg.readInt32("max hp");
582
        const int hp = msg.readInt32("hp");
583
        dstBeing->setMaxHP(maxHP);
584
        dstBeing->setHP(hp);
585
        msg.readInt8("is boss");
586
    }
587
588
    if (msg.getVersion() >= 20150513)
589
    {
590
        msg.readInt16("body2");
591
    }
592
    if (msg.getVersion() >= 20131223)
593
    {
594
        msg.readString(24, "name");
595
    }
596
597
    dstBeing->setStatusEffectOpitons(option,
598
        opt1,
599
        opt2,
600
        opt3);
601
}
602
603
void BeingRecv::processBeingMove(Net::MessageIn &msg)
604
{
605
    if (actorManager == nullptr)
606
        return;
607
608
    if (msg.getVersion() >= 20091103)
609
        msg.readInt16("len");
610
    BeingTypeT type;
611
    if (msg.getVersion() >= 20071106)
612
    {
613
        type = static_cast<BeingTypeT>(
614
            msg.readUInt8("object type"));
615
    }
616
    else
617
    {
618
        // need detect type based on id
619
        type = BeingType::MONSTER;
620
    }
621
622
    // Information about a being in range
623
    const BeingId id = msg.readBeingId("being id");
624
    if (msg.getVersion() >= 20131223)
625
        msg.readBeingId("char id");
626
    BeingId spawnId;
627
    if (id == Ea::BeingRecv::mSpawnId)
628
        spawnId = Ea::BeingRecv::mSpawnId;
629
    else
630
        spawnId = BeingId_zero;
631
    Ea::BeingRecv::mSpawnId = BeingId_zero;
632
    int16_t speed = msg.readInt16("speed");
633
    const uint32_t opt1 = msg.readInt16("opt1");
634
    // probably wrong effect usage
635
    const uint32_t opt2 = msg.readInt16("opt2");
636
    uint32_t option;
637
    if (msg.getVersion() >= 7)
638
        option = msg.readInt32("option");
639
    else
640
        option = msg.readInt16("option");
641
    const int16_t job = msg.readInt16("class");
642
643
    Being *dstBeing = actorManager->findBeing(id);
644
645
    if ((dstBeing != nullptr) && dstBeing->getType() == ActorType::Monster
646
        && !dstBeing->isAlive())
647
    {
648
        actorManager->destroy(dstBeing);
649
        actorManager->erase(dstBeing);
650
        dstBeing = nullptr;
651
    }
652
653
    if (dstBeing == nullptr)
654
    {
655
        if (actorManager->isBlocked(id) == true)
656
            return;
657
658
        dstBeing = createBeing2(msg, id, job, type);
659
        if (dstBeing == nullptr)
660
            return;
661
    }
662
    else
663
    {
664
        // undeleting marked for deletion being
665
        if (dstBeing->getType() == ActorType::Npc)
666
            actorManager->undelete(dstBeing);
667
    }
668
669
    if (dstBeing->getType() == ActorType::Player)
670
        dstBeing->setMoveTime();
671
672
    if (spawnId != BeingId_zero)
673
        dstBeing->setAction(BeingAction::SPAWN, 0);
674
675
    // Prevent division by 0 when calculating frame
676
    if (speed == 0)
677
        speed = 150;
678
679
    dstBeing->setWalkSpeed(speed);
680
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
681
    if (dstBeing->getType() == ActorType::Monster && (localPlayer != nullptr))
682
        localPlayer->checkNewName(dstBeing);
683
684
    const int hairStyle = msg.readInt16("hair style");
685
    uint32_t weapon;
686
    if (msg.getVersion() >= 7)
687
    {
688
        weapon = msg.readItemId("weapon");
689
        msg.readItemId("shield");
690
    }
691
    else
692
    {
693
        weapon = CAST_U32(msg.readInt16("weapon"));
694
    }
695
    const uint16_t headBottom = msg.readInt16("head bottom");
696
    msg.readInt32("tick");
697
    if (msg.getVersion() < 7)
698
        msg.readInt16("shield");
699
    const uint16_t headTop = msg.readInt16("head top");
700
    const uint16_t headMid = msg.readInt16("head mid");
701
    const ItemColor hairColor = fromInt(
702
        msg.readInt16("hair color"), ItemColor);
703
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
704
705
    const uint16_t gloves = msg.readInt16("head dir / gloves");
706
    // may be use robe as gloves?
707
    if (msg.getVersion() >= 20101124)
708
        msg.readInt16("robe");
709
    msg.readInt32("guild id");
710
    msg.readInt16("guild emblem");
711
    dstBeing->setManner(msg.readInt16("manner"));
712
    uint32_t opt3;
713
    if (msg.getVersion() >= 7)
714
        opt3 = msg.readInt32("opt3");
715
    else
716
        opt3 = msg.readInt16("opt3");
717
    dstBeing->setKarma(msg.readUInt8("karma"));
718
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
719
720
    setBasicFields(dstBeing,
721
        gender,
722
        hairStyle,
723
        hairColor,
724
        weapon,
725
        headBottom,
726
        headMid,
727
        headTop,
728
        shoes,
729
        gloves,
730
        !serverFeatures->haveMove3());
731
732
    uint16_t srcX;
733
    uint16_t srcY;
734
    uint16_t dstX;
735
    uint16_t dstY;
736
    msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path");
737
    msg.readUInt8("(sx<<4) | (sy&0x0f)");
738
    msg.readInt8("xs");
739
    msg.readInt8("ys");
740
    dstBeing->setAction(BeingAction::STAND, 0);
741
    dstBeing->setTileCoords(srcX, srcY);
742
    if (localPlayer != nullptr)
743
        localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
744
    if (serverFeatures->haveMove3())
745
        dstBeing->setCachedDestination(dstX, dstY);
746
    else
747
        dstBeing->setDestination(dstX, dstY);
748
749
    // because server don't send direction in move packet, we fixing it
750
751
    uint8_t d = 0;
752
    if (localPlayer != nullptr &&
753
        srcX == dstX &&
754
        srcY == dstY)
755
    {   // if player did one step from invisible area to visible,
756
        // move path is broken
757
        int x2 = localPlayer->getTileX();
758
        int y2 = localPlayer->getTileY();
759
        if (abs(x2 - srcX) > abs(y2 - srcY))
760
            y2 = srcY;
761
        else
762
            x2 = srcX;
763
        d = dstBeing->calcDirection(x2, y2);
764
    }
765
    else
766
    {
767
        d = dstBeing->calcDirection(dstX, dstY);
768
    }
769
    if ((d != 0U) && dstBeing->getDirection() != d)
770
        dstBeing->setDirection(d);
771
772
    const int level = CAST_S32(msg.readInt16("level"));
773
    if (level != 0)
774
        dstBeing->setLevel(level);
775
    if (msg.getVersion() >= 20080102)
776
        msg.readInt16("font");
777
    if (msg.getVersion() >= 20120221)
778
    {
779
        const int maxHP = msg.readInt32("max hp");
780
        const int hp = msg.readInt32("hp");
781
        dstBeing->setMaxHP(maxHP);
782
        dstBeing->setHP(hp);
783
        msg.readInt8("is boss");
784
    }
785
    if (msg.getVersion() >= 20150513)
786
    {
787
        msg.readInt16("body2");
788
    }
789
    if (msg.getVersion() >= 20131223)
790
    {
791
        msg.readString(24, "name");
792
    }
793
794
    dstBeing->setStatusEffectOpitons(option,
795
        opt1,
796
        opt2,
797
        opt3);
798
}
799
800
void BeingRecv::processBeingSpawn(Net::MessageIn &msg)
801
{
802
    if (actorManager == nullptr)
803
        return;
804
805
    // need get type from id
806
    BeingTypeT type = BeingType::MONSTER;
807
    if (msg.getVersion() >= 20091103)
808
    {
809
        msg.readInt16("len");
810
        type = static_cast<BeingTypeT>(
811
            msg.readUInt8("object type"));
812
    }
813
814
    // Information about a being in range
815
    const BeingId id = msg.readBeingId("being id");
816
    if (msg.getVersion() >= 20131223)
817
    {
818
        msg.readBeingId("char id");
819
    }
820
    Ea::BeingRecv::mSpawnId = id;
821
    const BeingId spawnId = id;
822
    int16_t speed = msg.readInt16("speed");
823
    const uint32_t opt1 = msg.readInt16("opt1");
824
    // probably wrong effect usage
825
    const uint32_t opt2 = msg.readInt16("opt2");
826
    uint32_t option;
827
    if (msg.getVersion() >= 20080102)
828
        option = msg.readInt32("option");
829
    else
830
        option = msg.readInt16("option");
831
    const int16_t job = msg.readInt16("class");
832
833
    Being *dstBeing = actorManager->findBeing(id);
834
835
    if ((dstBeing != nullptr) && dstBeing->getType() == ActorType::Monster
836
        && !dstBeing->isAlive())
837
    {
838
        actorManager->destroy(dstBeing);
839
        actorManager->erase(dstBeing);
840
        dstBeing = nullptr;
841
    }
842
843
    if (dstBeing == nullptr)
844
    {
845
        if (actorManager->isBlocked(id) == true)
846
            return;
847
848
        dstBeing = createBeing2(msg, id, job, type);
849
        if (dstBeing == nullptr)
850
            return;
851
    }
852
    else
853
    {
854
        // undeleting marked for deletion being
855
        if (dstBeing->getType() == ActorType::Npc)
856
            actorManager->undelete(dstBeing);
857
    }
858
859
    if (dstBeing->getType() == ActorType::Player)
860
        dstBeing->setMoveTime();
861
862
    if (spawnId != BeingId_zero)
863
        dstBeing->setAction(BeingAction::SPAWN, 0);
864
865
    // Prevent division by 0 when calculating frame
866
    if (speed == 0)
867
        speed = 150;
868
869
    dstBeing->setWalkSpeed(speed);
870
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
871
    if (dstBeing->getType() == ActorType::Monster && (localPlayer != nullptr))
872
        localPlayer->checkNewName(dstBeing);
873
874
    const int hairStyle = msg.readInt16("hair style");
875
    uint32_t weapon;
876
    if (msg.getVersion() >= 7)
877
    {
878
        weapon = msg.readItemId("weapon");
879
        msg.readItemId("shield");
880
    }
881
    else
882
    {
883
        weapon = CAST_U32(msg.readInt16("weapon"));
884
    }
885
    const uint16_t headBottom = msg.readInt16("head bottom");
886
    if (msg.getVersion() < 7)
887
        msg.readInt16("shield");
888
    const uint16_t headTop = msg.readInt16("head top");
889
    const uint16_t headMid = msg.readInt16("head mid");
890
    const ItemColor hairColor = fromInt(
891
        msg.readInt16("hair color"), ItemColor);
892
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
893
    const uint16_t gloves = msg.readInt16("head dir / gloves");
894
    // may be use robe as gloves?
895
    if (msg.getVersion() >= 20101124)
896
        msg.readInt16("robe");
897
    msg.readInt32("guild id");
898
    msg.readInt16("guild emblem");
899
    dstBeing->setManner(msg.readInt16("manner"));
900
    uint32_t opt3;
901
    if (msg.getVersion() >= 7)
902
        opt3 = msg.readInt32("opt3");
903
    else
904
        opt3 = msg.readInt16("opt3");
905
906
    dstBeing->setKarma(msg.readUInt8("karma"));
907
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
908
909
    setBasicFields(dstBeing,
910
        gender,
911
        hairStyle,
912
        hairColor,
913
        weapon,
914
        headBottom,
915
        headMid,
916
        headTop,
917
        shoes,
918
        gloves,
919
        true);
920
921
    uint8_t dir;
922
    uint16_t x;
923
    uint16_t y;
924
    msg.readCoordinates(x, y, dir, "position");
925
    msg.readInt8("xs");
926
    msg.readInt8("ys");
927
    dstBeing->setTileCoords(x, y);
928
929
    if (job == 45 && (socialWindow != nullptr) && (outfitWindow != nullptr))
930
    {
931
        const int num = socialWindow->getPortalIndex(x, y);
932
        if (num >= 0)
933
        {
934
            dstBeing->setName(KeyboardConfig::getKeyShortString(
935
                OutfitWindow::keyName(num)));
936
        }
937
        else
938
        {
939
            dstBeing->setName("");
940
        }
941
    }
942
943
    dstBeing->setDirection(dir);
944
945
    const int level = CAST_S32(msg.readInt16("level"));
946
    if (level != 0)
947
        dstBeing->setLevel(level);
948
    if (msg.getVersion() >= 20080102)
949
        msg.readInt16("font");
950
    if (msg.getVersion() >= 20120221)
951
    {
952
        const int maxHP = msg.readInt32("max hp");
953
        const int hp = msg.readInt32("hp");
954
        dstBeing->setMaxHP(maxHP);
955
        dstBeing->setHP(hp);
956
        msg.readInt8("is boss");
957
    }
958
    if (msg.getVersion() >= 20150513)
959
    {
960
        msg.readInt16("body2");
961
    }
962
    if (msg.getVersion() >= 20131223)
963
    {
964
        msg.readString(24, "name");
965
    }
966
967
    dstBeing->setStatusEffectOpitons(option,
968
        opt1,
969
        opt2,
970
        opt3);
971
}
972
973
void BeingRecv::processMapTypeProperty(Net::MessageIn &msg)
974
{
975
    const int16_t type = msg.readInt16("type");
976
    const int flags = msg.readInt32("flags");
977
    if (type == 0x28)
978
    {
979
        // +++ need get other flags from here
980
        MapTypeProperty2 props;
981
        props.data = CAST_U32(flags);
982
        const Game *const game = Game::instance();
983
        if (game == nullptr)
984
            return;
985
        Map *const map = game->getCurrentMap();
986
        if (map == nullptr)
987
            return;
988
        map->setPvpMode(props.bits.party | (props.bits.guild * 2));
989
    }
990
}
991
992
void BeingRecv::processMapType(Net::MessageIn &msg)
993
{
994
    const int16_t type = msg.readInt16("type");
995
    if (type == 19)
996
        NotifyManager::notify(NotifyTypes::MAP_TYPE_BATTLEFIELD);
997
    else
998
        UNIMPLEMENTEDPACKETFIELD(type);
999
}
1000
1001
void BeingRecv::processSkillCasting(Net::MessageIn &msg)
1002
{
1003
    const BeingId srcId = msg.readBeingId("src id");
1004
    const BeingId dstId = msg.readBeingId("dst id");
1005
    const int dstX = msg.readInt16("dst x");
1006
    const int dstY = msg.readInt16("dst y");
1007
    const int skillId = msg.readInt16("skill id");
1008
    msg.readInt32("property");  // can be used to trigger effect
1009
    const int castTime = msg.readInt32("cast time");
1010
    if (msg.getVersion() >= 20091124)
1011
        msg.readInt8("dispossable");
1012
1013
    processSkillCastingContinue(msg,
1014
        srcId, dstId,
1015
        dstX, dstY,
1016
        skillId,
1017
        1,
1018
        0,
1019
        SkillType2::Unknown,
1020
        castTime);
1021
}
1022
1023
void BeingRecv::processSkillCasting2(Net::MessageIn &msg)
1024
{
1025
    msg.readInt16("len");  // for now unused
1026
    const BeingId srcId = msg.readBeingId("src id");
1027
    const BeingId dstId = msg.readBeingId("dst id");
1028
    const int dstX = msg.readInt16("dst x");
1029
    const int dstY = msg.readInt16("dst y");
1030
    const int skillId = msg.readInt16("skill id");
1031
    const int skillLevel = msg.readInt16("skill level");
1032
    msg.readInt32("property");  // can be used to trigger effect
1033
    const int castTime = msg.readInt32("cast time");
1034
    const int range = msg.readInt32("skill range");
1035
    const SkillType2::SkillType2 inf2 =
1036
        static_cast<SkillType2::SkillType2>(msg.readInt32("inf2"));
1037
1038
    processSkillCastingContinue(msg,
1039
        srcId, dstId,
1040
        dstX, dstY,
1041
        skillId,
1042
        skillLevel,
1043
        range,
1044
        inf2,
1045
        castTime);
1046
}
1047
1048
void BeingRecv::processSkillCastingContinue(Net::MessageIn &msg,
1049
                                            const BeingId srcId,
1050
                                            const BeingId dstId,
1051
                                            const int dstX,
1052
                                            const int dstY,
1053
                                            const int skillId,
1054
                                            const int skillLevel,
1055
                                            const int range,
1056
                                            const SkillType2::SkillType2 inf2,
1057
                                            const int castTime)
1058
{
1059
    if (effectManager == nullptr ||
1060
        actorManager == nullptr)
1061
        return;
1062
1063
    if (srcId == BeingId_zero)
1064
    {
1065
        UNIMPLEMENTEDPACKETFIELD(0);
1066
        return;
1067
    }
1068
    Being *const srcBeing = actorManager->findBeing(srcId);
1069
    if (dstId != BeingId_zero)
1070
    {   // being to being
1071
        Being *const dstBeing = actorManager->findBeing(dstId);
1072
        if (srcBeing != nullptr)
1073
        {
1074
            srcBeing->handleSkillCasting(dstBeing, skillId, skillLevel);
1075
            if (dstBeing != nullptr)
1076
            {
1077
                srcBeing->addCast(dstBeing->getTileX(),
1078
                    dstBeing->getTileY(),
1079
                    skillId,
1080
                    skillLevel,
1081
                    range,
1082
                    castTime / MILLISECONDS_IN_A_TICK);
1083
            }
1084
        }
1085
    }
1086
    else if (dstX != 0 || dstY != 0)
1087
    {   // being to position
1088
        if (srcBeing != nullptr)
1089
            srcBeing->setAction(BeingAction::CAST, skillId);
1090
        skillDialog->playCastingDstTileEffect(skillId,
1091
            skillLevel,
1092
            dstX, dstY,
1093
            castTime);
1094
        if (srcBeing != nullptr)
1095
        {
1096
            srcBeing->addCast(dstX, dstY,
1097
                skillId,
1098
                skillLevel,
1099
                range,
1100
                castTime / MILLISECONDS_IN_A_TICK);
1101
        }
1102
    }
1103
    if ((localPlayer != nullptr) &&
1104
        srcBeing == localPlayer &&
1105
        (inf2 & SkillType2::FreeCastAny) == 0)
1106
    {
1107
        localPlayer->freezeMoving(castTime / MILLISECONDS_IN_A_TICK);
1108
    }
1109
}
1110
1111
void BeingRecv::processBeingStatusChange(Net::MessageIn &msg)
1112
{
1113
    BLOCK_START("BeingRecv::processBeingStatusChange")
1114
    if (actorManager == nullptr)
1115
    {
1116
        BLOCK_END("BeingRecv::processBeingStatusChange")
1117
        return;
1118
    }
1119
1120
    // Status change
1121
    const uint16_t status = msg.readInt16("status");
1122
    const BeingId id = msg.readBeingId("being id");
1123
    const Enable flag = fromBool(
1124
        msg.readUInt8("flag: 0: stop, 1: start"), Enable);
1125
    if (msg.getVersion() >= 20120618)
1126
        msg.readInt32("total");
1127
    if (msg.getVersion() >= 20090121)
1128
    {
1129
        msg.readInt32("left");
1130
        msg.readInt32("val1");
1131
        msg.readInt32("val2");
1132
        msg.readInt32("val3");
1133
    }
1134
1135
    const IsStart start = msg.getVersion() == 20090121 ?
1136
        IsStart_false : IsStart_true;
1137
1138
    Being *const dstBeing = actorManager->findBeing(id);
1139
    if (dstBeing != nullptr)
1140
        dstBeing->setStatusEffect(status, flag, start);
1141
    BLOCK_END("BeingRecv::processBeingStatusChange")
1142
}
1143
1144
void BeingRecv::processBeingMove2(Net::MessageIn &msg)
1145
{
1146
    BLOCK_START("BeingRecv::processBeingMove2")
1147
    if (actorManager == nullptr)
1148
    {
1149
        BLOCK_END("BeingRecv::processBeingMove2")
1150
        return;
1151
    }
1152
1153
    /*
1154
      * A simplified movement packet, used by the
1155
      * later versions of eAthena for both mobs and
1156
      * players
1157
      */
1158
    Being *const dstBeing = actorManager->findBeing(
1159
        msg.readBeingId("being id"));
1160
1161
    uint16_t srcX;
1162
    uint16_t srcY;
1163
    uint16_t dstX;
1164
    uint16_t dstY;
1165
    msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path");
1166
    msg.readUInt8("(sx<<4) | (sy&0x0f)");
1167
    msg.readInt32("tick");
1168
1169
    /*
1170
      * This packet doesn't have enough info to actually
1171
      * create a new being, so if the being isn't found,
1172
      * we'll just pretend the packet didn't happen
1173
      */
1174
1175
    if (dstBeing == nullptr)
1176
    {
1177
        BLOCK_END("BeingRecv::processBeingMove2")
1178
        return;
1179
    }
1180
1181
    dstBeing->setTileCoords(srcX, srcY);
1182
    if (localPlayer != nullptr)
1183
        localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
1184
    if (serverFeatures->haveMove3())
1185
        dstBeing->setCachedDestination(dstX, dstY);
1186
    else
1187
        dstBeing->setDestination(dstX, dstY);
1188
    if (dstBeing->getType() == ActorType::Player)
1189
        dstBeing->setMoveTime();
1190
    BLOCK_END("BeingRecv::processBeingMove2")
1191
}
1192
1193
void BeingRecv::processBeingAction2(Net::MessageIn &msg)
1194
{
1195
    BLOCK_START("BeingRecv::processBeingAction2")
1196
    if (actorManager == nullptr)
1197
    {
1198
        BLOCK_END("BeingRecv::processBeingAction2")
1199
        return;
1200
    }
1201
1202
    Being *const srcBeing = actorManager->findBeing(
1203
        msg.readBeingId("src being id"));
1204
    Being *const dstBeing = actorManager->findBeing(
1205
        msg.readBeingId("dst being id"));
1206
1207
    msg.readInt32("tick");
1208
    const int srcSpeed = msg.readInt32("src speed");
1209
    msg.readInt32("dst speed");
1210
    int param1;
1211
    if (msg.getVersion() >= 20071113)
1212
        param1 = msg.readInt32("damage");
1213
    else
1214
        param1 = msg.readInt16("damage");
1215
    if (msg.getVersion() >= 20131223)
1216
        msg.readUInt8("is sp damaged");
1217
    msg.readInt16("count");
1218
    const AttackTypeT type = static_cast<AttackTypeT>(
1219
        msg.readUInt8("action"));
1220
    if (msg.getVersion() >= 20071113)
1221
        msg.readInt32("left damage");
1222
    else
1223
        msg.readInt16("left damage");
1224
1225
    switch (type)
1226
    {
1227
        case AttackType::HIT:  // Damage
1228
        case AttackType::CRITICAL:  // Critical Damage
1229
        case AttackType::MULTI:  // Critical Damage
1230
        case AttackType::MULTI_REFLECT:
1231
        case AttackType::REFLECT:  // Reflected Damage
1232
        case AttackType::FLEE:  // Lucky Dodge
1233
        case AttackType::SPLASH:
1234
        case AttackType::SKILL:
1235
        case AttackType::REPEATE:
1236
            if (srcBeing != nullptr)
1237
            {
1238
                if (srcSpeed != 0 && srcBeing->getType() == ActorType::Player)
1239
                    srcBeing->setAttackDelay(srcSpeed);
1240
                // attackid=1, type
1241
                srcBeing->handleAttack(dstBeing, param1, 1);
1242
                if (srcBeing->getType() == ActorType::Player)
1243
                    srcBeing->setAttackTime();
1244
            }
1245
            if (dstBeing != nullptr)
1246
            {
1247
                // level not present, using 1
1248
                dstBeing->takeDamage(srcBeing, param1,
1249
                    static_cast<AttackTypeT>(type), 1, 1);
1250
            }
1251
            break;
1252
1253
        case AttackType::PICKUP:
1254
            break;
1255
1256
        case AttackType::TOUCH_SKILL:
1257
            break;
1258
1259
        case AttackType::SIT:
1260
            if (srcBeing != nullptr)
1261
            {
1262
                srcBeing->setAction(BeingAction::SIT, 0);
1263
                if (srcBeing->getType() == ActorType::Player)
1264
                {
1265
                    srcBeing->setMoveTime();
1266
                    if (localPlayer != nullptr)
1267
                        localPlayer->imitateAction(srcBeing, BeingAction::SIT);
1268
                }
1269
            }
1270
            break;
1271
1272
        case AttackType::STAND:
1273
            if (srcBeing != nullptr)
1274
            {
1275
                srcBeing->setAction(BeingAction::STAND, 0);
1276
                if (srcBeing->getType() == ActorType::Player)
1277
                {
1278
                    srcBeing->setMoveTime();
1279
                    if (localPlayer != nullptr)
1280
                    {
1281
                        localPlayer->imitateAction(srcBeing,
1282
                            BeingAction::STAND);
1283
                    }
1284
                }
1285
            }
1286
            break;
1287
        default:
1288
        case AttackType::MISS:
1289
        case AttackType::SKILLMISS:
1290
            UNIMPLEMENTEDPACKETFIELD(CAST_S32(type));
1291
            break;
1292
    }
1293
    BLOCK_END("BeingRecv::processBeingAction2")
1294
}
1295
1296
void BeingRecv::processBeingHp(Net::MessageIn &msg)
1297
{
1298
    if (actorManager == nullptr)
1299
        return;
1300
    Being *const dstBeing = actorManager->findBeing(
1301
        msg.readBeingId("being id"));
1302
    int hp;
1303
    int maxHP;
1304
    if (msg.getVersion() >= 20100126)
1305
    {
1306
        hp = msg.readInt32("hp");
1307
        maxHP = msg.readInt32("max hp");
1308
    }
1309
    else
1310
    {
1311
        hp = msg.readInt16("hp");
1312
        maxHP = msg.readInt16("max hp");
1313
    }
1314
    if (dstBeing != nullptr)
1315
    {
1316
        dstBeing->setHP(hp);
1317
        dstBeing->setMaxHP(maxHP);
1318
    }
1319
}
1320
1321
void BeingRecv::processMonsterHp(Net::MessageIn &msg)
1322
{
1323
    if (actorManager == nullptr)
1324
        return;
1325
    Being *const dstBeing = actorManager->findBeing(
1326
        msg.readBeingId("monster id"));
1327
    const int hp = msg.readInt32("hp");
1328
    const int maxHP = msg.readInt32("max hp");
1329
    if (dstBeing != nullptr)
1330
    {
1331
        dstBeing->setHP(hp);
1332
        dstBeing->setMaxHP(maxHP);
1333
    }
1334
}
1335
1336
void BeingRecv::processSkillAutoCast(Net::MessageIn &msg)
1337
{
1338
    const int id = msg.readInt16("skill id");
1339
    msg.readInt16("inf");
1340
    msg.readInt16("unused");
1341
    const int level = msg.readInt16("skill level");
1342
    msg.readInt16("sp");
1343
    msg.readInt16("range");
1344
    msg.readString(24, "skill name");
1345
    msg.readInt8("unused");
1346
1347
    if (localPlayer != nullptr)
1348
    {
1349
        localPlayer->handleSkill(localPlayer, 0, id, level);
1350
        localPlayer->takeDamage(localPlayer, 0, AttackType::SKILL, id, level);
1351
    }
1352
}
1353
1354
void BeingRecv::processRanksList(Net::MessageIn &msg)
1355
{
1356
    UNIMPLEMENTEDPACKET;
1357
    // +++ here need window with rank tables.
1358
    msg.readInt16("rank type");
1359
    for (int f = 0; f < 10; f ++)
1360
        msg.readString(24, "name");
1361
    for (int f = 0; f < 10; f ++)
1362
        msg.readInt32("points");
1363
    msg.readInt32("my points");
1364
}
1365
1366
void BeingRecv::processBlacksmithRanksList(Net::MessageIn &msg)
1367
{
1368
    UNIMPLEMENTEDPACKET;
1369
    // +++ here need window with rank tables.
1370
    for (int f = 0; f < 10; f ++)
1371
        msg.readString(24, "name");
1372
    for (int f = 0; f < 10; f ++)
1373
        msg.readInt32("points");
1374
}
1375
1376
void BeingRecv::processAlchemistRanksList(Net::MessageIn &msg)
1377
{
1378
    UNIMPLEMENTEDPACKET;
1379
    // +++ here need window with rank tables.
1380
    for (int f = 0; f < 10; f ++)
1381
        msg.readString(24, "name");
1382
    for (int f = 0; f < 10; f ++)
1383
        msg.readInt32("points");
1384
}
1385
1386
void BeingRecv::processTaekwonRanksList(Net::MessageIn &msg)
1387
{
1388
    UNIMPLEMENTEDPACKET;
1389
    // +++ here need window with rank tables.
1390
    for (int f = 0; f < 10; f ++)
1391
        msg.readString(24, "name");
1392
    for (int f = 0; f < 10; f ++)
1393
        msg.readInt32("points");
1394
}
1395
1396
void BeingRecv::processPkRanksList(Net::MessageIn &msg)
1397
{
1398
    UNIMPLEMENTEDPACKET;
1399
    // +++ here need window with rank tables.
1400
    for (int f = 0; f < 10; f ++)
1401
        msg.readString(24, "name");
1402
    for (int f = 0; f < 10; f ++)
1403
        msg.readInt32("points");
1404
}
1405
1406
void BeingRecv::processBeingChangeDirection(Net::MessageIn &msg)
1407
{
1408
    BLOCK_START("BeingRecv::processBeingChangeDirection")
1409
    if (actorManager == nullptr)
1410
    {
1411
        BLOCK_END("BeingRecv::processBeingChangeDirection")
1412
        return;
1413
    }
1414
1415
    Being *const dstBeing = actorManager->findBeing(
1416
        msg.readBeingId("being id"));
1417
1418
    msg.readInt16("head direction");
1419
1420
    const uint8_t dir = Net::MessageIn::fromServerDirection(
1421
        CAST_U8(msg.readUInt8("player direction") & 0x0FU));
1422
1423
    if (dstBeing == nullptr)
1424
    {
1425
        BLOCK_END("BeingRecv::processBeingChangeDirection")
1426
        return;
1427
    }
1428
1429
    dstBeing->setDirection(dir);
1430
    if (localPlayer != nullptr)
1431
        localPlayer->imitateDirection(dstBeing, dir);
1432
    BLOCK_END("BeingRecv::processBeingChangeDirection")
1433
}
1434
1435
void BeingRecv::processBeingSpecialEffect(Net::MessageIn &msg)
1436
{
1437
    if ((effectManager == nullptr) || (actorManager == nullptr))
1438
        return;
1439
1440
    const BeingId id = msg.readBeingId("being id");
1441
    Being *const being = actorManager->findBeing(id);
1442
    if (being == nullptr)
1443
    {
1444
        msg.readInt32("effect type");
1445
        return;
1446
    }
1447
1448
    const int effectType = msg.readInt32("effect type");
1449
1450
    if (ParticleEngine::enabled)
1451
        effectManager->trigger(effectType, being, 0);
1452
1453
    // +++ need dehard code effectType == 3
1454
    if (effectType == 3 && being->getType() == ActorType::Player
1455
        && (socialWindow != nullptr))
1456
    {   // reset received damage
1457
        socialWindow->resetDamage(being->getName());
1458
    }
1459
}
1460
1461
void BeingRecv::processBeingRemoveSpecialEffect(Net::MessageIn &msg)
1462
{
1463
    UNIMPLEMENTEDPACKET;
1464
    msg.readBeingId("being id");
1465
    msg.readInt32("effect type");
1466
}
1467
1468
void BeingRecv::processBeingHatEffects(Net::MessageIn &msg)
1469
{
1470
    // +++ add new type of permanent effects?
1471
    const int cnt = (msg.readInt16("len") - 9) / 2;
1472
    if (cnt > 0)
1473
    {
1474
        UNIMPLEMENTEDPACKET;
1475
    }
1476
    msg.readBeingId("being id");
1477
    msg.readUInt8("enable");
1478
    for (int f = 0; f < cnt; f ++)
1479
        msg.readInt16("hat effect");
1480
}
1481
1482
void BeingRecv::processBeingSpecialEffectNum(Net::MessageIn &msg)
1483
{
1484
    UNIMPLEMENTEDPACKET;
1485
    // +++ need somhow show this effects.
1486
    // type is not same with self/misc effect.
1487
    msg.readBeingId("account id");
1488
    msg.readInt32("effect type");
1489
    msg.readInt32("num");  // effect variable
1490
}
1491
1492
void BeingRecv::processBeingSoundEffect(Net::MessageIn &msg)
1493
{
1494
    UNIMPLEMENTEDPACKET;
1495
    // +++ need play this effect.
1496
    msg.readString(24, "sound effect name");
1497
    msg.readUInt8("type");
1498
    msg.readInt32("unused");
1499
    msg.readInt32("source being id");
1500
}
1501
1502
void BeingRecv::processSkillGroundNoDamage(Net::MessageIn &msg)
1503
{
1504
    UNIMPLEMENTEDPACKET;
1505
    msg.readInt16("skill id");
1506
    msg.readInt32("src id");
1507
    msg.readInt16("val");
1508
    msg.readInt16("x");
1509
    msg.readInt16("y");
1510
    msg.readInt32("tick");
1511
}
1512
1513
void BeingRecv::processSkillEntry(Net::MessageIn &msg)
1514
{
1515
    if (msg.getVersion() >= 20110718)
1516
        msg.readInt16("len");
1517
    const BeingId id = msg.readBeingId("skill unit id");
1518
    const BeingId creatorId = msg.readBeingId("creator accound id");
1519
    const int x = msg.readInt16("x");
1520
    const int y = msg.readInt16("y");
1521
    int job = 0;
1522
    if (msg.getVersion() >= 20121212)
1523
        job = msg.readInt32("job");
1524
    if (msg.getVersion() >= 20110718)
1525
        msg.readUInt8("radius");
1526
    msg.readUInt8("visible");
1527
    int level = 0;
1528
    if (msg.getVersion() >= 20130731)
1529
        level = msg.readUInt8("level");
1530
    Being *const dstBeing = createBeing2(msg,
1531
        id,
1532
        job,
1533
        BeingType::SKILL);
1534
    if (dstBeing == nullptr)
1535
        return;
1536
    dstBeing->setAction(BeingAction::STAND, 0);
1537
    dstBeing->setTileCoords(x, y);
1538
    dstBeing->setLevel(level);
1539
    dstBeing->setCreatorId(creatorId);
1540
}
1541
1542
void BeingRecv::processPlayerStatusChange(Net::MessageIn &msg)
1543
{
1544
    BLOCK_START("BeingRecv::processPlayerStop")
1545
    if (actorManager == nullptr)
1546
    {
1547
        BLOCK_END("BeingRecv::processPlayerStop")
1548
        return;
1549
    }
1550
1551
    // Change in players' flags
1552
    const BeingId id = msg.readBeingId("account id");
1553
    Being *const dstBeing = actorManager->findBeing(id);
1554
    if (dstBeing == nullptr)
1555
    {
1556
        msg.readInt16("opt1");
1557
        msg.readInt16("opt2");
1558
        if (msg.getVersion() >= 7)
1559
            msg.readInt32("option");
1560
        else
1561
            msg.readInt16("option");
1562
        msg.readUInt8("karma");
1563
        return;
1564
    }
1565
1566
    const uint32_t opt1 = msg.readInt16("opt1");
1567
    const uint32_t opt2 = msg.readInt16("opt2");
1568
    uint32_t option;
1569
    if (msg.getVersion() >= 7)
1570
        option = msg.readInt32("option");
1571
    else
1572
        option = msg.readInt16("option");
1573
    dstBeing->setKarma(msg.readUInt8("karma"));
1574
1575
    dstBeing->setStatusEffectOpitons(option,
1576
        opt1,
1577
        opt2);
1578
    BLOCK_END("BeingRecv::processPlayerStop")
1579
}
1580
1581
void BeingRecv::processPlayerStatusChange2(Net::MessageIn &msg)
1582
{
1583
    if (actorManager == nullptr)
1584
        return;
1585
1586
    // look like this function unused on server
1587
1588
    const BeingId id = msg.readBeingId("account id");
1589
    Being *const dstBeing = actorManager->findBeing(id);
1590
    if (dstBeing == nullptr)
1591
        return;
1592
1593
    const uint32_t option = msg.readInt32("option");
1594
    dstBeing->setLevel(msg.readInt32("level"));
1595
    msg.readInt32("showEFST");
1596
    dstBeing->setStatusEffectOpiton0(option);
1597
}
1598
1599
void BeingRecv::processBeingResurrect(Net::MessageIn &msg)
1600
{
1601
    BLOCK_START("BeingRecv::processBeingResurrect")
1602
    if (actorManager == nullptr ||
1603
        localPlayer == nullptr)
1604
    {
1605
        BLOCK_END("BeingRecv::processBeingResurrect")
1606
        return;
1607
    }
1608
1609
    // A being changed mortality status
1610
1611
    const BeingId id = msg.readBeingId("being id");
1612
    msg.readInt16("unused");
1613
    Being *const dstBeing = actorManager->findBeing(id);
1614
    if (dstBeing == nullptr)
1615
    {
1616
        DEBUGLOGSTR("insible player?");
1617
        BLOCK_END("BeingRecv::processBeingResurrect")
1618
        return;
1619
    }
1620
1621
    // If this is player's current target, clear it.
1622
    if (dstBeing == localPlayer->getTarget())
1623
        localPlayer->stopAttack(false);
1624
    if (dstBeing == localPlayer &&
1625
        deathNotice != nullptr)
1626
    {
1627
        deathNotice->scheduleDelete();
1628
        deathNotice = nullptr;
1629
    }
1630
1631
    dstBeing->setAction(BeingAction::STAND, 0);
1632
    BLOCK_END("BeingRecv::processBeingResurrect")
1633
}
1634
1635
void BeingRecv::processPlayerGuilPartyInfo(Net::MessageIn &msg)
1636
{
1637
    BLOCK_START("BeingRecv::processPlayerGuilPartyInfo")
1638
    if (actorManager == nullptr)
1639
    {
1640
        BLOCK_END("BeingRecv::processPlayerGuilPartyInfo")
1641
        return;
1642
    }
1643
1644
    const BeingId beingId = msg.readBeingId("being id");
1645
    const std::string name = msg.readString(24, "char name");
1646
    actorManager->updateNameId(name, beingId);
1647
    Being *const dstBeing = actorManager->findBeing(beingId);
1648
    if (dstBeing != nullptr)
1649
    {
1650
        if (beingId == localPlayer->getId())
1651
        {
1652
            localPlayer->pingResponse();
1653
        }
1654
        dstBeing->setName(name);
1655
        dstBeing->setPartyName(msg.readString(24, "party name"));
1656
        dstBeing->setGuildName(msg.readString(24, "guild name"));
1657
        dstBeing->setGuildPos(msg.readString(24, "guild pos"));
1658
        dstBeing->addToCache();
1659
    }
1660
    else
1661
    {
1662
        msg.readString(24, "party name");
1663
        msg.readString(24, "guild name");
1664
        msg.readString(24, "guild pos");
1665
    }
1666
    BLOCK_END("BeingRecv::processPlayerGuilPartyInfo")
1667
}
1668
1669
void BeingRecv::processPlayerGuilPartyInfo2(Net::MessageIn &msg)
1670
{
1671
    BLOCK_START("BeingRecv::processPlayerGuilPartyInfo2")
1672
    if (actorManager == nullptr)
1673
    {
1674
        BLOCK_END("BeingRecv::processPlayerGuilPartyInfo2")
1675
        return;
1676
    }
1677
1678
    const BeingId beingId = msg.readBeingId("being id");
1679
    const std::string name = msg.readString(24, "char name");
1680
    actorManager->updateNameId(name, beingId);
1681
    Being *const dstBeing = actorManager->findBeing(beingId);
1682
    if (dstBeing != nullptr)
1683
    {
1684
        if (beingId == localPlayer->getId())
1685
        {
1686
            localPlayer->pingResponse();
1687
        }
1688
        dstBeing->setName(name);
1689
        dstBeing->setPartyName(msg.readString(24, "party name"));
1690
        dstBeing->setGuildName(msg.readString(24, "guild name"));
1691
        dstBeing->setGuildPos(msg.readString(24, "guild pos"));
1692
        dstBeing->addToCache();
1693
    }
1694
    else
1695
    {
1696
        msg.readString(24, "party name");
1697
        msg.readString(24, "guild name");
1698
        msg.readString(24, "guild pos");
1699
    }
1700
    // +++ need use it for show player title
1701
    msg.readInt32("title");
1702
    BLOCK_END("BeingRecv::processPlayerGuilPartyInfo2")
1703
}
1704
1705
void BeingRecv::processBeingRemoveSkill(Net::MessageIn &msg)
1706
{
1707
    const BeingId id = msg.readBeingId("skill unit id");
1708
    if (actorManager == nullptr)
1709
        return;
1710
    Being *const dstBeing = actorManager->findBeing(id);
1711
    if (dstBeing == nullptr)
1712
        return;
1713
    actorManager->destroy(dstBeing);
1714
}
1715
1716
void BeingRecv::processBeingFakeName(Net::MessageIn &msg)
1717
{
1718
    uint16_t x;
1719
    uint16_t y;
1720
    uint8_t dir;
1721
    if (msg.getVersion() < 20071106)
1722
    {
1723
        msg.readBeingId("npc id");
1724
        msg.skip(8, "unused");
1725
        msg.readInt16("class?");  // 111
1726
        msg.skip(30, "unused");
1727
        msg.readCoordinates(x, y, dir, "position");
1728
        msg.readUInt8("sx");
1729
        msg.readUInt8("sy");
1730
        msg.skip(3, "unused");
1731
        return;
1732
    }
1733
    const BeingTypeT type = static_cast<BeingTypeT>(
1734
        msg.readUInt8("object type"));
1735
    const BeingId id = msg.readBeingId("npc id");
1736
    msg.skip(8, "unused");
1737
    const uint16_t job = msg.readInt16("class?");  // 111
1738
    msg.skip(30, "unused");
1739
    msg.readCoordinates(x, y, dir, "position");
1740
    msg.readUInt8("sx");
1741
    msg.readUInt8("sy");
1742
    msg.skip(3, "unused");
1743
1744
    Being *const dstBeing = createBeing2(msg, id, job, type);
1745
    if (dstBeing == nullptr)
1746
        return;
1747
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
1748
    dstBeing->setTileCoords(x, y);
1749
    dstBeing->setDirection(dir);
1750
}
1751
1752
void BeingRecv::processBeingStatUpdate1(Net::MessageIn &msg)
1753
{
1754
    const BeingId id = msg.readBeingId("account id");
1755
    const int type = msg.readInt16("type");
1756
    const int value = msg.readInt32("value");
1757
1758
    if (actorManager == nullptr)
1759
        return;
1760
    Being *const dstBeing = actorManager->findBeing(id);
1761
    if (dstBeing == nullptr)
1762
        return;
1763
1764
    if (type != Sp::MANNER)
1765
    {
1766
        UNIMPLEMENTEDPACKETFIELD(type);
1767
        return;
1768
    }
1769
    dstBeing->setManner(value);
1770
}
1771
1772
void BeingRecv::processBeingSelfEffect(Net::MessageIn &msg)
1773
{
1774
    BLOCK_START("BeingRecv::processBeingSelfEffect")
1775
    if ((effectManager == nullptr) || (actorManager == nullptr))
1776
    {
1777
        BLOCK_END("BeingRecv::processBeingSelfEffect")
1778
        return;
1779
    }
1780
1781
    const BeingId id = msg.readBeingId("being id");
1782
    Being *const being = actorManager->findBeing(id);
1783
    if (being == nullptr)
1784
    {
1785
        DEBUGLOGSTR("insible player?");
1786
        msg.readInt32("effect type");
1787
        BLOCK_END("BeingRecv::processBeingSelfEffect")
1788
        return;
1789
    }
1790
1791
    const int effectType = msg.readInt32("effect type");
1792
    if (ParticleEngine::enabled)
1793
        effectManager->trigger(effectType, being, 0);
1794
1795
    BLOCK_END("BeingRecv::processBeingSelfEffect")
1796
}
1797
1798
void BeingRecv::processMobInfo(Net::MessageIn &msg)
1799
{
1800
    if (actorManager == nullptr)
1801
        return;
1802
    const int len = msg.readInt16("len");
1803
    if (len < 12)
1804
        return;
1805
    Being *const dstBeing = actorManager->findBeing(
1806
        msg.readBeingId("monster id"));
1807
    const int attackRange = msg.readInt32("range");
1808
    if (dstBeing != nullptr)
1809
        dstBeing->setAttackRange(attackRange);
1810
}
1811
1812
void BeingRecv::processBeingAttrs(Net::MessageIn &msg)
1813
{
1814
    if (actorManager == nullptr)
1815
        return;
1816
    const int len = msg.readInt16("len");
1817
    if (len < 14)
1818
        return;
1819
1820
    Being *const dstBeing = actorManager->findBeing(
1821
        msg.readBeingId("player id"));
1822
    const int groupId = msg.readInt32("group id");
1823
    uint16_t mount = 0;
1824
    mount = msg.readInt16("mount");
1825
    int language = -1;
1826
    if (len > 14)
1827
        language = msg.readInt16("language");
1828
    int clanId = 0;
1829
    if (len > 16)
1830
        clanId = msg.readInt32("clan id");
1831
    if (dstBeing != nullptr)
1832
    {
1833
        if (dstBeing != localPlayer)
1834
        {
1835
            dstBeing->setGroupId(groupId);
1836
        }
1837
        dstBeing->setHorse(mount);
1838
        dstBeing->setLanguageId(language);
1839
        if (clanId != 0)
1840
        {
1841
            const ClanInfo *const info = ClanDb::get(clanId);
1842
            if (info == nullptr)
1843
                dstBeing->setClanName(std::string());
1844
            else
1845
                dstBeing->setClanName(info->name);
1846
        }
1847
        else
1848
        {
1849
            dstBeing->setClanName(std::string());
1850
        }
1851
        if (dstBeing == localPlayer)
1852
            PlayerInfo::setServerLanguage(language);
1853
    }
1854
}
1855
1856
void BeingRecv::processMonsterInfo(Net::MessageIn &msg)
1857
{
1858
    UNIMPLEMENTEDPACKET;
1859
1860
    msg.readInt16("class");
1861
    msg.readInt16("level");
1862
    msg.readInt16("size");
1863
    msg.readInt32("hp");
1864
    msg.readInt16("def");
1865
    msg.readInt16("race");
1866
    msg.readInt16("mdef");
1867
    msg.readInt16("ele");
1868
}
1869
1870
void BeingRecv::processClassChange(Net::MessageIn &msg)
1871
{
1872
    UNIMPLEMENTEDPACKET;
1873
1874
    msg.readBeingId("being id");
1875
    msg.readUInt8("type");
1876
    msg.readInt32("class");
1877
}
1878
1879
void BeingRecv::processSpiritBalls(Net::MessageIn &msg)
1880
{
1881
    if (actorManager == nullptr)
1882
        return;
1883
    Being *const dstBeing = actorManager->findBeing(
1884
        msg.readBeingId("being id"));
1885
    const int balls = msg.readInt16("spirits amount");
1886
    if (dstBeing != nullptr)
1887
        dstBeing->setSpiritBalls(balls);
1888
}
1889
1890
void BeingRecv::processBladeStop(Net::MessageIn &msg)
1891
{
1892
    UNIMPLEMENTEDPACKET;
1893
1894
    msg.readInt32("src being id");
1895
    msg.readInt32("dst being id");
1896
    msg.readInt32("flag");
1897
}
1898
1899
void BeingRecv::processComboDelay(Net::MessageIn &msg)
1900
{
1901
    UNIMPLEMENTEDPACKET;
1902
1903
    msg.readBeingId("being id");
1904
    msg.readInt32("wait");
1905
}
1906
1907
void BeingRecv::processWddingEffect(Net::MessageIn &msg)
1908
{
1909
    UNIMPLEMENTEDPACKET;
1910
1911
    msg.readBeingId("being id");
1912
}
1913
1914
void BeingRecv::processBeingSlide(Net::MessageIn &msg)
1915
{
1916
    if (actorManager == nullptr)
1917
        return;
1918
    Being *const dstBeing = actorManager->findBeing(
1919
        msg.readBeingId("being id"));
1920
    const int x = msg.readInt16("x");
1921
    const int y = msg.readInt16("y");
1922
    if (dstBeing == nullptr)
1923
        return;
1924
    if (localPlayer == dstBeing)
1925
    {
1926
        localPlayer->stopAttack(false);
1927
        localPlayer->navigateClean();
1928
        if (viewport != nullptr)
1929
            viewport->returnCamera();
1930
    }
1931
1932
    dstBeing->setAction(BeingAction::STAND, 0);
1933
    dstBeing->setTileCoords(x, y);
1934
}
1935
1936
void BeingRecv::processStarsKill(Net::MessageIn &msg)
1937
{
1938
    UNIMPLEMENTEDPACKET;
1939
1940
    msg.readString(24, "map name");
1941
    msg.readInt32("monster id");
1942
    msg.readUInt8("start");
1943
    msg.readUInt8("result");
1944
}
1945
1946
void BeingRecv::processGladiatorFeelRequest(Net::MessageIn &msg)
1947
{
1948
    UNIMPLEMENTEDPACKET;
1949
1950
    msg.readUInt8("which");
1951
}
1952
1953
void BeingRecv::processBossMapInfo(Net::MessageIn &msg)
1954
{
1955
    UNIMPLEMENTEDPACKET;
1956
1957
    msg.readUInt8("info type");
1958
    msg.readInt32("x");
1959
    msg.readInt32("y");
1960
    msg.readInt16("min hours");
1961
    msg.readInt16("min minutes");
1962
    msg.readInt16("max hours");
1963
    msg.readInt16("max minutes");
1964
    msg.readString(24, "monster name");  // really can be used 51 byte?
1965
}
1966
1967
void BeingRecv::processBeingFont(Net::MessageIn &msg)
1968
{
1969
    UNIMPLEMENTEDPACKET;
1970
1971
    msg.readBeingId("account id");
1972
    msg.readInt16("font");
1973
}
1974
1975
void BeingRecv::processBeingMilleniumShield(Net::MessageIn &msg)
1976
{
1977
    UNIMPLEMENTEDPACKET;
1978
1979
    msg.readBeingId("account id");
1980
    msg.readInt16("shields");
1981
    msg.readInt16("unused");
1982
}
1983
1984
void BeingRecv::processBeingCharm(Net::MessageIn &msg)
1985
{
1986
    UNIMPLEMENTEDPACKET;
1987
1988
    msg.readBeingId("account id");
1989
    msg.readInt16("charm type");
1990
    msg.readInt16("charm count");
1991
}
1992
1993
void BeingRecv::processBeingViewEquipment(Net::MessageIn &msg)
1994
{
1995
    UNIMPLEMENTEDPACKET;
1996
1997
    const int count = (msg.readInt16("len") - 45) / (21 + itemIdLen * 5);
1998
    msg.readString(24, "name");
1999
    msg.readInt16("job");
2000
    msg.readInt16("head");
2001
    msg.readInt16("accessory");
2002
    msg.readInt16("accessory2");
2003
    msg.readInt16("accessory3");
2004
    if (msg.getVersion() >= 20101124)
2005
        msg.readInt16("robe");
2006
    msg.readInt16("hair color");
2007
    msg.readInt16("body color");
2008
    msg.readUInt8("gender");
2009
    for (int f = 0; f < count; f ++)
2010
    {
2011
        msg.readInt16("index");
2012
        msg.readItemId("item id");
2013
        msg.readUInt8("item type");
2014
        msg.readInt32("location");
2015
        msg.readInt32("wear state");
2016
        msg.readInt8("refine");
2017
        for (int d = 0; d < maxCards; d ++)
2018
            msg.readItemId("card");
2019
        msg.readInt32("hire expire date (?)");
2020
        msg.readInt16("equip type");
2021
        msg.readInt16("item sprite number");
2022
        msg.readUInt8("flags");
2023
    }
2024
}
2025
2026
void BeingRecv::processBeingViewEquipment2(Net::MessageIn &msg)
2027
{
2028
    UNIMPLEMENTEDPACKET;
2029
2030
    const int count = (msg.readInt16("len") - 47) / (21 + itemIdLen * 5);
2031
    msg.readString(24, "name");
2032
    msg.readInt16("job");
2033
    msg.readInt16("head");
2034
    msg.readInt16("accessory");
2035
    msg.readInt16("accessory2");
2036
    msg.readInt16("accessory3");
2037
    msg.readInt16("robe");
2038
    msg.readInt16("hair color");
2039
    msg.readInt16("body color");
2040
    msg.readInt16("body2");
2041
    msg.readUInt8("gender");
2042
    for (int f = 0; f < count; f ++)
2043
    {
2044
        msg.readInt16("index");
2045
        msg.readItemId("item id");
2046
        msg.readUInt8("item type");
2047
        msg.readInt32("location");
2048
        msg.readInt32("wear state");
2049
        msg.readInt8("refine");
2050
        for (int d = 0; d < maxCards; d ++)
2051
            msg.readItemId("card");
2052
        msg.readInt32("hire expire date (?)");
2053
        msg.readInt16("equip type");
2054
        msg.readInt16("item sprite number");
2055
        msg.readUInt8("flags");
2056
    }
2057
}
2058
2059
void BeingRecv::processPvpSet(Net::MessageIn &msg)
2060
{
2061
    BLOCK_START("BeingRecv::processPvpSet")
2062
    const BeingId id = msg.readBeingId("being id");
2063
    const int rank = msg.readInt32("rank");
2064
    msg.readInt32("num");
2065
    if (actorManager != nullptr)
2066
    {
2067
        Being *const dstBeing = actorManager->findBeing(id);
2068
        if (dstBeing != nullptr)
2069
            dstBeing->setPvpRank(rank);
2070
    }
2071
    BLOCK_END("BeingRecv::processPvpSet")
2072
}
2073
2074
void BeingRecv::processNameResponse2(Net::MessageIn &msg)
2075
{
2076
    BLOCK_START("BeingRecv::processNameResponse2")
2077
    if ((actorManager == nullptr) || (localPlayer == nullptr))
2078
    {
2079
        BLOCK_END("BeingRecv::processNameResponse2")
2080
        return;
2081
    }
2082
2083
    const int len = msg.readInt16("len");
2084
    const BeingId beingId = msg.readBeingId("account id");
2085
    const std::string str = msg.readString(len - 8, "name");
2086
    actorManager->updateNameId(str, beingId);
2087
    Being *const dstBeing = actorManager->findBeing(beingId);
2088
    if (dstBeing != nullptr)
2089
    {
2090
        if (beingId == localPlayer->getId())
2091
        {
2092
            localPlayer->pingResponse();
2093
        }
2094
        else
2095
        {
2096
            dstBeing->setName(str);
2097
            dstBeing->updateGuild();
2098
            dstBeing->addToCache();
2099
2100
            if (dstBeing->getType() == ActorType::Player)
2101
                dstBeing->updateColors();
2102
2103
            if (localPlayer != nullptr)
2104
            {
2105
                const Party *const party = localPlayer->getParty();
2106
                if (party != nullptr && party->isMember(dstBeing->getId()))
2107
                {
2108
                    PartyMember *const member = party->getMember(
2109
                        dstBeing->getId());
2110
2111
                    if (member != nullptr)
2112
                        member->setName(dstBeing->getName());
2113
                }
2114
                localPlayer->checkNewName(dstBeing);
2115
            }
2116
        }
2117
    }
2118
    BLOCK_END("BeingRecv::processNameResponse2")
2119
}
2120
2121
Being *BeingRecv::createBeing2(Net::MessageIn &msg,
2122
                               const BeingId id,
2123
                               const int32_t job,
2124
                               const BeingTypeT beingType)
2125
{
2126
    if (actorManager == nullptr)
2127
        return nullptr;
2128
2129
    ActorTypeT type = ActorType::Unknown;
2130
    switch (beingType)
2131
    {
2132
        case BeingType::PC:
2133
            type = ActorType::Player;
2134
            break;
2135
        case BeingType::NPC:
2136
        case BeingType::NPC_EVENT:
2137
            type = ActorType::Npc;
2138
            break;
2139
        case BeingType::MONSTER:
2140
            type = ActorType::Monster;
2141
            break;
2142
        case BeingType::MERSOL:
2143
            type = ActorType::Mercenary;
2144
            break;
2145
        case BeingType::PET:
2146
            type = ActorType::Pet;
2147
            break;
2148
        case BeingType::HOMUN:
2149
            type = ActorType::Homunculus;
2150
            break;
2151
        case BeingType::SKILL:
2152
            type = ActorType::SkillUnit;
2153
            break;
2154
        case BeingType::ELEMENTAL:
2155
            type = ActorType::Elemental;
2156
            break;
2157
        case BeingType::ITEM:
2158
            logger->log("not supported object type: %d, job: %d",
2159
                CAST_S32(beingType), CAST_S32(job));
2160
            break;
2161
        case BeingType::CHAT:
2162
        default:
2163
            UNIMPLEMENTEDPACKETFIELD(CAST_S32(beingType));
2164
            type = ActorType::Monster;
2165
            logger->log("not supported object type: %d, job: %d",
2166
                CAST_S32(beingType), CAST_S32(job));
2167
            break;
2168
    }
2169
    if (job == 45 && beingType == BeingType::NPC_EVENT)
2170
        type = ActorType::Portal;
2171
2172
    Being *const being = actorManager->createBeing(
2173
        id, type, fromInt(job, BeingTypeId));
2174
    if (beingType == BeingType::MERSOL)
2175
    {
2176
        const MercenaryInfo *const info = PlayerInfo::getMercenary();
2177
        if ((info != nullptr) && info->id == id)
2178
            PlayerInfo::setMercenaryBeing(being);
2179
    }
2180
    else if (beingType == BeingType::PET)
2181
    {
2182
        if (PlayerInfo::getPetBeingId() == id)
2183
            PlayerInfo::setPetBeing(being);
2184
    }
2185
    return being;
2186
}
2187
2188
void BeingRecv::processSkillCancel(Net::MessageIn &msg)
2189
{
2190
    msg.readInt32("id?");
2191
}
2192
2193
void BeingRecv::processSolveCharName(Net::MessageIn &msg)
2194
{
2195
    if ((packets_re == true && msg.getVersion() >= 20180221) ||
2196
        (packets_main == true && msg.getVersion() >= 20180307) ||
2197
        (packets_zero == true && msg.getVersion() >= 20180328))
2198
    {
2199
        const int flag = msg.readInt16("flag");
2200
        // name request errors
2201
        if (flag != 3)
2202
            return;
2203
    }
2204
    const int id = msg.readInt32("char id");
2205
    if (actorManager == nullptr)
2206
    {
2207
        msg.readString(24, "name");
2208
        return;
2209
    }
2210
    actorManager->addChar(id, msg.readString(24, "name"));
2211
}
2212
2213
void BeingRecv::processGraffiti(Net::MessageIn &msg)
2214
{
2215
    const BeingId id = msg.readBeingId("graffiti id");
2216
    const BeingId creatorId = msg.readBeingId("creator id");
2217
    const int x = msg.readInt16("x");
2218
    const int y = msg.readInt16("y");
2219
    const int job = msg.readUInt8("job");
2220
    msg.readUInt8("visible");
2221
    msg.readUInt8("is content");
2222
    const std::string text = msg.readString(80, "text");
2223
2224
    Being *const dstBeing = createBeing2(msg, id, job, BeingType::SKILL);
2225
    if (dstBeing == nullptr)
2226
        return;
2227
2228
    dstBeing->setAction(BeingAction::STAND, 0);
2229
    dstBeing->setTileCoords(x, y);
2230
    dstBeing->setShowName(true);
2231
    dstBeing->setName(text);
2232
    dstBeing->setCreatorId(creatorId);
2233
}
2234
2235
void BeingRecv::processSkillDamage(Net::MessageIn &msg)
2236
{
2237
    BLOCK_START("BeingRecv::processSkillDamage")
2238
    if (actorManager == nullptr)
2239
    {
2240
        BLOCK_END("BeingRecv::processSkillDamage")
2241
        return;
2242
    }
2243
2244
    const int id = msg.readInt16("skill id");
2245
    Being *const srcBeing = actorManager->findBeing(
2246
        msg.readBeingId("src being id"));
2247
    Being *const dstBeing = actorManager->findBeing(
2248
        msg.readBeingId("dst being id"));
2249
    msg.readInt32("tick");
2250
    msg.readInt32("src speed");
2251
    msg.readInt32("dst speed");
2252
    int param1;
2253
    if (msg.getVersion() >= 3)
2254
        param1 = msg.readInt32("damage");
2255
    else
2256
        param1 = msg.readInt16("damage");
2257
    const int level = msg.readInt16("skill level");
2258
    msg.readInt16("div");
2259
    msg.readUInt8("skill hit/type?");
2260
    if (srcBeing != nullptr)
2261
        srcBeing->handleSkill(dstBeing, param1, id, level);
2262
    if (dstBeing != nullptr)
2263
        dstBeing->takeDamage(srcBeing, param1, AttackType::SKILL, id, level);
2264
    BLOCK_END("BeingRecv::processSkillDamage")
2265
}
2266
2267
void BeingRecv::processNavigateTo(Net::MessageIn &msg)
2268
{
2269
    UNIMPLEMENTEDPACKET;
2270
    // 0 position
2271
    // 1 no position
2272
    // 3 monster
2273
    msg.readUInt8("navigate type");
2274
    msg.readUInt8("transportation flag");
2275
    msg.readUInt8("hide window");
2276
    msg.readString(16, "map name");
2277
    msg.readInt16("x");
2278
    msg.readInt16("y");
2279
    msg.readInt16("mob id");
2280
}
2281
2282
void BeingRecv::applyPlayerAction(Net::MessageIn &msg,
2283
                                  Being *const being,
2284
                                  const uint8_t type)
2285
{
2286
    if (being == nullptr)
2287
        return;
2288
    switch (type)
2289
    {
2290
        case 0:
2291
            being->setAction(BeingAction::STAND, 0);
2292
            localPlayer->imitateAction(being, BeingAction::STAND);
2293
            break;
2294
2295
        case 1:
2296
            if (being->getCurrentAction() != BeingAction::DEAD)
2297
            {
2298
                being->setAction(BeingAction::DEAD, 0);
2299
                being->recalcSpritesOrder();
2300
            }
2301
            break;
2302
2303
        case 2:
2304
            being->setAction(BeingAction::SIT, 0);
2305
            localPlayer->imitateAction(being, BeingAction::SIT);
2306
            break;
2307
2308
        default:
2309
            UNIMPLEMENTEDPACKETFIELD(type);
2310
            break;
2311
    }
2312
}
2313
2314
2
}  // namespace EAthena