Exemple #1
0
        public static bool TryGetMail(
            this CombinationSlotState state,
            long blockIndex,
            long requiredBlockIndex,
            out CombinationMail combinationMail,
            out ItemEnhanceMail itemEnhanceMail)
        {
            combinationMail = null;
            itemEnhanceMail = null;

            if (!state.TryGetResultId(out var resultId))
            {
                return(false);
            }

            switch (state.Result)
            {
            case ItemEnhancement.ResultModel r:
                itemEnhanceMail = new ItemEnhanceMail(
                    r,
                    blockIndex,
                    resultId,
                    requiredBlockIndex);
                return(true);

            case ItemEnhancement7.ResultModel r:
                itemEnhanceMail = new ItemEnhanceMail(
                    r,
                    blockIndex,
                    resultId,
                    requiredBlockIndex);
                return(true);

            case CombinationConsumable5.ResultModel r:
                combinationMail = new CombinationMail(
                    r,
                    blockIndex,
                    resultId,
                    requiredBlockIndex);
                return(true);

            default:
                return(false);
            }
        }
Exemple #2
0
        public void Read(ItemEnhanceMail itemEnhanceMail)
        {
            var avatarAddress   = States.Instance.CurrentAvatarState.address;
            var attachment      = (ItemEnhancement.ResultModel)itemEnhanceMail.attachment;
            var popup           = Find <CombinationResultPopup>();
            var itemBase        = attachment.itemUsable ?? (ItemBase)attachment.costume;
            var nonFungibleItem = attachment.itemUsable ?? (INonFungibleItem)attachment.costume;
            var model           = new UI.Model.CombinationResultPopup(new CountableItem(itemBase, 1))
            {
                isSuccess     = true,
                materialItems = new List <CombinationMaterial>()
            };

            model.OnClickSubmit.Subscribe(_ =>
            {
                LocalLayerModifier.AddItem(avatarAddress, nonFungibleItem.ItemId, false);
                LocalLayerModifier.RemoveNewAttachmentMail(avatarAddress, itemEnhanceMail.id);
            });
            popup.Pop(model);
        }
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx         = context;
            var            states      = ctx.PreviousStates;
            var            slotAddress = avatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    slotIndex
                    )
                );

            if (ctx.Rehearsal)
            {
                return(states
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress)
                       .SetState(avatarAddress, MarkChanged)
                       .SetState(slotAddress, MarkChanged));
            }
            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Debug("ItemEnhancement exec started.");

            if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState,
                                                out AvatarState avatarState))
            {
                throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load.");
            }
            sw.Stop();
            Log.Debug("ItemEnhancement Get AgentAvatarStates: {Elapsed}", sw.Elapsed);
            sw.Restart();

            if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem))
            {
                throw new ItemDoesNotExistException(
                          $"Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory."
                          );
            }

            if (enhancementItem.RequiredBlockIndex > context.BlockIndex)
            {
                throw new RequiredBlockIndexException(
                          $"Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}."
                          );
            }

            if (!(enhancementItem is Equipment enhancementEquipment))
            {
                throw new InvalidCastException(
                          $"Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}."

                          );
            }

            var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex);

            if (slotState is null)
            {
                throw new FailedLoadStateException($"Aborted as the slot state was failed to load. #{slotIndex}");
            }

            if (!slotState.Validate(avatarState, ctx.BlockIndex))
            {
                throw new CombinationSlotUnlockException($"Aborted as the slot state was failed to invalid. #{slotIndex}");
            }

            sw.Stop();
            Log.Debug("ItemEnhancement Get Equipment: {Elapsed}", sw.Elapsed);
            sw.Restart();

            if (enhancementEquipment.level > 9)
            {
                // Maximum level exceeded.
                throw new EquipmentLevelExceededException(
                          $"Aborted due to invalid equipment level: {enhancementEquipment.level} < 9"
                          );
            }

            var result = new ItemEnhancement.ResultModel
            {
                itemUsable         = enhancementEquipment,
                materialItemIdList = new[] { materialId }
            };

            var requiredAP = ItemEnhancement.GetRequiredAp();

            if (avatarState.actionPoint < requiredAP)
            {
                throw new NotEnoughActionPointException(
                          $"Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}"
                          );
            }

            var enhancementCostSheet = states.GetSheet <EnhancementCostSheet>();
            var requiredNCG          = ItemEnhancement.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1);

            avatarState.actionPoint -= requiredAP;
            result.actionPoint       = requiredAP;

            if (requiredNCG > 0)
            {
                states = states.TransferAsset(
                    ctx.Signer,
                    BlacksmithAddress,
                    states.GetGoldCurrency() * requiredNCG
                    );
            }

            if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem))
            {
                throw new NotEnoughMaterialException(
                          $"Aborted as the signer does not have a necessary material ({materialId})."
                          );
            }

            if (materialItem.RequiredBlockIndex > context.BlockIndex)
            {
                throw new RequiredBlockIndexException(
                          $"Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}."
                          );
            }

            if (!(materialItem is Equipment materialEquipment))
            {
                throw new InvalidCastException(
                          $"Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}."
                          );
            }

            if (enhancementEquipment.ItemId == materialId)
            {
                throw new InvalidMaterialException(
                          $"Aborted as an equipment to enhance ({materialId}) was used as a material too."
                          );
            }

            if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType)
            {
                // Invalid ItemSubType
                throw new InvalidMaterialException(
                          $"Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}."
                          );
            }

            if (materialEquipment.Grade != enhancementEquipment.Grade)
            {
                // Invalid Grade
                throw new InvalidMaterialException(
                          $"Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match."
                          );
            }

            if (materialEquipment.level != enhancementEquipment.level)
            {
                // Invalid level
                throw new InvalidMaterialException(
                          $"Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match."
                          );
            }
            sw.Stop();
            Log.Debug("ItemEnhancement Get Material: {Elapsed}", sw.Elapsed);
            sw.Restart();
            materialEquipment.Unequip();

            enhancementEquipment.Unequip();

            enhancementEquipment = ItemEnhancement.UpgradeEquipment(enhancementEquipment);

            var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount;

            enhancementEquipment.Update(requiredBlockIndex);
            sw.Stop();
            Log.Debug("ItemEnhancement Upgrade Equipment: {Elapsed}", sw.Elapsed);
            sw.Restart();

            result.gold = requiredNCG;

            avatarState.inventory.RemoveNonFungibleItem(materialId);
            sw.Stop();
            Log.Debug("ItemEnhancement Remove Materials: {Elapsed}", sw.Elapsed);
            sw.Restart();
            var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex);

            result.id = mail.id;

            avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment);
            avatarState.UpdateV2(mail);
            avatarState.UpdateFromItemEnhancement(enhancementEquipment);

            var materialSheet = states.GetSheet <MaterialItemSheet>();

            avatarState.UpdateQuestRewards(materialSheet);

            slotState.Update(result, ctx.BlockIndex, requiredBlockIndex);

            sw.Stop();
            Log.Debug("ItemEnhancement Update AvatarState: {Elapsed}", sw.Elapsed);
            sw.Restart();
            states = states.SetState(avatarAddress, avatarState.Serialize());
            sw.Stop();
            Log.Debug("ItemEnhancement Set AvatarState: {Elapsed}", sw.Elapsed);
            var ended = DateTimeOffset.UtcNow;

            Log.Debug("ItemEnhancement Total Executed Time: {Elapsed}", ended - started);
            return(states.SetState(slotAddress, slotState.Serialize()));
        }
Exemple #4
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var ctx         = context;
            var states      = ctx.PreviousStates;
            var slotAddress = avatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    slotIndex
                    )
                );
            var inventoryAddress        = avatarAddress.Derive(LegacyInventoryKey);
            var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey);
            var questListAddress        = avatarAddress.Derive(LegacyQuestListKey);

            if (ctx.Rehearsal)
            {
                return(states
                       .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, BlacksmithAddress)
                       .SetState(avatarAddress, MarkChanged)
                       .SetState(inventoryAddress, MarkChanged)
                       .SetState(worldInformationAddress, MarkChanged)
                       .SetState(questListAddress, MarkChanged)
                       .SetState(slotAddress, MarkChanged));
            }

            var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress);

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex);
            if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out var agentState, out var avatarState))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load.");
            }

            if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem))
            {
                throw new ItemDoesNotExistException(
                          $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory."
                          );
            }

            if (enhancementItem.RequiredBlockIndex > context.BlockIndex)
            {
                throw new RequiredBlockIndexException(
                          $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet;" +
                          $" it will be available at the block #{enhancementItem.RequiredBlockIndex}."
                          );
            }

            if (!(enhancementItem is Equipment enhancementEquipment))
            {
                throw new InvalidCastException(
                          $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}."

                          );
            }

            var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex);

            if (slotState is null)
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}");
            }

            if (!slotState.Validate(avatarState, ctx.BlockIndex))
            {
                throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}");
            }
            sw.Stop();
            Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            var enhancementCostSheet = states.GetSheet <EnhancementCostSheetV2>();

            if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out var row))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), enhancementEquipment.level);
            }

            var maxLevel = GetEquipmentMaxLevel(enhancementEquipment, enhancementCostSheet);

            if (enhancementEquipment.level >= maxLevel)
            {
                throw new EquipmentLevelExceededException(
                          $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < {maxLevel}");
            }

            if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem))
            {
                throw new NotEnoughMaterialException(
                          $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})."
                          );
            }

            if (materialItem.RequiredBlockIndex > context.BlockIndex)
            {
                throw new RequiredBlockIndexException(
                          $"{addressesHex}Aborted as the material ({materialId}) is not available yet;" +
                          $" it will be available at the block #{materialItem.RequiredBlockIndex}."
                          );
            }

            if (!(materialItem is Equipment materialEquipment))
            {
                throw new InvalidCastException(
                          $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}."
                          );
            }

            if (enhancementEquipment.ItemId == materialId)
            {
                throw new InvalidMaterialException(
                          $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too."
                          );
            }

            if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType)
            {
                throw new InvalidMaterialException(
                          $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}," +
                          $" but {materialEquipment.ItemSubType}."
                          );
            }

            if (materialEquipment.Grade != enhancementEquipment.Grade)
            {
                throw new InvalidMaterialException(
                          $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade})" +
                          $" and a material ({materialEquipment.Grade}) does not match."
                          );
            }

            if (materialEquipment.level != enhancementEquipment.level)
            {
                throw new InvalidMaterialException(
                          $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level})" +
                          $" and a material ({materialEquipment.level}) does not match."
                          );
            }
            sw.Stop();
            Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            // Subtract required action point
            var requiredActionPoint = GetRequiredAp();

            if (avatarState.actionPoint < requiredActionPoint)
            {
                throw new NotEnoughActionPointException(
                          $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}"
                          );
            }
            avatarState.actionPoint -= requiredActionPoint;

            // TransferAsset (NCG)
            var requiredNcg = row.Cost;

            if (requiredNcg > 0)
            {
                states = states.TransferAsset(ctx.Signer, BlacksmithAddress, states.GetGoldCurrency() * requiredNcg);
            }

            // Unequip items
            materialEquipment.Unequip();
            enhancementEquipment.Unequip();

            // clone items
            var preItemUsable = new Equipment((Dictionary)enhancementEquipment.Serialize());

            // Equipment level up & Update
            var equipmentResult = GetEnhancementResult(row, ctx.Random);

            if (equipmentResult != EnhancementResult.Fail)
            {
                enhancementEquipment.LevelUpV2(ctx.Random, row, equipmentResult == EnhancementResult.GreatSuccess);
            }
            var requiredBlockCount = GetRequiredBlockCount(row, equipmentResult);
            var requiredBlockIndex = ctx.BlockIndex + requiredBlockCount;

            enhancementEquipment.Update(requiredBlockIndex);

            // Remove material
            avatarState.inventory.RemoveNonFungibleItem(materialId);
            sw.Stop();
            Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed);

            // Send scheduled mail
            var result = new ResultModel
            {
                preItemUsable      = preItemUsable,
                itemUsable         = enhancementEquipment,
                materialItemIdList = new[] { materialId },
                actionPoint        = requiredActionPoint,
                enhancementResult  = equipmentResult,
                gold = requiredNcg,
            };

            var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex);

            result.id = mail.id;
            avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment);
            avatarState.Update(mail);
            avatarState.UpdateFromItemEnhancement(enhancementEquipment);

            // Update quest reward
            var materialSheet = states.GetSheet <MaterialItemSheet>();

            avatarState.UpdateQuestRewards(materialSheet);

            // Update slot state
            slotState.Update(result, ctx.BlockIndex, requiredBlockIndex);

            // Set state
            sw.Restart();
            states = states
                     .SetState(inventoryAddress, avatarState.inventory.Serialize())
                     .SetState(worldInformationAddress, avatarState.worldInformation.Serialize())
                     .SetState(questListAddress, avatarState.questList.Serialize())
                     .SetState(avatarAddress, avatarState.SerializeV2());
            sw.Stop();
            Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started);
            return(states.SetState(slotAddress, slotState.Serialize()));
        }
Exemple #5
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx         = context;
            var            states      = ctx.PreviousStates;
            var            slotAddress = avatarAddress.Derive(
                string.Format(
                    CultureInfo.InvariantCulture,
                    CombinationSlotState.DeriveFormat,
                    slotIndex
                    )
                );

            if (ctx.Rehearsal)
            {
                return(states
                       .MarkBalanceChanged(GoldCurrencyMock, BlacksmithAddress)
                       .SetState(avatarAddress, MarkChanged)
                       .SetState(slotAddress, MarkChanged));
            }
            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Debug("ItemEnhancement exec started.");

            if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState,
                                                out AvatarState avatarState))
            {
                return(LogError(context, "Aborted as the avatar state of the signer was failed to load."));
            }
            sw.Stop();
            Log.Debug("ItemEnhancement Get AgentAvatarStates: {Elapsed}", sw.Elapsed);
            sw.Restart();

            if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem))
            {
                // 강화 장비가 없는 에러.
                return(LogError(
                           context,
                           "Aborted as the NonFungibleItem ({ItemId}) was failed to load from avatar's inventory.",
                           itemId
                           ));
            }

            if (enhancementItem.RequiredBlockIndex > context.BlockIndex)
            {
                return(LogError(
                           context,
                           "Aborted as the equipment to enhance ({ItemId}) is not available yet; it will be available at the block #{RequiredBlockIndex}.",
                           itemId,
                           enhancementItem.RequiredBlockIndex
                           ));
            }

            if (!(enhancementItem is Equipment enhancementEquipment))
            {
                // 캐스팅 버그. 예외상황.
                return(LogError(
                           context,
                           $"Aborted as the item is not a {nameof(Equipment)}, but {{ItemType}}.",
                           enhancementItem.GetType().Name
                           ));
            }

            var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex);

            if (slotState is null || !(slotState.Validate(avatarState, ctx.BlockIndex)))
            {
                return(LogError(context, "Aborted as the slot state was failed to load or invalid."));
            }

            sw.Stop();
            Log.Debug("ItemEnhancement Get Equipment: {Elapsed}", sw.Elapsed);
            sw.Restart();

            if (enhancementEquipment.level > 9)
            {
                // 최대 강화도 초과 에러.
                return(LogError(
                           context,
                           "Aborted due to invaild equipment level: {EquipmentLevel} < 9",
                           enhancementEquipment.level
                           ));
            }

            var result = new ResultModel
            {
                itemUsable         = enhancementEquipment,
                materialItemIdList = materialIds
            };

            var requiredAP = GetRequiredAp();

            if (avatarState.actionPoint < requiredAP)
            {
                // AP 부족 에러.
                return(LogError(
                           context,
                           "Aborted due to insufficient action point: {ActionPointBalance} < {ActionCost}",
                           avatarState.actionPoint,
                           requiredAP
                           ));
            }

            var tableSheets = TableSheets.FromActionContext(ctx);
            var requiredNCG = GetRequiredNCG(tableSheets, enhancementEquipment.Grade, enhancementEquipment.level + 1);

            avatarState.actionPoint -= requiredAP;
            result.actionPoint       = requiredAP;

            if (requiredNCG > 0)
            {
                states = states.TransferAsset(ctx.Signer, BlacksmithAddress, states.GetGoldCurrency(), requiredNCG);
            }

            sw.Stop();
            Log.Debug("ItemEnhancement Get TableSheets: {Elapsed}", sw.Elapsed);
            sw.Restart();
            var materials = new List <Equipment>();

            foreach (var materialId in materialIds)
            {
                if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem))
                {
                    // 인벤토리에 재료로 등록한 장비가 없는 에러.
                    return(LogError(
                               context,
                               "Aborted as the the signer does not have a necessary material ({MaterialId}).",
                               materialId
                               ));
                }

                if (materialItem.RequiredBlockIndex > context.BlockIndex)
                {
                    return(LogError(
                               context,
                               "Aborted as the material ({MaterialId}) is not available yet; it will be available at the block #{RequiredBlockIndex}.",
                               materialId,
                               materialItem.RequiredBlockIndex
                               ));
                }

                if (!(materialItem is Equipment materialEquipment))
                {
                    return(LogError(
                               context,
                               $"Aborted as the material item is not a {nameof(Equipment)}, but {{ItemType}}.",
                               materialItem.GetType().Name
                               ));
                }

                if (materials.Contains(materialEquipment))
                {
                    // 같은 guid의 아이템이 중복해서 등록된 에러.
                    return(LogError(
                               context,
                               "Aborted as the same material was used more than once: {Material}",
                               materialEquipment
                               ));
                }

                if (enhancementEquipment.ItemId == materialId)
                {
                    // 강화 장비와 재료로 등록한 장비가 같은 에러.
                    return(LogError(
                               context,
                               "Aborted as an equipment to enhance ({ItemId}) was used as a material too.",
                               materialId
                               ));
                }

                if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType)
                {
                    // 서브 타입이 다른 에러.
                    return(LogError(
                               context,
                               "Aborted as the material item is not a {ExpectedItemSubType}, but {MaterialSubType}.",
                               enhancementEquipment.ItemSubType,
                               materialEquipment.ItemSubType
                               ));
                }

                if (materialEquipment.Grade != enhancementEquipment.Grade)
                {
                    // 등급이 다른 에러.
                    return(LogError(
                               context,
                               "Aborted as grades of the equipment to enhance ({EquipmentGrade}) and a material ({MaterialGrade}) do not match.",
                               enhancementEquipment.Grade,
                               materialEquipment.Grade
                               ));
                }

                if (materialEquipment.level != enhancementEquipment.level)
                {
                    // 강화도가 다른 에러.
                    return(LogError(
                               context,
                               "Aborted as levels of the equipment to enhance ({EquipmentLevel}) and a material ({MaterialLevel}) do not match.",
                               enhancementEquipment.level,
                               materialEquipment.level
                               ));
                }
                sw.Stop();
                Log.Debug("ItemEnhancement Get Material: {Elapsed}", sw.Elapsed);
                sw.Restart();
                materialEquipment.Unequip();
                materials.Add(materialEquipment);
            }

            enhancementEquipment.Unequip();

            enhancementEquipment = UpgradeEquipment(enhancementEquipment);
            sw.Stop();
            Log.Debug("ItemEnhancement Upgrade Equipment: {Elapsed}", sw.Elapsed);
            sw.Restart();

            result.gold = 0;

            foreach (var material in materials)
            {
                avatarState.inventory.RemoveNonFungibleItem(material);
            }
            sw.Stop();
            Log.Debug("ItemEnhancement Remove Materials: {Elapsed}", sw.Elapsed);
            sw.Restart();
            var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex);

            result.id = mail.id;

            avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment);
            avatarState.Update(mail);
            avatarState.UpdateFromItemEnhancement(enhancementEquipment);

            avatarState.UpdateQuestRewards(ctx);

            slotState.Update(result, ctx.BlockIndex, ctx.BlockIndex);

            sw.Stop();
            Log.Debug("ItemEnhancement Update AvatarState: {Elapsed}", sw.Elapsed);
            sw.Restart();
            states = states.SetState(avatarAddress, avatarState.Serialize());
            sw.Stop();
            Log.Debug("ItemEnhancement Set AvatarState: {Elapsed}", sw.Elapsed);
            var ended = DateTimeOffset.UtcNow;

            Log.Debug("ItemEnhancement Total Executed Time: {Elapsed}", ended - started);
            return(states.SetState(slotAddress, slotState.Serialize()));
        }