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