GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/beingrecv.cpp Lines: 1 1183 0.1 %
Date: 2018-05-19 03:07:18 Branches: 0 746 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/okdialog.h"
46
#include "gui/windows/outfitwindow.h"
47
48
#include "net/character.h"
49
#include "net/charserverhandler.h"
50
#include "net/messagein.h"
51
#include "net/serverfeatures.h"
52
53
#include "net/ea/beingrecv.h"
54
55
#include "net/eathena/maptypeproperty2.h"
56
#include "net/eathena/sp.h"
57
#include "net/eathena/sprite.h"
58
59
#include "resources/claninfo.h"
60
#include "resources/iteminfo.h"
61
62
#include "resources/db/clandb.h"
63
#include "resources/db/itemdb.h"
64
65
#include "resources/map/map.h"
66
67
#include "utils/checkutils.h"
68
#include "utils/foreach.h"
69
#include "utils/timer.h"
70
71
#include "debug.h"
72
73
extern int serverVersion;
74
extern OkDialog *deathNotice;
75
extern bool packets_re;
76
extern bool packets_main;
77
extern bool packets_zero;
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.readInt16("id1");
207
    unsigned int id2 = msg.readInt16("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");
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
        weapon = CAST_U32(msg.readInt32("weapon"));
505
    else
506
        weapon = CAST_U32(msg.readInt16("weapon"));
507
    const uint16_t headBottom = msg.readInt16("head bottom");
508
    if (msg.getVersion() < 7)
509
        msg.readInt16("shield");
510
    const uint16_t headTop = msg.readInt16("head top");
511
    const uint16_t headMid = msg.readInt16("head mid");
512
    const ItemColor hairColor = fromInt(msg.readInt16("hair color"),
513
        ItemColor);
514
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
515
516
    const uint16_t gloves = msg.readInt16("head dir / gloves");
517
    // may be use robe as gloves?
518
    if (msg.getVersion() >= 20101124)
519
        msg.readInt16("robe");
520
    msg.readInt32("guild id");
521
    msg.readInt16("guild emblem");
522
    dstBeing->setManner(msg.readInt16("manner"));
523
    uint32_t opt3;
524
    if (msg.getVersion() >= 7)
525
        opt3 = msg.readInt32("opt3");
526
    else
527
        opt3 = msg.readInt16("opt3");
528
    dstBeing->setKarma(msg.readUInt8("karma"));
529
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
530
531
    setBasicFields(dstBeing,
532
        gender,
533
        hairStyle,
534
        hairColor,
535
        weapon,
536
        headBottom,
537
        headMid,
538
        headTop,
539
        shoes,
540
        gloves,
541
        true);
542
543
    uint8_t dir;
544
    uint16_t x, y;
545
    msg.readCoordinates(x, y, dir, "position");
546
    msg.readInt8("xs");
547
    msg.readInt8("ys");
548
    applyPlayerAction(msg, dstBeing, msg.readUInt8("action type"));
549
    dstBeing->setTileCoords(x, y);
550
551
    if (job == 45 && (socialWindow != nullptr) && (outfitWindow != nullptr))
552
    {
553
        const int num = socialWindow->getPortalIndex(x, y);
554
        if (num >= 0)
555
        {
556
            dstBeing->setName(KeyboardConfig::getKeyShortString(
557
                OutfitWindow::keyName(num)));
558
        }
559
        else
560
        {
561
            dstBeing->setName("");
562
        }
563
    }
564
565
    dstBeing->setDirection(dir);
566
567
    const int level = CAST_S32(msg.readInt16("level"));
568
    if (level != 0)
569
        dstBeing->setLevel(level);
570
    if (msg.getVersion() >= 20080102)
571
        msg.readInt16("font");
572
573
    if (msg.getVersion() >= 20120221)
574
    {
575
        const int maxHP = msg.readInt32("max hp");
576
        const int hp = msg.readInt32("hp");
577
        dstBeing->setMaxHP(maxHP);
578
        dstBeing->setHP(hp);
579
        msg.readInt8("is boss");
580
    }
581
582
    if (serverVersion == 0 ||
583
        serverVersion >= 16)
584
    {
585
        if (msg.getVersion() >= 20150513)
586
        {
587
            msg.readInt16("body2");
588
        }
589
        if (msg.getVersion() >= 20131223)
590
        {
591
            msg.readString(24, "name");
592
        }
593
    }
594
    else
595
    {
596
        if (msg.getVersion() >= 20150513)
597
        {
598
            msg.readInt16("body2");
599
            msg.readString(24, "name");
600
        }
601
    }
602
603
    dstBeing->setStatusEffectOpitons(option,
604
        opt1,
605
        opt2,
606
        opt3);
607
}
608
609
void BeingRecv::processBeingMove(Net::MessageIn &msg)
610
{
611
    if (actorManager == nullptr)
612
        return;
613
614
    if (msg.getVersion() >= 20091103)
615
        msg.readInt16("len");
616
    BeingTypeT type;
617
    if (msg.getVersion() >= 20071106)
618
    {
619
        type = static_cast<BeingTypeT>(
620
            msg.readUInt8("object type"));
621
    }
622
    else
623
    {
624
        // need detect type based on id
625
        type = BeingType::MONSTER;
626
    }
627
628
    // Information about a being in range
629
    const BeingId id = msg.readBeingId("being id");
630
    if (msg.getVersion() >= 20131223)
631
        msg.readBeingId("char id");
632
    BeingId spawnId;
633
    if (id == Ea::BeingRecv::mSpawnId)
634
        spawnId = Ea::BeingRecv::mSpawnId;
635
    else
636
        spawnId = BeingId_zero;
637
    Ea::BeingRecv::mSpawnId = BeingId_zero;
638
    int16_t speed = msg.readInt16("speed");
639
    const uint32_t opt1 = msg.readInt16("opt1");
640
    // probably wrong effect usage
641
    const uint32_t opt2 = msg.readInt16("opt2");
642
    uint32_t option;
643
    if (msg.getVersion() >= 7)
644
        option = msg.readInt32("option");
645
    else
646
        option = msg.readInt16("option");
647
    const int16_t job = msg.readInt16("class");
648
649
    Being *dstBeing = actorManager->findBeing(id);
650
651
    if ((dstBeing != nullptr) && dstBeing->getType() == ActorType::Monster
652
        && !dstBeing->isAlive())
653
    {
654
        actorManager->destroy(dstBeing);
655
        actorManager->erase(dstBeing);
656
        dstBeing = nullptr;
657
    }
658
659
    if (dstBeing == nullptr)
660
    {
661
        if (actorManager->isBlocked(id) == true)
662
            return;
663
664
        dstBeing = createBeing2(msg, id, job, type);
665
        if (dstBeing == nullptr)
666
            return;
667
    }
668
    else
669
    {
670
        // undeleting marked for deletion being
671
        if (dstBeing->getType() == ActorType::Npc)
672
            actorManager->undelete(dstBeing);
673
    }
674
675
    if (dstBeing->getType() == ActorType::Player)
676
        dstBeing->setMoveTime();
677
678
    if (spawnId != BeingId_zero)
679
        dstBeing->setAction(BeingAction::SPAWN, 0);
680
681
    // Prevent division by 0 when calculating frame
682
    if (speed == 0)
683
        speed = 150;
684
685
    dstBeing->setWalkSpeed(speed);
686
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
687
    if (dstBeing->getType() == ActorType::Monster && (localPlayer != nullptr))
688
        localPlayer->checkNewName(dstBeing);
689
690
    const int hairStyle = msg.readInt16("hair style");
691
    uint32_t weapon;
692
    if (msg.getVersion() >= 7)
693
        weapon = CAST_U32(msg.readInt32("weapon"));
694
    else
695
        weapon = CAST_U32(msg.readInt16("weapon"));
696
    const uint16_t headBottom = msg.readInt16("head bottom");
697
    msg.readInt32("tick");
698
    if (msg.getVersion() < 7)
699
        msg.readInt16("shield");
700
    const uint16_t headTop = msg.readInt16("head top");
701
    const uint16_t headMid = msg.readInt16("head mid");
702
    const ItemColor hairColor = fromInt(
703
        msg.readInt16("hair color"), ItemColor);
704
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
705
706
    const uint16_t gloves = msg.readInt16("head dir / gloves");
707
    // may be use robe as gloves?
708
    if (msg.getVersion() >= 20101124)
709
        msg.readInt16("robe");
710
    msg.readInt32("guild id");
711
    msg.readInt16("guild emblem");
712
    dstBeing->setManner(msg.readInt16("manner"));
713
    uint32_t opt3;
714
    if (msg.getVersion() >= 7)
715
        opt3 = msg.readInt32("opt3");
716
    else
717
        opt3 = msg.readInt16("opt3");
718
    dstBeing->setKarma(msg.readUInt8("karma"));
719
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
720
721
    setBasicFields(dstBeing,
722
        gender,
723
        hairStyle,
724
        hairColor,
725
        weapon,
726
        headBottom,
727
        headMid,
728
        headTop,
729
        shoes,
730
        gloves,
731
        !serverFeatures->haveMove3());
732
733
    uint16_t srcX, srcY, dstX, dstY;
734
    msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path");
735
    msg.readUInt8("(sx<<4) | (sy&0x0f)");
736
    msg.readInt8("xs");
737
    msg.readInt8("ys");
738
    dstBeing->setAction(BeingAction::STAND, 0);
739
    dstBeing->setTileCoords(srcX, srcY);
740
    if (localPlayer != nullptr)
741
        localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
742
    if (serverFeatures->haveMove3())
743
        dstBeing->setCachedDestination(dstX, dstY);
744
    else
745
        dstBeing->setDestination(dstX, dstY);
746
747
    // because server don't send direction in move packet, we fixing it
748
749
    uint8_t d = 0;
750
    if (localPlayer != nullptr &&
751
        srcX == dstX &&
752
        srcY == dstY)
753
    {   // if player did one step from invisible area to visible,
754
        // move path is broken
755
        int x2 = localPlayer->getTileX();
756
        int y2 = localPlayer->getTileY();
757
        if (abs(x2 - srcX) > abs(y2 - srcY))
758
            y2 = srcY;
759
        else
760
            x2 = srcX;
761
        d = dstBeing->calcDirection(x2, y2);
762
    }
763
    else
764
    {
765
        d = dstBeing->calcDirection(dstX, dstY);
766
    }
767
    if ((d != 0u) && dstBeing->getDirection() != d)
768
        dstBeing->setDirection(d);
769
770
    const int level = CAST_S32(msg.readInt16("level"));
771
    if (level != 0)
772
        dstBeing->setLevel(level);
773
    if (msg.getVersion() >= 20080102)
774
        msg.readInt16("font");
775
    if (msg.getVersion() >= 20120221)
776
    {
777
        const int maxHP = msg.readInt32("max hp");
778
        const int hp = msg.readInt32("hp");
779
        dstBeing->setMaxHP(maxHP);
780
        dstBeing->setHP(hp);
781
        msg.readInt8("is boss");
782
    }
783
    if (serverVersion == 0 ||
784
        serverVersion >= 16)
785
    {
786
        if (msg.getVersion() >= 20150513)
787
        {
788
            msg.readInt16("body2");
789
        }
790
        if (msg.getVersion() >= 20131223)
791
        {
792
            msg.readString(24, "name");
793
        }
794
    }
795
    else
796
    {
797
        if (msg.getVersion() >= 20150513)
798
        {
799
            msg.readInt16("body2");
800
            msg.readString(24, "name");
801
        }
802
    }
803
804
    dstBeing->setStatusEffectOpitons(option,
805
        opt1,
806
        opt2,
807
        opt3);
808
}
809
810
void BeingRecv::processBeingSpawn(Net::MessageIn &msg)
811
{
812
    if (actorManager == nullptr)
813
        return;
814
815
    // need get type from id
816
    BeingTypeT type = BeingType::MONSTER;
817
    if (msg.getVersion() >= 20091103)
818
    {
819
        msg.readInt16("len");
820
        type = static_cast<BeingTypeT>(
821
            msg.readUInt8("object type"));
822
    }
823
824
    // Information about a being in range
825
    const BeingId id = msg.readBeingId("being id");
826
    if (msg.getVersion() >= 20131223)
827
    {
828
        msg.readBeingId("char id");
829
    }
830
    Ea::BeingRecv::mSpawnId = id;
831
    const BeingId spawnId = id;
832
    int16_t speed = msg.readInt16("speed");
833
    const uint32_t opt1 = msg.readInt16("opt1");
834
    // probably wrong effect usage
835
    const uint32_t opt2 = msg.readInt16("opt2");
836
    uint32_t option;
837
    if (msg.getVersion() >= 20080102)
838
        option = msg.readInt32("option");
839
    else
840
        option = msg.readInt16("option");
841
    const int16_t job = msg.readInt16("class");
842
843
    Being *dstBeing = actorManager->findBeing(id);
844
845
    if ((dstBeing != nullptr) && dstBeing->getType() == ActorType::Monster
846
        && !dstBeing->isAlive())
847
    {
848
        actorManager->destroy(dstBeing);
849
        actorManager->erase(dstBeing);
850
        dstBeing = nullptr;
851
    }
852
853
    if (dstBeing == nullptr)
854
    {
855
        if (actorManager->isBlocked(id) == true)
856
            return;
857
858
        dstBeing = createBeing2(msg, id, job, type);
859
        if (dstBeing == nullptr)
860
            return;
861
    }
862
    else
863
    {
864
        // undeleting marked for deletion being
865
        if (dstBeing->getType() == ActorType::Npc)
866
            actorManager->undelete(dstBeing);
867
    }
868
869
    if (dstBeing->getType() == ActorType::Player)
870
        dstBeing->setMoveTime();
871
872
    if (spawnId != BeingId_zero)
873
        dstBeing->setAction(BeingAction::SPAWN, 0);
874
875
    // Prevent division by 0 when calculating frame
876
    if (speed == 0)
877
        speed = 150;
878
879
    dstBeing->setWalkSpeed(speed);
880
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
881
    if (dstBeing->getType() == ActorType::Monster && (localPlayer != nullptr))
882
        localPlayer->checkNewName(dstBeing);
883
884
    const int hairStyle = msg.readInt16("hair style");
885
    uint32_t weapon;
886
    if (msg.getVersion() >= 7)
887
        weapon = CAST_U32(msg.readInt32("weapon"));
888
    else
889
        weapon = CAST_U32(msg.readInt16("weapon"));
890
    const uint16_t headBottom = msg.readInt16("head bottom");
891
    if (msg.getVersion() < 7)
892
        msg.readInt16("shield");
893
    const uint16_t headTop = msg.readInt16("head top");
894
    const uint16_t headMid = msg.readInt16("head mid");
895
    const ItemColor hairColor = fromInt(
896
        msg.readInt16("hair color"), ItemColor);
897
    const uint16_t shoes = msg.readInt16("shoes or clothes color?");
898
    const uint16_t gloves = msg.readInt16("head dir / gloves");
899
    // may be use robe as gloves?
900
    if (msg.getVersion() >= 20101124)
901
        msg.readInt16("robe");
902
    msg.readInt32("guild id");
903
    msg.readInt16("guild emblem");
904
    dstBeing->setManner(msg.readInt16("manner"));
905
    uint32_t opt3;
906
    if (msg.getVersion() >= 7)
907
        opt3 = msg.readInt32("opt3");
908
    else
909
        opt3 = msg.readInt16("opt3");
910
911
    dstBeing->setKarma(msg.readUInt8("karma"));
912
    const uint8_t gender = CAST_U8(msg.readUInt8("gender") & 3);
913
914
    setBasicFields(dstBeing,
915
        gender,
916
        hairStyle,
917
        hairColor,
918
        weapon,
919
        headBottom,
920
        headMid,
921
        headTop,
922
        shoes,
923
        gloves,
924
        true);
925
926
    uint8_t dir;
927
    uint16_t x, y;
928
    msg.readCoordinates(x, y, dir, "position");
929
    msg.readInt8("xs");
930
    msg.readInt8("ys");
931
    dstBeing->setTileCoords(x, y);
932
933
    if (job == 45 && (socialWindow != nullptr) && (outfitWindow != nullptr))
934
    {
935
        const int num = socialWindow->getPortalIndex(x, y);
936
        if (num >= 0)
937
        {
938
            dstBeing->setName(KeyboardConfig::getKeyShortString(
939
                OutfitWindow::keyName(num)));
940
        }
941
        else
942
        {
943
            dstBeing->setName("");
944
        }
945
    }
946
947
    dstBeing->setDirection(dir);
948
949
    const int level = CAST_S32(msg.readInt16("level"));
950
    if (level != 0)
951
        dstBeing->setLevel(level);
952
    if (msg.getVersion() >= 20080102)
953
        msg.readInt16("font");
954
    if (msg.getVersion() >= 20120221)
955
    {
956
        const int maxHP = msg.readInt32("max hp");
957
        const int hp = msg.readInt32("hp");
958
        dstBeing->setMaxHP(maxHP);
959
        dstBeing->setHP(hp);
960
        msg.readInt8("is boss");
961
    }
962
    if (serverVersion == 0 ||
963
        serverVersion >= 16)
964
    {
965
        if (msg.getVersion() >= 20150513)
966
        {
967
            msg.readInt16("body2");
968
        }
969
        if (msg.getVersion() >= 20131223)
970
        {
971
            msg.readString(24, "name");
972
        }
973
    }
974
    else
975
    {
976
        if (msg.getVersion() >= 20150513)
977
        {
978
            msg.readInt16("body2");
979
            msg.readString(24, "name");
980
        }
981
    }
982
983
    dstBeing->setStatusEffectOpitons(option,
984
        opt1,
985
        opt2,
986
        opt3);
987
}
988
989
void BeingRecv::processMapTypeProperty(Net::MessageIn &msg)
990
{
991
    const int16_t type = msg.readInt16("type");
992
    const int flags = msg.readInt32("flags");
993
    if (type == 0x28)
994
    {
995
        // +++ need get other flags from here
996
        MapTypeProperty2 props;
997
        props.data = CAST_U32(flags);
998
        const Game *const game = Game::instance();
999
        if (game == nullptr)
1000
            return;
1001
        Map *const map = game->getCurrentMap();
1002
        if (map == nullptr)
1003
            return;
1004
        map->setPvpMode(props.bits.party | (props.bits.guild * 2));
1005
    }
1006
}
1007
1008
void BeingRecv::processMapType(Net::MessageIn &msg)
1009
{
1010
    const int16_t type = msg.readInt16("type");
1011
    if (type == 19)
1012
        NotifyManager::notify(NotifyTypes::MAP_TYPE_BATTLEFIELD);
1013
    else
1014
        UNIMPLEMENTEDPACKETFIELD(type);
1015
}
1016
1017
void BeingRecv::processSkillCasting(Net::MessageIn &msg)
1018
{
1019
    const BeingId srcId = msg.readBeingId("src id");
1020
    const BeingId dstId = msg.readBeingId("dst id");
1021
    const int dstX = msg.readInt16("dst x");
1022
    const int dstY = msg.readInt16("dst y");
1023
    const int skillId = msg.readInt16("skill id");
1024
    msg.readInt32("property");  // can be used to trigger effect
1025
    const int castTime = msg.readInt32("cast time");
1026
    if (msg.getVersion() >= 20091124)
1027
        msg.readInt8("dispossable");
1028
1029
    processSkillCastingContinue(msg,
1030
        srcId, dstId,
1031
        dstX, dstY,
1032
        skillId,
1033
        1,
1034
        0,
1035
        SkillType2::Unknown,
1036
        castTime);
1037
}
1038
1039
void BeingRecv::processSkillCasting2(Net::MessageIn &msg)
1040
{
1041
    msg.readInt16("len");  // for now unused
1042
    const BeingId srcId = msg.readBeingId("src id");
1043
    const BeingId dstId = msg.readBeingId("dst id");
1044
    const int dstX = msg.readInt16("dst x");
1045
    const int dstY = msg.readInt16("dst y");
1046
    const int skillId = msg.readInt16("skill id");
1047
    const int skillLevel = msg.readInt16("skill level");
1048
    msg.readInt32("property");  // can be used to trigger effect
1049
    const int castTime = msg.readInt32("cast time");
1050
    const int range = msg.readInt32("skill range");
1051
    const SkillType2::SkillType2 inf2 =
1052
        static_cast<SkillType2::SkillType2>(msg.readInt32("inf2"));
1053
1054
    processSkillCastingContinue(msg,
1055
        srcId, dstId,
1056
        dstX, dstY,
1057
        skillId,
1058
        skillLevel,
1059
        range,
1060
        inf2,
1061
        castTime);
1062
}
1063
1064
void BeingRecv::processSkillCastingContinue(Net::MessageIn &msg,
1065
                                            const BeingId srcId,
1066
                                            const BeingId dstId,
1067
                                            const int dstX,
1068
                                            const int dstY,
1069
                                            const int skillId,
1070
                                            const int skillLevel,
1071
                                            const int range,
1072
                                            const SkillType2::SkillType2 inf2,
1073
                                            const int castTime)
1074
{
1075
    if (effectManager == nullptr ||
1076
        actorManager == nullptr)
1077
        return;
1078
1079
    if (srcId == BeingId_zero)
1080
    {
1081
        UNIMPLEMENTEDPACKETFIELD(0);
1082
        return;
1083
    }
1084
    Being *const srcBeing = actorManager->findBeing(srcId);
1085
    if (dstId != BeingId_zero)
1086
    {   // being to being
1087
        Being *const dstBeing = actorManager->findBeing(dstId);
1088
        if (srcBeing != nullptr)
1089
        {
1090
            srcBeing->handleSkillCasting(dstBeing, skillId, skillLevel);
1091
            if (dstBeing != nullptr)
1092
            {
1093
                srcBeing->addCast(dstBeing->getTileX(),
1094
                    dstBeing->getTileY(),
1095
                    skillId,
1096
                    skillLevel,
1097
                    range,
1098
                    castTime / MILLISECONDS_IN_A_TICK);
1099
            }
1100
        }
1101
    }
1102
    else if (dstX != 0 || dstY != 0)
1103
    {   // being to position
1104
        if (srcBeing != nullptr)
1105
            srcBeing->setAction(BeingAction::CAST, skillId);
1106
        skillDialog->playCastingDstTileEffect(skillId,
1107
            skillLevel,
1108
            dstX, dstY,
1109
            castTime);
1110
        if (srcBeing != nullptr)
1111
        {
1112
            srcBeing->addCast(dstX, dstY,
1113
                skillId,
1114
                skillLevel,
1115
                range,
1116
                castTime / MILLISECONDS_IN_A_TICK);
1117
        }
1118
    }
1119
    if ((localPlayer != nullptr) &&
1120
        srcBeing == localPlayer &&
1121
        (inf2 & SkillType2::FreeCastAny) == 0)
1122
    {
1123
        localPlayer->freezeMoving(castTime / MILLISECONDS_IN_A_TICK);
1124
    }
1125
}
1126
1127
void BeingRecv::processBeingStatusChange(Net::MessageIn &msg)
1128
{
1129
    BLOCK_START("BeingRecv::processBeingStatusChange")
1130
    if (actorManager == nullptr)
1131
    {
1132
        BLOCK_END("BeingRecv::processBeingStatusChange")
1133
        return;
1134
    }
1135
1136
    // Status change
1137
    const uint16_t status = msg.readInt16("status");
1138
    const BeingId id = msg.readBeingId("being id");
1139
    const Enable flag = fromBool(
1140
        msg.readUInt8("flag: 0: stop, 1: start"), Enable);
1141
    if (msg.getVersion() >= 20120618)
1142
        msg.readInt32("total");
1143
    if (msg.getVersion() >= 20090121)
1144
    {
1145
        msg.readInt32("left");
1146
        msg.readInt32("val1");
1147
        msg.readInt32("val2");
1148
        msg.readInt32("val3");
1149
    }
1150
1151
    const IsStart start = msg.getVersion() == 20090121 ?
1152
        IsStart_false : IsStart_true;
1153
1154
    Being *const dstBeing = actorManager->findBeing(id);
1155
    if (dstBeing != nullptr)
1156
        dstBeing->setStatusEffect(status, flag, start);
1157
    BLOCK_END("BeingRecv::processBeingStatusChange")
1158
}
1159
1160
void BeingRecv::processBeingMove2(Net::MessageIn &msg)
1161
{
1162
    BLOCK_START("BeingRecv::processBeingMove2")
1163
    if (actorManager == nullptr)
1164
    {
1165
        BLOCK_END("BeingRecv::processBeingMove2")
1166
        return;
1167
    }
1168
1169
    /*
1170
      * A simplified movement packet, used by the
1171
      * later versions of eAthena for both mobs and
1172
      * players
1173
      */
1174
    Being *const dstBeing = actorManager->findBeing(
1175
        msg.readBeingId("being id"));
1176
1177
    uint16_t srcX, srcY, dstX, dstY;
1178
    msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path");
1179
    msg.readUInt8("(sx<<4) | (sy&0x0f)");
1180
    msg.readInt32("tick");
1181
1182
    /*
1183
      * This packet doesn't have enough info to actually
1184
      * create a new being, so if the being isn't found,
1185
      * we'll just pretend the packet didn't happen
1186
      */
1187
1188
    if (dstBeing == nullptr)
1189
    {
1190
        BLOCK_END("BeingRecv::processBeingMove2")
1191
        return;
1192
    }
1193
1194
    dstBeing->setTileCoords(srcX, srcY);
1195
    if (localPlayer != nullptr)
1196
        localPlayer->followMoveTo(dstBeing, srcX, srcY, dstX, dstY);
1197
    if (serverFeatures->haveMove3())
1198
        dstBeing->setCachedDestination(dstX, dstY);
1199
    else
1200
        dstBeing->setDestination(dstX, dstY);
1201
    if (dstBeing->getType() == ActorType::Player)
1202
        dstBeing->setMoveTime();
1203
    BLOCK_END("BeingRecv::processBeingMove2")
1204
}
1205
1206
void BeingRecv::processBeingAction2(Net::MessageIn &msg)
1207
{
1208
    BLOCK_START("BeingRecv::processBeingAction2")
1209
    if (actorManager == nullptr)
1210
    {
1211
        BLOCK_END("BeingRecv::processBeingAction2")
1212
        return;
1213
    }
1214
1215
    Being *const srcBeing = actorManager->findBeing(
1216
        msg.readBeingId("src being id"));
1217
    Being *const dstBeing = actorManager->findBeing(
1218
        msg.readBeingId("dst being id"));
1219
1220
    msg.readInt32("tick");
1221
    const int srcSpeed = msg.readInt32("src speed");
1222
    msg.readInt32("dst speed");
1223
    int param1;
1224
    if (msg.getVersion() >= 20071113)
1225
        param1 = msg.readInt32("damage");
1226
    else
1227
        param1 = msg.readInt16("damage");
1228
    if (msg.getVersion() >= 20131223)
1229
        msg.readUInt8("is sp damaged");
1230
    msg.readInt16("count");
1231
    const AttackTypeT type = static_cast<AttackTypeT>(
1232
        msg.readUInt8("action"));
1233
    if (msg.getVersion() >= 20071113)
1234
        msg.readInt32("left damage");
1235
    else
1236
        msg.readInt16("left damage");
1237
1238
    switch (type)
1239
    {
1240
        case AttackType::HIT:  // Damage
1241
        case AttackType::CRITICAL:  // Critical Damage
1242
        case AttackType::MULTI:  // Critical Damage
1243
        case AttackType::MULTI_REFLECT:
1244
        case AttackType::REFLECT:  // Reflected Damage
1245
        case AttackType::FLEE:  // Lucky Dodge
1246
        case AttackType::SPLASH:
1247
        case AttackType::SKILL:
1248
        case AttackType::REPEATE:
1249
            if (srcBeing != nullptr)
1250
            {
1251
                if (srcSpeed != 0 && srcBeing->getType() == ActorType::Player)
1252
                    srcBeing->setAttackDelay(srcSpeed);
1253
                // attackid=1, type
1254
                srcBeing->handleAttack(dstBeing, param1, 1);
1255
                if (srcBeing->getType() == ActorType::Player)
1256
                    srcBeing->setAttackTime();
1257
            }
1258
            if (dstBeing != nullptr)
1259
            {
1260
                // level not present, using 1
1261
                dstBeing->takeDamage(srcBeing, param1,
1262
                    static_cast<AttackTypeT>(type), 1, 1);
1263
            }
1264
            break;
1265
1266
        case AttackType::PICKUP:
1267
            break;
1268
1269
        case AttackType::TOUCH_SKILL:
1270
            break;
1271
1272
        case AttackType::SIT:
1273
            if (srcBeing != nullptr)
1274
            {
1275
                srcBeing->setAction(BeingAction::SIT, 0);
1276
                if (srcBeing->getType() == ActorType::Player)
1277
                {
1278
                    srcBeing->setMoveTime();
1279
                    if (localPlayer != nullptr)
1280
                        localPlayer->imitateAction(srcBeing, BeingAction::SIT);
1281
                }
1282
            }
1283
            break;
1284
1285
        case AttackType::STAND:
1286
            if (srcBeing != nullptr)
1287
            {
1288
                srcBeing->setAction(BeingAction::STAND, 0);
1289
                if (srcBeing->getType() == ActorType::Player)
1290
                {
1291
                    srcBeing->setMoveTime();
1292
                    if (localPlayer != nullptr)
1293
                    {
1294
                        localPlayer->imitateAction(srcBeing,
1295
                            BeingAction::STAND);
1296
                    }
1297
                }
1298
            }
1299
            break;
1300
        default:
1301
        case AttackType::MISS:
1302
        case AttackType::SKILLMISS:
1303
            UNIMPLEMENTEDPACKETFIELD(CAST_S32(type));
1304
            break;
1305
    }
1306
    BLOCK_END("BeingRecv::processBeingAction2")
1307
}
1308
1309
void BeingRecv::processBeingHp(Net::MessageIn &msg)
1310
{
1311
    if (actorManager == nullptr)
1312
        return;
1313
    Being *const dstBeing = actorManager->findBeing(
1314
        msg.readBeingId("being id"));
1315
    int hp;
1316
    int maxHP;
1317
    if (msg.getVersion() >= 20100126)
1318
    {
1319
        hp = msg.readInt32("hp");
1320
        maxHP = msg.readInt32("max hp");
1321
    }
1322
    else
1323
    {
1324
        hp = msg.readInt16("hp");
1325
        maxHP = msg.readInt16("max hp");
1326
    }
1327
    if (dstBeing != nullptr)
1328
    {
1329
        dstBeing->setHP(hp);
1330
        dstBeing->setMaxHP(maxHP);
1331
    }
1332
}
1333
1334
void BeingRecv::processMonsterHp(Net::MessageIn &msg)
1335
{
1336
    if (actorManager == nullptr)
1337
        return;
1338
    Being *const dstBeing = actorManager->findBeing(
1339
        msg.readBeingId("monster id"));
1340
    const int hp = msg.readInt32("hp");
1341
    const int maxHP = msg.readInt32("max hp");
1342
    if (dstBeing != nullptr)
1343
    {
1344
        dstBeing->setHP(hp);
1345
        dstBeing->setMaxHP(maxHP);
1346
    }
1347
}
1348
1349
void BeingRecv::processSkillAutoCast(Net::MessageIn &msg)
1350
{
1351
    const int id = msg.readInt16("skill id");
1352
    msg.readInt16("inf");
1353
    msg.readInt16("unused");
1354
    const int level = msg.readInt16("skill level");
1355
    msg.readInt16("sp");
1356
    msg.readInt16("range");
1357
    msg.readString(24, "skill name");
1358
    msg.readInt8("unused");
1359
1360
    if (localPlayer != nullptr)
1361
    {
1362
        localPlayer->handleSkill(localPlayer, 0, id, level);
1363
        localPlayer->takeDamage(localPlayer, 0, AttackType::SKILL, id, level);
1364
    }
1365
}
1366
1367
void BeingRecv::processRanksList(Net::MessageIn &msg)
1368
{
1369
    UNIMPLEMENTEDPACKET;
1370
    // +++ here need window with rank tables.
1371
    msg.readInt16("rank type");
1372
    for (int f = 0; f < 10; f ++)
1373
        msg.readString(24, "name");
1374
    for (int f = 0; f < 10; f ++)
1375
        msg.readInt32("points");
1376
    msg.readInt32("my points");
1377
}
1378
1379
void BeingRecv::processBlacksmithRanksList(Net::MessageIn &msg)
1380
{
1381
    UNIMPLEMENTEDPACKET;
1382
    // +++ here need window with rank tables.
1383
    for (int f = 0; f < 10; f ++)
1384
        msg.readString(24, "name");
1385
    for (int f = 0; f < 10; f ++)
1386
        msg.readInt32("points");
1387
}
1388
1389
void BeingRecv::processAlchemistRanksList(Net::MessageIn &msg)
1390
{
1391
    UNIMPLEMENTEDPACKET;
1392
    // +++ here need window with rank tables.
1393
    for (int f = 0; f < 10; f ++)
1394
        msg.readString(24, "name");
1395
    for (int f = 0; f < 10; f ++)
1396
        msg.readInt32("points");
1397
}
1398
1399
void BeingRecv::processTaekwonRanksList(Net::MessageIn &msg)
1400
{
1401
    UNIMPLEMENTEDPACKET;
1402
    // +++ here need window with rank tables.
1403
    for (int f = 0; f < 10; f ++)
1404
        msg.readString(24, "name");
1405
    for (int f = 0; f < 10; f ++)
1406
        msg.readInt32("points");
1407
}
1408
1409
void BeingRecv::processPkRanksList(Net::MessageIn &msg)
1410
{
1411
    UNIMPLEMENTEDPACKET;
1412
    // +++ here need window with rank tables.
1413
    for (int f = 0; f < 10; f ++)
1414
        msg.readString(24, "name");
1415
    for (int f = 0; f < 10; f ++)
1416
        msg.readInt32("points");
1417
}
1418
1419
void BeingRecv::processBeingChangeDirection(Net::MessageIn &msg)
1420
{
1421
    BLOCK_START("BeingRecv::processBeingChangeDirection")
1422
    if (actorManager == nullptr)
1423
    {
1424
        BLOCK_END("BeingRecv::processBeingChangeDirection")
1425
        return;
1426
    }
1427
1428
    Being *const dstBeing = actorManager->findBeing(
1429
        msg.readBeingId("being id"));
1430
1431
    msg.readInt16("head direction");
1432
1433
    const uint8_t dir = Net::MessageIn::fromServerDirection(
1434
        CAST_U8(msg.readUInt8("player direction") & 0x0FU));
1435
1436
    if (dstBeing == nullptr)
1437
    {
1438
        BLOCK_END("BeingRecv::processBeingChangeDirection")
1439
        return;
1440
    }
1441
1442
    dstBeing->setDirection(dir);
1443
    if (localPlayer != nullptr)
1444
        localPlayer->imitateDirection(dstBeing, dir);
1445
    BLOCK_END("BeingRecv::processBeingChangeDirection")
1446
}
1447
1448
void BeingRecv::processBeingSpecialEffect(Net::MessageIn &msg)
1449
{
1450
    if ((effectManager == nullptr) || (actorManager == nullptr))
1451
        return;
1452
1453
    const BeingId id = msg.readBeingId("being id");
1454
    Being *const being = actorManager->findBeing(id);
1455
    if (being == nullptr)
1456
    {
1457
        msg.readInt32("effect type");
1458
        return;
1459
    }
1460
1461
    const int effectType = msg.readInt32("effect type");
1462
1463
    if (ParticleEngine::enabled)
1464
        effectManager->trigger(effectType, being, 0);
1465
1466
    // +++ need dehard code effectType == 3
1467
    if (effectType == 3 && being->getType() == ActorType::Player
1468
        && (socialWindow != nullptr))
1469
    {   // reset received damage
1470
        socialWindow->resetDamage(being->getName());
1471
    }
1472
}
1473
1474
void BeingRecv::processBeingHatEffects(Net::MessageIn &msg)
1475
{
1476
    // +++ add new type of permanent effects?
1477
    const int cnt = (msg.readInt16("len") - 9) / 2;
1478
    if (cnt > 0)
1479
    {
1480
        UNIMPLEMENTEDPACKET;
1481
    }
1482
    msg.readBeingId("being id");
1483
    msg.readUInt8("enable");
1484
    for (int f = 0; f < cnt; f ++)
1485
        msg.readInt16("hat effect");
1486
}
1487
1488
void BeingRecv::processBeingSpecialEffectNum(Net::MessageIn &msg)
1489
{
1490
    UNIMPLEMENTEDPACKET;
1491
    // +++ need somhow show this effects.
1492
    // type is not same with self/misc effect.
1493
    msg.readBeingId("account id");
1494
    msg.readInt32("effect type");
1495
    msg.readInt32("num");  // effect variable
1496
}
1497
1498
void BeingRecv::processBeingSoundEffect(Net::MessageIn &msg)
1499
{
1500
    UNIMPLEMENTEDPACKET;
1501
    // +++ need play this effect.
1502
    msg.readString(24, "sound effect name");
1503
    msg.readUInt8("type");
1504
    msg.readInt32("unused");
1505
    msg.readInt32("source being id");
1506
}
1507
1508
void BeingRecv::processSkillGroundNoDamage(Net::MessageIn &msg)
1509
{
1510
    UNIMPLEMENTEDPACKET;
1511
    msg.readInt16("skill id");
1512
    msg.readInt32("src id");
1513
    msg.readInt16("val");
1514
    msg.readInt16("x");
1515
    msg.readInt16("y");
1516
    msg.readInt32("tick");
1517
}
1518
1519
void BeingRecv::processSkillEntry(Net::MessageIn &msg)
1520
{
1521
    if (msg.getVersion() >= 20110718)
1522
        msg.readInt16("len");
1523
    const BeingId id = msg.readBeingId("skill unit id");
1524
    const BeingId creatorId = msg.readBeingId("creator accound id");
1525
    const int x = msg.readInt16("x");
1526
    const int y = msg.readInt16("y");
1527
    int job = 0;
1528
    if (msg.getVersion() >= 20121212)
1529
        job = msg.readInt32("job");
1530
    if (msg.getVersion() >= 20110718)
1531
        msg.readUInt8("radius");
1532
    msg.readUInt8("visible");
1533
    int level = 0;
1534
    if (msg.getVersion() >= 20130731)
1535
        level = msg.readUInt8("level");
1536
    Being *const dstBeing = createBeing2(msg,
1537
        id,
1538
        job,
1539
        BeingType::SKILL);
1540
    if (dstBeing == nullptr)
1541
        return;
1542
    dstBeing->setAction(BeingAction::STAND, 0);
1543
    dstBeing->setTileCoords(x, y);
1544
    dstBeing->setLevel(level);
1545
    dstBeing->setCreatorId(creatorId);
1546
}
1547
1548
void BeingRecv::processPlayerStatusChange(Net::MessageIn &msg)
1549
{
1550
    BLOCK_START("BeingRecv::processPlayerStop")
1551
    if (actorManager == nullptr)
1552
    {
1553
        BLOCK_END("BeingRecv::processPlayerStop")
1554
        return;
1555
    }
1556
1557
    // Change in players' flags
1558
    const BeingId id = msg.readBeingId("account id");
1559
    Being *const dstBeing = actorManager->findBeing(id);
1560
    if (dstBeing == nullptr)
1561
    {
1562
        msg.readInt16("opt1");
1563
        msg.readInt16("opt2");
1564
        if (msg.getVersion() >= 7)
1565
            msg.readInt32("option");
1566
        else
1567
            msg.readInt16("option");
1568
        msg.readUInt8("karma");
1569
        return;
1570
    }
1571
1572
    const uint32_t opt1 = msg.readInt16("opt1");
1573
    const uint32_t opt2 = msg.readInt16("opt2");
1574
    uint32_t option;
1575
    if (msg.getVersion() >= 7)
1576
        option = msg.readInt32("option");
1577
    else
1578
        option = msg.readInt16("option");
1579
    dstBeing->setKarma(msg.readUInt8("karma"));
1580
1581
    dstBeing->setStatusEffectOpitons(option,
1582
        opt1,
1583
        opt2);
1584
    BLOCK_END("BeingRecv::processPlayerStop")
1585
}
1586
1587
void BeingRecv::processPlayerStatusChange2(Net::MessageIn &msg)
1588
{
1589
    if (actorManager == nullptr)
1590
        return;
1591
1592
    // look like this function unused on server
1593
1594
    const BeingId id = msg.readBeingId("account id");
1595
    Being *const dstBeing = actorManager->findBeing(id);
1596
    if (dstBeing == nullptr)
1597
        return;
1598
1599
    const uint32_t option = msg.readInt32("option");
1600
    dstBeing->setLevel(msg.readInt32("level"));
1601
    msg.readInt32("showEFST");
1602
    dstBeing->setStatusEffectOpiton0(option);
1603
}
1604
1605
void BeingRecv::processBeingResurrect(Net::MessageIn &msg)
1606
{
1607
    BLOCK_START("BeingRecv::processBeingResurrect")
1608
    if (actorManager == nullptr ||
1609
        localPlayer == nullptr)
1610
    {
1611
        BLOCK_END("BeingRecv::processBeingResurrect")
1612
        return;
1613
    }
1614
1615
    // A being changed mortality status
1616
1617
    const BeingId id = msg.readBeingId("being id");
1618
    msg.readInt16("unused");
1619
    Being *const dstBeing = actorManager->findBeing(id);
1620
    if (dstBeing == nullptr)
1621
    {
1622
        DEBUGLOGSTR("insible player?");
1623
        BLOCK_END("BeingRecv::processBeingResurrect")
1624
        return;
1625
    }
1626
1627
    // If this is player's current target, clear it.
1628
    if (dstBeing == localPlayer->getTarget())
1629
        localPlayer->stopAttack(false);
1630
    if (dstBeing == localPlayer &&
1631
        deathNotice != nullptr)
1632
    {
1633
        deathNotice->scheduleDelete();
1634
        deathNotice = nullptr;
1635
    }
1636
1637
    dstBeing->setAction(BeingAction::STAND, 0);
1638
    BLOCK_END("BeingRecv::processBeingResurrect")
1639
}
1640
1641
void BeingRecv::processPlayerGuilPartyInfo(Net::MessageIn &msg)
1642
{
1643
    BLOCK_START("BeingRecv::processPlayerGuilPartyInfo")
1644
    if (actorManager == nullptr)
1645
    {
1646
        BLOCK_END("BeingRecv::processPlayerGuilPartyInfo")
1647
        return;
1648
    }
1649
1650
    const BeingId beingId = msg.readBeingId("being id");
1651
    const std::string name = msg.readString(24, "char name");
1652
    actorManager->updateNameId(name, beingId);
1653
    Being *const dstBeing = actorManager->findBeing(beingId);
1654
    if (dstBeing != nullptr)
1655
    {
1656
        if (beingId == localPlayer->getId())
1657
        {
1658
            localPlayer->pingResponse();
1659
        }
1660
        dstBeing->setName(name);
1661
        dstBeing->setPartyName(msg.readString(24, "party name"));
1662
        dstBeing->setGuildName(msg.readString(24, "guild name"));
1663
        dstBeing->setGuildPos(msg.readString(24, "guild pos"));
1664
        dstBeing->addToCache();
1665
    }
1666
    else
1667
    {
1668
        msg.readString(24, "party name");
1669
        msg.readString(24, "guild name");
1670
        msg.readString(24, "guild pos");
1671
    }
1672
    BLOCK_END("BeingRecv::processPlayerGuilPartyInfo")
1673
}
1674
1675
void BeingRecv::processBeingRemoveSkill(Net::MessageIn &msg)
1676
{
1677
    const BeingId id = msg.readBeingId("skill unit id");
1678
    if (actorManager == nullptr)
1679
        return;
1680
    Being *const dstBeing = actorManager->findBeing(id);
1681
    if (dstBeing == nullptr)
1682
        return;
1683
    actorManager->destroy(dstBeing);
1684
}
1685
1686
void BeingRecv::processBeingFakeName(Net::MessageIn &msg)
1687
{
1688
    uint16_t x, y;
1689
    uint8_t dir;
1690
    if (msg.getVersion() < 20071106)
1691
    {
1692
        msg.readBeingId("npc id");
1693
        msg.skip(8, "unused");
1694
        msg.readInt16("class?");  // 111
1695
        msg.skip(30, "unused");
1696
        msg.readCoordinates(x, y, dir, "position");
1697
        msg.readUInt8("sx");
1698
        msg.readUInt8("sy");
1699
        msg.skip(3, "unused");
1700
        return;
1701
    }
1702
    const BeingTypeT type = static_cast<BeingTypeT>(
1703
        msg.readUInt8("object type"));
1704
    const BeingId id = msg.readBeingId("npc id");
1705
    msg.skip(8, "unused");
1706
    const uint16_t job = msg.readInt16("class?");  // 111
1707
    msg.skip(30, "unused");
1708
    msg.readCoordinates(x, y, dir, "position");
1709
    msg.readUInt8("sx");
1710
    msg.readUInt8("sy");
1711
    msg.skip(3, "unused");
1712
1713
    Being *const dstBeing = createBeing2(msg, id, job, type);
1714
    if (dstBeing == nullptr)
1715
        return;
1716
    dstBeing->setSubtype(fromInt(job, BeingTypeId), 0);
1717
    dstBeing->setTileCoords(x, y);
1718
    dstBeing->setDirection(dir);
1719
}
1720
1721
void BeingRecv::processBeingStatUpdate1(Net::MessageIn &msg)
1722
{
1723
    const BeingId id = msg.readBeingId("account id");
1724
    const int type = msg.readInt16("type");
1725
    const int value = msg.readInt32("value");
1726
1727
    if (actorManager == nullptr)
1728
        return;
1729
    Being *const dstBeing = actorManager->findBeing(id);
1730
    if (dstBeing == nullptr)
1731
        return;
1732
1733
    if (type != Sp::MANNER)
1734
    {
1735
        UNIMPLEMENTEDPACKETFIELD(type);
1736
        return;
1737
    }
1738
    dstBeing->setManner(value);
1739
}
1740
1741
void BeingRecv::processBeingSelfEffect(Net::MessageIn &msg)
1742
{
1743
    BLOCK_START("BeingRecv::processBeingSelfEffect")
1744
    if ((effectManager == nullptr) || (actorManager == nullptr))
1745
    {
1746
        BLOCK_END("BeingRecv::processBeingSelfEffect")
1747
        return;
1748
    }
1749
1750
    const BeingId id = msg.readBeingId("being id");
1751
    Being *const being = actorManager->findBeing(id);
1752
    if (being == nullptr)
1753
    {
1754
        DEBUGLOGSTR("insible player?");
1755
        msg.readInt32("effect type");
1756
        BLOCK_END("BeingRecv::processBeingSelfEffect")
1757
        return;
1758
    }
1759
1760
    const int effectType = msg.readInt32("effect type");
1761
    if (ParticleEngine::enabled)
1762
        effectManager->trigger(effectType, being, 0);
1763
1764
    BLOCK_END("BeingRecv::processBeingSelfEffect")
1765
}
1766
1767
void BeingRecv::processMobInfo(Net::MessageIn &msg)
1768
{
1769
    if (actorManager == nullptr)
1770
        return;
1771
    const int len = msg.readInt16("len");
1772
    if (len < 12)
1773
        return;
1774
    Being *const dstBeing = actorManager->findBeing(
1775
        msg.readBeingId("monster id"));
1776
    const int attackRange = msg.readInt32("range");
1777
    if (dstBeing != nullptr)
1778
        dstBeing->setAttackRange(attackRange);
1779
}
1780
1781
void BeingRecv::processBeingAttrs(Net::MessageIn &msg)
1782
{
1783
    if (actorManager == nullptr)
1784
        return;
1785
    const int len = msg.readInt16("len");
1786
    if (len < 14)
1787
        return;
1788
1789
    Being *const dstBeing = actorManager->findBeing(
1790
        msg.readBeingId("player id"));
1791
    const int groupId = msg.readInt32("group id");
1792
    uint16_t mount = 0;
1793
    mount = msg.readInt16("mount");
1794
    int language = -1;
1795
    if (serverVersion >= 17 && len > 14)
1796
        language = msg.readInt16("language");
1797
    int clanId = 0;
1798
    if (len > 16)
1799
        clanId = msg.readInt32("clan id");
1800
    if (dstBeing != nullptr)
1801
    {
1802
        if (serverVersion <= 17 ||
1803
            dstBeing != localPlayer)
1804
        {
1805
            dstBeing->setGroupId(groupId);
1806
        }
1807
        dstBeing->setHorse(mount);
1808
        dstBeing->setLanguageId(language);
1809
        if (clanId != 0)
1810
        {
1811
            const ClanInfo *const info = ClanDb::get(clanId);
1812
            if (info == nullptr)
1813
                dstBeing->setClanName(std::string());
1814
            else
1815
                dstBeing->setClanName(info->name);
1816
        }
1817
        else
1818
        {
1819
            dstBeing->setClanName(std::string());
1820
        }
1821
        if (dstBeing == localPlayer)
1822
            PlayerInfo::setServerLanguage(language);
1823
    }
1824
}
1825
1826
void BeingRecv::processMonsterInfo(Net::MessageIn &msg)
1827
{
1828
    UNIMPLEMENTEDPACKET;
1829
1830
    msg.readInt16("class");
1831
    msg.readInt16("level");
1832
    msg.readInt16("size");
1833
    msg.readInt32("hp");
1834
    msg.readInt16("def");
1835
    msg.readInt16("race");
1836
    msg.readInt16("mdef");
1837
    msg.readInt16("ele");
1838
}
1839
1840
void BeingRecv::processClassChange(Net::MessageIn &msg)
1841
{
1842
    UNIMPLEMENTEDPACKET;
1843
1844
    msg.readBeingId("being id");
1845
    msg.readUInt8("type");
1846
    msg.readInt32("class");
1847
}
1848
1849
void BeingRecv::processSpiritBalls(Net::MessageIn &msg)
1850
{
1851
    if (actorManager == nullptr)
1852
        return;
1853
    Being *const dstBeing = actorManager->findBeing(
1854
        msg.readBeingId("being id"));
1855
    const int balls = msg.readInt16("spirits amount");
1856
    if (dstBeing != nullptr)
1857
        dstBeing->setSpiritBalls(balls);
1858
}
1859
1860
void BeingRecv::processBladeStop(Net::MessageIn &msg)
1861
{
1862
    UNIMPLEMENTEDPACKET;
1863
1864
    msg.readInt32("src being id");
1865
    msg.readInt32("dst being id");
1866
    msg.readInt32("flag");
1867
}
1868
1869
void BeingRecv::processComboDelay(Net::MessageIn &msg)
1870
{
1871
    UNIMPLEMENTEDPACKET;
1872
1873
    msg.readBeingId("being id");
1874
    msg.readInt32("wait");
1875
}
1876
1877
void BeingRecv::processWddingEffect(Net::MessageIn &msg)
1878
{
1879
    UNIMPLEMENTEDPACKET;
1880
1881
    msg.readBeingId("being id");
1882
}
1883
1884
void BeingRecv::processBeingSlide(Net::MessageIn &msg)
1885
{
1886
    if (actorManager == nullptr)
1887
        return;
1888
    Being *const dstBeing = actorManager->findBeing(
1889
        msg.readBeingId("being id"));
1890
    const int x = msg.readInt16("x");
1891
    const int y = msg.readInt16("y");
1892
    if (dstBeing == nullptr)
1893
        return;
1894
    if (localPlayer == dstBeing)
1895
    {
1896
        localPlayer->stopAttack(false);
1897
        localPlayer->navigateClean();
1898
        if (viewport != nullptr)
1899
            viewport->returnCamera();
1900
    }
1901
1902
    dstBeing->setAction(BeingAction::STAND, 0);
1903
    dstBeing->setTileCoords(x, y);
1904
}
1905
1906
void BeingRecv::processStarsKill(Net::MessageIn &msg)
1907
{
1908
    UNIMPLEMENTEDPACKET;
1909
1910
    msg.readString(24, "map name");
1911
    msg.readInt32("monster id");
1912
    msg.readUInt8("start");
1913
    msg.readUInt8("result");
1914
}
1915
1916
void BeingRecv::processGladiatorFeelRequest(Net::MessageIn &msg)
1917
{
1918
    UNIMPLEMENTEDPACKET;
1919
1920
    msg.readUInt8("which");
1921
}
1922
1923
void BeingRecv::processBossMapInfo(Net::MessageIn &msg)
1924
{
1925
    UNIMPLEMENTEDPACKET;
1926
1927
    msg.readUInt8("info type");
1928
    msg.readInt32("x");
1929
    msg.readInt32("y");
1930
    msg.readInt16("min hours");
1931
    msg.readInt16("min minutes");
1932
    msg.readInt16("max hours");
1933
    msg.readInt16("max minutes");
1934
    msg.readString(24, "monster name");  // really can be used 51 byte?
1935
}
1936
1937
void BeingRecv::processBeingFont(Net::MessageIn &msg)
1938
{
1939
    UNIMPLEMENTEDPACKET;
1940
1941
    msg.readBeingId("account id");
1942
    msg.readInt16("font");
1943
}
1944
1945
void BeingRecv::processBeingMilleniumShield(Net::MessageIn &msg)
1946
{
1947
    UNIMPLEMENTEDPACKET;
1948
1949
    msg.readBeingId("account id");
1950
    msg.readInt16("shields");
1951
    msg.readInt16("unused");
1952
}
1953
1954
void BeingRecv::processBeingCharm(Net::MessageIn &msg)
1955
{
1956
    UNIMPLEMENTEDPACKET;
1957
1958
    msg.readBeingId("account id");
1959
    msg.readInt16("charm type");
1960
    msg.readInt16("charm count");
1961
}
1962
1963
void BeingRecv::processBeingViewEquipment(Net::MessageIn &msg)
1964
{
1965
    UNIMPLEMENTEDPACKET;
1966
1967
    const int count = (msg.readInt16("len") - 45) / 31;
1968
    msg.readString(24, "name");
1969
    msg.readInt16("job");
1970
    msg.readInt16("head");
1971
    msg.readInt16("accessory");
1972
    msg.readInt16("accessory2");
1973
    msg.readInt16("accessory3");
1974
    if (msg.getVersion() >= 20101124)
1975
        msg.readInt16("robe");
1976
    msg.readInt16("hair color");
1977
    msg.readInt16("body color");
1978
    msg.readUInt8("gender");
1979
    for (int f = 0; f < count; f ++)
1980
    {
1981
        msg.readInt16("index");
1982
        msg.readInt16("item id");
1983
        msg.readUInt8("item type");
1984
        msg.readInt32("location");
1985
        msg.readInt32("wear state");
1986
        msg.readInt8("refine");
1987
        for (int d = 0; d < maxCards; d ++)
1988
            msg.readUInt16("card");
1989
        msg.readInt32("hire expire date (?)");
1990
        msg.readInt16("equip type");
1991
        msg.readInt16("item sprite number");
1992
        msg.readUInt8("flags");
1993
    }
1994
}
1995
1996
void BeingRecv::processPvpSet(Net::MessageIn &msg)
1997
{
1998
    BLOCK_START("BeingRecv::processPvpSet")
1999
    const BeingId id = msg.readBeingId("being id");
2000
    const int rank = msg.readInt32("rank");
2001
    msg.readInt32("num");
2002
    if (actorManager != nullptr)
2003
    {
2004
        Being *const dstBeing = actorManager->findBeing(id);
2005
        if (dstBeing != nullptr)
2006
            dstBeing->setPvpRank(rank);
2007
    }
2008
    BLOCK_END("BeingRecv::processPvpSet")
2009
}
2010
2011
void BeingRecv::processNameResponse2(Net::MessageIn &msg)
2012
{
2013
    BLOCK_START("BeingRecv::processNameResponse2")
2014
    if ((actorManager == nullptr) || (localPlayer == nullptr))
2015
    {
2016
        BLOCK_END("BeingRecv::processNameResponse2")
2017
        return;
2018
    }
2019
2020
    const int len = msg.readInt16("len");
2021
    const BeingId beingId = msg.readBeingId("account id");
2022
    const std::string str = msg.readString(len - 8, "name");
2023
    actorManager->updateNameId(str, beingId);
2024
    Being *const dstBeing = actorManager->findBeing(beingId);
2025
    if (dstBeing != nullptr)
2026
    {
2027
        if (beingId == localPlayer->getId())
2028
        {
2029
            localPlayer->pingResponse();
2030
        }
2031
        else
2032
        {
2033
            dstBeing->setName(str);
2034
            dstBeing->updateGuild();
2035
            dstBeing->addToCache();
2036
2037
            if (dstBeing->getType() == ActorType::Player)
2038
                dstBeing->updateColors();
2039
2040
            if (localPlayer != nullptr)
2041
            {
2042
                const Party *const party = localPlayer->getParty();
2043
                if (party != nullptr && party->isMember(dstBeing->getId()))
2044
                {
2045
                    PartyMember *const member = party->getMember(
2046
                        dstBeing->getId());
2047
2048
                    if (member != nullptr)
2049
                        member->setName(dstBeing->getName());
2050
                }
2051
                localPlayer->checkNewName(dstBeing);
2052
            }
2053
        }
2054
    }
2055
    BLOCK_END("BeingRecv::processNameResponse2")
2056
}
2057
2058
Being *BeingRecv::createBeing2(Net::MessageIn &msg,
2059
                               const BeingId id,
2060
                               const int32_t job,
2061
                               const BeingTypeT beingType)
2062
{
2063
    if (actorManager == nullptr)
2064
        return nullptr;
2065
2066
    ActorTypeT type = ActorType::Unknown;
2067
    switch (beingType)
2068
    {
2069
        case BeingType::PC:
2070
            type = ActorType::Player;
2071
            break;
2072
        case BeingType::NPC:
2073
        case BeingType::NPC_EVENT:
2074
            type = ActorType::Npc;
2075
            break;
2076
        case BeingType::MONSTER:
2077
            type = ActorType::Monster;
2078
            break;
2079
        case BeingType::MERSOL:
2080
            type = ActorType::Mercenary;
2081
            break;
2082
        case BeingType::PET:
2083
            type = ActorType::Pet;
2084
            break;
2085
        case BeingType::HOMUN:
2086
            type = ActorType::Homunculus;
2087
            break;
2088
        case BeingType::SKILL:
2089
            type = ActorType::SkillUnit;
2090
            break;
2091
        case BeingType::ELEMENTAL:
2092
            type = ActorType::Elemental;
2093
            break;
2094
        case BeingType::ITEM:
2095
            logger->log("not supported object type: %d, job: %d",
2096
                CAST_S32(beingType), CAST_S32(job));
2097
            break;
2098
        case BeingType::CHAT:
2099
        default:
2100
            UNIMPLEMENTEDPACKETFIELD(CAST_S32(beingType));
2101
            type = ActorType::Monster;
2102
            logger->log("not supported object type: %d, job: %d",
2103
                CAST_S32(beingType), CAST_S32(job));
2104
            break;
2105
    }
2106
    if (job == 45 && beingType == BeingType::NPC_EVENT)
2107
        type = ActorType::Portal;
2108
2109
    Being *const being = actorManager->createBeing(
2110
        id, type, fromInt(job, BeingTypeId));
2111
    if (beingType == BeingType::MERSOL)
2112
    {
2113
        const MercenaryInfo *const info = PlayerInfo::getMercenary();
2114
        if ((info != nullptr) && info->id == id)
2115
            PlayerInfo::setMercenaryBeing(being);
2116
    }
2117
    else if (beingType == BeingType::PET)
2118
    {
2119
        if (PlayerInfo::getPetBeingId() == id)
2120
            PlayerInfo::setPetBeing(being);
2121
    }
2122
    return being;
2123
}
2124
2125
void BeingRecv::processSkillCancel(Net::MessageIn &msg)
2126
{
2127
    msg.readInt32("id?");
2128
}
2129
2130
void BeingRecv::processSolveCharName(Net::MessageIn &msg)
2131
{
2132
    if ((packets_re == true && msg.getVersion() >= 20180221) ||
2133
        (packets_main == true && msg.getVersion() >= 20180307) ||
2134
        (packets_zero == true && msg.getVersion() >= 20180328))
2135
    {
2136
        const int flag = msg.readInt16("flag");
2137
        // name request errors
2138
        if (flag != 3)
2139
            return;
2140
    }
2141
    const int id = msg.readInt32("char id");
2142
    if (actorManager == nullptr)
2143
    {
2144
        msg.readString(24, "name");
2145
        return;
2146
    }
2147
    actorManager->addChar(id, msg.readString(24, "name"));
2148
}
2149
2150
void BeingRecv::processGraffiti(Net::MessageIn &msg)
2151
{
2152
    const BeingId id = msg.readBeingId("graffiti id");
2153
    const BeingId creatorId = msg.readBeingId("creator id");
2154
    const int x = msg.readInt16("x");
2155
    const int y = msg.readInt16("y");
2156
    const int job = msg.readUInt8("job");
2157
    msg.readUInt8("visible");
2158
    msg.readUInt8("is content");
2159
    const std::string text = msg.readString(80, "text");
2160
2161
    Being *const dstBeing = createBeing2(msg, id, job, BeingType::SKILL);
2162
    if (dstBeing == nullptr)
2163
        return;
2164
2165
    dstBeing->setAction(BeingAction::STAND, 0);
2166
    dstBeing->setTileCoords(x, y);
2167
    dstBeing->setShowName(true);
2168
    dstBeing->setName(text);
2169
    dstBeing->setCreatorId(creatorId);
2170
}
2171
2172
void BeingRecv::processSkillDamage(Net::MessageIn &msg)
2173
{
2174
    BLOCK_START("BeingRecv::processSkillDamage")
2175
    if (actorManager == nullptr)
2176
    {
2177
        BLOCK_END("BeingRecv::processSkillDamage")
2178
        return;
2179
    }
2180
2181
    const int id = msg.readInt16("skill id");
2182
    Being *const srcBeing = actorManager->findBeing(
2183
        msg.readBeingId("src being id"));
2184
    Being *const dstBeing = actorManager->findBeing(
2185
        msg.readBeingId("dst being id"));
2186
    msg.readInt32("tick");
2187
    msg.readInt32("src speed");
2188
    msg.readInt32("dst speed");
2189
    int param1;
2190
    if (msg.getVersion() >= 3)
2191
        param1 = msg.readInt32("damage");
2192
    else
2193
        param1 = msg.readInt16("damage");
2194
    const int level = msg.readInt16("skill level");
2195
    msg.readInt16("div");
2196
    msg.readUInt8("skill hit/type?");
2197
    if (srcBeing != nullptr)
2198
        srcBeing->handleSkill(dstBeing, param1, id, level);
2199
    if (dstBeing != nullptr)
2200
        dstBeing->takeDamage(srcBeing, param1, AttackType::SKILL, id, level);
2201
    BLOCK_END("BeingRecv::processSkillDamage")
2202
}
2203
2204
void BeingRecv::processNavigateTo(Net::MessageIn &msg)
2205
{
2206
    UNIMPLEMENTEDPACKET;
2207
    // 0 position
2208
    // 1 no position
2209
    // 3 monster
2210
    msg.readUInt8("navigate type");
2211
    msg.readUInt8("transportation flag");
2212
    msg.readUInt8("hide window");
2213
    msg.readString(16, "map name");
2214
    msg.readInt16("x");
2215
    msg.readInt16("y");
2216
    msg.readInt16("mob id");
2217
}
2218
2219
void BeingRecv::applyPlayerAction(Net::MessageIn &msg,
2220
                                  Being *const being,
2221
                                  const uint8_t type)
2222
{
2223
    if (being == nullptr)
2224
        return;
2225
    switch (type)
2226
    {
2227
        case 0:
2228
            being->setAction(BeingAction::STAND, 0);
2229
            localPlayer->imitateAction(being, BeingAction::STAND);
2230
            break;
2231
2232
        case 1:
2233
            if (being->getCurrentAction() != BeingAction::DEAD)
2234
            {
2235
                being->setAction(BeingAction::DEAD, 0);
2236
                being->recalcSpritesOrder();
2237
            }
2238
            break;
2239
2240
        case 2:
2241
            being->setAction(BeingAction::SIT, 0);
2242
            localPlayer->imitateAction(being, BeingAction::SIT);
2243
            break;
2244
2245
        default:
2246
            UNIMPLEMENTEDPACKETFIELD(type);
2247
            break;
2248
    }
2249
}
2250
2251
4
}  // namespace EAthena