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