#include "../Platform/stdafx.h" #include "../Headers/net.minecraft.world.item.h" #include "../Headers/net.minecraft.world.item.enchantment.h" #include "../Headers/net.minecraft.world.entity.h" #include "../Headers/net.minecraft.world.entity.player.h" #include "../Headers/net.minecraft.world.damagesource.h" #include "../Util/WeighedRandom.h" #include "EnchantmentHelper.h" Random EnchantmentHelper::random; int EnchantmentHelper::getEnchantmentLevel( int enchantmentId, std::shared_ptr piece) { if (piece == NULL) { return 0; } ListTag* enchantmentTags = piece->getEnchantmentTags(); if (enchantmentTags == NULL) { return 0; } for (int i = 0; i < enchantmentTags->size(); i++) { int type = enchantmentTags->get(i)->getShort( (wchar_t*)ItemInstance::TAG_ENCH_ID); int level = enchantmentTags->get(i)->getShort( (wchar_t*)ItemInstance::TAG_ENCH_LEVEL); if (type == enchantmentId) { return level; } } return 0; } std::unordered_map* EnchantmentHelper::getEnchantments( std::shared_ptr item) { std::unordered_map* result = new std::unordered_map(); ListTag* list = item->id == Item::enchantedBook_Id ? Item::enchantedBook->getEnchantments(item) : item->getEnchantmentTags(); if (list != NULL) { for (int i = 0; i < list->size(); i++) { int type = list->get(i)->getShort((wchar_t*)ItemInstance::TAG_ENCH_ID); int level = list->get(i)->getShort((wchar_t*)ItemInstance::TAG_ENCH_LEVEL); result->insert( std::unordered_map::value_type(type, level)); } } return result; } void EnchantmentHelper::setEnchantments( std::unordered_map* enchantments, std::shared_ptr item) { ListTag* list = new ListTag(); // for (int id : enchantments.keySet()) for (AUTO_VAR(it, enchantments->begin()); it != enchantments->end(); ++it) { int id = it->first; CompoundTag* tag = new CompoundTag(); tag->putShort((wchar_t*)ItemInstance::TAG_ENCH_ID, (short)id); tag->putShort((wchar_t*)ItemInstance::TAG_ENCH_LEVEL, (short)(int)it->second); list->add(tag); if (item->id == Item::enchantedBook_Id) { Item::enchantedBook->addEnchantment( item, new EnchantmentInstance(id, it->second)); } } if (list->size() > 0) { if (item->id != Item::enchantedBook_Id) { item->addTagElement(L"ench", list); } } else if (item->hasTag()) { item->getTag()->remove(L"ench"); } } int EnchantmentHelper::getEnchantmentLevel(int enchantmentId, ItemInstanceArray inventory) { if (inventory.data == NULL) return 0; int bestLevel = 0; // for (ItemInstance piece : inventory) for (unsigned int i = 0; i < inventory.length; ++i) { int newLevel = getEnchantmentLevel(enchantmentId, inventory[i]); if (newLevel > bestLevel) { bestLevel = newLevel; } } return bestLevel; } void EnchantmentHelper::runIterationOnItem( EnchantmentIterationMethod& method, std::shared_ptr piece) { if (piece == NULL) { return; } ListTag* enchantmentTags = piece->getEnchantmentTags(); if (enchantmentTags == NULL) { return; } for (int i = 0; i < enchantmentTags->size(); i++) { int type = enchantmentTags->get(i)->getShort( (wchar_t*)ItemInstance::TAG_ENCH_ID); int level = enchantmentTags->get(i)->getShort( (wchar_t*)ItemInstance::TAG_ENCH_LEVEL); if (Enchantment::enchantments[type] != NULL) { method.doEnchantment(Enchantment::enchantments[type], level); } } } void EnchantmentHelper::runIterationOnInventory( EnchantmentIterationMethod& method, ItemInstanceArray inventory) { // for (ItemInstance piece : inventory) for (unsigned int i = 0; i < inventory.length; ++i) { runIterationOnItem(method, inventory[i]); } } void EnchantmentHelper::GetDamageProtectionIteration::doEnchantment( Enchantment* enchantment, int level) { sum += enchantment->getDamageProtection(level, source); } EnchantmentHelper::GetDamageProtectionIteration EnchantmentHelper::getDamageProtectionIteration; /** * Fetches the protection value for enchanted items. * * @param inventory * @param source * @return */ int EnchantmentHelper::getDamageProtection(ItemInstanceArray armor, DamageSource* source) { getDamageProtectionIteration.sum = 0; getDamageProtectionIteration.source = source; runIterationOnInventory(getDamageProtectionIteration, armor); if (getDamageProtectionIteration.sum > 25) { getDamageProtectionIteration.sum = 25; } // enchantment protection is on the scale of 0 to 25, where 20 or more // will nullify nearly all damage (there will be damage spill) return ((getDamageProtectionIteration.sum + 1) >> 1) + random.nextInt((getDamageProtectionIteration.sum >> 1) + 1); } void EnchantmentHelper::GetDamageBonusIteration::doEnchantment( Enchantment* enchantment, int level) { sum += enchantment->getDamageBonus(level, target); } EnchantmentHelper::GetDamageBonusIteration EnchantmentHelper::getDamageBonusIteration; /** * * @param inventory * @param target * @return */ float EnchantmentHelper::getDamageBonus(std::shared_ptr source, std::shared_ptr target) { getDamageBonusIteration.sum = 0; getDamageBonusIteration.target = target; runIterationOnItem(getDamageBonusIteration, source->getCarriedItem()); return getDamageBonusIteration.sum; } int EnchantmentHelper::getKnockbackBonus(std::shared_ptr source, std::shared_ptr target) { return getEnchantmentLevel(Enchantment::knockback->id, source->getCarriedItem()); } int EnchantmentHelper::getFireAspect(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::fireAspect->id, source->getCarriedItem()); } int EnchantmentHelper::getOxygenBonus(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::drownProtection->id, source->getEquipmentSlots()); } int EnchantmentHelper::getDiggingBonus(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::diggingBonus->id, source->getCarriedItem()); } int EnchantmentHelper::getDigDurability(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::digDurability->id, source->getCarriedItem()); } bool EnchantmentHelper::hasSilkTouch(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::untouching->id, source->getCarriedItem()) > 0; } int EnchantmentHelper::getDiggingLootBonus( std::shared_ptr source) { return getEnchantmentLevel(Enchantment::resourceBonus->id, source->getCarriedItem()); } int EnchantmentHelper::getKillingLootBonus( std::shared_ptr source) { return getEnchantmentLevel(Enchantment::lootBonus->id, source->getCarriedItem()); } bool EnchantmentHelper::hasWaterWorkerBonus( std::shared_ptr source) { return getEnchantmentLevel(Enchantment::waterWorker->id, source->getEquipmentSlots()) > 0; } int EnchantmentHelper::getArmorThorns(std::shared_ptr source) { return getEnchantmentLevel(Enchantment::thorns->id, source->getEquipmentSlots()); } std::shared_ptr EnchantmentHelper::getRandomItemWith( Enchantment* enchantment, std::shared_ptr source) { ItemInstanceArray items = source->getEquipmentSlots(); for (unsigned int i = 0; i < items.length; ++i) { std::shared_ptr item = items[i]; if (item != NULL && getEnchantmentLevel(enchantment->id, item) > 0) { return item; } } return nullptr; } /** * * @param random * @param slot * The table slot, 0-2 * @param bookcases * How many book cases that are found around the table. * @param itemInstance * Which item that is being enchanted. * @return The enchantment cost, 0 means unchantable, 50 is max. */ int EnchantmentHelper::getEnchantmentCost( Random* random, int slot, int bookcases, std::shared_ptr itemInstance) { Item* item = itemInstance->getItem(); int itemValue = item->getEnchantmentValue(); if (itemValue <= 0) { // not enchantable return 0; } // 4J Stu - Updated function to 1.3 version for TU7 if (bookcases > 15) { bookcases = 15; } int selected = random->nextInt(8) + 1 + (bookcases >> 1) + random->nextInt(bookcases + 1); if (slot == 0) { return std::max((selected / 3), 1); } if (slot == 1) { return std::max(selected, bookcases * 2); } return selected; } std::shared_ptr EnchantmentHelper::enchantItem( Random* random, std::shared_ptr itemInstance, int enchantmentCost) { std::vector* newEnchantment = EnchantmentHelper::selectEnchantment(random, itemInstance, enchantmentCost); bool isBook = itemInstance->id == Item::book_Id; if (isBook) itemInstance->id = Item::enchantedBook_Id; if (newEnchantment != NULL) { for (AUTO_VAR(it, newEnchantment->begin()); it != newEnchantment->end(); ++it) { EnchantmentInstance* e = *it; if (isBook) { Item::enchantedBook->addEnchantment(itemInstance, e); } else { itemInstance->enchant(e->enchantment, e->level); } delete e; } delete newEnchantment; } return itemInstance; } /** * * @param random * @param itemInstance * @param enchantmentCost * @return */ std::vector* EnchantmentHelper::selectEnchantment( Random* random, std::shared_ptr itemInstance, int enchantmentCost) { // withdraw bonus from item Item* item = itemInstance->getItem(); int itemBonus = item->getEnchantmentValue(); if (itemBonus <= 0) { return NULL; } // 4J Stu - Update function to 1.3 version for TU7 itemBonus /= 2; itemBonus = 1 + random->nextInt((itemBonus >> 1) + 1) + random->nextInt((itemBonus >> 1) + 1); int enchantmentValue = itemBonus + enchantmentCost; // the final enchantment cost will have another random span of +- 15% float deviation = (random->nextFloat() + random->nextFloat() - 1.0f) * .15f; int realValue = (int)((float)enchantmentValue * (1.0f + deviation) + .5f); if (realValue < 1) { realValue = 1; } std::vector* results = NULL; std::unordered_map* availableEnchantments = getAvailableEnchantmentResults(realValue, itemInstance); if (availableEnchantments != NULL && !availableEnchantments->empty()) { std::vector values; for (AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) { values.push_back(it->second); } EnchantmentInstance* instance = (EnchantmentInstance*)WeighedRandom::getRandomItem(random, &values); values.clear(); if (instance != NULL) { results = new std::vector(); results->push_back( instance->copy()); // 4J Stu - Inserting a copy so we can clear // memory from the availableEnchantments // collection int bonusChance = realValue; while (random->nextInt(50) <= bonusChance) { // remove incompatible enchantments from previous result // final Iterator mapIter = // availableEnchantments.keySet().iterator(); while // (mapIter.hasNext()) for (AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end();) { int nextEnchantment = it->first; // mapIter.next(); bool valid = true; // for (EnchantmentInstance *current : results) for (AUTO_VAR(resIt, results->begin()); resIt != results->end(); ++resIt) { EnchantmentInstance* current = *resIt; if (!current->enchantment->isCompatibleWith( Enchantment::enchantments[nextEnchantment])) { valid = false; break; } } if (!valid) { // mapIter.remove(); delete it->second; it = availableEnchantments->erase(it); } else { ++it; } } if (!availableEnchantments->empty()) { for (AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) { values.push_back(it->second); } EnchantmentInstance* nextInstance = (EnchantmentInstance*)WeighedRandom::getRandomItem( random, &values); values.clear(); results->push_back( nextInstance ->copy()); // 4J Stu - Inserting a copy so we can // clear memory from the // availableEnchantments collection } bonusChance >>= 1; } } } if (availableEnchantments != NULL) { for (AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) { delete it->second; } delete availableEnchantments; } return results; } std::unordered_map* EnchantmentHelper::getAvailableEnchantmentResults( int value, std::shared_ptr itemInstance) { Item* item = itemInstance->getItem(); std::unordered_map* results = NULL; bool isBook = itemInstance->id == Item::book_Id; // for (Enchantment e : Enchantment.enchantments) for (unsigned int i = 0; i < Enchantment::enchantments.length; ++i) { Enchantment* e = Enchantment::enchantments[i]; if (e == NULL) { continue; } // Only picks "normal" enchantments, no specialcases if (!e->category->canEnchant(item) && !isBook) { continue; } for (int level = e->getMinLevel(); level <= e->getMaxLevel(); level++) { if (value >= e->getMinCost(level) && value <= e->getMaxCost(level)) { if (results == NULL) { results = new std::unordered_map(); } AUTO_VAR(it, results->find(e->id)); if (it != results->end()) { delete it->second; } (*results)[e->id] = new EnchantmentInstance(e, level); } } } return results; }