0


[WinForm开源]概率计算器 - Genshin Impact(V1.0)

创作目的:为方便旅行者估算自己拥有的纠缠之缘能否达到自己的目的,作者使用C#开发了一款小型软件供旅行者参考使用。

创作说明:此软件所涉及到的一切概率与规则完全按照游戏《原神》(V4.4.0)内公示的概率与规则(包括保底机制)进行编写,仅用于计模拟已知纠缠之缘数量对已知抽取五星目标的成功频率(可看作成功率),无任何暗箱操作。


一、软件介绍

1.软件主页面展示

2.软件适用范围

该软件适用范围为:2位UP角色不同卡池但一同保底计数、2把专武在同一卡池且只能定轨一把武器。[说通俗点就是目前能够支持到4.4下半版本,后期如开启混池,作者将会对软件进行更新]

3.软件使用方法

点击顶部「卡池选择」下拉框可切换当前的卡池信息,目前支持4.3与4.4上下半卡池总计4期卡池。

点击顶部「点击查看卡池海报」按钮可以查看选择的当期卡池的海报。

左侧「计划抽取」一栏可以选择旅行者计划抽取的角色、武器的数量,默认为0,角色最大为7,武器最大为5 。当旅行者四栏全部选择为0时,将不被允许进行模拟。

左侧「当前资源」一栏填写旅行者计划投入的纠缠之缘与原石的数量,当填入的资源不足1抽时将不被允许进行模拟,最大支持999抽。

左侧「其他情况」一栏旅行者可以勾选自己对应的情况,若不清楚对于情况,可进入游戏进行查看;旅行者勾选的选项将影响到模拟结果。

中间「用户须知」一栏为简要的用户须知,如页面过小可点击放大到新窗口。旅行者阅读完毕请勾选其下方的“我已认真阅读上述内容”。

点击中间「仅将原石转化为纠缠之缘」按钮,软件将根据用户填入的原石数量,转化为纠缠之缘并将合计的纠缠之缘数量、剩余的原石数量展现给旅行者。

点击中间「开始模拟」按钮,软件将进行10,000次模拟,每次模拟到抽取到目标为止,若有纠缠之缘剩余,则计入剩余总数,最终计算平均剩余数。软件最终将给出「达成预期频率」「超越预期频率」「综合成功频率」(后面介绍)。模拟的计算速度取决于旅行者的纠缠之缘数量与计算机的运算速度。

右侧「达成预期频率」一栏将展示软件对旅行者给出的资源数量与目标进行1W次模拟后得到的恰好达到目标的情况的数量与模拟总量的比值。“达成预期”判定标准:在达成所有抽取目标后,纠缠之缘剩余量不小于0时,所获得的所有5星物品(含常驻)与填写的抽取目标完全一致。

右侧「超出预期频率」一栏将展示软件对旅行者给出的资源数量与目标进行1W次模拟后得到的超出预期目标的情况的数量与模拟总量的比值。“超出预期”判定标准:在达成所有抽取目标后,纠缠之缘剩余量不小于0时,所获得的所有5星物品(含常驻)超出填写的抽取目标(常驻默认为0)。

右侧「综合成功频率」一栏将展示软件对旅行者给出的资源数量与目标进行1W次模拟后得到的能够完成给定的抽取目标的情况的数量与模拟总量的比值。该栏为上述两栏之和。

点击右侧「保存为图片」按钮,软件将会把当前页面保存为PNG图片。

二、获取途径

1.软件获取

(1)CSDN站内下载:https://download.csdn.net/download/qq_64516527/88817489

(2)百度网盘下载:https://pan.baidu.com/s/1Uw1mMIONUh9-heMUHRgOOQ?pwd=SLLH 提取码:SLLH

(3)123云盘下载:https://www.123pan.com/s/Il2bVv-CwvQh.html

2.项目获取

(1)CSDN站内下载:https://download.csdn.net/download/qq_64516527/88817874

(2)123云盘下载:https://www.123pan.com/s/Il2bVv-NwvQh.html

三、项目开源

注:接下来的代码主要按页面进行分栏,代码中注释比较全,可直接阅读。

1.MainForm页面(软件主页面)

设计器:

页面代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.Security.Cryptography;
using System.IO;

namespace 概率计算器___Genshin_Impact
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        #region 变量的声明
        public bool Avaluable = true;
        Thread Thread_Load = null;//淡入淡出的线程
        Thread Thread_DataDetection = null;//用于检测数据并调控Enable属性的线程
        Thread T_Imitate = null;//用于进行模拟的线程
        public int NUM_Ball = 0, NUM_Stone = 0;//纠缠之缘与原石的数量
        public int[] Success = new int[2] { 0, 0 };//分别表示恰好达到预期与超出预期的次数
        public int Success_Sum = 0;//表示综合成功次数
        public bool NextRoleIsBig = false;//表示下次角色池是否为大保底
        public bool NextWeaponIsUp = false;//表示下次武器池是否为UP武器
        public int Num_Weapon_Base = 0;//表示武器池定轨数
        string[,] RoleName = new string[4, 2] {
            {"娜维娅", "神里绫华" },//4.3上半卡池,卡池编码为0
            {"雷电将军", "宵宫" },//4.3下半卡池,卡池编码为1
            { "闲云", "纳西妲" }, //4.4上半卡池,卡池编码为2
            { "魈", "八重神子" }//4.4下半卡池,卡池编码为3
        };
        //第一维度为版本编号,第二维度为卡池序号
        //比如4.4上半对应第一维度,而角色祈愿-2对应第二维度
        //角色头像图片名称为RoleName[m,n] + "·头像.png"
        //专武图片名称为RoleName[m,n] + "专武.png"
        #endregion

        private void MainForm_Load(object sender, EventArgs e)
        {
            panelMain.BackColor = Color.FromArgb(191, 0, 0, 0);
            panelMain.Height = 0;
            Thread_Load = new Thread(() => LoadThread());
            Thread_Load.Start();
            Thread_DataDetection = new Thread(() => T_DataDetection());
            Thread_DataDetection.Start();

            foreach (Control control in panelMain.Controls)//遍历panel中的所有控件
                if (!((control is ComboBox) || (control is TextBox) || (control is RichTextBox)))
                    control.BackColor = Color.FromArgb(0, 0, 0, 0);
                else if (control is ComboBox)
                {
                    ComboBox CB = (ComboBox)control;
                    CB.SelectedIndex = 0;
                }
            pictureBox_README.BackColor = Color.FromArgb(64, 0, 0, 0);

            comboBox_VersionChoose.SelectedIndex = 0;//设置初始选择值
            #region 关于SelectedIndex的说明
            /* 0为4.4上半(闲云 纳西妲)
             * 1为4.4下半(魈 八重神子)
             */
            #endregion

            label1.Text = "还未进行过模拟";
        }

        private void T_DataDetection()
        {
            bool B = false;//用于判断能否抽卡
            while(true)
            {
                if (NUM_Stone >= 160)
                    Invoke(new Action(() => { button_Transport.Enabled = true; }));//跨线程操作
                else Invoke(new Action(() => { button_Transport.Enabled = false; }));//跨线程操作
                B = ((NUM_Ball > 0) && (NUM_Stone >= 0)) || ((NUM_Ball == 0) && (NUM_Stone >= 160));
                if (B && Avaluable)
                        Invoke(new Action(() => { button_Start.Enabled = true; }));//跨线程操作
                else Invoke(new Action(() => { button_Start.Enabled = false; }));//跨线程操作
                Thread.Sleep(50);
            }
        }//用于检测数据并调控Enable属性的线程

        private void LoadThread()
        {
            do
            {
                Invoke(new Action(() =>
                {
                    panelMain.Height += 20;
                }));//跨线程操作
                Thread.Sleep(2);
            } while (panelMain.Height <= 750);
        }//黑色半透明下降的动画的线程

        #region 为获取comboBox控件的SelectedIndex属性而定义的内容
        // 定义一个委托来执行操作  
        delegate int GetSelectedIndexDelegate();

        // 在主线程中执行操作的方法  
        private int GetCBWNSelectedIndex()
        {
            if (comboBox_WeaponN.InvokeRequired)
            {
                // 如果需要跨线程调用,则使用Invoke  
                return (int)comboBox_WeaponN.Invoke(new GetSelectedIndexDelegate(GetCBWNSelectedIndex));
            }
            else
            {
                // 直接返回SelectedIndex  
                return comboBox_WeaponN.SelectedIndex;
            }
        }
        #endregion

        private void imitate(int N,int[] Target,CheckBox[] CB, ComboBox comboBox_WN)
        {
            //Target储存四者的抽取目标
            double Average_Residue = 0;//平均剩余的粉球
            for (int i=1;i<=10000;i++)
            {
                NextRoleIsBig = CB[2].Checked;

                #region 获取并储存comboBox_WN的SelectedIndex属性值的代码段
                int CB_WN_SI;//储存comboBox_WN的SelectedIndex属性值
                CB_WN_SI = GetCBWNSelectedIndex();
                #endregion

                if (CB[3].Checked)//定轨不为0/2的情况
                    Num_Weapon_Base = CB_WN_SI + 1;
                if (CB[3].Checked && CB_WN_SI == 0 && CB[4].Checked)
                    NextWeaponIsUp = true;//定轨0/2歪常驻变成1/2的情况,下次必定是UP武器

                int N_Ball;
                if (CB[0].Checked) N_Ball = (int)(N / 0.9);
                else N_Ball = N;
                bool[] Process_Role_Weapon = new bool[4] { false, false, false, false };//记录四者的抽取目标达成情况
                int[] Reality = new int[5] { 0, 0, 0, 0, 0};//记录实际抽取到的数量,第五个表示歪的常驻(包括角色与武器)

                for (int j = 0; j < 4; j++)
                    if (Target[j] == 0)
                        Process_Role_Weapon[j] = true;//抽取目标为0的直接标记为已达成目标
                //然后调用方法进行模拟并通过返回值得到消耗的纠缠之缘数量
                if (!CB[1].Checked)
                {
                    for (int j = 0; j <= 1; j++)
                        if (!Process_Role_Weapon[j])
                            N_Ball -= RolePrayer(Target[j], Reality, Process_Role_Weapon, j);
                    for (int j = 2; j <= 3; j++)
                        if (!Process_Role_Weapon[j])
                            N_Ball -= WeaponPrayer(Target[j], Reality, Process_Role_Weapon, j);
                }
                else
                {//如果勾选了checkBox2,那么优先抽取武器
                    for (int j = 2; j <= 3; j++)
                        if (!Process_Role_Weapon[j])
                            N_Ball -= WeaponPrayer(Target[j], Reality, Process_Role_Weapon, j);
                    for (int j = 0; j <= 1; j++)
                        if (!Process_Role_Weapon[j])
                            N_Ball -= RolePrayer(Target[j], Reality, Process_Role_Weapon, j);
                }

                //然后对该次模拟结果进行统计
                if (i == 1 && N_Ball >= 0) Average_Residue = N_Ball;
                else if (N_Ball >= 0) Average_Residue = (double)(Average_Residue * (i - 1) + N_Ball) / i;
                else Average_Residue = (double)(Average_Residue * (i - 1)) / i;//统计平均剩余粉球

                if (Process_Role_Weapon[0] && 
                    Process_Role_Weapon[1] && 
                    Process_Role_Weapon[2] && 
                    Process_Role_Weapon[3] &&
                    N_Ball>=0)
                {//说明达成目标
                    bool NotOver = true;
                    for (int j = 0; j < 4; j++)
                        NotOver = NotOver && (Reality[j] == Target[j]);
                    NotOver = NotOver && (Reality[4] == 0);
                    if (NotOver) Success[0]++;//这里是恰好达成目标
                    else Success[1]++;//这里是超出预期
                    Success_Sum++;
                }
                Invoke(new Action(() =>
                {
                    if (i != 10000)
                        label1.Text = "已进行" + i.ToString() + "次模拟……";
                    else label1.Text = "已进行10000次模拟,每次模拟投入" + N.ToString() + "纠缠。";
                    label_Succ_1_3.Text = "共计" + Success[0].ToString() + "次出现";
                    label_Succ_2_3.Text = "共计" + Success[1].ToString() + "次出现";
                    label_Succ_3_3.Text = "共计" + Success_Sum.ToString() + "次出现";
                }));
            }
            Avaluable = true;
            double[] Rate = new double[3] { (double)Success[0] / 10000,
                (double)Success[1] / 10000,
                (double)Success_Sum / 10000 };
            Invoke(new Action(() =>
            {
                button_Start.Text = "开始模拟";
                label_Succ_1_3.Text = "共计" + Success[0].ToString() + "次出现";
                label_Succ_2_3.Text = "共计" + Success[1].ToString() + "次出现";
                label_Succ_3_3.Text = "共计" + Success_Sum.ToString() + "次出现";
                label_Succ_1_2.Text = (Rate[0]*100).ToString() + "%";
                label_Succ_2_2.Text = (Rate[1]*100).ToString() + "%";
                label_Succ_3_2.Text = (Rate[2]*100).ToString() + "%";
                label_Remain.Text = "平均剩余纠缠之缘" + Average_Residue.ToString("F2") + "个";
                if (Rate[2] >= 0 && Rate[2] <= 0.2)
                    label_Label.Text = "白日做梦";
                else if (Rate[2] > 0.2 && Rate[2] <= 0.4)
                    label_Label.Text = "考验人品";
                else if (Rate[2] > 0.4 && Rate[2] <= 0.6)
                    label_Label.Text = "成败参半";
                else if (Rate[2] > 0.6 && Rate[2] <= 0.8)
                    label_Label.Text = "值得一试";
                else if (Rate[2] > 0.8 && Rate[2] < 1)
                    label_Label.Text = "十拿九稳";
                else label_Label.Text = "板上钉钉";
                label_Remain.Visible = label2.Visible = label_Label.Visible = true;
            }));//跨线程操作
            Success[0] = Success[1] = Success_Sum = 0;
        }//进行抽卡模拟的线程

        private int RolePrayer(int N,int[] Re, bool[] PRW, int Code)
        {   /* N表示目标抽取数量
             * Re[5]是储存实际抽到数量的数组
             * PRW[4]是记录目标达成情况的bool型数组
             */
            int N_Used = 0;//使用的纠缠之缘数量
            int RandomNumber = 0;
            int BaoDi = 0;//保底计数器
            while(Re[Code] < N)
            {
                RandomNumber = randomNum(1000);
                BaoDi++;
                N_Used++;
                if ((RandomNumber>=1 && RandomNumber<=6)||(BaoDi == 90))
                {//抽到了5星的情况
                    if (NextRoleIsBig)//如果是大保底
                    {
                        Re[Code]++;
                        NextRoleIsBig = false;
                    }
                    else//小保底但是不确定会不会歪常驻
                    {
                        RandomNumber = randomNum(1000);
                        if (RandomNumber >= 1 && RandomNumber <= 500)//没歪
                            Re[Code]++;
                        else//歪了
                        {
                            Re[4]++;//R[4]表示歪的常驻的数量
                            NextRoleIsBig = true;//下次就是大保底了
                        }
                    }
                    BaoDi = 0;
                }
            }
            PRW[Code] = true;
            return N_Used;
        }//角色池祈愿的方法

        private int WeaponPrayer(int N, int[] Re, bool[] PRW, int Code)
        {   /* N表示目标抽取数量
             * Re[5]是储存实际抽到数量的数组
             * PRW[4]是记录目标达成情况的bool型数组
             */
            int N_Used = 0;//使用的纠缠之缘数量
            int RandomNumber = 0;
            int BaoDi = 0;//保底计数器
            if (Code == 3 && Num_Weapon_Base != 0)
                Num_Weapon_Base = 0;//抽第二把武器的时候更换定轨,归零
            while (Re[Code] < N)
            {
                RandomNumber = randomNum(1000);
                BaoDi++;
                N_Used++;
                if ((RandomNumber >= 1 && RandomNumber <= 7) || (BaoDi == 80))
                {//出金的情况
                    if (Num_Weapon_Base==2)//满定轨的情况
                    {
                        Re[Code]++;//必定出定轨武器
                        Num_Weapon_Base = 0;//定轨归零
                    }
                    else//非满定轨
                    {
                        if (NextWeaponIsUp)//上一次出金歪的常驻
                        {
                            RandomNumber = randomNum(10);//两个UP武器中随机一个出
                            if (RandomNumber>=1 && RandomNumber<=5)
                            {//出了定轨武器
                                Re[Code]++;
                                Num_Weapon_Base = 0;
                            }
                            else
                            {//歪了另一把UP武器
                                Num_Weapon_Base++;
                                if (Code == 2) Re[3]++;
                                else Re[2]++;
                            }
                            NextWeaponIsUp = false;
                        }
                        else//上一次没歪常驻,包括定轨0/2时的情况
                        {
                            RandomNumber = randomNum(20);
                            if (RandomNumber >= 1 && RandomNumber <= 15)//75%的概率出UP武器
                            {
                                RandomNumber = randomNum(10);
                                if (RandomNumber >= 1 && RandomNumber <= 5)
                                {//没歪
                                    Re[Code]++;
                                    Num_Weapon_Base = 0;
                                }
                                else
                                {//歪了另一把UP武器
                                    Num_Weapon_Base++;
                                    if (Code == 2) Re[3]++;
                                    else Re[2]++;
                                }
                            }
                            else//25%概率歪常驻了!
                            {
                                NextWeaponIsUp = true;
                                Re[4]++;//获得的常驻数量+1
                            }
                        }
                    }
                    BaoDi = 0;
                }
            }
            PRW[Code] = true;
            NextWeaponIsUp = false;
            return N_Used;
        }//武器池祈愿的方法

        private int randomNum(int N)//使用RNGCryptoServiceProvider类生成随机数
        {
            Random r = new Random();
            int randomNumber;
            using (var rng = new RNGCryptoServiceProvider())
            {
                // 生成一个随机字节数组  
                byte[] randomBytes = new byte[4]; // 4 bytes for a uint32  
                rng.GetBytes(randomBytes);

                // 将字节数组转换为整数  
                randomNumber = BitConverter.ToInt32(randomBytes, 0);
                randomNumber += r.Next();
                randomNumber = Math.Abs(randomNumber) % N + 1;
                return randomNumber;
            }
        }//生成一个范围在[1,N]的随机数的方法

        private void comboBox_VersionChoose_SelectedIndexChanged(object sender, EventArgs e)
        {
            ReTry://如果用户丢失了文件并选择了重试,就跳回这里
            //label_VersionChoose.Text = comboBox_VersionChoose.SelectedIndex.ToString();//调试用
            string path;
            bool FileLose = false;//没有文件丢失就是false

            path = "bins\\" + RoleName[comboBox_VersionChoose.SelectedIndex, 0] + "·头像.png";
            if (File.Exists(path))
                PB_Role_1.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            path = "bins\\" + RoleName[comboBox_VersionChoose.SelectedIndex, 1] + "·头像.png";
            if (File.Exists(path))
                PB_Role_2.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            path = "bins\\" + RoleName[comboBox_VersionChoose.SelectedIndex, 0] + "专武.png";
            if (File.Exists(path))
                PB_Weapon_1.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            path = "bins\\" + RoleName[comboBox_VersionChoose.SelectedIndex, 1] + "专武.png";
            if (File.Exists(path))
                PB_Weapon_2.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            if (FileLose)
            {
                DialogResult DR=MessageBox.Show("文件已丢失,建议旅行者找回丢失资源或尝试卸载并重新安装。", "温馨提示", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information);
                if (DR == DialogResult.Retry)
                    goto ReTry;
                else
                {
                    if (Thread_Load != null)
                        Thread_Load.Abort();
                    if (T_Imitate != null)
                        T_Imitate.Abort();
                    Thread_DataDetection.Abort();
                    Application.Exit();
                }
            }

            foreach (Control C in panelMain.Controls)
                if ((C is ComboBox) && (C!=comboBox_VersionChoose))
                {
                    ComboBox CB = (ComboBox)C;
                    CB.SelectedIndex = 0;
                }
        }//卡池选择改变时触发

        private void button_More_Click(object sender, EventArgs e)
        {
            Form_Poster FP = new Form_Poster(comboBox_VersionChoose.SelectedIndex);
            FP.Show();
        }

        private void panelMain_Paint(object sender, PaintEventArgs e)
        {

        }

        private void pictureBox_Exit_Click(object sender, EventArgs e)
        {
            DialogResult DR = MessageBox.Show("确认退出软件?", "询问", MessageBoxButtons.OKCancel);
            if (DR == DialogResult.OK)
            {
                DialogResult Dr = MessageBox.Show("祝屏幕前的旅行者十连多金、小保底不歪!", "诚挚的祝福", MessageBoxButtons.OK);
                if (Dr==DialogResult.OK)
                {
                    if (Thread_Load != null)
                        Thread_Load.Abort();
                    if (T_Imitate != null)
                        T_Imitate.Abort();
                    Thread_DataDetection.Abort();
                    Application.Exit();
                }
            }
        }

        private void textBox_Ball_TextChanged(object sender, EventArgs e)
        {
            if (textBox_Ball.Text == "") textBox_Ball.Text = "0";
            else if ((textBox_Ball.Text[0]=='0') && (textBox_Ball.Text.Length == 2))//去掉多余的0,包括光标在0右侧时输入的情况
                    textBox_Ball.Text = textBox_Ball.Text[1].ToString();

            //接下来将Text属性转化为数据并存储入NUM_Ball
            try
            {
                NUM_Ball = int.Parse(textBox_Ball.Text);
            }
            catch (FormatException)
            {// 转换失败,用户可能输入了非数字字符  
                MessageBox.Show("包含非法字符,该输入框已清空,请输入有效的数字以表示拥有的纠缠之缘的数量!", "数据无效",MessageBoxButtons.OK,MessageBoxIcon.Warning);
                textBox_Ball.Text = "0";
                NUM_Ball = 0;
                return;
            }
            textBox_Ball.SelectionStart = textBox_Ball.Text.Length;//每次将光标移动到末尾
        }

        private void textBox_Stone_TextChanged(object sender, EventArgs e)
        {
            if (textBox_Stone.Text == "") textBox_Stone.Text = "0";
            else if ((textBox_Stone.Text[0] == '0') && (textBox_Stone.Text.Length == 2))//去掉多余的0,包括光标在0右侧时输入的情况
                textBox_Stone.Text = textBox_Stone.Text[1].ToString();

            //接下来将Text属性转化为数据并存储入NUM_Stone
            try
            {
                NUM_Stone = int.Parse(textBox_Stone.Text);
            }
            catch (FormatException)
            {// 转换失败,用户可能输入了非数字字符  
                MessageBox.Show("包含非法字符,该输入框已清空,请输入有效的数字以表示拥有的原石的数量!", "数据无效", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                textBox_Stone.Text = "0";
                NUM_Stone = 0;
                return;
            }
            textBox_Stone.SelectionStart = textBox_Stone.Text.Length;//每次将光标移动到末尾
        }

        private void checkBox4_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox4.Checked)
                comboBox_WeaponN.Enabled = true;
            else comboBox_WeaponN.Enabled = false;

            if (checkBox4.Checked && comboBox_WeaponN.SelectedIndex == 0)
                checkBox5.Enabled = true;
            else checkBox5.Enabled = false;
        }

        private void comboBox_Weapon_1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0)
                checkBox5.Enabled = checkBox4.Enabled = false;
            else checkBox5.Enabled = checkBox4.Enabled = true;//不计划抽取武器,则无法选取武器池定轨选项
            if ((comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0) ||
                (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0))
                checkBox2.Enabled = false;
            else checkBox2.Enabled = true;//武器角色二种只抽取一种则无法选取“优先抽取武器”
        }

        private void comboBox_Weapon_2_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0)
                checkBox5.Enabled = checkBox4.Enabled = false;
            else checkBox5.Enabled = checkBox4.Enabled = true;//不计划抽取武器,则无法选取武器池定轨选项
            if ((comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0) ||
                (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0))
                checkBox2.Enabled = false;
            else checkBox2.Enabled = true;//武器角色二种只抽取一种则无法选取“优先抽取武器”
        }

        private void comboBox_Role_1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if ((comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0) ||
                (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0))
                checkBox2.Enabled = false;
            else checkBox2.Enabled = true;//武器角色二种只抽取一种则无法选取“优先抽取武器”
            if (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0)
                checkBox1.Enabled = checkBox3.Enabled = false;
            else checkBox1.Enabled = checkBox3.Enabled = true;//不抽取角色则无法选择“角色卡池为9折”
        }

        private void comboBox_Role_2_SelectedIndexChanged(object sender, EventArgs e)
        {
            if ((comboBox_Weapon_1.SelectedIndex + comboBox_Weapon_2.SelectedIndex == 0) ||
                (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0))
                checkBox2.Enabled = false;
            else checkBox2.Enabled = true;//武器角色二种只抽取一种则无法选取“优先抽取武器”
            if (comboBox_Role_1.SelectedIndex + comboBox_Role_2.SelectedIndex == 0)
                checkBox1.Enabled = checkBox3.Enabled = false;
            else checkBox1.Enabled = checkBox3.Enabled = true;//不抽取角色则无法选择“角色卡池为9折”
        }

        private void button_Transport_Click(object sender, EventArgs e)
        {
            if (NUM_Stone>=160)
            {
                NUM_Ball += NUM_Stone / 160;
                NUM_Stone = NUM_Stone - NUM_Stone / 160 * 160;
                textBox_Ball.Text = NUM_Ball.ToString();
                textBox_Stone.Text = NUM_Stone.ToString();
            }
        }

        private void button_Start_Click(object sender, EventArgs e)
        {
            int Ball = NUM_Ball + NUM_Stone / 160;//抽取的次数
            bool NotSelectedTarget = (comboBox_Role_1.SelectedIndex == 0) && 
                (comboBox_Role_2.SelectedIndex == 0) && 
                (comboBox_Weapon_1.SelectedIndex == 0) && 
                (comboBox_Weapon_2.SelectedIndex == 0);//如果全部为0,就说明没有选择,那么值为true
            if ((Ball>0 && Ball<=999)&&(checkBox_Ensure.Checked)&&(!NotSelectedTarget))
            {
                //先将数据清空表示所有给出的资源全部用于抽卡
                NUM_Ball = 0;
                NUM_Stone -= NUM_Stone / 160 * 160;
                textBox_Ball.Text = NUM_Ball.ToString();
                textBox_Stone.Text = NUM_Stone.ToString();
                label_Succ_1_3.Visible = label_Succ_2_3.Visible = label_Succ_3_3.Visible = true;
                //接下来调用线程进行模拟
                int[] Temp = new int[4] {comboBox_Role_1.SelectedIndex,
                    comboBox_Role_2.SelectedIndex,
                    comboBox_Weapon_1.SelectedIndex,
                    comboBox_Weapon_2.SelectedIndex};//简单粗暴地避免跨线程访问问题

                NextRoleIsBig = checkBox3.Checked;
                if (checkBox4.Checked)//定轨不为0/2的情况
                    Num_Weapon_Base = comboBox_WeaponN.SelectedIndex + 1;
                if (checkBox4.Checked && comboBox_WeaponN.SelectedIndex == 0 && checkBox5.Checked)
                    NextWeaponIsUp = true;//定轨0/2歪常驻变成1/2的情况,下次必定是UP武器

                CheckBox[] CB = new CheckBox[5] { checkBox1, checkBox2, checkBox3, checkBox4, checkBox5 };
                T_Imitate = new Thread(() => imitate(Ball,Temp, CB, comboBox_WeaponN));
                T_Imitate.Start();
                Avaluable = false;
                button_Start.Text = "正在模拟,请稍等……";
            }
            else if (!(checkBox_Ensure.Checked))
                    MessageBox.Show("请仔细阅读用户须知并勾选下方选项!");
                else
                {
                    if (NotSelectedTarget)
                        MessageBox.Show("请选择你的抽取目标!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    else
                    {
                        MessageBox.Show("填入的已有资源超过允许模拟的最大限度!", "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        NUM_Ball = NUM_Stone = 0;
                        textBox_Ball.Text = textBox_Stone.Text = "0";
                    }
                }
            textBox_Ball.SelectionStart = textBox_Ball.Text.Length;
        }

        private void linkLabel_ScreenShot_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            try
            {
                linkLabel_ScreenShot.Visible = false;

                //接下来开始截图与保存操作
                Bitmap BM = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
                using (Graphics G = Graphics.FromImage(BM))
                {
                    G.CopyFromScreen(this.PointToScreen(new Point(0, 0)), Point.Empty, this.ClientSize);
                }
                DateTime CurruntTime = DateTime.Now;
                string FileName = "ScreenShot_" + CurruntTime.ToString("yyyyMMddHHmmss") + ".png";
                BM.Save("bins\\ScreenShot\\" + FileName, System.Drawing.Imaging.ImageFormat.Png);

                //到此

                linkLabel_ScreenShot.Visible = true;
                MessageBox.Show("截图已保存至 安装目录/bins/ScreenShot/" + FileName);
            }
            catch(Exception Ex)
            {
                MessageBox.Show("发生错误:" + Ex.Message);
            }
        }

        private void label_Succ_2_2_Click(object sender, EventArgs e)
        {

        }

        private void label_Succ_2_3_Click(object sender, EventArgs e)
        {

        }

        private void label_Succ_2_1_Click(object sender, EventArgs e)
        {

        }

        private void comboBox_WeaponN_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (checkBox4.Checked && comboBox_WeaponN.SelectedIndex == 0)
                checkBox5.Enabled = true;
            else checkBox5.Enabled = false;
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void pictureBox_README_Click(object sender, EventArgs e)
        {
            README RM = new README();
            RM.Show();
        }
    }
}

2.Form_Poster页面(祈愿海报页面)

设计器:

页面代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.IO;

namespace 概率计算器___Genshin_Impact
{
    public partial class Form_Poster : Form
    {
        public Form_Poster(int N_Mode)
        {
            InitializeComponent();

            Mode = N_Mode;

            pictureBox_Down.DoubleClick += pictureBox_Down_Click;
            pictureBox_Up.DoubleClick += pictureBox_Up_Click;
        }

        #region 变量的声明
        public int Mode = 0;
        Thread TTF = null;
        public int CurruntPage = 1;
        string[,] RoleName = new string[4, 2] {
            {"娜维娅", "神里绫华" },//4.3上半卡池,卡池编码为0
            {"雷电将军", "宵宫" },//4.3下半卡池,卡池编码为1
            { "闲云", "纳西妲" }, //4.4上半卡池,卡池编码为2
            { "魈", "八重神子" }//4.4下半卡池,卡池编码为3
        };
        //第一维度为版本编号,第二维度为卡池序号
        //比如4.4上半对应第一维度,而角色祈愿-2对应第二维度
        //角色池图片名称为RoleName[m,n] + ".png"
        //武器池图片名称为RoleName[m,0] + "-" + RoleName[m,1] + "-专武.png"
        #endregion

        private void Form_Poster_Load(object sender, EventArgs e)
        {
            this.Opacity = 0;
            TTF = new Thread(() => Thread_TransparencyFade());
            TTF.Start();
            foreach (Control Con in this.Controls)
                Con.BackColor = Color.FromArgb(0, 0, 0, 0);

            ReTrySearchFile://如果选择ReTry就跳回这里
            string path;
            bool FileLose = false;//文件没丢失就是false

            path = "bins\\" + RoleName[Mode, 0] + ".png";
            if (File.Exists(path))
                pictureBox_Role_1.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            path = "bins\\" + RoleName[Mode, 1] + ".png";
            if (File.Exists(path))
                pictureBox_Role_2.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            path = "bins\\" + RoleName[Mode, 0] + "-" + RoleName[Mode, 1] + "-专武.png";
            if (File.Exists(path))
                pictureBox_Weapon.Image = Image.FromFile(path);
            else MessageBox.Show("文件丢失:" + path, "文件丢失", MessageBoxButtons.OK, MessageBoxIcon.Error);
            FileLose = FileLose || (!File.Exists(path));

            if (FileLose)
            {//文件已丢失
                DialogResult DR = MessageBox.Show("文件已丢失,建议旅行者找回丢失资源或尝试卸载并重新安装。", "温馨提示", MessageBoxButtons.RetryCancel, MessageBoxIcon.Information);
                if (DR == DialogResult.Retry)
                    goto ReTrySearchFile;
                else
                {
                    if (TTF != null)
                        TTF.Abort();
                    Application.Exit();
                }
            }

            pictureBox_Role_2.Location = pictureBox_Weapon.Location = pictureBox_Role_1.Location;
            pictureBox_Role_2.Visible = pictureBox_Weapon.Visible = false;
        }

        private void Thread_TransparencyFade()
        {
            int transparency = -5;
            do
            {
                transparency += 5;
                Invoke(new Action(() =>
                {
                    this.Opacity = (double)transparency / 100;
                }));//跨线程操作
                Thread.Sleep(5);
            } while (transparency < 100);
        }//透明淡入的线程

        private void Thread_Out()
        {
            int transparency = 105;
            do
            {
                transparency -= 5;
                Invoke(new Action(() =>
                {
                    this.Opacity = (double)transparency / 100;
                }));//跨线程操作
                Thread.Sleep(5);
            } while (transparency > 0);
            Invoke(new Action(() =>
            {
                this.Close();
            }));//跨线程操作
        }//透明淡出的线程

        private void pictureBox_Out_Click(object sender, EventArgs e)
        {
            if (TTF != null)
            {
                TTF.Abort();
                TTF = new Thread(() => Thread_Out());
                TTF.Start();
            }
        }

        private void pictureBox_Up_Click(object sender, EventArgs e)
        {
            if (CurruntPage >= 2) CurruntPage--;
            else CurruntPage = 3;
            switch(CurruntPage)
            {
                case 1:
                    pictureBox_Role_2.Visible = false;
                    pictureBox_Role_1.Visible = true;
                    break;
                case 2:
                    pictureBox_Weapon.Visible = false;
                    pictureBox_Role_2.Visible = true;
                    break;
                case 3:
                    pictureBox_Role_1.Visible = false;
                    pictureBox_Weapon.Visible = true;
                    break;
            }
        }

        private void pictureBox_Down_Click(object sender, EventArgs e)
        {
            if (CurruntPage <= 3) CurruntPage++;
            else CurruntPage = 1;
            switch (CurruntPage)
            {
                case 1:
                    pictureBox_Weapon.Visible = false;
                    pictureBox_Role_1.Visible = true;
                    break;
                case 2:
                    pictureBox_Role_1.Visible = false;
                    pictureBox_Role_2.Visible = true;
                    break;
                case 3:
                    pictureBox_Role_2.Visible = false;
                    pictureBox_Weapon.Visible = true;
                    break;
            }
        }
    }
}

3.README页面(说明页面)

设计器:

页面代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace 概率计算器___Genshin_Impact
{
    public partial class README : Form
    {
        public README()
        {
            InitializeComponent();
        }

        private void README_Load(object sender, EventArgs e)
        {

        }

        private void label_Link_Click(object sender, EventArgs e)
        {
            try
            {
                Process.Start("http://sherrychou.blog.csdn.net");
            }
            catch (Exception E)
            {
                MessageBox.Show("出现错误:" + E.Message, "出错啦", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            this.Close();
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }
    }
}

4. 应用程序清单文件

将以下代码取消注释。

  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
  </application>

四、其他说明

1.本软件不用于盈利,也不允许被用于盈利,如发现用该软件盈利的行为,欢迎举报!

2.本软件内的一切图片素材版权仍归 上海米哈游网络科技股份有限公司 与 上海米哈游天命科技有限公司 所有。

3.使用过程中如发现任何不应该出现的技术问题,欢迎联系作者反馈!

文章字数统计君:22565字

标签: 开源 c# 个人开发

本文转载自: https://blog.csdn.net/qq_64516527/article/details/136070214
版权归原作者 青が絶える(青绝悲) 所有, 如有侵权,请联系我们删除。

“[WinForm开源]概率计算器 - Genshin Impact(V1.0)”的评论:

还没有评论