00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <algorithm>
00023 #include <cassert>
00024
00025 #include "defines.h"
00026 #include "game-server/gamehandler.hpp"
00027 #include "game-server/inventory.hpp"
00028 #include "game-server/item.hpp"
00029 #include "game-server/itemmanager.hpp"
00030 #include "net/messageout.hpp"
00031 #include "utils/logger.h"
00032
00033 Inventory::Inventory(Character *p, bool d):
00034 mPoss(&p->getPossessions()), msg(GPMSG_INVENTORY), mClient(p),
00035 mDelayed(d), mChangedLook(false)
00036 {
00037 }
00038
00039 Inventory::~Inventory()
00040 {
00041 if (msg.getLength() > 2)
00042 {
00043 update();
00044 gameHandler->sendTo(mClient, msg);
00045 }
00046 }
00047
00048 void Inventory::restart()
00049 {
00050 msg.clear();
00051 msg.writeShort(GPMSG_INVENTORY);
00052 mChangedLook = false;
00053 }
00054
00055 void Inventory::cancel()
00056 {
00057 assert(mDelayed);
00058 Possessions &poss = mClient->getPossessions();
00059 if (mPoss != &poss)
00060 {
00061 delete mPoss;
00062 mPoss = &poss;
00063 }
00064 restart();
00065 }
00066
00067 void Inventory::update()
00068 {
00069 if (mDelayed)
00070 {
00071 Possessions &poss = mClient->getPossessions();
00072 if (mPoss != &poss)
00073 {
00074 poss = *mPoss;
00075 delete mPoss;
00076 mPoss = &poss;
00077 }
00078 }
00079 if (mChangedLook)
00080 {
00081 mClient->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE);
00082 }
00083 }
00084
00085 void Inventory::commit()
00086 {
00087 if (msg.getLength() > 2)
00088 {
00089 update();
00090 gameHandler->sendTo(mClient, msg);
00091 restart();
00092 }
00093 }
00094
00095 void Inventory::prepare()
00096 {
00097 if (!mDelayed)
00098 {
00099 return;
00100 }
00101 Possessions &poss = mClient->getPossessions();
00102 if (mPoss == &poss)
00103 {
00104 mPoss = new Possessions(poss);
00105 }
00106 }
00107
00108 void Inventory::sendFull() const
00109 {
00110 MessageOut m(GPMSG_INVENTORY_FULL);
00111
00112 for (int i = 0; i < EQUIPMENT_SLOTS; ++i)
00113 {
00114 if (int id = mPoss->equipment[i])
00115 {
00116 m.writeByte(i);
00117 m.writeShort(id);
00118 }
00119 }
00120
00121 int slot = EQUIP_CLIENT_INVENTORY;
00122 for (std::vector< InventoryItem >::const_iterator i = mPoss->inventory.begin(),
00123 i_end = mPoss->inventory.end(); i != i_end; ++i)
00124 {
00125 if (i->itemId)
00126 {
00127 m.writeByte(slot);
00128 m.writeShort(i->itemId);
00129 m.writeByte(i->amount);
00130 ++slot;
00131 }
00132 else
00133 {
00134 slot += i->amount;
00135 }
00136 }
00137
00138 m.writeByte(255);
00139 m.writeLong(mPoss->money);
00140
00141 gameHandler->sendTo(mClient, m);
00142 }
00143
00144 void Inventory::initialize()
00145 {
00146 assert(!mDelayed);
00147
00148
00149 for (int i = 0; i < EQUIP_PROJECTILE_SLOT; ++i)
00150 {
00151 int itemId = mPoss->equipment[i];
00152 if (!itemId) continue;
00153 if (ItemClass *ic = ItemManager::getItem(itemId))
00154 {
00155 ic->getModifiers().applyAttributes(mClient);
00156 }
00157 else
00158 {
00159 mPoss->equipment[i] = 0;
00160 LOG_WARN("Removed unknown item " << itemId << " from equipment "
00161 "of character " << mClient->getDatabaseID() << '.');
00162 }
00163 }
00164
00165
00166 int i = 0;
00167 while (i < (int)mPoss->inventory.size())
00168 {
00169 int itemId = mPoss->inventory[i].itemId;
00170 if (itemId)
00171 {
00172 ItemClass *ic = ItemManager::getItem(itemId);
00173 if (!ic)
00174 {
00175 LOG_WARN("Removed unknown item " << itemId << " from inventory"
00176 " of character " << mClient->getDatabaseID() << '.');
00177 freeIndex(i);
00178 continue;
00179 }
00180 }
00181 ++i;
00182 }
00183 }
00184
00185 int Inventory::getItem(int slot) const
00186 {
00187 for (std::vector< InventoryItem >::const_iterator i = mPoss->inventory.begin(),
00188 i_end = mPoss->inventory.end(); i != i_end; ++i)
00189 {
00190 if (slot == 0)
00191 {
00192 return i->itemId;
00193 }
00194
00195 slot -= i->itemId ? 1 : i->amount;
00196
00197 if (slot < 0)
00198 {
00199 return 0;
00200 }
00201 }
00202 return 0;
00203 }
00204
00205 int Inventory::getIndex(int slot) const
00206 {
00207 int index = 0;
00208
00209 for (std::vector< InventoryItem >::const_iterator i = mPoss->inventory.begin(),
00210 i_end = mPoss->inventory.end(); i != i_end; ++i, ++index)
00211 {
00212 if (slot == 0)
00213 {
00214 return i->itemId ? index : -1;
00215 }
00216
00217 slot -= i->itemId ? 1 : i->amount;
00218
00219 if (slot < 0)
00220 {
00221 return -1;
00222 }
00223 }
00224 return -1;
00225 }
00226
00227 int Inventory::getSlot(int index) const
00228 {
00229 int slot = 0;
00230 for (std::vector< InventoryItem >::const_iterator i = mPoss->inventory.begin(),
00231 i_end = mPoss->inventory.begin() + index; i != i_end; ++i)
00232 {
00233 slot += i->itemId ? 1 : i->amount;
00234 }
00235 return slot;
00236 }
00237
00238 int Inventory::fillFreeSlot(int itemId, int amount, int maxPerSlot)
00239 {
00240 int slot = 0;
00241 for (int i = 0, i_end = mPoss->inventory.size(); i < i_end; ++i)
00242 {
00243 InventoryItem &it = mPoss->inventory[i];
00244 if (it.itemId == 0)
00245 {
00246 int nb = std::min(amount, maxPerSlot);
00247 if (it.amount <= 1)
00248 {
00249 it.itemId = itemId;
00250 it.amount = nb;
00251 }
00252 else
00253 {
00254 --it.amount;
00255 InventoryItem iu = { itemId, nb };
00256 mPoss->inventory.insert(mPoss->inventory.begin() + i, iu);
00257 ++i_end;
00258 }
00259
00260 msg.writeByte(slot + EQUIP_CLIENT_INVENTORY);
00261 msg.writeShort(itemId);
00262 msg.writeByte(nb);
00263
00264 amount -= nb;
00265 if (amount == 0)
00266 {
00267 return 0;
00268 }
00269 }
00270 ++slot;
00271 }
00272
00273 while (slot < INVENTORY_SLOTS - 1 && amount > 0)
00274 {
00275 int nb = std::min(amount, maxPerSlot);
00276 amount -= nb;
00277 InventoryItem it = { itemId, nb };
00278 mPoss->inventory.push_back(it);
00279
00280 msg.writeByte(slot + EQUIP_CLIENT_INVENTORY);
00281 msg.writeShort(itemId);
00282 msg.writeByte(nb);
00283 ++slot;
00284 }
00285
00286 return amount;
00287 }
00288
00289 int Inventory::insert(int itemId, int amount)
00290 {
00291 if (itemId == 0 || amount == 0)
00292 {
00293 return 0;
00294 }
00295
00296 prepare();
00297
00298 int maxPerSlot = ItemManager::getItem(itemId)->getMaxPerSlot();
00299 if (maxPerSlot == 1)
00300 {
00301 return fillFreeSlot(itemId, amount, maxPerSlot);
00302 }
00303
00304 int slot = 0;
00305 for (std::vector< InventoryItem >::iterator i = mPoss->inventory.begin(),
00306 i_end = mPoss->inventory.end(); i != i_end; ++i)
00307 {
00308 if (i->itemId == itemId && i->amount < maxPerSlot)
00309 {
00310 int nb = std::min(maxPerSlot - i->amount, amount);
00311 i->amount += nb;
00312 amount -= nb;
00313
00314 msg.writeByte(slot + EQUIP_CLIENT_INVENTORY);
00315 msg.writeShort(itemId);
00316 msg.writeByte(i->amount);
00317
00318 if (amount == 0)
00319 {
00320 return 0;
00321 }
00322 ++slot;
00323 }
00324 else
00325 {
00326 slot += i->itemId ? 1 : i->amount;
00327 }
00328 }
00329
00330 return fillFreeSlot(itemId, amount, maxPerSlot);
00331 }
00332
00333 int Inventory::count(int itemId) const
00334 {
00335 int nb = 0;
00336
00337 for (std::vector< InventoryItem >::const_iterator i = mPoss->inventory.begin(),
00338 i_end = mPoss->inventory.end(); i != i_end; ++i)
00339 {
00340 if (i->itemId == itemId)
00341 {
00342 nb += i->amount;
00343 }
00344 }
00345
00346 return nb;
00347 }
00348
00349 bool Inventory::changeMoney(int amount)
00350 {
00351 if (amount == 0)
00352 {
00353 return true;
00354 }
00355
00356 int money = mPoss->money + amount;
00357 if (money < 0)
00358 {
00359 return false;
00360 }
00361
00362 prepare();
00363
00364 mPoss->money = money;
00365 msg.writeByte(255);
00366 msg.writeLong(money);
00367 return true;
00368 }
00369
00370 void Inventory::freeIndex(int i)
00371 {
00372 InventoryItem &it = mPoss->inventory[i];
00373
00374
00375 if (i == (int)mPoss->inventory.size() - 1)
00376 {
00377 mPoss->inventory.pop_back();
00378 if (i > 0 && mPoss->inventory[i - 1].itemId == 0)
00379 {
00380 mPoss->inventory.pop_back();
00381 }
00382 return;
00383 }
00384
00385 it.itemId = 0;
00386
00387
00388 if (mPoss->inventory[i + 1].itemId == 0)
00389 {
00390 it.amount = mPoss->inventory[i + 1].amount + 1;
00391 mPoss->inventory.erase(mPoss->inventory.begin() + i + 1);
00392 }
00393 else
00394 {
00395 it.amount = 1;
00396 }
00397
00398
00399 if (i > 0 && mPoss->inventory[i - 1].itemId == 0)
00400 {
00401
00402 mPoss->inventory[i - 1].amount += mPoss->inventory[i].amount;
00403 mPoss->inventory.erase(mPoss->inventory.begin() + i);
00404 }
00405 }
00406
00407 int Inventory::remove(int itemId, int amount)
00408 {
00409 if (itemId == 0 || amount == 0)
00410 {
00411 return 0;
00412 }
00413
00414 prepare();
00415
00416 for (int i = mPoss->inventory.size() - 1; i >= 0; --i)
00417 {
00418 InventoryItem &it = mPoss->inventory[i];
00419 if (it.itemId == itemId)
00420 {
00421 int nb = std::min((int)it.amount, amount);
00422 it.amount -= nb;
00423 amount -= nb;
00424
00425 msg.writeByte(getSlot(i) + EQUIP_CLIENT_INVENTORY);
00426 if (it.amount == 0)
00427 {
00428 msg.writeShort(0);
00429 freeIndex(i);
00430 }
00431 else
00432 {
00433 msg.writeShort(itemId);
00434 msg.writeByte(it.amount);
00435 }
00436
00437 if (amount == 0)
00438 {
00439 return 0;
00440 }
00441 }
00442 }
00443
00444 return amount;
00445 }
00446
00447 int Inventory::move(int slot1, int slot2, int amount)
00448 {
00449 if (amount == 0 || slot1 == slot2 || slot2 >= INVENTORY_SLOTS)
00450 {
00451 return amount;
00452 }
00453
00454 int i1 = getIndex(slot1);
00455 if (i1 < 0)
00456 {
00457 return amount;
00458 }
00459
00460 prepare();
00461
00462 InventoryItem &it1 = mPoss->inventory[i1];
00463 int i2 = getIndex(slot2);
00464
00465 if (i2 >= 0)
00466 {
00467 InventoryItem &it2 = mPoss->inventory[i2];
00468 if (it1.itemId == it2.itemId)
00469 {
00470
00471 int maxPerSlot = ItemManager::getItem(it1.itemId)->getMaxPerSlot();
00472 int nb = std::min(std::min(amount, (int)it1.amount), maxPerSlot - it2.amount);
00473 if (nb == 0)
00474 {
00475 return amount;
00476 }
00477
00478 it1.amount -= nb;
00479 it2.amount += nb;
00480 amount -= nb;
00481
00482 msg.writeByte(slot2 + EQUIP_CLIENT_INVENTORY);
00483 msg.writeShort(it2.itemId);
00484 msg.writeByte(it2.amount);
00485
00486 msg.writeByte(slot1 + EQUIP_CLIENT_INVENTORY);
00487 if (it1.amount == 0)
00488 {
00489 msg.writeShort(0);
00490 freeIndex(i1);
00491 }
00492 else
00493 {
00494 msg.writeShort(it1.itemId);
00495 msg.writeByte(it1.amount);
00496 }
00497 return amount;
00498 }
00499
00500
00501 if (it1.amount != amount)
00502 {
00503 return amount;
00504 }
00505
00506 std::swap(it1, it2);
00507
00508 msg.writeByte(slot1 + EQUIP_CLIENT_INVENTORY);
00509 msg.writeShort(it1.itemId);
00510 msg.writeByte(it1.amount);
00511 msg.writeByte(slot2 + EQUIP_CLIENT_INVENTORY);
00512 msg.writeShort(it2.itemId);
00513 msg.writeByte(it2.amount);
00514 return 0;
00515 }
00516
00517
00518 int id = it1.itemId;
00519 int nb = std::min((int)it1.amount, amount);
00520 it1.amount -= nb;
00521 amount -= nb;
00522
00523 msg.writeByte(slot1 + EQUIP_CLIENT_INVENTORY);
00524 if (it1.amount == 0)
00525 {
00526 msg.writeShort(0);
00527 freeIndex(i1);
00528 }
00529 else
00530 {
00531 msg.writeShort(id);
00532 msg.writeByte(it1.amount);
00533 }
00534
00535
00536 msg.writeByte(slot2 + EQUIP_CLIENT_INVENTORY);
00537 msg.writeShort(id);
00538 msg.writeByte(nb);
00539
00540 for (std::vector< InventoryItem >::iterator i = mPoss->inventory.begin(),
00541 i_end = mPoss->inventory.end(); i != i_end; ++i)
00542 {
00543 if (i->itemId)
00544 {
00545 --slot2;
00546 continue;
00547 }
00548
00549 if (slot2 >= i->amount)
00550 {
00551 slot2 -= i->amount;
00552 continue;
00553 }
00554
00555 assert(slot2 >= 0 && i + 1 != i_end);
00556
00557 if (i->amount == 1)
00558 {
00559
00560 i->itemId = id;
00561 i->amount = nb;
00562 return amount;
00563 }
00564
00565 InventoryItem it = { id, nb };
00566 --i->amount;
00567
00568 if (slot2 == 0)
00569 {
00570
00571 mPoss->inventory.insert(i, it);
00572 return amount;
00573 }
00574
00575 if (slot2 == i->amount)
00576 {
00577
00578 mPoss->inventory.insert(i + 1, it);
00579 return amount;
00580 }
00581
00582 InventoryItem it3 = { 0, slot2 };
00583 i->amount -= slot2;
00584 i = mPoss->inventory.insert(i, it);
00585 mPoss->inventory.insert(i, it3);
00586 return amount;
00587 }
00588
00589
00590 assert(slot2 >= 0);
00591 if (slot2 != 0)
00592 {
00593 InventoryItem it = { 0, slot2 };
00594 mPoss->inventory.insert(mPoss->inventory.end(), it);
00595 }
00596 InventoryItem it = { id, nb };
00597 mPoss->inventory.insert(mPoss->inventory.end(), it);
00598 return amount;
00599 }
00600
00601 int Inventory::removeFromSlot(int slot, int amount)
00602 {
00603 if (amount == 0)
00604 {
00605 return 0;
00606 }
00607
00608 int i = getIndex(slot);
00609 if (i < 0)
00610 {
00611 return amount;
00612 }
00613
00614 prepare();
00615
00616 InventoryItem &it = mPoss->inventory[i];
00617 int nb = std::min((int)it.amount, amount);
00618 it.amount -= nb;
00619 amount -= nb;
00620
00621 msg.writeByte(slot + EQUIP_CLIENT_INVENTORY);
00622 if (it.amount == 0)
00623 {
00624 msg.writeShort(0);
00625 freeIndex(i);
00626 }
00627 else
00628 {
00629 msg.writeShort(it.itemId);
00630 msg.writeByte(it.amount);
00631 }
00632
00633 return amount;
00634 }
00635
00636 void Inventory::replaceInSlot(int slot, int itemId, int amount)
00637 {
00638 int i = getIndex(slot);
00639 assert(i >= 0);
00640 prepare();
00641
00642 msg.writeByte(slot + EQUIP_CLIENT_INVENTORY);
00643 if (itemId == 0 || amount == 0)
00644 {
00645 msg.writeShort(0);
00646 freeIndex(i);
00647 }
00648 else
00649 {
00650 InventoryItem &it = mPoss->inventory[i];
00651 it.itemId = itemId;
00652 it.amount = amount;
00653 msg.writeShort(itemId);
00654 msg.writeByte(amount);
00655 }
00656 }
00657
00658 void Inventory::changeEquipment(int slot, int itemId)
00659 {
00660
00661 assert(!mDelayed);
00662
00663 int oldId = mPoss->equipment[slot];
00664 if (oldId == itemId)
00665 {
00666 return;
00667 }
00668
00669 if (oldId)
00670 {
00671 ItemManager::getItem(oldId)->getModifiers().cancelAttributes(mClient);
00672 }
00673
00674 if (itemId)
00675 {
00676 ItemManager::getItem(itemId)->getModifiers().applyAttributes(mClient);
00677 }
00678
00679 msg.writeByte(slot);
00680 msg.writeShort(itemId);
00681 mPoss->equipment[slot] = itemId;
00682 mChangedLook = true;
00683
00684
00685 mClient->modifiedAttribute(BASE_ATTR_EVADE);
00686 }
00687
00688 void Inventory::equip(int slot)
00689 {
00690 int itemId = getItem(slot);
00691 if (!itemId)
00692 {
00693 return;
00694 }
00695
00696 prepare();
00697
00698 int availableSlots = 0, firstSlot = 0, secondSlot = 0;
00699
00700 switch (ItemManager::getItem(itemId)->getType())
00701 {
00702 case ITEM_EQUIPMENT_TWO_HANDS_WEAPON:
00703 {
00704
00705 int id1 = mPoss->equipment[EQUIP_FIGHT1_SLOT],
00706 id2 = mPoss->equipment[EQUIP_FIGHT2_SLOT];
00707
00708 if (id2)
00709 {
00710 if (id1 && insert(id1, 1) != 0)
00711 {
00712 return;
00713 }
00714 id1 = id2;
00715 }
00716
00717 replaceInSlot(slot, id1, 1);
00718 changeEquipment(EQUIP_FIGHT1_SLOT, itemId);
00719 changeEquipment(EQUIP_FIGHT2_SLOT, 0);
00720 return;
00721 }
00722
00723 case ITEM_EQUIPMENT_AMMO:
00724 msg.writeByte(EQUIP_PROJECTILE_SLOT);
00725 msg.writeShort(itemId);
00726 mPoss->equipment[EQUIP_PROJECTILE_SLOT] = itemId;
00727 return;
00728
00729 case ITEM_EQUIPMENT_ONE_HAND_WEAPON:
00730 case ITEM_EQUIPMENT_SHIELD:
00731 availableSlots = 2;
00732 firstSlot = EQUIP_FIGHT1_SLOT;
00733 secondSlot = EQUIP_FIGHT2_SLOT;
00734 break;
00735 case ITEM_EQUIPMENT_RING:
00736 availableSlots = 2;
00737 firstSlot = EQUIP_RING1_SLOT;
00738 secondSlot = EQUIP_RING2_SLOT;
00739 break;
00740 case ITEM_EQUIPMENT_TORSO:
00741 availableSlots = 1;
00742 firstSlot = EQUIP_TORSO_SLOT;
00743 break;
00744 case ITEM_EQUIPMENT_ARMS:
00745 availableSlots = 1;
00746 firstSlot = EQUIP_ARMS_SLOT;
00747 break;
00748 case ITEM_EQUIPMENT_HEAD:
00749 availableSlots = 1;
00750 firstSlot = EQUIP_HEAD_SLOT;
00751 break;
00752 case ITEM_EQUIPMENT_LEGS:
00753 availableSlots = 1;
00754 firstSlot = EQUIP_LEGS_SLOT;
00755 break;
00756 case ITEM_EQUIPMENT_NECKLACE:
00757 availableSlots = 1;
00758 firstSlot = EQUIP_NECKLACE_SLOT;
00759 break;
00760 case ITEM_EQUIPMENT_FEET:
00761 availableSlots = 1;
00762 firstSlot = EQUIP_FEET_SLOT;
00763 break;
00764
00765 case ITEM_UNUSABLE:
00766 case ITEM_USABLE:
00767 default:
00768 return;
00769 }
00770
00771 int id = mPoss->equipment[firstSlot];
00772
00773 if (availableSlots == 2 && id && !mPoss->equipment[secondSlot] &&
00774 ItemManager::getItem(id)->getType() != ITEM_EQUIPMENT_TWO_HANDS_WEAPON)
00775 {
00776
00777 id = 0;
00778 firstSlot = secondSlot;
00779 }
00780
00781
00782 replaceInSlot(slot, id, 1);
00783 changeEquipment(firstSlot, itemId);
00784 }
00785
00786 void Inventory::unequip(int slot)
00787 {
00788 int itemId = mPoss->equipment[slot];
00789 if (!itemId)
00790 {
00791 return;
00792 }
00793
00794
00795 if (insert(itemId, 1) == 0)
00796 {
00797 changeEquipment(slot, 0);
00798 }
00799 }