Mana
Loading...
Searching...
No Matches
inventoryhandler.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include "equipment.h"
25#include "event.h"
26#include "game.h"
27#include "inventory.h"
28#include "item.h"
29#include "itemshortcut.h"
30#include "localplayer.h"
31#include "log.h"
32
33#include "net/tmwa/messagein.h"
34#include "net/tmwa/messageout.h"
35#include "net/tmwa/protocol.h"
36
37#include "resources/iteminfo.h"
38
39#include "utils/gettext.h"
40
42
43namespace TmwAthena {
44
45static const EquipmentSlot EQUIP_POINTS[EQUIP_VECTOR_END] = {
57};
58
59static int getSlot(int eAthenaSlot)
60{
61 if (eAthenaSlot == 0)
62 {
63 return EQUIP_VECTOR_END;
64 }
65
66 if (eAthenaSlot & 0x8000)
68
69 int mask = 1;
70 int position = 0;
71 while (!(eAthenaSlot & mask))
72 {
73 mask <<= 1;
74 position++;
75 }
76 return EQUIP_POINTS[position];
77}
78
79enum { debugInventory = 1 };
80
107
109{
110 if (mStorageWindow)
111 {
113 mStorageWindow = nullptr;
114 }
115
116 delete mStorage;
117}
118
120{
121 int number, flag;
122 int index, amount, itemId, equipType;
123 int identified, cards[4], itemType;
124 Inventory *inventory = PlayerInfo::getInventory();
125
126 switch (msg.getId())
127 {
130 if (msg.getId() == SMSG_PLAYER_INVENTORY)
131 {
132 // Clear inventory - this will be a complete refresh
133 mEquips.clear();
134 inventory->clear();
135 }
136 else
137 {
138 mInventoryItems.clear();
139 }
140
141 msg.readInt16(); // length
142 number = (msg.getLength() - 4) / 18;
143
144 for (int loop = 0; loop < number; loop++)
145 {
146 index = msg.readInt16();
147 itemId = msg.readInt16();
148 itemType = msg.readInt8();
149 identified = msg.readInt8();
150 amount = msg.readInt16();
151 msg.readInt16(); // Arrow
152 for (int &card : cards)
153 card = msg.readInt16();
154
155 index -= (msg.getId() == SMSG_PLAYER_INVENTORY) ?
156 INVENTORY_OFFSET : STORAGE_OFFSET;
157
158 if (debugInventory)
159 {
160 Log::info("Index: %d, ID: %d, Type: %d, Identified: %d, "
161 "Qty: %d, Cards: %d, %d, %d, %d",
162 index, itemId, itemType, identified, amount,
163 cards[0], cards[1], cards[2], cards[3]);
164 }
165
166 if (msg.getId() == SMSG_PLAYER_INVENTORY)
167 inventory->setItem(index, itemId, amount);
168 else
169 mInventoryItems.push_back(
170 InventoryItem { index, itemId, amount, false });
171 }
172 break;
173
175 msg.readInt16(); // length
176 number = (msg.getLength() - 4) / 20;
177
178 for (int loop = 0; loop < number; loop++)
179 {
180 index = msg.readInt16() - STORAGE_OFFSET;
181 itemId = msg.readInt16();
182 itemType = msg.readInt8();
183 identified = msg.readInt8();
184 amount = 1;
185 msg.readInt16(); // Equip Point?
186 msg.readInt16(); // Another Equip Point?
187 msg.readInt8(); // Attribute (broken)
188 msg.readInt8(); // Refine level
189 for (int &card : cards)
190 card = msg.readInt16();
191
192 if (debugInventory)
193 {
194 Log::info("Index: %d, ID: %d, Type: %d, Identified: %d, "
195 "Qty: %d, Cards: %d, %d, %d, %d",
196 index, itemId, itemType, identified, amount,
197 cards[0], cards[1], cards[2], cards[3]);
198 }
199
200 mInventoryItems.push_back(
201 InventoryItem { index, itemId, amount, false });
202 }
203 break;
204
206 index = msg.readInt16() - INVENTORY_OFFSET;
207 amount = msg.readInt16();
208 itemId = msg.readInt16();
209 identified = msg.readInt8();
210 msg.readInt8(); // attribute
211 msg.readInt8(); // refine
212 for (int &card : cards)
213 card = msg.readInt16();
214 msg.readInt16(); // EquipType
215 itemType = msg.readInt8();
216
217 {
218 const ItemInfo &itemInfo = itemDb->get(itemId);
219
220 unsigned char err = msg.readInt8();
221 if (err)
222 {
223 local_player->pickedUp(itemInfo, 0, err);
224 }
225 else
226 {
227 local_player->pickedUp(itemInfo, amount, PICKUP_OKAY);
228
229 Item *item = inventory->getItem(index);
230
231 if (item && item->getId() == itemId)
232 amount += inventory->getItem(index)->getQuantity();
233
234 inventory->setItem(index, itemId, amount);
235 }
236
238 } break;
239
241 index = msg.readInt16() - INVENTORY_OFFSET;
242 amount = msg.readInt16();
243 if (Item *item = inventory->getItem(index))
244 {
245 item->increaseQuantity(-amount);
246 if (item->getQuantity() == 0)
247 inventory->removeItemAt(index);
249 }
250 break;
251
253 index = msg.readInt16() - INVENTORY_OFFSET;
254 msg.readInt16(); // item id
255 msg.readInt32(); // id
256 amount = msg.readInt16();
257 msg.readInt8(); // type
258
259 if (Item *item = inventory->getItem(index))
260 {
261 if (amount)
262 item->setQuantity(amount);
263 else
264 inventory->removeItemAt(index);
265
267 }
268
269 break;
270
272 index = msg.readInt16() - INVENTORY_OFFSET;
273 amount = msg.readInt16();
274
275 if (msg.readInt8() == 0)
276 {
277 serverNotice(_("Failed to use item."));
278 }
279 else
280 {
281 if (Item *item = inventory->getItem(index))
282 {
283 if (amount)
284 item->setQuantity(amount);
285 else
286 inventory->removeItemAt(index);
287
289 }
290 }
291 break;
292
294 /*
295 * This is the closest we get to an "Open Storage" packet from the
296 * server. It always comes after the two SMSG_PLAYER_STORAGE_...
297 * packets that update storage contents.
298 */
299 {
300 msg.readInt16(); // Used count
301 int size = msg.readInt16(); // Max size
302
303 if (!mStorage)
305
306 for (auto &item : mInventoryItems)
307 mStorage->setItem(item.slot, item.id, item.quantity);
308 mInventoryItems.clear();
309
310 if (!mStorageWindow)
312 }
313 break;
314
316 // Move an item into storage
317 index = msg.readInt16() - STORAGE_OFFSET;
318 amount = msg.readInt32();
319 itemId = msg.readInt16();
320 identified = msg.readInt8();
321 msg.readInt8(); // attribute
322 msg.readInt8(); // refine
323 for (int &card : cards)
324 card = msg.readInt16();
325
326 if (Item *item = mStorage->getItem(index))
327 {
328 item->setId(itemId);
329 item->increaseQuantity(amount);
330 }
331 else
332 mStorage->setItem(index, itemId, amount);
333 break;
334
336 // Move an item out of storage
337 index = msg.readInt16() - STORAGE_OFFSET;
338 amount = msg.readInt16();
339 if (Item *item = mStorage->getItem(index))
340 {
341 item->increaseQuantity(-amount);
342 if (item->getQuantity() == 0)
343 mStorage->removeItemAt(index);
344 }
345 break;
346
348 // Storage access has been closed
349
350 // Storage window deletes itself
351 mStorageWindow = nullptr;
352
353 mStorage->clear();
354 delete mStorage;
355 mStorage = nullptr;
356 break;
357
359 msg.readInt16(); // length
360 number = (msg.getLength() - 4) / 20;
361
362 for (int loop = 0; loop < number; loop++)
363 {
364 index = msg.readInt16() - INVENTORY_OFFSET;
365 itemId = msg.readInt16();
366 msg.readInt8(); // type
367 msg.readInt8(); // identify flag
368 msg.readInt16(); // equip type
369 equipType = msg.readInt16();
370 msg.readInt8(); // attribute
371 msg.readInt8(); // refine
372 msg.skip(8); // card
373
374 inventory->setItem(index, itemId, 1);
375
376 if (equipType)
377 {
378 mEquips.setEquipment(getSlot(equipType), index);
379 }
380 }
381 break;
382
384 index = msg.readInt16() - INVENTORY_OFFSET;
385 equipType = msg.readInt16();
386 flag = msg.readInt8();
387
388 if (!flag)
389 serverNotice(_("Unable to equip."));
390 else
391 mEquips.setEquipment(getSlot(equipType), index);
392 break;
393
395 index = msg.readInt16() - INVENTORY_OFFSET;
396 equipType = msg.readInt16();
397 flag = msg.readInt8();
398
399 if (!flag)
400 {
401 serverNotice(_("Unable to unequip."));
402 }
403 else
404 {
405 mEquips.setEquipment(getSlot(equipType), -1);
406 // Reset the attack range to unarmed.
408 }
409 break;
410
412 {
413 // The range is in tiles, so we translate it back to pixels
414 Map *map = Game::instance()->getCurrentMap();
415 if (map)
416 {
418 * map->getTileWidth());
419 }
420 else
421 {
422 Log::info("Couldn't set attacke range due to the lack"
423 "of an initialized map.");
425 }
426 }
427 break;
428
430 index = msg.readInt16();
431
432 if (index <= 1)
433 break;
434
435 index -= INVENTORY_OFFSET;
436
437 Log::info("Arrows equipped: %i", index);
439 break;
440 }
441}
442
444 const Event &event)
445{
446 if (channel == Event::ItemChannel)
447 {
448 if (event.getType() == Event::DoCloseInventory)
449 {
450 // No need to worry about type
452 }
453 else
454 {
455 Item *item = event.getItem("item");
456
457 if (!item)
458 return;
459
460 int index = item->getInvIndex() + INVENTORY_OFFSET;
461
462 if (event.getType() == Event::DoEquip)
463 {
465 outMsg.writeInt16(index);
466 outMsg.writeInt16(0);
467 }
468 else if (event.getType() == Event::DoUnequip)
469 {
471 outMsg.writeInt16(index);
472 }
473 else if (event.getType() == Event::DoUse)
474 {
476 outMsg.writeInt16(index);
477 outMsg.writeInt32(item->getId()); // unused
478 }
479 else if (event.getType() == Event::DoDrop)
480 {
481 int amount = event.getInt("amount", 1);
482
483 // TODO: Fix wrong coordinates of drops, serverside?
484 // (what's wrong here?)
486 outMsg.writeInt16(index);
487 outMsg.writeInt16(amount);
488 }
489 else if (event.getType() == Event::DoMove)
490 {
491 int newIndex = event.getInt("newIndex", -1);
492
493 if (newIndex >= 0)
494 {
495 // Not implemented for tmwAthena (possible?)
496 }
497 else
498 {
499 int source = event.getInt("source");
500 int destination = event.getInt("destination");
501 int amount = event.getInt("amount", 1);
502
503 if (source == Inventory::INVENTORY
504 && destination == Inventory::STORAGE)
505 {
507 outMsg.writeInt16(index);
508 outMsg.writeInt32(amount);
509 }
510 else if (source == Inventory::STORAGE
511 && destination == Inventory::INVENTORY)
512 {
514 outMsg.writeInt16(index - INVENTORY_OFFSET
515 + STORAGE_OFFSET);
516 outMsg.writeInt32(amount);
517 }
518 }
519 }
520 }
521 }
522}
523
524size_t InventoryHandler::getSize(int type) const
525{
526 switch (type)
527 {
529 return 100;
531 return 0; // Comes from server after items
532 case Inventory::TRADE:
533 return 12;
534 case GUILD_STORAGE:
535 return 0; // Comes from server after items
536 default:
537 return 0;
538 }
539}
540
541} // namespace TmwAthena
void listen(Event::Channel channel)
Definition event.h:42
@ DoUse
Definition event.h:79
@ DoCloseInventory
Definition event.h:74
@ DoDrop
Definition event.h:75
@ DoMove
Definition event.h:77
@ DoEquip
Definition event.h:76
@ DoUnequip
Definition event.h:78
Channel
Definition event.h:45
@ ItemChannel
Definition event.h:53
Map * getCurrentMap()
Returns the currently active map.
Definition game.h:70
static Game * instance()
Provides access to the game instance.
Definition game.h:53
Inventory dialog.
void updateButtons()
Updates the buttons.
void close() override
Closes the Storage Window, as well as telling the server that the window has been closed.
void clear()
Reset all item slots.
Definition inventory.cpp:96
void setItem(int index, int id, int quantity)
Sets the item at the given position.
Definition inventory.cpp:66
Item * getItem(int index) const
Returns the item at the specified index.
Definition inventory.cpp:44
void removeItemAt(int index)
Remove the item at the specified index from the inventory.
const ItemInfo & get(int id) const
Definition itemdb.cpp:145
Defines a class for storing generic item infos.
Definition iteminfo.h:100
Represents one or more instances of a certain item type.
Definition item.h:35
int getQuantity() const
Returns the number of items.
Definition item.h:69
int getInvIndex() const
Returns the inventory index of this item.
Definition item.h:94
int getId() const
Returns the item id.
Definition item.h:49
void setAttackRange(int range)
Sets the attack range.
void pickedUp(const ItemInfo &itemInfo, int amount, unsigned char fail)
Shows item pickup notifications.
A tile map.
Definition map.h:147
int getTileWidth() const
Returns the tile width of this map.
Definition map.h:265
const uint16_t * handledMessages
void setEquipment(int index, int inventoryIndex)
size_t getSize(int type) const override
void event(Event::Channel channel, const Event &event) override
void handleMessage(MessageIn &msg) override
std::vector< InventoryItem > mInventoryItems
Used for parsing an incoming message from eAthena.
Definition messagein.h:35
void skip(unsigned int length)
Skips a given number of bytes.
uint16_t readInt16()
Reads an unsigned 16-bit integer from the message.
Definition messagein.cpp:57
uint8_t readInt8()
Reads an unsigned 8-bit integer from the message.
Definition messagein.cpp:46
uint16_t getId() const
Returns the message ID.
Definition messagein.h:42
uint32_t readInt32()
Reads an unsigned 32-bit integer from the message.
Definition messagein.cpp:69
unsigned int getLength() const
Returns the message length.
Definition messagein.h:47
Used for building an outgoing message to eAthena.
Definition messageout.h:35
void writeInt32(uint32_t value)
Writes an unsigned 32-bit integer to the message.
void writeInt16(uint16_t value)
Writes an unsigned 16-bit integer to the message.
ItemDB * itemDb
Items info database.
Definition client.cpp:106
void serverNotice(const std::string &message)
Definition event.h:319
InventoryWindow * inventoryWindow
Definition game.cpp:96
#define _(s)
Definition gettext.h:38
LocalPlayer * local_player
@ PICKUP_OKAY
Definition localplayer.h:62
Net::InventoryHandler * inventoryHandler
Definition net.cpp:50
void info(const char *log_text,...) LOG_PRINTF_ATTR
Inventory * getInventory()
Returns the player's inventory.
Warning: buffers and other variables are shared, so there can be only one connection active at a time...
@ EQUIP_ARMS_SLOT
Definition iteminfo.h:167
@ EQUIP_LEGS_SLOT
Definition iteminfo.h:171
@ EQUIP_FIGHT2_SLOT
Definition iteminfo.h:184
@ EQUIP_RING2_SLOT
Definition iteminfo.h:176
@ EQUIP_FEET_SLOT
Definition iteminfo.h:173
@ EQUIP_FIGHT1_SLOT
Definition iteminfo.h:183
@ EQUIP_RING1_SLOT
Definition iteminfo.h:175
@ EQUIP_HEAD_SLOT
Definition iteminfo.h:169
@ EQUIP_VECTOR_END
Definition iteminfo.h:188
@ EQUIP_NECKLACE_SLOT
Definition iteminfo.h:178
@ EQUIP_PROJECTILE_SLOT
Definition iteminfo.h:187
@ EQUIP_TORSO_SLOT
Definition iteminfo.h:165
@ SMSG_PLAYER_ATTACK_RANGE
Definition protocol.h:269
@ CMSG_MOVE_TO_STORAGE
Definition protocol.h:240
@ SMSG_PLAYER_INVENTORY_ADD
Definition protocol.h:190
@ CMSG_PLAYER_UNEQUIP
Definition protocol.h:199
@ SMSG_PLAYER_INVENTORY_USE
Definition protocol.h:285
@ SMSG_PLAYER_STORAGE_CLOSE
Definition protocol.h:245
@ CMSG_CLOSE_STORAGE
Definition protocol.h:244
@ SMSG_PLAYER_STORAGE_REMOVE
Definition protocol.h:243
@ SMSG_PLAYER_INVENTORY_REMOVE
Definition protocol.h:201
@ CMSG_PLAYER_INVENTORY_USE
Definition protocol.h:195
@ SMSG_PLAYER_EQUIP
Definition protocol.h:198
@ SMSG_PLAYER_STORAGE_ADD
Definition protocol.h:241
@ SMSG_PLAYER_EQUIPMENT
Definition protocol.h:193
@ SMSG_PLAYER_STORAGE_EQUIP
Definition protocol.h:194
@ SMSG_PLAYER_UNEQUIP
Definition protocol.h:200
@ SMSG_PLAYER_INVENTORY
Definition protocol.h:293
@ CMSG_PLAYER_EQUIP
Definition protocol.h:197
@ CMSG_PLAYER_INVENTORY_DROP
Definition protocol.h:192
@ SMSG_PLAYER_ARROW_EQUIP
Definition protocol.h:271
@ CMSG_MOVE_FROM_STORAGE
Definition protocol.h:242
@ SMSG_PLAYER_STORAGE_STATUS
Definition protocol.h:239
@ SMSG_PLAYER_STORAGE_ITEMS
Definition protocol.h:294
@ SMSG_ITEM_USE_RESPONSE
Definition protocol.h:196
Used to cache storage data until we get size data for it.