/**
* Grasscutter Tools
* Copyright (C) 2022 jie65535
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
**/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using GrasscutterTools.Game;
using GrasscutterTools.Game.Gacha;
using GrasscutterTools.Properties;
using Newtonsoft.Json;
namespace GrasscutterTools.Forms
{
///
/// 卡池编辑器
///
public partial class FormGachaBannerEditor2 : Form
{
public FormGachaBannerEditor2()
{
InitializeComponent();
Icon = Resources.IconGrasscutter;
CmbBannerType.SelectedIndex = 0;
InitBannerPrefab();
InitCheckedListBoxs();
ShowBanner(new GachaBanner2());
}
private void InitBannerPrefab()
{
CmbPrefab.DisplayMember = "Value";
CmbPrefab.DataSource = GameData.GachaBannerPrefabs;
//CmbPrefab.Items.Clear();
//CmbPrefab.Items.AddRange(GameData.GachaBannerPrefabs.Names);
}
private void LnkWeightHelp_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("https://github.com/Grasscutters/Grasscutter/pull/639");
}
private void LnkOpenOldEditor_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
new FormGachaBannerEditor().ShowDialog();
}
#region - 卡池 -
private void InitCheckedListBoxs()
{
ListFallbackItems.BeginUpdate();
var a5 = ListFallbackItems.Groups["GroupA5"];
var a4 = ListFallbackItems.Groups["GroupA4"];
var w5 = ListFallbackItems.Groups["GroupW5"];
var w4 = ListFallbackItems.Groups["GroupW4"];
var w3 = ListFallbackItems.Groups["GroupW3"];
var avatars = GetAvatarsByColor("5")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, a5) { ForeColor = Color.OrangeRed })
.Concat(GetAvatarsByColor("4")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, a4) { ForeColor = Color.Purple }));
var weapons = GetWeaponsByColor("yellow")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, w5) { ForeColor = Color.OrangeRed })
.Concat(GetWeaponsByColor("purple")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, w4) { ForeColor = Color.Purple }))
.Concat(GetWeaponsByColor("blue")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, w3) { ForeColor = Color.Blue }));
ListFallbackItems.Items.Clear();
ListFallbackItems.Items.AddRange(avatars.Concat(weapons).ToArray());
ListUpItems.BeginUpdate();
var ua5 = ListUpItems.Groups["GroupUpA5"];
var ua4 = ListUpItems.Groups["GroupUpA4"];
var uw5 = ListUpItems.Groups["GroupUpW5"];
var uw4 = ListUpItems.Groups["GroupUpW4"];
var upAvatars = GetAvatarsByColor("5")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, ua5) { ForeColor = Color.OrangeRed })
.Concat(GetAvatarsByColor("4")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, ua4) { ForeColor = Color.Purple }));
var upWeapons = GetWeaponsByColor("yellow")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, uw5) { ForeColor = Color.OrangeRed })
.Concat(GetWeaponsByColor("purple")
.Select(it => new ListViewItem(new string[] { it.Item1.ToString(), it.Item2 }, uw4) { ForeColor = Color.Purple }));
ListUpItems.Items.Clear();
ListUpItems.Items.AddRange(upAvatars.Concat(upWeapons).ToArray());
ListFallbackItems.EndUpdate();
ListUpItems.EndUpdate();
}
private IEnumerable<(int, string)> GetAvatarsByColor(string color)
{
for (int i = 0; i < GameData.AvatarColors.Count; i++)
{
if (GameData.AvatarColors.Names[i] == color)
{
var id = GameData.AvatarColors.Ids[i];
var index = Array.IndexOf(GameData.Avatars.Ids, id);
if (index >= 0)
yield return (id, GameData.Avatars.Names[index]);
}
}
}
private IEnumerable<(int, string)> GetWeaponsByColor(string color)
{
for (int i = 0; i < GameData.WeaponColors.Count; i++)
{
if (GameData.WeaponColors.Names[i] == color)
{
var id = GameData.WeaponColors.Ids[i];
var index = Array.IndexOf(GameData.Weapons.Ids, id);
if (index >= 0)
yield return (id, GameData.Weapons.Names[index]);
}
}
}
private void InitItems(GachaBanner2 banner)
{
var f = banner.FallbackItems3
.Concat(banner.FallbackItems4Pool1)
.Concat(banner.FallbackItems4Pool2)
.Concat(banner.FallbackItems5Pool1)
.Concat(banner.FallbackItems5Pool2)
.ToArray();
foreach (ListViewItem item in ListFallbackItems.Items)
item.Checked = Array.IndexOf(f, int.Parse(item.Text)) >= 0;
var u = banner.RateUpItems4.Concat(banner.RateUpItems5).ToArray();
foreach (ListViewItem item in ListUpItems.Items)
item.Checked = Array.IndexOf(u, int.Parse(item.Text)) >= 0;
}
private IEnumerable GetCheckedItems(ListView list, ListViewGroup group)
{
foreach (ListViewItem item in list.CheckedItems)
if (item.Group == group)
yield return int.Parse(item.Text);
}
#endregion - 卡池 -
#region - 权重 -
private struct GachaWeight
{
public int Count;
public int Weight;
public GachaWeight(int count, int weight)
{
Count = count;
Weight = weight;
}
}
private void InitWeights(GachaBanner2 banner)
{
TxtWeight5.Text = '[' + string.Join(", ", SelectWeights(banner.Weights5).Select(w => $"[{w.Count}, {w.Weight}]")) + ']';
TxtWeight4.Text = '[' + string.Join(", ", SelectWeights(banner.Weights4).Select(w => $"[{w.Count}, {w.Weight}]")) + ']';
TxtPoolWeight5.Text = '[' + string.Join(", ", SelectWeights(banner.PoolBalanceWeights5).Select(w => $"[{w.Count}, {w.Weight}]")) + ']';
TxtPoolWeight4.Text = '[' + string.Join(", ", SelectWeights(banner.PoolBalanceWeights4).Select(w => $"[{w.Count}, {w.Weight}]")) + ']';
ChartWeights.SuspendLayout();
ChartWeights.Series[0].Points.Clear();
foreach (var w in SelectWeights(banner.Weights5))
ChartWeights.Series[0].Points.AddXY(w.Count, w.Weight / 100.0);
ChartWeights.Series[1].Points.Clear();
foreach (var w in SelectWeights(banner.Weights4))
ChartWeights.Series[1].Points.AddXY(w.Count, w.Weight / 100.0);
ChartWeights.ResumeLayout();
}
private IEnumerable SelectWeights(int[,] weights)
{
for (int i = 0; i < weights.GetLength(0); i++)
yield return new GachaWeight(weights[i, 0], weights[i, 1]);
}
private int[,] GetWeights(string weights)
{
return JsonConvert.DeserializeObject(weights);
}
#endregion - 权重 -
#region - 序列化 -
private static string TrimPrefixAndSuffix(string text, int prefixLength, int suffixLength)
{
return text.Substring(prefixLength, text.Length - prefixLength - suffixLength);
}
private void ShowBanner(GachaBanner2 banner)
{
try
{
NUDGachaType.Value = banner.GachaType;
NUDScheduleId.Value = banner.ScheduleId;
CmbBannerType.SelectedIndex = (int)banner.BannerType;
const string titlePrefix = "UI_GACHA_SHOW_PANEL_";
const string suffix = "_TITLE";
if (string.IsNullOrEmpty(banner.TitlePath) || banner.TitlePath.Length <= titlePrefix.Length + suffix.Length)
{
CmbPrefab.SelectedIndex = -1;
}
else
{
var titleKey = TrimPrefixAndSuffix(banner.TitlePath, titlePrefix.Length, suffix.Length);
CmbPrefab.SelectedIndex = GameData.GachaBannerTitles.FindIndex(it => it.Key == titleKey);
}
RbCostItem224.Checked = banner.CostItem == 224;
RbCostItem223.Checked = banner.CostItem == 223;
DTPBeginTime.Value = DateTimeOffset.FromUnixTimeSeconds(banner.BeginTime).DateTime;
DTPEndTime.Value = DateTimeOffset.FromUnixTimeSeconds(banner.EndTime).DateTime;
NUDSortId.Value = banner.SortId;
NUDEventChance5.Value = banner.EventChance5;
NUDEventChance4.Value = banner.EventChance4;
ChkRemoveC6FormPool.Checked = banner.RemoveC6FromPool;
ChkAutoStripRateUpFromFallback.Checked = banner.AutoStripRateUpFromFallback;
InitItems(banner);
InitWeights(banner);
}
catch (Exception ex)
{
MessageBox.Show("UI更新失败:" + ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private GachaBanner2 ParseBanner()
{
if (CmbBannerType.SelectedIndex < 0)
{
MessageBox.Show("请选择奖池类型", Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
if (CmbPrefab.SelectedIndex < 0)
{
MessageBox.Show("请选择奖池预制", Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
var prefabId = GameData.GachaBannerPrefabs[CmbPrefab.SelectedIndex].Key;
var banner = new GachaBanner2
{
GachaType = (int)NUDGachaType.Value,
ScheduleId = (int)NUDScheduleId.Value,
BannerType = (BannerType)CmbBannerType.SelectedIndex,
PrefabPath = $"GachaShowPanel_{prefabId}",
PreviewPrefabPath = $"UI_Tab_GachaShowPanel_{prefabId}",
TitlePath = $"UI_GACHA_SHOW_PANEL_{prefabId}_TITLE",
CostItem = RbCostItem224.Checked ? 224 : 223,
BeginTime = (int)new DateTimeOffset(DTPBeginTime.Value, TimeSpan.Zero).ToUnixTimeSeconds(),
EndTime = (int)new DateTimeOffset(DTPEndTime.Value, TimeSpan.Zero).ToUnixTimeSeconds(),
SortId = (int)NUDSortId.Value,
EventChance5 = (int)NUDEventChance5.Value,
EventChance4 = (int)NUDEventChance4.Value,
RateUpItems4 = GetCheckedItems(ListUpItems, ListUpItems.Groups["GroupUpA4"])
.Concat(GetCheckedItems(ListUpItems, ListUpItems.Groups["GroupUpW4"]))
.ToArray(),
RateUpItems5 = GetCheckedItems(ListUpItems, ListUpItems.Groups["GroupUpA5"])
.Concat(GetCheckedItems(ListUpItems, ListUpItems.Groups["GroupUpW5"]))
.ToArray(),
FallbackItems3 = GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupA3"])
.Concat(GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupW3"]))
.ToArray(),
FallbackItems4Pool1 = GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupA4"]).ToArray(),
FallbackItems4Pool2 = GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupW4"]).ToArray(),
FallbackItems5Pool1 = GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupA5"]).ToArray(),
FallbackItems5Pool2 = GetCheckedItems(ListFallbackItems, ListFallbackItems.Groups["GroupW5"]).ToArray(),
RemoveC6FromPool = ChkRemoveC6FormPool.Checked,
AutoStripRateUpFromFallback = ChkAutoStripRateUpFromFallback.Checked,
Weights4 = GetWeights(TxtWeight4.Text),
Weights5 = GetWeights(TxtWeight5.Text),
PoolBalanceWeights4 = GetWeights(TxtPoolWeight4.Text),
PoolBalanceWeights5 = GetWeights(TxtPoolWeight5.Text),
};
return banner;
}
private void BtnGen_Click(object sender, EventArgs e)
{
try
{
var banner = ParseBanner();
if (banner != null)
{
var json = JsonConvert.SerializeObject(banner);
json = json.Replace(",\"", ",\r\n \"").Insert(1, "\r\n ");
TxtJson.Text = json.Insert(json.Length - 1, "\r\n");
ShowBanner(banner);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BtnParse_Click(object sender, EventArgs e)
{
try
{
ShowBanner(JsonConvert.DeserializeObject(TxtJson.Text));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion - 序列化 -
}
}