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