//----[ onDeath ]------------------------------------------------------------ bool ClientItemsInterface::onDeath(Magic::MagicIndex* on_death_magic, int* wishing_percent) { assert(on_death_magic); assert(wishing_percent); bool found_on_death_magic = false; int wishing_percent_sum = 0; // iterate backward because we might remove an element for (int i = items_in_inventory_; i > 0;) { --i; if (!inventory_[i].equipped) continue; ItemInstance* item = inventory_[i].item.dereference(); assert(item); Magic::MagicIndex death_magic = item->getDescription()->on_death_magic; wishing_percent_sum += item->getDescription()->wishing_percent; if (!found_on_death_magic && (death_magic != Magic::INVALID_MAGIC_INDEX)) { found_on_death_magic = true; *on_death_magic = death_magic; update_inventory_ = true; GlobalDatabaseManager::singleton() ->itemOnAvatarErased(item->getUniqueID()); GlobalItemManager::singleton() ->releaseItem(&inventory_[i].item); shiftDownToEraseInInventory(i); } } *wishing_percent = wishing_percent_sum; return found_on_death_magic; }
//----[ findStackableItemInInventory ]--------------------------------------- bool ClientItemsInterface::findStackableItemInInventory( ItemInstance* find_match_for, unsigned int start_search_index, unsigned int* found_index, ItemInstance** current_item) { if (!find_match_for) return false; // no item? const Evidyon::Item::ItemServerDescription* description = find_match_for->getDescription(); if (description->max_stack_size <= 1) return false; // not stackable? unsigned int i = start_search_index; while (i < items_in_inventory_) { ItemInstance* item = inventory_[i].item.dereference(); if (!item) { // This item is invalid, but is listed as valid--fix the problem, // then try again. This will loop until no problems are found // or the list is emptied because all are bugged. repackInventory(); } else { if (item->getDescription() == description && !inventory_[i].equipped) { // don't stack equipped items *found_index = start_search_index; *current_item = item; return true; } else { ++i; } } } return false; }
//----[ eraseItems ]--------------------------------------------------------- int ClientItemsInterface::eraseItems(unsigned int item_type, int quantity) { for (unsigned int i = 0; (quantity > 0) && (i < items_in_inventory_);) { ItemInstance* item = inventory_[i].item.dereference(); if (!item) { repackInventory(); break; } else { if (item->getDescription()->type_index == item_type) { quantity = item->removeQuantity(quantity); update_inventory_ = true; equipment_changed_ = equipment_changed_ || inventory_[i].equipped; inventory_dirty_bits_.set(i); // check to see if this item was destroyed by removing // the given quantity. if (quantity >= 0) { GlobalDatabaseManager::singleton() ->itemOnAvatarErased(item->getUniqueID()); GlobalItemManager::singleton()->releaseItem(&inventory_[i].item); shiftDownToEraseInInventory(i); } // If the quantity has been consumed, we're done! if (quantity <= 0) return 0; } else { ++i; } } } return quantity; }
//----[ splitItem ]---------------------------------------------------------- bool ClientItemsInterface::splitItem(unsigned int index) { if (!inventoryItemIsValid(index)) return false; AvatarInventoryItem* inventory_item = &inventory_[index]; if (!inventory_item->stackable || // item must be stackable... inventory_item->equipped) return false; // ...and in the inventory ItemInstance* item = inventory_item->item.dereferenceAssumingValid(); // justified by validity check if (canHoldItems(1) == false) return false; // const int original_quantity = item->getQuantity(); if (original_quantity <= 1) return false; // can't split a stack of 1 int new_quantity = original_quantity >> 1; // divide in half int quantity_remaining = original_quantity - new_quantity; // Create the new item ItemPointer new_item; if (!GlobalItemManager::singleton()->acquireNewItem(new_quantity, item->getDescription()->type_index, &new_item)) { return false; } // Put the new item in the inventory if (!addToInventory(new_item, NULL)) { GlobalItemManager::singleton()->releaseItem(&new_item); } // Adjust the quantity of the original item item->setQuantity(quantity_remaining); return true; }
//----[ setStorageItem ]----------------------------------------------------- void ClientItemsInterface::setStorageItem(unsigned int index, ItemPointer* item_pointer) { ItemInstance* item = item_pointer->dereferenceAssumingValid(); storage_[index].id = item->getUniqueID(); storage_[index].quantity = item->getQuantity(); storage_[index].type = item->getDescription()->type_index; GlobalItemManager::singleton()->releaseItem(item_pointer); storage_dirty_bits_.set(index); update_storage_ = true; }
//----[ geosidSacrificeItem ]------------------------------------------------ bool ClientItemsInterface::geosidSacrificeItem(unsigned int index, Geosid::Geonite* geonite_value) { if (!inventoryItemIsValid(index)) return false; AvatarInventoryItem* inventory_item = &inventory_[index]; if (inventory_item->equipped) return false; // can't sacrifice equipped items ItemInstance* item = inventory_item->item.dereferenceAssumingValid(); *geonite_value = item->getDescription()->geonite_value; GlobalItemManager::singleton()->releaseItem(&inventory_item->item); shiftDownToEraseInInventory(index); return true; }
//----[ isCarrying ]--------------------------------------------------------- bool ClientItemsInterface::isCarrying(unsigned int item_type, int quantity) { for (unsigned int i = 0; i < items_in_inventory_; ++i) { ItemInstance* item = inventory_[i].item.dereference(); if (!item) { repackInventory(); return false; } else { if (item->getDescription()->type_index == item_type) { quantity -= item->getQuantity(); if (quantity <= 0) return true; } } } return false; }
//----[ isCarryingNew ]------------------------------------------------------ bool ClientItemsInterface::isCarryingNew(unsigned int item_type, int quantity, ItemIDType oldest_id) { for (unsigned int i = 0; i < items_in_inventory_; ++i) { ItemInstance* item = inventory_[i].item.dereference(); if (!item) { repackInventory(); return false; } else { if (item->getDescription()->type_index == item_type && item->getUniqueID() >= oldest_id) { // consider only new items quantity -= item->getQuantity(); if (quantity <= 0) return true; } } } return false; }
//----[ consumeItem ]-------------------------------------------------------- bool ClientItemsInterface::consumeItem(unsigned int index, Magic::MagicIndex* magic_index) { assert(magic_index); if (!inventoryItemIsValid(index)) return false; ItemInstance* item = inventory_[index].item.dereferenceAssumingValid(); // guaranteed OK const Item::ItemServerDescription* description = item->getDescription(); if (description->consumable_magic == Magic::INVALID_MAGIC_INDEX) return false; *magic_index = description->consumable_magic; if (item->removeQuantity(1) >= 0) { // this item has been fully consumed GlobalDatabaseManager::singleton()->itemOnAvatarErased(item->getUniqueID()); GlobalItemManager::singleton()->releaseItem(&inventory_[index].item); shiftDownToEraseInInventory(index); } else { // item is still available update_inventory_ = true; inventory_dirty_bits_.set(index); } todo("karl","if equipped consumable and consumed, change equipment"); return true; }
//----[ changeEquipped ]----------------------------------------------------- bool ClientItemsInterface::changeEquipped(unsigned int item_index) { if (!inventoryItemIsValid(item_index)) return false; ItemInstance* item = inventory_[item_index].item.dereference(); int quantity = item->getQuantity(); if (!canHoldItems(1) && quantity > 1) return false; equipment_changed_ = true; inventory_[item_index].equipped = !inventory_[item_index].equipped; update_inventory_ = true; inventory_dirty_bits_.set(item_index); if (quantity > 1) { int quantity_to_remove = quantity - 1; item->removeQuantity(quantity_to_remove); ItemPointer new_item; bool succeeded = false; if (GlobalItemManager::singleton()->acquireNewItem( quantity_to_remove, item->getDescription()->type_index, &new_item)) { unsigned int new_index; if (giveItem(&new_item, &new_index)) { succeeded = true; } } if (!succeeded) { // put the quantity back since this new item doesn't exist item->addQuantity(quantity_to_remove); // make the item unequipped inventory_[item_index].equipped = false; } } return true; }
//----[ dropItemsOnDeath ]--------------------------------------------------- void ClientItemsInterface::dropItemsOnDeath(int equipped_items_to_drop, Map* map, int x, int y) { WorldRegion* region = map->acquireRegionFromPoint(x, y); if (!region || (items_in_inventory_ == 0)) return; // equipped items are temporarily listed here kg::Array<AvatarInventoryItem,AVATAR_INVENTORY_SIZE> items_not_dropped; // drop all inventory items and count the equipped items for (unsigned int i = 0; i < items_in_inventory_; ++i) { ItemInstance* item = inventory_[i].item.dereference(); confirm(item) else continue; if (!item->getDescription()->drop_on_death || inventory_[i].equipped) { items_not_dropped.add(inventory_[i]); } else { region->addItemOnGround(true, x, y, inventory_[i].item); } inventory_[i].item.reset(); inventory_[i].stackable = false; inventory_[i].equipped = false; } // NOTE: items_in_inventory_ (the private member variable) will hold // the OLD number of items until the end of this method--so // don't touch it! this is so that we can tell if the # of items // changed. // move non-drop items back into the inventory unsigned int items_in_inventory = 0; for (unsigned int i = 0; i < items_not_dropped.count;) { ItemInstance* item = items_not_dropped[i].item.dereference(); if (!item->getDescription()->drop_on_death) { memcpy(&inventory_[items_in_inventory], &items_not_dropped[i], sizeof(AvatarInventoryItem)); ++items_in_inventory; items_not_dropped[i].item.reset(); items_not_dropped.swapRemoveMemcpy(i); } else { ++i; } } // drop equipped items if (equipped_items_to_drop >= items_not_dropped.count) { // drop everything for (unsigned int i = 0; i < items_not_dropped.count;) { ItemInstance* item = items_not_dropped[i].item.dereference(); region->addItemOnGround(true, x, y, items_not_dropped[i].item); items_not_dropped[i].item.reset(); items_not_dropped.swapRemoveMemcpy(i); } } else { // randomly pick items to drop int number_of_items_dropped = 0; while (number_of_items_dropped < equipped_items_to_drop) { confirm(items_not_dropped.count > 0) else break; int slot = rand()%items_not_dropped.count; ItemInstance* item = items_not_dropped[slot].item.dereference(); region->addItemOnGround(true, x, y, items_not_dropped[slot].item); items_not_dropped[slot].item.reset(); items_not_dropped.swapRemoveMemcpy(slot); ++number_of_items_dropped; } } // move items that weren't dropped back into the inventory for (unsigned int i = 0; i < items_not_dropped.count; ++i) { ItemInstance* item = items_not_dropped[i].item.dereference(); memcpy(&inventory_[items_in_inventory], &items_not_dropped[i], sizeof(AvatarInventoryItem)); // If the item provides on-death magic, ensure it is no longer // equipped. This causes Valtus Mederi Charms and the like to // only prevent death once (until the user can get back up and // re-equip one) even if multiple are equipped. if (item->getDescription()->on_death_magic != Magic::INVALID_MAGIC_INDEX) { inventory_[items_in_inventory].equipped = false; } ++items_in_inventory; } /* // drop all inventory items and count the equipped items unsigned int first_remaining_item = AVATAR_INVENTORY_SIZE, last_remaining_item = 0; for (unsigned int i = 0; i < items_in_inventory_; ++i) { ItemInstance* item = inventory_[i].item.dereference(); assert(item); if (!item->getDescription()->drop_on_death || inventory_[i].equipped) { if (i < first_remaining_item) first_remaining_item = i; last_remaining_item = i; } else { region->addItemOnGround(true, x, y, inventory_[i].item); inventory_[i].item.reset(); inventory_[i].stackable = false; } } // will hold the number of items left in the inventory int items_in_inventory = 0; // copy down remaining equipped items for (unsigned int i = first_remaining_item; i <= last_remaining_item; ++i) { if (inventory_[i].item.invalid()) continue; assert(inventory_[i].equipped || !inventory_[i].item.dereference()->getDescription()->drop_on_death); if (i != items_in_inventory) { memcpy(&inventory_[items_in_inventory], &inventory_[i], sizeof(AvatarInventoryItem)); } ++items_in_inventory; } if (equipped_items_to_drop >= items_in_inventory) { int items_remaining = 0; // drop everything; leave nothing (except non-drop items) for (unsigned int i = 0; i < items_in_inventory; ++i) { ItemInstance* item = inventory_[i].item.dereference(); assert(item); if (item->getDescription()->drop_on_death) { equipment_changed_ = true; region->addItemOnGround(true, x, y, inventory_[i].item); inventory_[i].item.reset(); inventory_[i].stackable = false; inventory_[i].equipped = false; } else { if (i != items_remaining) { memcpy(&inventory_[items_remaining], &inventory_[i], sizeof(AvatarInventoryItem)); } ++items_remaining; } } items_in_inventory = items_remaining; } else { // go in a loop assert(items_in_inventory > 0); int first_item = rand()%items_in_inventory; int i = first_item; int number_of_items_dropped = 0; do { ItemInstance* item = inventory_[i].item.dereference(); assert(item); if (item->getDescription()->drop_on_death) { assert(inventory_[i].equipped); region->addItemOnGround(true, x, y, inventory_[i].item); inventory_[i].item.reset(); inventory_[i].stackable = false; inventory_[i].equipped = false; ++number_of_items_dropped; } i = (i + 1) % items_in_inventory; } while (i != first_item && (number_of_items_dropped < equipped_items_to_drop)); // copy down all remaining valid items, but unequip them // if they gave on-death magic int remaining_items = 0; for (int j = 0; j < items_in_inventory; ++j) { if (inventory_[j].item.invalid() == false) { if (inventory_[j].item.dereference()->getDescription()->on_death_magic != Magic::INVALID_MAGIC_INDEX) { // this should cause Valtus Mederi Charms and other special // items to unequip themselves when a character dies, but // it will leave all non-dropped equipment on the character inventory_[j].equipped = false; } if (j != remaining_items) { memcpy(&inventory_[remaining_items], &inventory_[j], sizeof(AvatarInventoryItem)); ++remaining_items; } } } items_in_inventory_ = remaining_items; } */ // did anything change? if (items_in_inventory != items_in_inventory_) { equipment_changed_ = true; region->broadcastUpdateItemsOnLocation(x, y); inventory_dirty_bits_.setRange(0, items_in_inventory_); items_in_inventory_ = items_in_inventory; commit(); } }