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