C# 使用WinForm写的一个魔兽世界单机版T端修改Boss掉落数量的小工具

单机版的掉落机制比较复杂,一般大家修改掉落数量都是简单粗暴的把所有分组取消但是这样就完全依赖掉率了

我之前研究了一段时间的T端掉落,摸索出几个机制

1,概率掉率,当掉率表中的ChanceOrQuestChance大于0时,是概率掉落,ChanceOrQuestChance为负时,为任务物品掉率

2,分组掉落,当groupid大于0时,会进行分组掉落,如果groupid都是1,则只掉落1个,如果groupid有1,2,则掉落2个,有几个不同的groupid就掉落几个

3,引用掉落,当mincountOrRef为负数时,去掉负号的数值就是reference_loot_template表中的entry,它会从reference_loot_template表中抽maxcount个物品进行掉落

因此我就根据以上机制进行更改

1,如果是概率掉落,我就让其乘以指定的倍率,修改ChanceOrQuestChance

2,如果是分组掉落,我就将相同分组中的后一半物品改为新的分组

3,如果是引用掉落,我就让其maxcount*2,这样从引用表中抽取的物品数据也会翻倍

估计还有其他机制我没搞清楚,也可能有些是我理解错了,不过按照这个进行修改已经能很好提高掉落数量了

而且我会先查询一遍物品表,查询出高品质的物品,避免全部更改后掉落大量垃圾物品

源码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LootChange {
    public partial class MainForm : Form {
        /// <summary>
        /// mysql连接字符串
        /// </summary>
        string connString;
        /// <summary>
        /// 代币掉落数量倍率
        /// </summary>
        float rateTokenMoney;
        /// <summary>
        /// 有效掉率掉落倍率
        /// </summary>
        float rateChange;
        public MainForm() {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e) {
            TxtMsg.AppendText("1.执行操作前请做好数据备份,避免数据损坏" + Environment.NewLine);
            TxtMsg.AppendText("2.执行操作可能很慢并且界面卡住,请耐心等待" + Environment.NewLine);
            TxtMsg.AppendText("3.每次只能执行一次操作,再次执行需要重启程序" + Environment.NewLine);
            TxtMsg.AppendText("4.每成功执行一次,掉落数量翻一倍" + Environment.NewLine);
        }

        private void BtnOK_Click(object sender, EventArgs e) {
            try {
                if (txtPid.Text == "" || txtPwd.Text == "") {
                    MessageBox.Show("请输入必要信息!");
                    return;
                }
                connString = $"Database=world;Data Source=127.0.0.1;User Id={txtPid.Text};Password={txtPwd.Text};port=3306;SslMode=None";
                rateTokenMoney = Convert.ToSingle(TxtRateTokenMoney.Text);
                rateChange = Convert.ToSingle(TxtRateChange.Text);
            } catch (Exception ex) {
                MessageBox.Show("输入的信息异常:" + ex.Message);
                return;
            }
            //所有控件置为不响应,避免重复操作
            foreach (Control control in Controls) {
                control.Enabled = false;
            }
            Enabled = false; //窗体也变灰,避免中途关闭窗体
            Task.Run(ProgressAsync);
        }
        /// <summary>
        /// 输出执行信息
        /// </summary>
        void ShowMsg(string msg) {
            TxtMsg.Invoke((Action)(() => {
                TxtMsg.AppendText(DateTime.Now.ToString("mm:ss") + " " + msg + Environment.NewLine);
            }));
        }
        /// <summary>
        /// 异步执行操作,避免业务逻辑导致界面卡死
        /// </summary>
        void ProgressAsync() {
            string[] tableNames = new string[] { //3个相关的掉落表
                "creature_loot_template", //Boss掉落
                "item_loot_template", //剩下的2个猜测是宝箱掉落,只是猜测
                "gameobject_loot_template"
            };
            try {
                HashSet<int> items = GetItems();
                ShowMsg("查询品质高于绿色的武器和装备" + items.Count);
                HashSet<int> mincountOrRefs = GetEntrysInReference_loot_template(items);
                ShowMsg("查询引用表中包含符合条件物品的掉落引用" + mincountOrRefs.Count);
                HashSet<int> item10s = GetItems_10();
                ShowMsg("查询到代币" + item10s.Count);
                foreach (var tableName in tableNames) {
                    try {
                        var dict = GetAllGroup(tableName);
                        ShowMsg($"{tableName} 查询全部分组信息" + dict.Count);
                        ShowMsg($"{tableName} 更新分组完毕,执行" + UpdateGroupid(tableName, dict, items));
                        ShowMsg($"{tableName} 更新引用掉落maxcount,执行" + UpdateMaxcountByMincountOrRef(tableName, mincountOrRefs));
                        ShowMsg($"{tableName} 更新代币,倍率{rateTokenMoney},执行" + UpdateCountForClassIs10(tableName, item10s));
                        ShowMsg($"{tableName} 更新有效爆率,倍率{rateChange},执行" + UpdateChange(tableName));
                    } catch (Exception ex) {
                        MessageBox.Show($"{tableName}表更新异常:{ex.Message}");
                        break;
                    }
                }
                ShowMsg("全部执行完毕,请关闭窗口!");
                Invoke((Action)(() => {
                    Enabled = true;
                    TxtMsg.Enabled = true;
                }));
            } catch (Exception ex) {
                MessageBox.Show("失败!" + ex.Message);
            }
        }
        /// <summary>
        /// 查询品质高于绿色品质的武器和装备,避免出现大量垃圾物品
        /// </summary>
        HashSet<int> GetItems() {
            HashSet<int> hs = new HashSet<int>();
            var sql = "select entry from item_template where class in (2,4) and Quality>=3;";
            using (var dr = new MySqlDBHelper(connString).GetDataReader(sql)) {
                while (dr.Read()) {
                    hs.Add(Convert.ToInt32(dr["entry"]));
                }
            }
            return hs;
        }
        /// <summary>
        /// 得到引用表中包含符合条件物品id的引用id,用以判定是否更改引用掉落的个数
        /// </summary>
        HashSet<int> GetEntrysInReference_loot_template(HashSet<int> items) {
            HashSet<int> entrys = new HashSet<int>();
            string sql = $"select entry,item from reference_loot_template;";
            using (var dr = new MySqlDBHelper(connString).GetDataReader(sql)) {
                while (dr.Read()) {
                    if (items.Contains(Convert.ToInt32(dr["item"]))) {
                        int entry = Convert.ToInt32(dr["entry"]);
                        if (!entrys.Contains(entry)) {
                            entrys.Add(entry);
                        }
                    }
                }
            }
            return entrys;
        }
        /// <summary>
        /// 得到高品质的代币id,class=10即各种代币,比如凯旋纹章,寒冰纹章
        /// </summary>
        HashSet<int> GetItems_10() {
            HashSet<int> hs = new HashSet<int>();
            var sql = "select entry from item_template where class=10 and Quality>=3;";
            using (var dr = new MySqlDBHelper(connString).GetDataReader(sql)) {
                while (dr.Read()) {
                    hs.Add(Convert.ToInt32(dr["entry"]));
                }
            }
            return hs;
        }
        /// <summary>
        /// 查询分组掉落的全部分组
        /// </summary>
        Dictionary<int, List<Model>> GetAllGroup(string tableName) {
            Dictionary<int, List<Model>> dict_entry_loots = new Dictionary<int, List<Model>>();
            string sql = $"select entry,item,groupid from {tableName} where groupid>0;";
            using (var dr = new MySqlDBHelper(connString).GetDataReader(sql)) {
                while (dr.Read()) {
                    int entry = Convert.ToInt32(dr["entry"]); //以掉落id为键
                    if (!dict_entry_loots.TryGetValue(entry, out List<Model> list)) {
                        list = new List<Model>();
                        dict_entry_loots.Add(entry, list);
                    }
                    list.Add(new Model() { //关联具体的掉落信息
                        entry = entry,
                        item = Convert.ToInt32(dr["item"]),
                        groupId = Convert.ToInt32(dr["groupid"]),
                    });
                }
            }
            return dict_entry_loots;
        }
        /// <summary>
        /// 更新分组,将每个掉落的分组数量翻倍
        /// </summary>
        int UpdateGroupid(string tableName, Dictionary<int, List<Model>> dict, HashSet<int> items) {
            StringBuilder sql = new StringBuilder();
            sql.Append("start transaction;");
            int counter = 0;
            foreach (var kv in dict) { //循环每个掉落
                Dictionary<int, List<Model>> kv_groupId_loots = new Dictionary<int, List<Model>>();  //记录分组
                foreach (var p in kv.Value) {
                    if (!kv_groupId_loots.TryGetValue(p.groupId, out List<Model> list)) {
                        list = new List<Model>();
                        kv_groupId_loots.Add(p.groupId, list); //以分组id为键,记录所有分组,避免分组id冲突
                    }
                    if (items.Contains(p.item)) { //但是只保存符合条件的
                        list.Add(p);
                    }
                }
                int groupId = kv_groupId_loots.Keys.Max() + 1; //新编groupId的值  
                foreach (var loots in kv_groupId_loots.Values) { //循环每个分组,理论上最多会新增一倍的新分组
                    if (loots.Count > 1) { //如果该组数量大于1
                        foreach (var loot in loots.Skip(loots.Count / 2)) { //就将后一半数据的groupId更新成新的
                            sql.Append($"update {tableName} set groupid={groupId} where entry={loot.entry} and item={loot.item};");
                        }
                        groupId++;
                        counter++;
                    }
                }
            }
            sql.Append("commit;");
            ShowMsg($"{tableName} 新建分组{counter},开始执行sql,字符{sql.Length}");
            int rs = new MySqlDBHelper(connString).ExecuteCommand(sql.ToString(), 300);
            ShowMsg($"{tableName} 执行sql完毕");
            return rs;
        }
        /// <summary>
        /// 有的掉落会通过mincountOrRef使用引用表,maxcount取决掉落几个,因此这里要尝试更新maxcount
        /// </summary>
        int UpdateMaxcountByMincountOrRef(string tableName, HashSet<int> mincountOrRefs) {
            StringBuilder sb = new StringBuilder();
            sb.Append("start transaction;");
            foreach (var mincountOrRef in mincountOrRefs) { //循环每个掉落 
                /*
                 * 如果当前表引用了引用表,就将maxcount*2,需要注意maxcount最大255,如果其原数值大于等于128,就会超出范围报错,
                 * 当前表引用引用表的方式是通过mincountOrRef等于负的引用表id实现的
                 * */
                sb.Append($"update {tableName} set maxcount=maxcount*2 where mincountOrRef=-{mincountOrRef} and maxcount<128;");
            }
            sb.Append("commit;");
            return new MySqlDBHelper(connString).ExecuteCommand(sb.ToString(), 300);
        }
        /// <summary>
        /// class=10的是代币,即各种纹章
        /// </summary>
        int UpdateCountForClassIs10(string tableName, HashSet<int> item10s) {
            float divide = 256 / rateChange;
            StringBuilder sb = new StringBuilder();
            sb.Append("start transaction;");
            foreach (var item in item10s) { //更新每个表中符合条件的代币的掉落数量倍率,注意最大不能超出255
                sb.Append($"update {tableName} set mincountOrRef=mincountOrRef*{rateTokenMoney},maxcount=maxcount*{rateTokenMoney} where item={item} and maxcount<{divide};");
            }
            sb.Append("commit;");
            return new MySqlDBHelper(connString).ExecuteCommand(sb.ToString(), 300);
        }
        /// <summary>
        /// 更新有效爆率,ChanceOrQuestChance大于0时,为有效爆率,ChanceOrQuestChance为负数是任务物品的掉率,这里不考虑
        /// </summary>
        int UpdateChange(string tableName) {
            float divide = 100 / rateChange;
            //如果更新后爆率没有超过100,就直接更新
            string sql = $"update {tableName} set ChanceOrQuestChance=ChanceOrQuestChance*{rateChange} " +
                $"where ChanceOrQuestChance>0 and ChanceOrQuestChance<{divide};";
            int rs = new MySqlDBHelper(connString).ExecuteCommand(sql, 300);
            //如果更新后超出了100,就直接更新为100,避免溢出报错
            sql = $"update {tableName} set ChanceOrQuestChance=100 " +
                $"where ChanceOrQuestChance>0 and ChanceOrQuestChance>={divide};";
            rs += new MySqlDBHelper(connString).ExecuteCommand(sql, 300);
            return rs;
        }
    }
}
View Code

Model模型

namespace LootChange {
    internal class Model {
        public int entry;
        public int item;
        public int groupId;
    }
}
View Code

 贴一个编译后直接可以用的exe,注意,这个只适合T端!

链接: https://pan.baidu.com/s/1IDAerFrIg9zYXEK21HgwLQ?pwd=gvnz 提取码: gvnz

posted @ 2023-10-19 10:23  WmW  阅读(523)  评论(0编辑  收藏  举报