From 21d908ac07a4d3830c7ca36458cabeff7cb778de Mon Sep 17 00:00:00 2001
From: Masaki Inoue <45062199+2RiniaR@users.noreply.github.com>
Date: Sun, 1 Dec 2024 23:15:55 +0900
Subject: [PATCH] =?UTF-8?q?Common:=20=E5=80=8D=E7=8E=87=E3=82=92=E8=A1=A8?=
=?UTF-8?q?=E3=81=99=E6=A7=8B=E9=80=A0=E4=BD=93=E3=82=92=E8=BF=BD=E5=8A=A0?=
=?UTF-8?q?=20(#46)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Common/Master/SettingMaster.cs | 10 ++
Common/Master/SlotItemMaster.cs | 4 +
Common/Models/AppService.cs | 19 ++-
Common/Models/AppState.cs | 61 --------
Common/Models/Gacha.cs | 112 ++++++++++++++
Common/Models/GachaItem.cs | 27 ++++
Common/Models/GachaManager.cs | 140 -----------------
Common/Models/GachaProbability.cs | 15 --
Common/Models/Slot.cs | 27 ++--
Common/Models/User.cs | 14 +-
Common/Multiplier.cs | 141 ++++++++++++++++++
Common/Random/RandomManager.cs | 30 ++--
Common/Utility/NumberUtility.cs | 30 ----
Events/Admin/AdminMasterReloadPresenter.cs | 22 ---
Events/DailyReset/DailyResetPresenter.cs | 15 +-
Events/Gacha/GachaCommandPresenter.cs | 7 +-
Events/Gacha/GachaInfoCommandPresenter.cs | 5 +-
Events/Gacha/GachaInteractReplyPresenter.cs | 3 +-
Events/Gacha/GachaRareReplyPresenter.cs | 3 +-
Events/Gacha/GachaUtility.cs | 13 +-
Events/Slot/SlotExecutePresenter.cs | 3 +-
.../20241201133806_CreateGacha.Designer.cs | 105 +++++++++++++
Migrations/20241201133806_CreateGacha.cs | 86 +++++++++++
Migrations/AppServiceModelSnapshot.cs | 45 ++++--
Program.cs | 43 ++++--
25 files changed, 639 insertions(+), 341 deletions(-)
delete mode 100644 Common/Models/AppState.cs
create mode 100644 Common/Models/Gacha.cs
create mode 100644 Common/Models/GachaItem.cs
delete mode 100644 Common/Models/GachaManager.cs
delete mode 100644 Common/Models/GachaProbability.cs
create mode 100644 Common/Multiplier.cs
create mode 100644 Migrations/20241201133806_CreateGacha.Designer.cs
create mode 100644 Migrations/20241201133806_CreateGacha.cs
diff --git a/Common/Master/SettingMaster.cs b/Common/Master/SettingMaster.cs
index 0b486a8..6a22337 100644
--- a/Common/Master/SettingMaster.cs
+++ b/Common/Master/SettingMaster.cs
@@ -70,11 +70,15 @@ private int GetInt(string key)
///
public int MaxRareReplyProbabilityPermillage => GetInt(nameof(MaxRareReplyProbabilityPermillage));
+ public Multiplier MaxRareReplyProbability => Multiplier.FromPermillage(MaxRareReplyProbabilityPermillage);
+
///
/// 確率返信の抽選単位(千分率)
///
public int RareReplyProbabilityStepPermillage => GetInt(nameof(RareReplyProbabilityStepPermillage));
+ public Multiplier RareReplyProbabilityStep => Multiplier.FromPermillage(RareReplyProbabilityStepPermillage);
+
///
/// 単発ガチャ1回の価格
///
@@ -120,15 +124,21 @@ private int GetInt(string key)
///
public int SlotMaxConditionOffsetPermillage => GetInt(nameof(SlotMaxConditionOffsetPermillage));
+ public Multiplier SlotMaxConditionOffset => Multiplier.FromPermillage(SlotMaxConditionOffsetPermillage);
+
///
/// スロットの調子の最小値(千分率)
///
public int SlotMinConditionOffsetPermillage => GetInt(nameof(SlotMinConditionOffsetPermillage));
+ public Multiplier SlotMinConditionOffset => Multiplier.FromPermillage(SlotMinConditionOffsetPermillage);
+
///
/// スロットの次に同じ出目が確定する確率の最大値(千分率)
///
public int SlotRepeatPermillageUpperBound => GetInt(nameof(SlotRepeatPermillageUpperBound));
+
+ public Multiplier SlotRepeatUpperBound => Multiplier.FromPermillage(SlotRepeatPermillageUpperBound);
}
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
diff --git a/Common/Master/SlotItemMaster.cs b/Common/Master/SlotItemMaster.cs
index acca821..a7709c6 100644
--- a/Common/Master/SlotItemMaster.cs
+++ b/Common/Master/SlotItemMaster.cs
@@ -29,9 +29,13 @@ public class SlotItem : MasterRecord
[field: MasterIntValue("return_rate_permillage")]
public int ReturnRatePermillage { get; }
+ public Multiplier ReturnRate => Multiplier.FromPermillage(ReturnRatePermillage);
+
///
/// 次に同じ出目が確定する確率(千分率)
///
[field: MasterIntValue("repeat_permillage")]
public int RepeatPermillage { get; }
+
+ public Multiplier RepeatProbability => Multiplier.FromPermillage(RepeatPermillage);
}
diff --git a/Common/Models/AppService.cs b/Common/Models/AppService.cs
index 30f5fe1..1b6ce65 100644
--- a/Common/Models/AppService.cs
+++ b/Common/Models/AppService.cs
@@ -7,10 +7,10 @@ namespace Approvers.King.Common;
///
public class AppService : DbContext
{
- public DbSet AppStates { get; set; }
public DbSet Slots { get; set; }
+ public DbSet Gachas { get; set; }
public DbSet Users { get; set; }
- public DbSet GachaProbabilities { get; set; }
+ public DbSet GachaItems { get; set; }
///
/// セッションを作成する
@@ -41,7 +41,7 @@ public async Task FindOrCreateUserAsync(ulong discordId)
Add(user);
return user;
}
-
+
public async Task GetDefaultSlotAsync()
{
var slot = await Slots.FirstOrDefaultAsync();
@@ -54,4 +54,17 @@ public async Task GetDefaultSlotAsync()
Add(slot);
return slot;
}
+
+ public async Task GetDefaultGachaAsync()
+ {
+ var gacha = await Gachas.Include(x => x.GachaItems).FirstOrDefaultAsync();
+ if (gacha != null)
+ {
+ return gacha;
+ }
+
+ gacha = new Gacha { Id = Guid.NewGuid() };
+ Add(gacha);
+ return gacha;
+ }
}
diff --git a/Common/Models/AppState.cs b/Common/Models/AppState.cs
deleted file mode 100644
index 9ccdba9..0000000
--- a/Common/Models/AppState.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using Microsoft.EntityFrameworkCore;
-
-namespace Approvers.King.Common;
-
-public static class AppStateDbSetExtensions
-{
- public async static Task GetIntAsync(this DbSet source, AppStateType type)
- {
- var state = await source.FindAsync(type);
- return int.TryParse(state?.Value ?? string.Empty, out var result) ? result : null;
- }
-
- public async static Task SetIntAsync(this DbSet source, AppStateType type, int value)
- {
- var state = await source.FindAsync(type);
- var valueString = value.ToString();
- if (state == null)
- {
- source.Add(new AppState { Type = type, Value = valueString });
- }
- else
- {
- state.Value = valueString;
- }
- }
-
- public async static Task GetStringAsync(this DbSet source, AppStateType type)
- {
- var state = await source.FindAsync(type);
- return state?.Value;
- }
-
- public async static Task SetStringAsync(this DbSet source, AppStateType type, string value)
- {
- var state = await source.FindAsync(type);
- var valueString = value;
- if (state == null)
- {
- source.Add(new AppState { Type = type, Value = valueString });
- }
- else
- {
- state.Value = valueString;
- }
- }
-}
-
-///
-/// アプリ全体の状態
-///
-public class AppState
-{
- [Key] public AppStateType Type { get; set; }
- public string Value { get; set; } = null!;
-}
-
-public enum AppStateType
-{
- RareReplyProbabilityPermillage,
-}
diff --git a/Common/Models/Gacha.cs b/Common/Models/Gacha.cs
new file mode 100644
index 0000000..254e884
--- /dev/null
+++ b/Common/Models/Gacha.cs
@@ -0,0 +1,112 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Approvers.King.Common;
+
+public class Gacha
+{
+ [Key] public Guid Id { get; set; }
+
+ public int HitProbabilityPermillage { get; set; }
+
+ ///
+ /// 現在のメッセージに反応する確率
+ ///
+ [NotMapped]
+ public Multiplier HitProbability
+ {
+ get => Multiplier.FromPermillage(HitProbabilityPermillage);
+ set => HitProbabilityPermillage = value.Permillage;
+ }
+
+ ///
+ /// 各メッセージの確率
+ ///
+ public List GachaItems { get; set; } = [];
+
+ ///
+ /// 単発ガチャを回す
+ ///
+ public GachaItem? RollOnce()
+ {
+ if (RandomManager.IsHit(HitProbability) == false)
+ {
+ return null;
+ }
+
+ return GetRandomResult();
+ }
+
+ ///
+ /// 確定ガチャを回す
+ ///
+ public GachaItem RollOnceCertain()
+ {
+ return GetRandomResult();
+ }
+
+ private GachaItem GetRandomResult()
+ {
+ var total = GachaItems.Sum(x => x.Probability.Permillage);
+ if (total <= 0)
+ {
+ return GachaItems.First();
+ }
+
+ var value = RandomManager.GetRandomInt(total);
+
+ foreach (var element in GachaItems)
+ {
+ if (value < element.Probability.Permillage) return element;
+ value -= element.Probability.Permillage;
+ }
+
+ return GachaItems.Last();
+ }
+
+ ///
+ /// メッセージに反応する確率を再抽選する
+ ///
+ public void ShuffleRareReplyRate()
+ {
+ // 確率は step の単位で max まで変動(ただし0にはならない)
+ var step = MasterManager.SettingMaster.RareReplyProbabilityStep;
+ var max = MasterManager.SettingMaster.MaxRareReplyProbability;
+ var rates = Enumerable.Range(0, max.Permillage / step.Permillage).Select(i => step * (i + 1));
+ HitProbability = RandomManager.PickRandom(rates);
+ }
+
+ ///
+ /// 各メッセージの確率を再抽選する
+ ///
+ public void ShuffleMessageRates()
+ {
+ var items = MasterManager.RandomMessageMaster
+ .GetAll(x => x.Type == RandomMessageType.GeneralReply)
+ .Select(randomMessage => GachaItems.FirstOrDefault(item => item.RandomMessageId == randomMessage.Id) ?? new GachaItem()
+ {
+ GachaId = Id,
+ RandomMessageId = randomMessage.Id,
+ Probability = Multiplier.Zero
+ })
+ .ToList();
+
+ // いい感じに確率がばらけるように、カイ二乗分布を適用
+ var borders = Enumerable.Range(0, items.Count - 1)
+ .Select(_ => (float)Math.Pow(RandomManager.GetRandomFloat(1f), 2))
+ .Select(Multiplier.FromRate)
+ .OrderBy(x => x)
+ .ToList();
+ borders.Add(Multiplier.One);
+ var randomIndices = RandomManager.Shuffle(Enumerable.Range(0, items.Count)).ToList();
+
+ items[randomIndices[0]].Probability = borders[0];
+ for (var i = 1; i < items.Count; i++)
+ {
+ items[randomIndices[i]].Probability = borders[i] - borders[i - 1];
+ }
+
+ GachaItems.Clear();
+ GachaItems.AddRange(items);
+ }
+}
diff --git a/Common/Models/GachaItem.cs b/Common/Models/GachaItem.cs
new file mode 100644
index 0000000..4e0f778
--- /dev/null
+++ b/Common/Models/GachaItem.cs
@@ -0,0 +1,27 @@
+using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore;
+
+namespace Approvers.King.Common;
+
+[PrimaryKey(nameof(GachaId), nameof(RandomMessageId))]
+public class GachaItem
+{
+ public Guid GachaId { get; set; }
+ public Gacha Gacha { get; set; } = null!;
+ public string RandomMessageId { get; set; } = null!;
+
+ public int ProbabilityPermillage { get; set; }
+
+ [NotMapped]
+ public Multiplier Probability
+ {
+ get => Multiplier.FromPermillage(ProbabilityPermillage);
+ set => ProbabilityPermillage = value.Permillage;
+ }
+
+ private RandomMessage? _randomMessage;
+
+ public RandomMessage? RandomMessage => _randomMessage == null || _randomMessage.Id != RandomMessageId
+ ? _randomMessage = MasterManager.RandomMessageMaster.Find(RandomMessageId)
+ : _randomMessage;
+}
diff --git a/Common/Models/GachaManager.cs b/Common/Models/GachaManager.cs
deleted file mode 100644
index 1b96a69..0000000
--- a/Common/Models/GachaManager.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-
-namespace Approvers.King.Common;
-
-public class GachaManager : Singleton
-{
- private readonly List _replyMessageTable = new();
-
- ///
- /// 現在のメッセージに反応する確率
- ///
- public float RareReplyRate { get; private set; }
-
- ///
- /// 各メッセージの確率
- ///
- public IReadOnlyList ReplyMessageTable => _replyMessageTable;
-
- public bool IsTableEmpty => ReplyMessageTable.Count == 0;
-
- ///
- /// 現在の状態を読み込む
- ///
- public async Task LoadAsync()
- {
- await using var app = AppService.CreateSession();
-
- var probabilities = await app.GachaProbabilities.ToListAsync();
- _replyMessageTable.Clear();
- _replyMessageTable.AddRange(probabilities.Where(x => x.RandomMessage != null));
-
- var rareReplyRatePermillage = await app.AppStates.GetIntAsync(AppStateType.RareReplyProbabilityPermillage);
- RareReplyRate = NumberUtility.GetPercentFromPermillage(rareReplyRatePermillage ?? 0);
- }
-
- ///
- /// 現在の状態を保存する
- ///
- public async Task SaveAsync()
- {
- await using var app = AppService.CreateSession();
-
- app.GachaProbabilities.RemoveRange(app.GachaProbabilities);
- app.GachaProbabilities.AddRange(_replyMessageTable);
-
- var rareReplyRatePermillage = NumberUtility.GetPermillageFromPercent(RareReplyRate);
- await app.AppStates.SetIntAsync(AppStateType.RareReplyProbabilityPermillage, rareReplyRatePermillage);
-
- await app.SaveChangesAsync();
- }
-
- ///
- /// マスタデータを読み込む
- ///
- public void LoadMaster()
- {
- // 同じIDのメッセージは確率を保持し、新規追加分は確率0%で初期化する
- var messages = MasterManager.RandomMessageMaster
- .GetAll(x => x.Type == RandomMessageType.GeneralReply)
- .Select(x => new GachaProbability()
- {
- RandomMessageId = x.Id,
- Probability = _replyMessageTable.FirstOrDefault(m => m.RandomMessageId == x.Id)?.Probability ?? 0
- })
- .ToList();
-
- _replyMessageTable.Clear();
- _replyMessageTable.AddRange(messages);
- }
-
- ///
- /// 単発ガチャを回す
- ///
- public GachaProbability? Roll()
- {
- if (RandomManager.IsHit(RareReplyRate) == false) return null;
- return GetRandomResult();
- }
-
- ///
- /// 確定ガチャを回す
- ///
- public GachaProbability RollWithoutNone()
- {
- return GetRandomResult();
- }
-
- private GachaProbability GetRandomResult()
- {
- var totalRate = _replyMessageTable.Sum(x => x.Probability);
- if (totalRate <= 0)
- {
- return _replyMessageTable[0];
- }
-
- var value = RandomManager.GetRandomFloat(totalRate);
-
- foreach (var element in _replyMessageTable)
- {
- if (value < element.Probability) return element;
- value -= element.Probability;
- }
-
- return _replyMessageTable[^1];
- }
-
- ///
- /// メッセージに反応する確率を再抽選する
- ///
- public void ShuffleRareReplyRate()
- {
- // 確率は step の単位で max まで変動(ただし0にはならない)
- var step = MasterManager.SettingMaster.RareReplyProbabilityStepPermillage;
- var max = MasterManager.SettingMaster.MaxRareReplyProbabilityPermillage;
- var rates = Enumerable.Range(0, max / step)
- .Select(i => NumberUtility.GetPercentFromPermillage((i + 1) * step));
- RareReplyRate = RandomManager.PickRandom(rates);
- }
-
- ///
- /// 各メッセージの確率を再抽選する
- ///
- public void ShuffleMessageRates()
- {
- // いい感じに確率がばらけるように、カイ二乗分布を適用
- var borders = Enumerable.Range(0, _replyMessageTable.Count - 1)
- .Select(x => (float)Math.Pow(RandomManager.GetRandomFloat(1f), 2))
- .Select(x => (int)Math.Floor(x * 100f))
- .OrderBy(x => x)
- .ToList();
- borders.Add(100);
- var randomIndices = RandomManager.Shuffle(Enumerable.Range(0, _replyMessageTable.Count)).ToList();
-
- _replyMessageTable[randomIndices[0]].Probability = borders[0] * 0.01f;
- for (var i = 1; i < _replyMessageTable.Count; i++)
- {
- _replyMessageTable[randomIndices[i]].Probability = (borders[i] - borders[i - 1]) * 0.01f;
- }
- }
-}
diff --git a/Common/Models/GachaProbability.cs b/Common/Models/GachaProbability.cs
deleted file mode 100644
index eda0547..0000000
--- a/Common/Models/GachaProbability.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Approvers.King.Common;
-
-public class GachaProbability
-{
- [Key] public string RandomMessageId { get; set; } = null!;
- public float Probability { get; set; }
-
- private RandomMessage? _randomMessage;
-
- public RandomMessage? RandomMessage => _randomMessage == null || _randomMessage.Id != RandomMessageId
- ? _randomMessage = MasterManager.RandomMessageMaster.Find(RandomMessageId)
- : _randomMessage;
-}
diff --git a/Common/Models/Slot.cs b/Common/Models/Slot.cs
index 721d82c..bdd76d7 100644
--- a/Common/Models/Slot.cs
+++ b/Common/Models/Slot.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
namespace Approvers.King.Common;
@@ -8,10 +9,17 @@ public class Slot
[Key] public Guid Id { get; set; }
+ public int ConditionPermillage { get; set; }
+
///
/// 調子(千分率)
///
- public int ConditionPermillage { get; set; }
+ [NotMapped]
+ public Multiplier Condition
+ {
+ get => Multiplier.FromPermillage(ConditionPermillage);
+ set => ConditionPermillage = value.Permillage;
+ }
public int ExecutePrice => MasterManager.SettingMaster.PricePerSlotOnce;
@@ -20,9 +28,9 @@ public class Slot
///
public void ShuffleCondition()
{
- var max = MasterManager.SettingMaster.SlotMaxConditionOffsetPermillage;
- var min = MasterManager.SettingMaster.SlotMinConditionOffsetPermillage;
- ConditionPermillage = RandomManager.GetRandomInt(min, max + 1);
+ var max = MasterManager.SettingMaster.SlotMaxConditionOffset;
+ var min = MasterManager.SettingMaster.SlotMinConditionOffset;
+ Condition = RandomManager.GetRandomMultiplier(min, max);
}
///
@@ -44,8 +52,7 @@ public SlotExecuteResult Execute()
// 一定確率で直前と同じ出目が出る
// 確率はマスタデータの設定値に加え、調子により変動する
- var repeatPermillage = Math.Clamp(reelItems[i - 1].RepeatPermillage + ConditionPermillage, 0, MasterManager.SettingMaster.SlotRepeatPermillageUpperBound);
- var repeatProbability = NumberUtility.GetProbabilityFromPermillage(repeatPermillage);
+ var repeatProbability = (reelItems[i - 1].RepeatProbability + Condition).Clamp(Multiplier.Zero, MasterManager.SettingMaster.SlotRepeatUpperBound);
var isRepeat = RandomManager.IsHit(repeatProbability);
if (isRepeat)
{
@@ -57,13 +64,13 @@ public SlotExecuteResult Execute()
}
var isWin = reelItems.Select(x => x.Id).Distinct().Count() == 1;
- var resultRatePermillage = isWin ? reelItems[0].ReturnRatePermillage : 0;
+ var resultRate = isWin ? reelItems[0].ReturnRate : Multiplier.Zero;
return new SlotExecuteResult()
{
ReelItems = reelItems,
IsWin = isWin,
- ResultRatePermillage = resultRatePermillage
+ ResultRate = resultRate
};
}
}
@@ -81,7 +88,7 @@ public class SlotExecuteResult
public bool IsWin { get; init; }
///
- /// キャッシュバック倍率(千分率)
+ /// キャッシュバック倍率
///
- public int ResultRatePermillage { get; init; }
+ public Multiplier ResultRate { get; init; }
}
diff --git a/Common/Models/User.cs b/Common/Models/User.cs
index 100df88..548de7a 100644
--- a/Common/Models/User.cs
+++ b/Common/Models/User.cs
@@ -29,29 +29,29 @@ public void ResetDailyState()
///
/// 単発ガチャを回す
///
- public GachaProbability? RollGachaOnce()
+ public GachaItem? RollGachaOnce(Gacha gacha)
{
MonthlyGachaPurchasePrice += MasterManager.SettingMaster.PricePerGachaOnce;
- return GachaManager.Instance.Roll();
+ return gacha.RollOnce();
}
///
/// 単発確定ガチャを回す
///
- public GachaProbability RollGachaOnceCertain()
+ public GachaItem RollGachaOnceCertain(Gacha gacha)
{
MonthlyGachaPurchasePrice += MasterManager.SettingMaster.PricePerGachaOnceCertain;
- return GachaManager.Instance.RollWithoutNone();
+ return gacha.RollOnceCertain();
}
///
/// 10連ガチャを回す
///
- public List RollGachaTenTimes()
+ public List RollGachaTenTimes(Gacha gacha)
{
const int pickCount = 10;
MonthlyGachaPurchasePrice += MasterManager.SettingMaster.PricePerGachaTenTimes;
- return Enumerable.Range(0, pickCount).Select(_ => GachaManager.Instance.Roll()).ToList();
+ return Enumerable.Range(0, pickCount).Select(_ => gacha.RollOnce()).ToList();
}
///
@@ -66,7 +66,7 @@ public SlotExecuteResult ExecuteSlot(Slot slot)
var price = slot.ExecutePrice;
var result = slot.Execute();
- var reward = (int)(NumberUtility.GetPercentFromPermillage(result.ResultRatePermillage) * price);
+ var reward = (int)(price * result.ResultRate);
MonthlySlotProfitPrice += reward - price;
TodaySlotExecuteCount++;
return result;
diff --git a/Common/Multiplier.cs b/Common/Multiplier.cs
new file mode 100644
index 0000000..b2e77dd
--- /dev/null
+++ b/Common/Multiplier.cs
@@ -0,0 +1,141 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Approvers.King.Common;
+
+///
+/// 確率・倍率を表す構造体
+///
+public readonly struct Multiplier : IEquatable, IComparable, IComparable
+{
+ ///
+ /// 千分率 (50% -> 500)
+ ///
+ public int Permillage { get; }
+
+ ///
+ /// 倍率 (50% -> 0.5)
+ ///
+ public float Rate => Permillage / 1000f;
+
+ ///
+ /// パーセント (50% -> 50)
+ ///
+ public float Percent => Permillage / 10f;
+
+ public static Multiplier Zero { get; } = new(0);
+ public static Multiplier One { get; } = new(1000);
+ public static Multiplier FromRate(float rate) => new((int)(rate * 1000f));
+ public static Multiplier FromPercent(float percent) => new((int)(percent * 10f));
+ public static Multiplier FromPermillage(int permillage) => new(permillage);
+
+ private Multiplier(int permillage)
+ {
+ Permillage = permillage;
+ }
+
+ public int CompareTo(Multiplier other)
+ {
+ return Permillage.CompareTo(other.Permillage);
+ }
+
+ public int CompareTo(object? obj)
+ {
+ if (obj is null) return 1;
+ return obj is Multiplier other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Multiplier)}");
+ }
+
+ public static bool operator <(Multiplier left, Multiplier right)
+ {
+ return left.CompareTo(right) < 0;
+ }
+
+ public static bool operator >(Multiplier left, Multiplier right)
+ {
+ return left.CompareTo(right) > 0;
+ }
+
+ public static bool operator <=(Multiplier left, Multiplier right)
+ {
+ return left.CompareTo(right) <= 0;
+ }
+
+ public static bool operator >=(Multiplier left, Multiplier right)
+ {
+ return left.CompareTo(right) >= 0;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return base.Equals(obj);
+ }
+
+ public bool Equals(Multiplier other)
+ {
+ return Permillage == other.Permillage;
+ }
+
+ public static bool operator ==(Multiplier left, Multiplier right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Multiplier left, Multiplier right)
+ {
+ return !(left == right);
+ }
+
+ public override int GetHashCode()
+ {
+ return Permillage;
+ }
+
+ public override string ToString()
+ {
+ return $"{Rate:P0}";
+ }
+
+ public static Multiplier operator +(Multiplier left, Multiplier right)
+ {
+ return new Multiplier(left.Permillage + right.Permillage);
+ }
+
+ public static Multiplier operator -(Multiplier left, Multiplier right)
+ {
+ return new Multiplier(left.Permillage - right.Permillage);
+ }
+
+ public static Multiplier operator *(Multiplier left, Multiplier right)
+ {
+ return new Multiplier((int)(left.Rate * right.Rate * 1000f));
+ }
+
+ public static Multiplier operator /(Multiplier left, Multiplier right)
+ {
+ return new Multiplier((int)(left.Rate / right.Rate * 1000f));
+ }
+
+ public static Multiplier operator *(Multiplier left, int right)
+ {
+ return new Multiplier(left.Permillage * right);
+ }
+
+ public static Multiplier operator /(Multiplier left, int right)
+ {
+ return new Multiplier(left.Permillage / right);
+ }
+
+ public static float operator *(float left, Multiplier right)
+ {
+ return left * right.Rate;
+ }
+
+ public static float operator /(float left, Multiplier right)
+ {
+ return left / right.Rate;
+ }
+
+ public Multiplier Clamp(Multiplier min, Multiplier max)
+ {
+ return new Multiplier(Math.Clamp(Permillage, min.Permillage, max.Permillage));
+ }
+}
diff --git a/Common/Random/RandomManager.cs b/Common/Random/RandomManager.cs
index be34623..ad6b0ae 100644
--- a/Common/Random/RandomManager.cs
+++ b/Common/Random/RandomManager.cs
@@ -4,29 +4,39 @@ public class RandomManager : Singleton
{
private readonly Random _random = new((int)DateTime.Now.Ticks & 0x0000FFFF);
- public static float GetRandomFloat(float max)
+ public static float GetRandomFloat(float maxExclusive)
{
- return GetRandomFloat(0f, max);
+ return GetRandomFloat(0f, maxExclusive);
}
- public static float GetRandomFloat(float min, float max)
+ public static float GetRandomFloat(float minInclusive, float maxExclusive)
{
- return (float)(min + (max - min) * Instance._random.NextDouble());
+ return (float)(minInclusive + (maxExclusive - minInclusive) * Instance._random.NextDouble());
}
- public static int GetRandomInt(int max)
+ public static int GetRandomInt(int maxExclusive)
{
- return GetRandomInt(0, max);
+ return GetRandomInt(0, maxExclusive);
}
- public static int GetRandomInt(int min, int max)
+ public static int GetRandomInt(int minInclusive, int maxExclusive)
{
- return Instance._random.Next(min, max);
+ return Instance._random.Next(minInclusive, maxExclusive);
}
- public static bool IsHit(float probability)
+ public static Multiplier GetRandomMultiplier(Multiplier maxInclusive)
{
- return GetRandomFloat(1f) <= probability;
+ return GetRandomMultiplier(Multiplier.Zero, maxInclusive);
+ }
+
+ public static Multiplier GetRandomMultiplier(Multiplier minInclusive, Multiplier maxInclusive)
+ {
+ return Multiplier.FromPermillage(Instance._random.Next(minInclusive.Permillage, maxInclusive.Permillage + 1));
+ }
+
+ public static bool IsHit(Multiplier probability)
+ {
+ return GetRandomMultiplier(Multiplier.One) <= probability;
}
public static T PickRandom(IEnumerable source)
diff --git a/Common/Utility/NumberUtility.cs b/Common/Utility/NumberUtility.cs
index a4246a9..fc6bcee 100644
--- a/Common/Utility/NumberUtility.cs
+++ b/Common/Utility/NumberUtility.cs
@@ -2,38 +2,8 @@ namespace Approvers.King.Common;
public static class NumberUtility
{
- public static DateTime GetBaseDateTime()
- {
- return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- }
-
- public static bool IsApproximate(this float a, float b)
- {
- return Math.Abs(a - b) < float.Epsilon;
- }
-
public static float GetSecondsFromMilliseconds(float milliseconds)
{
return milliseconds / 1000f;
}
-
- public static TimeSpan GetTimeSpanFromMilliseconds(float milliseconds)
- {
- return TimeSpan.FromMilliseconds(milliseconds);
- }
-
- public static float GetProbabilityFromPermillage(int permillage)
- {
- return permillage / 100000f;
- }
-
- public static float GetPercentFromPermillage(int permillage)
- {
- return permillage / 1000f;
- }
-
- public static int GetPermillageFromPercent(float percent)
- {
- return (int)(percent * 1000f);
- }
}
diff --git a/Events/Admin/AdminMasterReloadPresenter.cs b/Events/Admin/AdminMasterReloadPresenter.cs
index b8de11f..5120b94 100644
--- a/Events/Admin/AdminMasterReloadPresenter.cs
+++ b/Events/Admin/AdminMasterReloadPresenter.cs
@@ -13,27 +13,5 @@ protected override async Task MainAsync()
await Message.ReplyAsync("マスターをリロードするぞ");
await MasterManager.FetchAsync();
await Message.ReplyAsync("マスターをリロードしたぞ");
-
- await UpdateGachaTableAsync();
- }
-
- private async Task UpdateGachaTableAsync()
- {
- var beforeTable = GachaManager.Instance.ReplyMessageTable.Select(x => x.RandomMessageId).ToHashSet();
- GachaManager.Instance.LoadMaster();
- var afterTable = GachaManager.Instance.ReplyMessageTable.Select(x => x.RandomMessageId).ToHashSet();
-
- // テーブルに差分がある場合は排出率を更新する
- var hasDiff = beforeTable.SetEquals(afterTable);
- if (hasDiff == false)
- {
- GachaManager.Instance.ShuffleMessageRates();
- await GachaManager.Instance.SaveAsync();
-
- // 排出率を投稿する
- var guild = DiscordManager.Client.GetGuild(EnvironmentManager.DiscordTargetGuildId);
- await guild.GetTextChannel(EnvironmentManager.DiscordMainChannelId)
- .SendMessageAsync(embed: GachaUtility.GetInfoEmbedBuilder().Build());
- }
}
}
diff --git a/Events/DailyReset/DailyResetPresenter.cs b/Events/DailyReset/DailyResetPresenter.cs
index e9d56e7..806c972 100644
--- a/Events/DailyReset/DailyResetPresenter.cs
+++ b/Events/DailyReset/DailyResetPresenter.cs
@@ -13,24 +13,25 @@ public class DailyResetPresenter : SchedulerJobPresenterBase
protected override async Task MainAsync()
{
await using var app = AppService.CreateSession();
+
var slotMaxUsers = await app.Users
.Where(x => x.TodaySlotExecuteCount >= MasterManager.SettingMaster.UserSlotExecuteLimitPerDay)
.Select(x => x.DeepCopy())
.ToListAsync();
await app.Users.ForEachAsync(user => user.ResetDailyState());
- await app.SaveChangesAsync();
// 排出確率を変える
- GachaManager.Instance.LoadMaster();
- GachaManager.Instance.ShuffleRareReplyRate();
- GachaManager.Instance.ShuffleMessageRates();
- await GachaManager.Instance.SaveAsync();
+ var gacha = await app.GetDefaultGachaAsync();
+ gacha.ShuffleRareReplyRate();
+ gacha.ShuffleMessageRates();
+
+ await app.SaveChangesAsync();
// 名前を更新する
- await DiscordManager.GetClientUser().ModifyAsync(x => x.Nickname = $"{GachaManager.Instance.RareReplyRate:P0}の確率でわかってくれる創造主");
+ await DiscordManager.GetClientUser().ModifyAsync(x => x.Nickname = $"{gacha.HitProbability.Rate:P0}の確率でわかってくれる創造主");
// 排出率を投稿する
- await DiscordManager.GetMainChannel().SendMessageAsync(embed: GachaUtility.GetInfoEmbedBuilder().Build());
+ await DiscordManager.GetMainChannel().SendMessageAsync(embed: GachaUtility.GetInfoEmbedBuilder(gacha).Build());
// スロットの実行回数が最大になったユーザーを通知する
await NotifySlotMaxUsers(slotMaxUsers);
diff --git a/Events/Gacha/GachaCommandPresenter.cs b/Events/Gacha/GachaCommandPresenter.cs
index 6c67b82..9f9daf6 100644
--- a/Events/Gacha/GachaCommandPresenter.cs
+++ b/Events/Gacha/GachaCommandPresenter.cs
@@ -13,14 +13,15 @@ protected override async Task MainAsync()
{
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);
+ var gacha = await app.GetDefaultGachaAsync();
- var results = user.RollGachaTenTimes();
+ var results = user.RollGachaTenTimes(gacha);
await SendReplyAsync(user, results);
await app.SaveChangesAsync();
}
- private async Task SendReplyAsync(User user, IReadOnlyList results)
+ private async Task SendReplyAsync(User user, IReadOnlyList results)
{
var builder = new StringBuilder();
builder.AppendLine($"↓↓↓ いっそう{results.Count}連おみくじ ↓↓↓");
@@ -28,7 +29,7 @@ private async Task SendReplyAsync(User user, IReadOnlyList re
{
builder.AppendLine(result != null
? Format.Bold(
- $"・{result.RandomMessage?.Content ?? MessageConst.MissingMessage} ({result.Probability:P0})")
+ $"・{result.RandomMessage?.Content ?? MessageConst.MissingMessage} ({result.Probability.Rate:P0})")
: Format.Code("x"));
}
diff --git a/Events/Gacha/GachaInfoCommandPresenter.cs b/Events/Gacha/GachaInfoCommandPresenter.cs
index f50d18b..509b7cf 100644
--- a/Events/Gacha/GachaInfoCommandPresenter.cs
+++ b/Events/Gacha/GachaInfoCommandPresenter.cs
@@ -9,10 +9,13 @@ public class GachaInfoCommandPresenter : DiscordMessagePresenterBase
{
protected override async Task MainAsync()
{
+ await using var app = AppService.CreateSession();
+ var gacha = await app.GetDefaultGachaAsync();
+
// 排出率を投稿する
await DiscordManager.Client
.GetGuild(EnvironmentManager.DiscordTargetGuildId)
.GetTextChannel(EnvironmentManager.DiscordMainChannelId)
- .SendMessageAsync(embed: GachaUtility.GetInfoEmbedBuilder().Build());
+ .SendMessageAsync(embed: GachaUtility.GetInfoEmbedBuilder(gacha).Build());
}
}
diff --git a/Events/Gacha/GachaInteractReplyPresenter.cs b/Events/Gacha/GachaInteractReplyPresenter.cs
index 6c19514..2db828c 100644
--- a/Events/Gacha/GachaInteractReplyPresenter.cs
+++ b/Events/Gacha/GachaInteractReplyPresenter.cs
@@ -12,8 +12,9 @@ protected override async Task MainAsync()
{
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);
+ var gacha = await app.GetDefaultGachaAsync();
- var message = user.RollGachaOnceCertain();
+ var message = user.RollGachaOnceCertain(gacha);
await SendReplyAsync(message.RandomMessage?.Content ?? MessageConst.MissingMessage);
await app.SaveChangesAsync();
diff --git a/Events/Gacha/GachaRareReplyPresenter.cs b/Events/Gacha/GachaRareReplyPresenter.cs
index d77b81c..fe44977 100644
--- a/Events/Gacha/GachaRareReplyPresenter.cs
+++ b/Events/Gacha/GachaRareReplyPresenter.cs
@@ -17,8 +17,9 @@ protected override async Task MainAsync()
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);
+ var gacha = await app.GetDefaultGachaAsync();
- var message = user.RollGachaOnce();
+ var message = user.RollGachaOnce(gacha);
if (message != null)
{
await SendReplyAsync(message.RandomMessage?.Content ?? MessageConst.MissingMessage);
diff --git a/Events/Gacha/GachaUtility.cs b/Events/Gacha/GachaUtility.cs
index ee27bf1..ba9ce85 100644
--- a/Events/Gacha/GachaUtility.cs
+++ b/Events/Gacha/GachaUtility.cs
@@ -6,20 +6,21 @@ namespace Approvers.King.Events;
public static class GachaUtility
{
- public static EmbedBuilder GetInfoEmbedBuilder()
+ public static EmbedBuilder GetInfoEmbedBuilder(Gacha gacha)
{
- var records = GachaManager.Instance.ReplyMessageTable
+ var minProbability = Multiplier.FromPercent(1);
+ var records = gacha.GachaItems
.OrderByDescending(x => x.Probability)
- .Where(x => x.Probability.IsApproximate(0f) == false)
- .Select(x => (x.RandomMessage?.Content ?? MessageConst.MissingMessage, x.Probability.ToString("P0")));
+ .Where(x => x.Probability > minProbability)
+ .Select(x => (x.RandomMessage?.Content ?? MessageConst.MissingMessage, x.Probability.Rate.ToString("P0")));
return new EmbedBuilder()
.WithTitle(
$"{IssoUtility.SmileStamp}{IssoUtility.SmileStamp}{IssoUtility.SmileStamp} 本日のいっそう {IssoUtility.SmileStamp}{IssoUtility.SmileStamp}{IssoUtility.SmileStamp}")
.WithColor(new Color(0xf1, 0xc4, 0x0f))
- .WithDescription($"本日は {Format.Bold($"{GachaManager.Instance.RareReplyRate:P0}")} の確率で反応します")
+ .WithDescription($"本日は {Format.Bold($"{gacha.HitProbability.Rate:P0}")} の確率で反応します")
.AddField("排出確率", DiscordMessageUtility.Table(records));
}
-
+
public static string CreateRankingView(IReadOnlyList rankingUsers)
{
var embedBuilder = new StringBuilder();
diff --git a/Events/Slot/SlotExecutePresenter.cs b/Events/Slot/SlotExecutePresenter.cs
index 85cf44a..2c62c5d 100644
--- a/Events/Slot/SlotExecutePresenter.cs
+++ b/Events/Slot/SlotExecutePresenter.cs
@@ -72,8 +72,7 @@ private static string CreatePurchaseMessage(SlotExecuteResult result, User user,
{
if (result.IsWin)
{
- var rate = NumberUtility.GetPercentFromPermillage(result.ResultRatePermillage);
- sb.AppendLine(Format.Bold($"Y O U W I N ! ! x{rate:F1}"));
+ sb.AppendLine(Format.Bold($"Y O U W I N ! ! x{result.ResultRate.Rate:F1}"));
}
else
{
diff --git a/Migrations/20241201133806_CreateGacha.Designer.cs b/Migrations/20241201133806_CreateGacha.Designer.cs
new file mode 100644
index 0000000..c8e89ba
--- /dev/null
+++ b/Migrations/20241201133806_CreateGacha.Designer.cs
@@ -0,0 +1,105 @@
+//
+using System;
+using Approvers.King.Common;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Approvers.King.Migrations
+{
+ [DbContext(typeof(AppService))]
+ [Migration("20241201133806_CreateGacha")]
+ partial class CreateGacha
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.10");
+
+ modelBuilder.Entity("Approvers.King.Common.Gacha", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("HitProbabilityPermillage")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.ToTable("Gachas");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.GachaItem", b =>
+ {
+ b.Property("GachaId")
+ .HasColumnType("TEXT");
+
+ b.Property("RandomMessageId")
+ .HasColumnType("TEXT");
+
+ b.Property("ProbabilityPermillage")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("GachaId", "RandomMessageId");
+
+ b.ToTable("GachaItems");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.Slot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ConditionPermillage")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.ToTable("Slots");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.User", b =>
+ {
+ b.Property("DiscordId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("MonthlyGachaPurchasePrice")
+ .HasColumnType("INTEGER");
+
+ b.Property("MonthlySlotProfitPrice")
+ .HasColumnType("INTEGER");
+
+ b.Property("TodaySlotExecuteCount")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("DiscordId");
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.GachaItem", b =>
+ {
+ b.HasOne("Approvers.King.Common.Gacha", "Gacha")
+ .WithMany("GachaItems")
+ .HasForeignKey("GachaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Gacha");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.Gacha", b =>
+ {
+ b.Navigation("GachaItems");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Migrations/20241201133806_CreateGacha.cs b/Migrations/20241201133806_CreateGacha.cs
new file mode 100644
index 0000000..0e16716
--- /dev/null
+++ b/Migrations/20241201133806_CreateGacha.cs
@@ -0,0 +1,86 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Approvers.King.Migrations
+{
+ ///
+ public partial class CreateGacha : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "AppStates");
+
+ migrationBuilder.DropTable(
+ name: "GachaProbabilities");
+
+ migrationBuilder.CreateTable(
+ name: "Gachas",
+ columns: table => new
+ {
+ Id = table.Column(type: "TEXT", nullable: false),
+ HitProbabilityPermillage = table.Column(type: "INTEGER", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Gachas", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "GachaItems",
+ columns: table => new
+ {
+ GachaId = table.Column(type: "TEXT", nullable: false),
+ RandomMessageId = table.Column(type: "TEXT", nullable: false),
+ ProbabilityPermillage = table.Column(type: "INTEGER", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_GachaItems", x => new { x.GachaId, x.RandomMessageId });
+ table.ForeignKey(
+ name: "FK_GachaItems_Gachas_GachaId",
+ column: x => x.GachaId,
+ principalTable: "Gachas",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "GachaItems");
+
+ migrationBuilder.DropTable(
+ name: "Gachas");
+
+ migrationBuilder.CreateTable(
+ name: "AppStates",
+ columns: table => new
+ {
+ Type = table.Column(type: "INTEGER", nullable: false),
+ Value = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AppStates", x => x.Type);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "GachaProbabilities",
+ columns: table => new
+ {
+ RandomMessageId = table.Column(type: "TEXT", nullable: false),
+ Probability = table.Column(type: "REAL", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_GachaProbabilities", x => x.RandomMessageId);
+ });
+ }
+ }
+}
diff --git a/Migrations/AppServiceModelSnapshot.cs b/Migrations/AppServiceModelSnapshot.cs
index b27d054..8217dec 100644
--- a/Migrations/AppServiceModelSnapshot.cs
+++ b/Migrations/AppServiceModelSnapshot.cs
@@ -17,31 +17,34 @@ protected override void BuildModel(ModelBuilder modelBuilder)
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.10");
- modelBuilder.Entity("Approvers.King.Common.AppState", b =>
+ modelBuilder.Entity("Approvers.King.Common.Gacha", b =>
{
- b.Property("Type")
- .HasColumnType("INTEGER");
-
- b.Property("Value")
- .IsRequired()
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
.HasColumnType("TEXT");
- b.HasKey("Type");
+ b.Property("HitProbabilityPermillage")
+ .HasColumnType("INTEGER");
- b.ToTable("AppStates");
+ b.HasKey("Id");
+
+ b.ToTable("Gachas");
});
- modelBuilder.Entity("Approvers.King.Common.GachaProbability", b =>
+ modelBuilder.Entity("Approvers.King.Common.GachaItem", b =>
{
+ b.Property("GachaId")
+ .HasColumnType("TEXT");
+
b.Property("RandomMessageId")
.HasColumnType("TEXT");
- b.Property("Probability")
- .HasColumnType("REAL");
+ b.Property("ProbabilityPermillage")
+ .HasColumnType("INTEGER");
- b.HasKey("RandomMessageId");
+ b.HasKey("GachaId", "RandomMessageId");
- b.ToTable("GachaProbabilities");
+ b.ToTable("GachaItems");
});
modelBuilder.Entity("Approvers.King.Common.Slot", b =>
@@ -77,6 +80,22 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("Users");
});
+
+ modelBuilder.Entity("Approvers.King.Common.GachaItem", b =>
+ {
+ b.HasOne("Approvers.King.Common.Gacha", "Gacha")
+ .WithMany("GachaItems")
+ .HasForeignKey("GachaId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Gacha");
+ });
+
+ modelBuilder.Entity("Approvers.King.Common.Gacha", b =>
+ {
+ b.Navigation("GachaItems");
+ });
#pragma warning restore 612, 618
}
}
diff --git a/Program.cs b/Program.cs
index 26a06d6..99ad03b 100644
--- a/Program.cs
+++ b/Program.cs
@@ -16,20 +16,48 @@ private static void Main(string[] args)
private static async Task BuildAsync(string[] args)
{
- // 共通基盤系を初期化する
+ await InitializeModulesAsync();
+ await InitializeStatesAsync();
+ RegisterEvents();
+
+ // 永久に待つ
+ await Task.Delay(-1);
+ }
+
+ ///
+ /// 共通基盤系を初期化する
+ ///
+ private static async Task InitializeModulesAsync()
+ {
TimeManager.Instance.Initialize();
await MasterManager.FetchAsync();
- await GachaManager.Instance.LoadAsync();
SchedulerManager.Initialize();
await DiscordManager.InitializeAsync();
+ }
+
+ private static async Task InitializeStatesAsync()
+ {
+ await using var app = AppService.CreateSession();
- if (GachaManager.Instance.IsTableEmpty)
+ var gacha = await app.GetDefaultGachaAsync();
+ if (gacha.GachaItems.Count == 0)
{
- // 起動時にデータがない場合、ガチャ確率を初期化する
- await new DailyResetPresenter().RunAsync();
+ gacha.ShuffleMessageRates();
}
- // ここからイベント登録
+ if (gacha.HitProbability == Multiplier.Zero)
+ {
+ gacha.ShuffleRareReplyRate();
+ }
+
+ await app.SaveChangesAsync();
+ }
+
+ ///
+ /// 全てのイベントを登録する
+ ///
+ private static void RegisterEvents()
+ {
DiscordManager.Client.MessageReceived += message =>
{
OnMessageReceived(message);
@@ -40,9 +68,6 @@ private static async Task BuildAsync(string[] args)
SchedulerManager.RegisterYearly(TimeManager.Birthday + TimeManager.DailyResetTime + TimeSpan.FromSeconds(1));
SchedulerManager.RegisterMonthly(TimeManager.MonthlyResetDay, TimeManager.DailyResetTime);
SchedulerManager.RegisterOn(x => x.Minute is 0);
-
- // 永久に待つ
- await Task.Delay(-1);
}
///