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