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