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