GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/npchandler.cpp Lines: 1 186 0.5 %
Date: 2019-02-12 Branches: 0 220 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/npchandler.h"
24
25
#include "being/localplayer.h"
26
27
#include "const/net/inventory.h"
28
29
#include "gui/windows/npcdialog.h"
30
31
#include "gui/widgets/createwidget.h"
32
33
#include "net/messagein.h"
34
35
#include "net/ea/npcrecv.h"
36
37
#include "net/eathena/messageout.h"
38
#include "net/eathena/npcrecv.h"
39
40
#include "net/eathena/protocolout.h"
41
42
#include "utils/foreach.h"
43
44
#include "resources/item/shopitem.h"
45
46
#include "debug.h"
47
48
extern int packetVersion;
49
extern int packetVersionMain;
50
extern int packetVersionRe;
51
extern bool packets_zero;
52
extern int itemIdLen;
53
54
namespace EAthena
55
{
56
57
NpcHandler::NpcHandler() :
58
    Ea::NpcHandler()
59
{
60
    npcHandler = this;
61
}
62
63
NpcHandler::~NpcHandler()
64
{
65
    npcHandler = nullptr;
66
}
67
68
void NpcHandler::talk(const Being *const being) const
69
{
70
    if (being == nullptr)
71
        return;
72
    createOutPacket(CMSG_NPC_TALK);
73
    outMsg.writeBeingId(being->getId(), "npc id");
74
    outMsg.writeInt8(0, "unused");
75
    EAthena::NpcRecv::mNpcTypeId = being->getSubType();
76
}
77
78
void NpcHandler::nextDialog(const BeingId npcId) const
79
{
80
    createOutPacket(CMSG_NPC_NEXT_REQUEST);
81
    outMsg.writeBeingId(npcId, "npc id");
82
}
83
84
void NpcHandler::closeDialog(const BeingId npcId)
85
{
86
    createOutPacket(CMSG_NPC_CLOSE);
87
    outMsg.writeBeingId(npcId, "npc id");
88
89
    const NpcDialogs::iterator it = NpcDialog::mNpcDialogs.find(npcId);
90
    if (it != NpcDialog::mNpcDialogs.end())
91
    {
92
        NpcDialog *const dialog = (*it).second;
93
        if (dialog != nullptr)
94
            dialog->close();
95
        if (dialog == Ea::NpcRecv::mDialog)
96
            Ea::NpcRecv::mDialog = nullptr;
97
        NpcDialog::mNpcDialogs.erase(it);
98
    }
99
}
100
101
void NpcHandler::listInput(const BeingId npcId,
102
                           const unsigned char value) const
103
{
104
    createOutPacket(CMSG_NPC_LIST_CHOICE);
105
    outMsg.writeBeingId(npcId, "npc id");
106
    outMsg.writeInt8(value, "value");
107
}
108
109
void NpcHandler::integerInput(const BeingId npcId,
110
                              const int value) const
111
{
112
    createOutPacket(CMSG_NPC_INT_RESPONSE);
113
    outMsg.writeBeingId(npcId, "npc id");
114
    outMsg.writeInt32(value, "value");
115
}
116
117
void NpcHandler::stringInput(const BeingId npcId,
118
                             const std::string &value) const
119
{
120
    createOutPacket(CMSG_NPC_STR_RESPONSE);
121
    if (packetVersion >= 20151029)
122
    {
123
        outMsg.writeInt16(CAST_S16(value.length() + 8), "len");
124
        outMsg.writeBeingId(npcId, "npc id");
125
        outMsg.writeString(value, CAST_S32(value.length()), "value");
126
    }
127
    else
128
    {
129
        outMsg.writeInt16(CAST_S16(value.length() + 9), "len");
130
        outMsg.writeBeingId(npcId, "npc id");
131
        outMsg.writeString(value, CAST_S32(value.length()), "value");
132
        outMsg.writeInt8(0, "null byte");
133
    }
134
}
135
136
void NpcHandler::buy(const Being *const being) const
137
{
138
    if (being == nullptr)
139
        return;
140
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
141
    outMsg.writeBeingId(being->getId(), "npc id");
142
    outMsg.writeInt8(0, "action");
143
    EAthena::NpcRecv::mNpcTypeId = being->getSubType();
144
}
145
146
void NpcHandler::buy(const BeingId beingId) const
147
{
148
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
149
    outMsg.writeBeingId(beingId, "npc id");
150
    outMsg.writeInt8(0, "action");
151
    EAthena::NpcRecv::mNpcTypeId = BeingTypeId_zero;
152
}
153
154
void NpcHandler::sell(const BeingId beingId) const
155
{
156
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
157
    outMsg.writeBeingId(beingId, "npc id");
158
    outMsg.writeInt8(1, "action");
159
}
160
161
void NpcHandler::buyItem(const BeingId beingId A_UNUSED,
162
                         const int itemId,
163
                         const ItemColor color A_UNUSED,
164
                         const int amount) const
165
{
166
    createOutPacket(CMSG_NPC_BUY_REQUEST);
167
    outMsg.writeInt16(8, "len");
168
    outMsg.writeInt16(CAST_S16(amount), "amount");
169
    outMsg.writeItemId(itemId, "item id");
170
}
171
172
void NpcHandler::buyItems(STD_VECTOR<ShopItem*> &items) const
173
{
174
    int cnt = 0;
175
    const int pairSize = 2 + itemIdLen;
176
177
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
178
    {
179
        ShopItem *const item = *it;
180
        const int usedQuantity = item->getUsedQuantity();
181
        const ItemTypeT type = item->getType();
182
        if (usedQuantity == 0)
183
            continue;
184
        if (type == ItemType::Weapon ||
185
            type == ItemType::Armor ||
186
            type == ItemType::PetEgg ||
187
            type == ItemType::PetArmor)
188
        {
189
            cnt += item->getUsedQuantity();
190
        }
191
        else
192
        {
193
            cnt ++;
194
        }
195
    }
196
197
    if (cnt > 100)
198
        return;
199
200
    createOutPacket(CMSG_NPC_BUY_REQUEST);
201
    outMsg.writeInt16(CAST_S16(4 + pairSize * cnt), "len");
202
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
203
    {
204
        ShopItem *const item = *it;
205
        const int usedQuantity = item->getUsedQuantity();
206
        if (usedQuantity == 0)
207
            continue;
208
        item->increaseUsedQuantity(-usedQuantity);
209
        item->update();
210
        const ItemTypeT type = item->getType();
211
        if (type == ItemType::Weapon ||
212
            type == ItemType::Armor ||
213
            type == ItemType::PetEgg ||
214
            type == ItemType::PetArmor)
215
        {
216
            for (int f = 0; f < usedQuantity; f ++)
217
            {
218
                outMsg.writeInt16(CAST_S16(1), "amount");
219
                outMsg.writeItemId(item->getId(),
220
                    "item id");
221
            }
222
        }
223
        else
224
        {
225
            outMsg.writeInt16(CAST_S16(usedQuantity), "amount");
226
            outMsg.writeItemId(item->getId(), "item id");
227
        }
228
    }
229
}
230
231
void NpcHandler::sellItem(const BeingId beingId A_UNUSED,
232
                          const int itemId, const int amount) const
233
{
234
    createOutPacket(CMSG_NPC_SELL_REQUEST);
235
    outMsg.writeInt16(8, "len");
236
    outMsg.writeInt16(CAST_S16(itemId + INVENTORY_OFFSET),
237
        "item index");
238
    outMsg.writeInt16(CAST_S16(amount), "amount");
239
}
240
241
void NpcHandler::sellItems(STD_VECTOR<ShopItem*> &items) const
242
{
243
    const int pairSize = 4;
244
    int cnt = 0;
245
246
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
247
    {
248
        ShopItem *const item = *it;
249
        const int usedQuantity = item->getUsedQuantity();
250
        if (usedQuantity == 0)
251
            continue;
252
        cnt ++;
253
    }
254
255
    createOutPacket(CMSG_NPC_SELL_REQUEST);
256
    outMsg.writeInt16(CAST_S16(4 + pairSize * cnt), "len");
257
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
258
    {
259
        ShopItem *const item = *it;
260
        const int usedQuantity = item->getUsedQuantity();
261
        if (usedQuantity == 0)
262
            continue;
263
        item->increaseUsedQuantity(-usedQuantity);
264
        item->update();
265
        outMsg.writeInt16(CAST_S16(
266
            item->getCurrentInvIndex() + INVENTORY_OFFSET),
267
            "item index");
268
        outMsg.writeInt16(CAST_S16(usedQuantity), "amount");
269
    }
270
}
271
272
void NpcHandler::completeProgressBar() const
273
{
274
    createOutPacket(CMSG_NPC_COMPLETE_PROGRESS_BAR);
275
}
276
277
void NpcHandler::produceMix(const int nameId,
278
                            const int materialId1,
279
                            const int materialId2,
280
                            const int materialId3) const
281
{
282
    createOutPacket(CMSG_NPC_PRODUCE_MIX);
283
    outMsg.writeItemId(nameId, "item id");
284
    outMsg.writeItemId(materialId1, "material 1");
285
    outMsg.writeItemId(materialId2, "material 2");
286
    outMsg.writeItemId(materialId3, "material 3");
287
}
288
289
void NpcHandler::cooking(const CookingTypeT type,
290
                         const int nameId) const
291
{
292
    createOutPacket(CMSG_NPC_COOKING);
293
    outMsg.writeInt16(CAST_S16(type), "type");
294
    outMsg.writeItemId(nameId, "item id");
295
}
296
297
void NpcHandler::repair(const int index) const
298
{
299
    createOutPacket(CMSG_NPC_REPAIR);
300
    outMsg.writeInt16(CAST_S16(index), "index");
301
    // unused fields.
302
    outMsg.writeItemId(0, "item id");
303
    outMsg.writeInt8(0, "refine");
304
    for (int f = 0; f < maxCards; f ++)
305
        outMsg.writeItemId(0, "card");
306
}
307
308
void NpcHandler::refine(const int index) const
309
{
310
    createOutPacket(CMSG_NPC_REFINE);
311
    outMsg.writeInt32(index, "index");
312
}
313
314
void NpcHandler::identify(const int index) const
315
{
316
    createOutPacket(CMSG_NPC_IDENTIFY);
317
    outMsg.writeInt16(CAST_S16(index), "index");
318
}
319
320
void NpcHandler::selectArrow(const int nameId) const
321
{
322
    createOutPacket(CMSG_NPC_SELECT_ARROW);
323
    outMsg.writeItemId(nameId, "item id");
324
}
325
326
void NpcHandler::selectAutoSpell(const int skillId) const
327
{
328
    createOutPacket(CMSG_NPC_SELECT_AUTO_SPELL);
329
    outMsg.writeInt32(CAST_S16(skillId), "skill id");
330
}
331
332
BeingId NpcHandler::getNpc(Net::MessageIn &msg,
333
                           const NpcActionT action)
334
{
335
    const BeingId npcId = msg.readBeingId("npc id");
336
337
    const NpcDialogs::const_iterator diag = NpcDialog::mNpcDialogs.find(npcId);
338
339
    if (diag == NpcDialog::mNpcDialogs.end())
340
    {
341
        Ea::NpcRecv::mDialog = nullptr;
342
        // Empty dialogs don't help
343
        if (action == NpcAction::Close)
344
        {
345
            closeDialog(npcId);
346
            return npcId;
347
        }
348
        else if (action == NpcAction::Next)
349
        {
350
            nextDialog(npcId);
351
            return npcId;
352
        }
353
        else
354
        {
355
            CREATEWIDGETV(Ea::NpcRecv::mDialog, NpcDialog, npcId);
356
            Ea::NpcRecv::mDialog->saveCamera();
357
            if (localPlayer != nullptr)
358
                localPlayer->stopWalking(false);
359
            NpcDialog::mNpcDialogs[npcId] = Ea::NpcRecv::mDialog;
360
        }
361
    }
362
    else
363
    {
364
        NpcDialog *const dialog = diag->second;
365
        if (Ea::NpcRecv::mDialog != nullptr && Ea::NpcRecv::mDialog != dialog)
366
            Ea::NpcRecv::mDialog->restoreCamera();
367
        Ea::NpcRecv::mDialog = dialog;
368
        if (Ea::NpcRecv::mDialog != nullptr)
369
            Ea::NpcRecv::mDialog->saveCamera();
370
    }
371
    return npcId;
372
}
373
374
void NpcHandler::requestAirship(const std::string &mapName,
375
                                const int itemId) const
376
{
377
    if (packetVersionRe < 20180321 &&
378
        packetVersionMain < 20180620 &&
379
        packets_zero == false)
380
    {
381
        return;
382
    }
383
    createOutPacket(CMSG_PRIVATE_AIRSHIP_REQUEST);
384
    outMsg.writeString(mapName, 16, "map name");
385
    outMsg.writeItemId(itemId, "item");
386
}
387
388
2
}  // namespace EAthena