GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/skillrecv.cpp Lines: 1 274 0.4 %
Date: 2021-03-17 Branches: 0 256 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
#include "net/eathena/skillrecv.h"
25
26
#include "notifymanager.h"
27
28
#include "being/localplayer.h"
29
#include "being/playerinfo.h"
30
31
#include "const/net/skill.h"
32
33
#include "enums/resources/notifytypes.h"
34
35
#include "gui/widgets/createwidget.h"
36
37
#include "gui/windows/skilldialog.h"
38
#include "gui/windows/textselectdialog.h"
39
40
#include "listeners/skillwarplistener.h"
41
42
#include "net/messagein.h"
43
44
#include "net/eathena/menu.h"
45
46
#include "resources/iteminfo.h"
47
48
#include "resources/db/itemdb.h"
49
50
#include "resources/skill/skillinfo.h"
51
52
#include "utils/gettext.h"
53
#include "utils/stringutils.h"
54
55
#include "debug.h"
56
57
static const unsigned int RFAIL                = 10;
58
static const unsigned int RFAIL_SUMMON         = 19;
59
static const unsigned int RFAIL_NEED_ITEM      = 71;
60
static const unsigned int RFAIL_NEED_EQUIPMENT = 72;
61
static const unsigned int RFAIL_SPIRITS        = 74;
62
63
extern int serverVersion;
64
extern int itemIdLen;
65
66
namespace EAthena
67
{
68
69
void SkillRecv::processPlayerSkills(Net::MessageIn &msg)
70
{
71
    msg.readInt16("len");
72
    const int sz = (serverVersion > 0) ? 41 : 37;
73
    const int skillCount = (msg.getLength() - 4) / sz;
74
    int updateSkill = 0;
75
76
    if (skillDialog != nullptr)
77
        skillDialog->hideSkills(SkillOwner::Player);
78
    for (int k = 0; k < skillCount; k++)
79
    {
80
        const int skillId = msg.readInt16("skill id");
81
        const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
82
            msg.readInt32("inf"));
83
        if (serverVersion > 0)
84
            msg.readInt32("inf2");
85
        const int level = msg.readInt16("skill level");
86
        const int sp = msg.readInt16("sp");
87
        const int range = msg.readInt16("range");
88
        const std::string name = msg.readString(24, "skill name");
89
        const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
90
        const int oldLevel = PlayerInfo::getSkillLevel(skillId);
91
        if ((oldLevel != 0) && oldLevel != level)
92
            updateSkill = skillId;
93
        PlayerInfo::setSkillLevel(skillId, level);
94
        if (skillDialog != nullptr)
95
        {
96
            if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
97
            {
98
                skillDialog->addSkill(SkillOwner::Player,
99
                    skillId, name, level, range, up, inf, sp);
100
            }
101
        }
102
    }
103
    if (skillDialog != nullptr)
104
    {
105
        skillDialog->update();
106
        skillDialog->updateModelsHidden();
107
        if (updateSkill != 0)
108
            skillDialog->playUpdateEffect(updateSkill);
109
    }
110
}
111
112
void SkillRecv::processSkillAdd(Net::MessageIn &msg)
113
{
114
    int updateSkill = 0;
115
    const int skillId = msg.readInt16("skill id");
116
    const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
117
        msg.readInt32("inf"));
118
    const int level = msg.readInt16("skill level");
119
    const int sp = msg.readInt16("sp");
120
    const int range = msg.readInt16("range");
121
    const std::string name = msg.readString(24, "skill name");
122
    const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
123
    const int oldLevel = PlayerInfo::getSkillLevel(skillId);
124
    if ((oldLevel != 0) && oldLevel != level)
125
        updateSkill = skillId;
126
    PlayerInfo::setSkillLevel(skillId, level);
127
    if (skillDialog != nullptr)
128
    {
129
        if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
130
        {
131
            skillDialog->addSkill(SkillOwner::Player,
132
                skillId, name, level, range, up, inf, sp);
133
        }
134
        skillDialog->update();
135
        if (updateSkill != 0)
136
            skillDialog->playUpdateEffect(updateSkill);
137
    }
138
}
139
140
void SkillRecv::processSkillAdd2(Net::MessageIn &msg)
141
{
142
    int updateSkill = 0;
143
    msg.readInt16("len");  // for now unused
144
    const int skillId = msg.readInt16("skill id");
145
    const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
146
        msg.readInt32("inf"));
147
    msg.readInt32("inf2");
148
    const int level = msg.readInt16("skill level");
149
    const int sp = msg.readInt16("sp");
150
    const int range = msg.readInt16("range");
151
    const std::string name = msg.readString(24, "skill name");
152
    const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
153
    const int oldLevel = PlayerInfo::getSkillLevel(skillId);
154
    if ((oldLevel != 0) && oldLevel != level)
155
        updateSkill = skillId;
156
    PlayerInfo::setSkillLevel(skillId, level);
157
    if (skillDialog != nullptr)
158
    {
159
        if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
160
        {
161
            skillDialog->addSkill(SkillOwner::Player,
162
                skillId, name, level, range, up, inf, sp);
163
        }
164
        skillDialog->update();
165
        if (updateSkill != 0)
166
            skillDialog->playUpdateEffect(updateSkill);
167
    }
168
}
169
170
void SkillRecv::processSkillUpdate(Net::MessageIn &msg)
171
{
172
    int updateSkill = 0;
173
    const int skillId = msg.readInt16("skill id");
174
    const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
175
        msg.readInt32("inf"));
176
    const int level = msg.readInt16("skill level");
177
    const int sp = msg.readInt16("sp");
178
    const int range = msg.readInt16("range");
179
    const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
180
    const int oldLevel = PlayerInfo::getSkillLevel(skillId);
181
    if ((oldLevel != 0) && oldLevel != level)
182
        updateSkill = skillId;
183
    PlayerInfo::setSkillLevel(skillId, level);
184
    if (skillDialog != nullptr)
185
    {
186
        if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
187
        {
188
            skillDialog->addSkill(SkillOwner::Player,
189
                skillId, "", level, range, up, inf, sp);
190
        }
191
        skillDialog->update();
192
        if (updateSkill != 0)
193
            skillDialog->playUpdateEffect(updateSkill);
194
    }
195
}
196
197
void SkillRecv::processSkillUpdate2(Net::MessageIn &msg)
198
{
199
    int updateSkill = 0;
200
    msg.readInt16("len");  // for now unused
201
    const int skillId = msg.readInt16("skill id");
202
    const SkillType::SkillType inf = static_cast<SkillType::SkillType>(
203
        msg.readInt32("inf"));
204
    msg.readInt32("inf2");
205
    const int level = msg.readInt16("skill level");
206
    const int sp = msg.readInt16("sp");
207
    const int range = msg.readInt16("range");
208
    const Modifiable up = fromBool(msg.readUInt8("up flag"), Modifiable);
209
    const int oldLevel = PlayerInfo::getSkillLevel(skillId);
210
    if ((oldLevel != 0) && oldLevel != level)
211
        updateSkill = skillId;
212
    PlayerInfo::setSkillLevel(skillId, level);
213
    if (skillDialog != nullptr)
214
    {
215
        if (!skillDialog->updateSkill(skillId, range, up, inf, sp))
216
        {
217
            skillDialog->addSkill(SkillOwner::Player,
218
                skillId, "", level, range, up, inf, sp);
219
        }
220
        skillDialog->update();
221
        if (updateSkill != 0)
222
            skillDialog->playUpdateEffect(updateSkill);
223
    }
224
}
225
226
void SkillRecv::processSkillDelete(Net::MessageIn &msg)
227
{
228
    int updateSkill = 0;
229
    const int skillId = msg.readInt16("skill id");
230
    const int oldLevel = PlayerInfo::getSkillLevel(skillId);
231
    if (oldLevel != 0)
232
        updateSkill = skillId;
233
    PlayerInfo::setSkillLevel(skillId, 0);
234
    if (skillDialog != nullptr)
235
    {
236
        skillDialog->removeSkill(skillId);
237
        skillDialog->update();
238
        if (updateSkill != 0)
239
            skillDialog->playRemoveEffect(updateSkill);
240
    }
241
}
242
243
void SkillRecv::processSkillCoolDown(Net::MessageIn &msg)
244
{
245
    const int skillId = msg.readInt16("skill id");
246
    const int duration = msg.readInt32("duration");
247
    if (skillDialog != nullptr)
248
        skillDialog->setSkillDuration(SkillOwner::Player, skillId, duration);
249
}
250
251
void SkillRecv::processSkillCoolDownList(Net::MessageIn &msg)
252
{
253
    int packetLen;
254
    if (msg.getVersion() >= 20120604)
255
        packetLen = 10;
256
    else
257
        packetLen = 6;
258
    const int count = (msg.readInt16("len") - 4) / packetLen;
259
    for (int f = 0; f < count; f ++)
260
    {
261
        const int skillId = msg.readInt16("skill id");
262
        if (msg.getVersion() >= 20120604)
263
            msg.readInt32("total");
264
        const int duration = msg.readInt32("duration");
265
        if (skillDialog != nullptr)
266
        {
267
            skillDialog->setSkillDuration(SkillOwner::Player,
268
                skillId, duration);
269
        }
270
    }
271
}
272
273
void SkillRecv::processSkillFailed(Net::MessageIn &msg)
274
{
275
    // Action failed (ex. sit because you have not reached the
276
    // right level)
277
    const int skillId = msg.readInt16("skill id");
278
    const int bskill  = msg.readItemId("btype");
279
    const int itemId  = msg.readItemId("item id");
280
    const signed char success = msg.readUInt8("success");
281
    const signed char reason  = msg.readUInt8("reason");
282
    if (success != CAST_S32(SKILL_FAILED)
283
        && bskill == CAST_S32(BSKILL_EMOTE))
284
    {
285
        logger->log("Action: %d/%d", bskill, success);
286
    }
287
288
    if (localPlayer != nullptr)
289
        localPlayer->stopCast(true);
290
    std::string txt;
291
    if (success == CAST_S32(SKILL_FAILED) && bskill != 0)
292
    {
293
        if ((localPlayer != nullptr) && bskill == CAST_S32(BSKILL_EMOTE)
294
            && reason == CAST_S32(RFAIL_SKILLDEP))
295
        {
296
            localPlayer->stopAdvert();
297
        }
298
299
        const SkillInfo *const info = skillDialog->getSkill(bskill);
300
        if (info != nullptr)
301
        {
302
            txt = info->errorText;
303
        }
304
        else
305
        {
306
            // TRANSLATORS: skill error message
307
            txt = strprintf(_("Unknown skill error: %d"), bskill);
308
        }
309
    }
310
    else
311
    {
312
        const SkillInfo *const info = skillDialog->getSkill(skillId);
313
        if (info != nullptr)
314
        {
315
            txt = info->errorText + ".";
316
        }
317
        else
318
        {
319
            // TRANSLATORS: skill error message
320
            txt = strprintf(_("Unknown skill error: %d."), skillId);
321
        }
322
    }
323
324
    txt.append(" ");
325
    switch (reason)
326
    {
327
        case RFAIL_SKILLDEP:
328
            // TRANSLATORS: error message
329
            txt.append(_("You have not yet reached a high enough lvl!"));
330
            break;
331
        case RFAIL_INSUFHP:
332
            // TRANSLATORS: error message
333
            txt.append(_("Insufficient HP!"));
334
            break;
335
        case RFAIL_INSUFSP:
336
            // TRANSLATORS: error message
337
            txt.append(_("Insufficient SP!"));
338
            break;
339
        case RFAIL_NOMEMO:
340
            // TRANSLATORS: error message
341
            txt.append(_("You have no memos!"));
342
            break;
343
        case RFAIL_SKILLDELAY:
344
            // TRANSLATORS: error message
345
            txt.append(_("You cannot do that right now!"));
346
            break;
347
        case RFAIL_ZENY:
348
            // TRANSLATORS: error message
349
            txt.append(_("Seems you need more money... ;-)"));
350
            break;
351
        case RFAIL_WEAPON:
352
            // TRANSLATORS: error message
353
            txt.append(_("You cannot use this skill with that "
354
                "kind of weapon!"));
355
            break;
356
        case RFAIL_REDGEM:
357
            // TRANSLATORS: error message
358
            txt.append(_("You need another red gem!"));
359
            break;
360
        case RFAIL_BLUEGEM:
361
            // TRANSLATORS: error message
362
            txt.append(_("You need another blue gem!"));
363
            break;
364
        case RFAIL_OVERWEIGHT:
365
            // TRANSLATORS: error message
366
            txt.append(_("You're carrying to much to do this!"));
367
            break;
368
        case RFAIL_SUMMON:
369
            // TRANSLATORS: error message
370
            txt.append(_("Fail summon."));
371
            break;
372
        case RFAIL_SPIRITS:
373
            // TRANSLATORS: error message
374
            txt.append(_("Need spirits."));
375
            break;
376
        case RFAIL_NEED_EQUIPMENT:
377
        {
378
            const int amount = bskill;
379
            const ItemInfo &info = ItemDB::get(itemId);
380
            if (amount == 1)
381
            {
382
                // TRANSLATORS: skill error message
383
                txt.append(strprintf(_("Need equipment %s."),
384
                    info.getLink().c_str()));
385
            }
386
            else
387
            {
388
                // TRANSLATORS: skill error message
389
                txt.append(strprintf(_("Need equipment %s and amount %d"),
390
                    info.getLink().c_str(),
391
                    amount));
392
            }
393
            break;
394
        }
395
        case RFAIL_NEED_ITEM:
396
        {
397
            const int amount = bskill;
398
            const ItemInfo &info = ItemDB::get(itemId);
399
            if (amount == 1)
400
            {
401
                // TRANSLATORS: skill error message
402
                txt.append(strprintf(_("Need item %s."),
403
                    info.getLink().c_str()));
404
            }
405
            else
406
            {
407
                // TRANSLATORS: skill error message
408
                txt.append(strprintf(_("Need item %s and amount %d"),
409
                    info.getLink().c_str(),
410
                    amount));
411
            }
412
            break;
413
        }
414
        case RFAIL:
415
        {
416
            // TRANSLATORS: error message
417
            txt.append(_("Skill failed!"));
418
            break;
419
        }
420
421
        default:
422
            UNIMPLEMENTEDPACKETFIELD(reason);
423
            break;
424
    }
425
426
    NotifyManager::notify(NotifyTypes::SKILL_FAIL_MESSAGE, txt);
427
}
428
429
void SkillRecv::processSkillWarpPoint(Net::MessageIn &msg)
430
{
431
    const int skillId = msg.readInt16("skill id");
432
433
    TextSelectDialog *const dialog = CREATEWIDGETR(TextSelectDialog,
434
        // TRANSLATORS: warp select window name
435
        _("Select warp target"),
436
        // TRANSLATORS: warp select button
437
        _("Warp"),
438
        AllowQuit_false);
439
    skillWarpListener.setDialog(dialog);
440
    skillWarpListener.setSkill(skillId);
441
    dialog->addActionListener(&skillWarpListener);
442
    for (int f = 0; f < 4; f ++)
443
        dialog->addText(msg.readString(16, "map name"));
444
}
445
446
void SkillRecv::processSkillWarpPoint2(Net::MessageIn &msg)
447
{
448
    const int count = (msg.readInt16("len") - 6) / 16;
449
    const int skillId = msg.readInt16("skill id");
450
451
    TextSelectDialog *const dialog = CREATEWIDGETR(TextSelectDialog,
452
        // TRANSLATORS: warp select window name
453
        _("Select warp target"),
454
        // TRANSLATORS: warp select button
455
        _("Warp"),
456
        AllowQuit_false);
457
    skillWarpListener.setDialog(dialog);
458
    skillWarpListener.setSkill(skillId);
459
    dialog->addActionListener(&skillWarpListener);
460
    for (int f = 0; f < count; f ++)
461
        dialog->addText(msg.readString(16, "map name"));
462
}
463
464
void SkillRecv::processSkillMemoMessage(Net::MessageIn &msg)
465
{
466
    const int type = msg.readUInt8("type");
467
    switch (type)
468
    {
469
        case 0:
470
            NotifyManager::notify(NotifyTypes::SKILL_MEMO_SAVED);
471
            break;
472
        case 1:
473
            NotifyManager::notify(NotifyTypes::SKILL_MEMO_ERROR_LEVEL);
474
            break;
475
        case 2:
476
            NotifyManager::notify(NotifyTypes::SKILL_MEMO_ERROR_SKILL);
477
            break;
478
        default:
479
            UNIMPLEMENTEDPACKETFIELD(type);
480
            break;
481
    }
482
}
483
484
void SkillRecv::processSkillProduceMixList(Net::MessageIn &msg)
485
{
486
    UNIMPLEMENTEDPACKET;
487
488
    const int count = (msg.readInt16("len") - 4) / 4 * itemIdLen;
489
    for (int f = 0; f < count; f ++)
490
    {
491
        msg.readItemId("item id");
492
        for (int d = 0; d < 3; d ++)
493
            msg.readItemId("material id");
494
    }
495
}
496
497
void SkillRecv::processSkillProduceEffect(Net::MessageIn &msg)
498
{
499
    UNIMPLEMENTEDPACKET;
500
501
    msg.readInt16("flag");
502
    msg.readItemId("item id");
503
}
504
505
void SkillRecv::processSkillUnitUpdate(Net::MessageIn &msg)
506
{
507
    UNIMPLEMENTEDPACKET;
508
509
    msg.readBeingId("being id");
510
}
511
512
void SkillRecv::processSkillArrowCreateList(Net::MessageIn &msg)
513
{
514
    UNIMPLEMENTEDPACKET;
515
516
    const int count = (msg.readInt16("len") - 4) / itemIdLen;
517
    for (int f = 0; f < count; f ++)
518
        msg.readItemId("item id");
519
}
520
521
void SkillRecv::processSkillAutoSpells1(Net::MessageIn &msg)
522
{
523
    UNIMPLEMENTEDPACKET;
524
525
    for (int f = 0; f < 7; f ++)
526
        msg.readInt32("skill id");
527
528
    menu = MenuType::AutoSpell;
529
}
530
531
void SkillRecv::processSkillAutoSpells2(Net::MessageIn &msg)
532
{
533
    UNIMPLEMENTEDPACKET;
534
535
    const int count = (msg.readInt16("len") - 4) / 4;
536
    for (int f = 0; f < count; f ++)
537
        msg.readInt32("skill id");
538
539
    menu = MenuType::AutoSpell;
540
}
541
542
void SkillRecv::processSkillDevotionEffect(Net::MessageIn &msg)
543
{
544
    UNIMPLEMENTEDPACKET;
545
546
    msg.readBeingId("being id");
547
    for (int f = 0; f < 5; f ++)
548
        msg.readInt32("devotee id");
549
    msg.readInt16("range");
550
}
551
552
void SkillRecv::processSkillItemListWindow(Net::MessageIn &msg)
553
{
554
    UNIMPLEMENTEDPACKET;
555
556
    msg.readInt32("skill level");
557
    msg.readInt32("unused");
558
}
559
560
void SkillRecv::processSkillScale(Net::MessageIn &msg)
561
{
562
    msg.readBeingId("being id");
563
    msg.readInt16("skill id");
564
    msg.readInt16("skill level");
565
    msg.readInt16("x");
566
    msg.readInt16("y");
567
    msg.readInt32("cast time");
568
}
569
570
2
}  // namespace EAthena