public static PacketWriter OpenWindow(long itemUid, EnchantScrollMetadata metadata, float successRate)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.EnchantScroll);

        pWriter.Write(EnchantScrollMode.OpenWindow);
        pWriter.WriteLong(itemUid);
        pWriter.Write(metadata.ScrollType);
        pWriter.WriteBool(false); // untradeable reminder
        pWriter.WriteInt(metadata.EnchantLevels.First());
        pWriter.WriteInt((int)(successRate * 10000));
        pWriter.WriteShort(metadata.MinLevel);
        pWriter.WriteShort(metadata.MaxLevel);
        pWriter.WriteInt(metadata.ItemTypes.Count);
        foreach (ItemType type in metadata.ItemTypes)
        {
            pWriter.Write(type);
        }
        pWriter.WriteInt(metadata.Rarities.Count);
        foreach (int rarity in metadata.Rarities)
        {
            pWriter.WriteInt(rarity);
        }

        if (metadata.ScrollType == EnchantScrollType.RandomEnchant)
        {
            pWriter.WriteInt(metadata.EnchantLevels.First());
            pWriter.WriteInt(metadata.EnchantLevels.Last());
        }
        return(pWriter);
    }
    private static void HandleEnchantScroll(GameSession session, Item item)
    {
        EnchantScrollMetadata metadata = EnchantScrollMetadataStorage.GetMetadata(item.Function.Id);

        if (metadata is null)
        {
            return;
        }

        Script script      = ScriptLoader.GetScript("Functions/ItemEnchantScroll/getSuccessRate");
        float  successRate = (float)script.RunFunction("getSuccessRate", metadata.Id).Number;

        session.Send(EnchantScrollPacket.OpenWindow(item.Uid, metadata, successRate));
    }
    public override void Handle(GameSession session, PacketReader packet)
    {
        EnchantScrollMode mode = (EnchantScrollMode)packet.ReadByte();

        long scrollUid = packet.ReadLong();
        long equipUid  = packet.ReadLong();

        Player player = session.Player;

        if (!player.Inventory.HasItem(equipUid) && !player.Inventory.HasItem(scrollUid))
        {
            session.Send(EnchantScrollPacket.UseScroll((short)EnchantScrollError.ItemsNoLongerValid));
            return;
        }

        Item scroll = player.Inventory.GetByUid(scrollUid);
        Item equip  = player.Inventory.GetByUid(equipUid);

        EnchantScrollMetadata metadata = EnchantScrollMetadataStorage.GetMetadata(scroll.Function.Id);

        if (metadata is null)
        {
            return;
        }

        if (!metadata.Rarities.Contains(equip.Rarity))
        {
            session.Send(EnchantScrollPacket.UseScroll((short)EnchantScrollError.CannotBeEnchantedDueToRarity));
            return;
        }

        if (metadata.MinLevel > equip.Level || metadata.MaxLevel < equip.Level)
        {
            session.Send(EnchantScrollPacket.UseScroll((short)EnchantScrollError.InsufficientItemLevel));
            return;
        }

        if (!metadata.ItemTypes.Contains(equip.Type))
        {
            session.Send(EnchantScrollPacket.UseScroll((short)EnchantScrollError.IneligibleItem));
            return;
        }

        if (equip.EnchantLevel >= metadata.EnchantLevels.Last())
        {
            session.Send(EnchantScrollPacket.UseScroll((short)EnchantScrollError.GearCannotBeEnchanted));
            return;
        }

        int enchantLevelIndex = Random.Shared.Next(metadata.EnchantLevels.Count);
        Dictionary <StatAttribute, ItemStat> enchantStats = EnchantHelper.GetEnchantStats(metadata.EnchantLevels[enchantLevelIndex], equip.Type, equip.Level);

        switch (mode)
        {
        case EnchantScrollMode.AddItem:
            session.Send(EnchantScrollPacket.AddItem(equipUid, enchantStats));
            break;

        case EnchantScrollMode.UseScroll:
            HandleUseScroll(session, equip, scroll, enchantStats, metadata.EnchantLevels[enchantLevelIndex], metadata.Id);
            break;

        default:
            LogUnknownMode(mode);
            break;
        }
    }