GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/npchandler.cpp Lines: 1 173 0.6 %
Date: 2017-11-29 Branches: 0 196 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "net/eathena/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
50
namespace EAthena
51
{
52
53
NpcHandler::NpcHandler() :
54
    Ea::NpcHandler()
55
{
56
    npcHandler = this;
57
}
58
59
NpcHandler::~NpcHandler()
60
{
61
    npcHandler = nullptr;
62
}
63
64
void NpcHandler::talk(const Being *const being) const
65
{
66
    if (being == nullptr)
67
        return;
68
    createOutPacket(CMSG_NPC_TALK);
69
    outMsg.writeBeingId(being->getId(), "npc id");
70
    outMsg.writeInt8(0, "unused");
71
    EAthena::NpcRecv::mNpcTypeId = being->getSubType();
72
}
73
74
void NpcHandler::nextDialog(const BeingId npcId) const
75
{
76
    createOutPacket(CMSG_NPC_NEXT_REQUEST);
77
    outMsg.writeBeingId(npcId, "npc id");
78
}
79
80
void NpcHandler::closeDialog(const BeingId npcId)
81
{
82
    createOutPacket(CMSG_NPC_CLOSE);
83
    outMsg.writeBeingId(npcId, "npc id");
84
85
    const NpcDialogs::iterator it = NpcDialog::mNpcDialogs.find(npcId);
86
    if (it != NpcDialog::mNpcDialogs.end())
87
    {
88
        NpcDialog *const dialog = (*it).second;
89
        if (dialog != nullptr)
90
            dialog->close();
91
        if (dialog == Ea::NpcRecv::mDialog)
92
            Ea::NpcRecv::mDialog = nullptr;
93
        NpcDialog::mNpcDialogs.erase(it);
94
    }
95
}
96
97
void NpcHandler::listInput(const BeingId npcId,
98
                           const unsigned char value) const
99
{
100
    createOutPacket(CMSG_NPC_LIST_CHOICE);
101
    outMsg.writeBeingId(npcId, "npc id");
102
    outMsg.writeInt8(value, "value");
103
}
104
105
void NpcHandler::integerInput(const BeingId npcId,
106
                              const int value) const
107
{
108
    createOutPacket(CMSG_NPC_INT_RESPONSE);
109
    outMsg.writeBeingId(npcId, "npc id");
110
    outMsg.writeInt32(value, "value");
111
}
112
113
void NpcHandler::stringInput(const BeingId npcId,
114
                             const std::string &value) const
115
{
116
    createOutPacket(CMSG_NPC_STR_RESPONSE);
117
    if (packetVersion >= 20151029)
118
    {
119
        outMsg.writeInt16(CAST_S16(value.length() + 8), "len");
120
        outMsg.writeBeingId(npcId, "npc id");
121
        outMsg.writeString(value, CAST_S32(value.length()), "value");
122
    }
123
    else
124
    {
125
        outMsg.writeInt16(CAST_S16(value.length() + 9), "len");
126
        outMsg.writeBeingId(npcId, "npc id");
127
        outMsg.writeString(value, CAST_S32(value.length()), "value");
128
        outMsg.writeInt8(0, "null byte");
129
    }
130
}
131
132
void NpcHandler::buy(const Being *const being) const
133
{
134
    if (being == nullptr)
135
        return;
136
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
137
    outMsg.writeBeingId(being->getId(), "npc id");
138
    outMsg.writeInt8(0, "action");
139
    EAthena::NpcRecv::mNpcTypeId = being->getSubType();
140
}
141
142
void NpcHandler::buy(const BeingId beingId) const
143
{
144
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
145
    outMsg.writeBeingId(beingId, "npc id");
146
    outMsg.writeInt8(0, "action");
147
    EAthena::NpcRecv::mNpcTypeId = BeingTypeId_zero;
148
}
149
150
void NpcHandler::sell(const BeingId beingId) const
151
{
152
    createOutPacket(CMSG_NPC_BUY_SELL_REQUEST);
153
    outMsg.writeBeingId(beingId, "npc id");
154
    outMsg.writeInt8(1, "action");
155
}
156
157
void NpcHandler::buyItem(const BeingId beingId A_UNUSED,
158
                         const int itemId,
159
                         const ItemColor color A_UNUSED,
160
                         const int amount) const
161
{
162
    createOutPacket(CMSG_NPC_BUY_REQUEST);
163
    outMsg.writeInt16(8, "len");
164
    outMsg.writeInt16(CAST_S16(amount), "amount");
165
    outMsg.writeInt16(CAST_S16(itemId), "item id");
166
}
167
168
void NpcHandler::buyItems(STD_VECTOR<ShopItem*> &items) const
169
{
170
    int cnt = 0;
171
    const int pairSize = 4;
172
173
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
174
    {
175
        ShopItem *const item = *it;
176
        const int usedQuantity = item->getUsedQuantity();
177
        const ItemTypeT type = item->getType();
178
        if (usedQuantity == 0)
179
            continue;
180
        if (type == ItemType::Weapon ||
181
            type == ItemType::Armor ||
182
            type == ItemType::PetEgg ||
183
            type == ItemType::PetArmor)
184
        {
185
            cnt += item->getUsedQuantity();
186
        }
187
        else
188
        {
189
            cnt ++;
190
        }
191
    }
192
193
    if (cnt > 100)
194
        return;
195
196
    createOutPacket(CMSG_NPC_BUY_REQUEST);
197
    outMsg.writeInt16(CAST_S16(4 + pairSize * cnt), "len");
198
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
199
    {
200
        ShopItem *const item = *it;
201
        const int usedQuantity = item->getUsedQuantity();
202
        if (usedQuantity == 0)
203
            continue;
204
        item->increaseUsedQuantity(-usedQuantity);
205
        item->update();
206
        const ItemTypeT type = item->getType();
207
        if (type == ItemType::Weapon ||
208
            type == ItemType::Armor ||
209
            type == ItemType::PetEgg ||
210
            type == ItemType::PetArmor)
211
        {
212
            for (int f = 0; f < usedQuantity; f ++)
213
            {
214
                outMsg.writeInt16(CAST_S16(1), "amount");
215
                outMsg.writeInt16(CAST_S16(item->getId()),
216
                    "item id");
217
            }
218
        }
219
        else
220
        {
221
            outMsg.writeInt16(CAST_S16(usedQuantity), "amount");
222
            outMsg.writeInt16(CAST_S16(item->getId()), "item id");
223
        }
224
    }
225
}
226
227
void NpcHandler::sellItem(const BeingId beingId A_UNUSED,
228
                          const int itemId, const int amount) const
229
{
230
    createOutPacket(CMSG_NPC_SELL_REQUEST);
231
    outMsg.writeInt16(8, "len");
232
    outMsg.writeInt16(CAST_S16(itemId + INVENTORY_OFFSET),
233
        "item index");
234
    outMsg.writeInt16(CAST_S16(amount), "amount");
235
}
236
237
void NpcHandler::sellItems(STD_VECTOR<ShopItem*> &items) const
238
{
239
    const int pairSize = 4;
240
    int cnt = 0;
241
242
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
243
    {
244
        ShopItem *const item = *it;
245
        const int usedQuantity = item->getUsedQuantity();
246
        if (usedQuantity == 0)
247
            continue;
248
        cnt ++;
249
    }
250
251
    createOutPacket(CMSG_NPC_SELL_REQUEST);
252
    outMsg.writeInt16(CAST_S16(4 + pairSize * cnt), "len");
253
    FOR_EACH (STD_VECTOR<ShopItem*>::iterator, it, items)
254
    {
255
        ShopItem *const item = *it;
256
        const int usedQuantity = item->getUsedQuantity();
257
        if (usedQuantity == 0)
258
            continue;
259
        item->increaseUsedQuantity(-usedQuantity);
260
        item->update();
261
        outMsg.writeInt16(CAST_S16(
262
            item->getCurrentInvIndex() + INVENTORY_OFFSET),
263
            "item index");
264
        outMsg.writeInt16(CAST_S16(usedQuantity), "amount");
265
    }
266
}
267
268
void NpcHandler::completeProgressBar() const
269
{
270
    createOutPacket(CMSG_NPC_COMPLETE_PROGRESS_BAR);
271
}
272
273
void NpcHandler::produceMix(const int nameId,
274
                            const int materialId1,
275
                            const int materialId2,
276
                            const int materialId3) const
277
{
278
    createOutPacket(CMSG_NPC_PRODUCE_MIX);
279
    outMsg.writeInt16(CAST_S16(nameId), "name id");
280
    outMsg.writeInt16(CAST_S16(materialId1), "material 1");
281
    outMsg.writeInt16(CAST_S16(materialId2), "material 2");
282
    outMsg.writeInt16(CAST_S16(materialId3), "material 3");
283
}
284
285
void NpcHandler::cooking(const CookingTypeT type,
286
                         const int nameId) const
287
{
288
    createOutPacket(CMSG_NPC_COOKING);
289
    outMsg.writeInt16(CAST_S16(type), "type");
290
    outMsg.writeInt16(CAST_S16(nameId), "name id");
291
}
292
293
void NpcHandler::repair(const int index) const
294
{
295
    createOutPacket(CMSG_NPC_REPAIR);
296
    outMsg.writeInt16(CAST_S16(index), "index");
297
}
298
299
void NpcHandler::refine(const int index) const
300
{
301
    createOutPacket(CMSG_NPC_REFINE);
302
    outMsg.writeInt32(index, "index");
303
}
304
305
void NpcHandler::identify(const int index) const
306
{
307
    createOutPacket(CMSG_NPC_IDENTIFY);
308
    outMsg.writeInt16(CAST_S16(index), "index");
309
}
310
311
void NpcHandler::selectArrow(const int nameId) const
312
{
313
    createOutPacket(CMSG_NPC_SELECT_ARROW);
314
    outMsg.writeInt16(CAST_S16(nameId), "name id");
315
}
316
317
void NpcHandler::selectAutoSpell(const int skillId) const
318
{
319
    createOutPacket(CMSG_NPC_SELECT_AUTO_SPELL);
320
    outMsg.writeInt32(CAST_S16(skillId), "skill id");
321
}
322
323
BeingId NpcHandler::getNpc(Net::MessageIn &msg,
324
                           const NpcActionT action)
325
{
326
    const BeingId npcId = msg.readBeingId("npc id");
327
328
    const NpcDialogs::const_iterator diag = NpcDialog::mNpcDialogs.find(npcId);
329
    Ea::NpcRecv::mDialog = nullptr;
330
331
    if (diag == NpcDialog::mNpcDialogs.end())
332
    {
333
        // Empty dialogs don't help
334
        if (action == NpcAction::Close)
335
        {
336
            closeDialog(npcId);
337
            return npcId;
338
        }
339
        else if (action == NpcAction::Next)
340
        {
341
            nextDialog(npcId);
342
            return npcId;
343
        }
344
        else
345
        {
346
            CREATEWIDGETV(Ea::NpcRecv::mDialog, NpcDialog, npcId);
347
            Ea::NpcRecv::mDialog->saveCamera();
348
            if (localPlayer != nullptr)
349
                localPlayer->stopWalking(false);
350
            NpcDialog::mNpcDialogs[npcId] = Ea::NpcRecv::mDialog;
351
        }
352
    }
353
    else
354
    {
355
        NpcDialog *const dialog = diag->second;
356
        if (Ea::NpcRecv::mDialog != nullptr && Ea::NpcRecv::mDialog != dialog)
357
            Ea::NpcRecv::mDialog->restoreCamera();
358
        Ea::NpcRecv::mDialog = dialog;
359
        if (Ea::NpcRecv::mDialog != nullptr)
360
            Ea::NpcRecv::mDialog->saveCamera();
361
    }
362
    return npcId;
363
}
364
365
4
}  // namespace EAthena