diff --git a/Common/Log/LogManager.cs b/Common/Log/LogManager.cs index dac20b6..9c3e107 100644 --- a/Common/Log/LogManager.cs +++ b/Common/Log/LogManager.cs @@ -7,6 +7,11 @@ private static string CreateTimestamp() return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); } + public static void Log(object message) + { + Console.WriteLine($"{CreateTimestamp()}: [INFO] {message}"); + } + public static void Log(string message) { Console.WriteLine($"{CreateTimestamp()}: [INFO] {message}"); diff --git a/Common/Master/SettingMaster.cs b/Common/Master/SettingMaster.cs index 24238fa..67a9eaf 100644 --- a/Common/Master/SettingMaster.cs +++ b/Common/Master/SettingMaster.cs @@ -47,11 +47,13 @@ private int GetInt(string key) public int PricePerGachaTenTimes => GetInt(nameof(PricePerGachaTenTimes)); public int PricePerGachaOnceCertain => GetInt(nameof(PricePerGachaOnceCertain)); public int PurchaseInfoRankingViewUserCount => GetInt(nameof(PurchaseInfoRankingViewUserCount)); - public string SilentReplyMessage => GetString(nameof(SilentReplyMessage)); public int PricePerSlotOnce => GetInt(nameof(PricePerSlotOnce)); public string SlotReelRollingFormat => GetString(nameof(SlotReelRollingFormat)); public string SlotLeverFormat => GetString(nameof(SlotLeverFormat)); public int UserSlotExecuteLimitPerDay => GetInt(nameof(UserSlotExecuteLimitPerDay)); + public int SlotMaxConditionOffsetPermillage => GetInt(nameof(SlotMaxConditionOffsetPermillage)); + public int SlotMinConditionOffsetPermillage => GetInt(nameof(SlotMinConditionOffsetPermillage)); + public int SlotRepeatPermillageUpperBound => GetInt(nameof(SlotRepeatPermillageUpperBound)); } [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] diff --git a/Common/Models/AppState.cs b/Common/Models/AppState.cs index 0a7ac81..37f4fbb 100644 --- a/Common/Models/AppState.cs +++ b/Common/Models/AppState.cs @@ -55,4 +55,5 @@ public class AppState public enum AppStateType { RareReplyProbabilityPermillage, + SlotConditionOffsetPermillage, } diff --git a/Common/Models/SlotManager.cs b/Common/Models/SlotManager.cs index 18d5ad8..69c8426 100644 --- a/Common/Models/SlotManager.cs +++ b/Common/Models/SlotManager.cs @@ -3,6 +3,7 @@ namespace Approvers.King.Common; public class SlotManager : Singleton { private readonly List _items = []; + private int _conditionOffsetPermillage; private const int ReelCount = 3; public void LoadMaster() @@ -12,6 +13,26 @@ public void LoadMaster() _items.AddRange(items); } + public async Task LoadAsync() + { + await using var app = AppService.CreateSession(); + _conditionOffsetPermillage = await app.AppStates.GetIntAsync(AppStateType.SlotConditionOffsetPermillage) ?? 0; + } + + public async Task SaveAsync() + { + await using var app = AppService.CreateSession(); + await app.AppStates.SetIntAsync(AppStateType.SlotConditionOffsetPermillage, _conditionOffsetPermillage); + await app.SaveChangesAsync(); + } + + public void RefreshCondition() + { + var max = MasterManager.SettingMaster.SlotMaxConditionOffsetPermillage; + var min = MasterManager.SettingMaster.SlotMinConditionOffsetPermillage; + _conditionOffsetPermillage = RandomManager.GetRandomInt(min, max + 1); + } + public SlotExecuteResult Execute() { var itemCount = _items.Count; @@ -26,7 +47,8 @@ public SlotExecuteResult Execute() } // 一定確率で直前と同じ出目が出る - var repeatProbability = NumberUtility.GetProbabilityFromPermillage(reelItems[i - 1].RepeatPermillage); + var repeatPermillage = Math.Clamp(reelItems[i - 1].RepeatPermillage + _conditionOffsetPermillage, 0, MasterManager.SettingMaster.SlotRepeatPermillageUpperBound); + var repeatProbability = NumberUtility.GetProbabilityFromPermillage(repeatPermillage); var isRepeat = RandomManager.IsHit(repeatProbability); if (isRepeat) { diff --git a/Common/Scheduler/SchedulerJobRunner.cs b/Common/Scheduler/SchedulerJobRunner.cs index 1cef0ce..1fdc3e4 100644 --- a/Common/Scheduler/SchedulerJobRunner.cs +++ b/Common/Scheduler/SchedulerJobRunner.cs @@ -2,6 +2,8 @@ public abstract class SchedulerJobRunner { + public bool OnRiseOnly { get; set; } + public bool? PreviousCondition = null; public Predicate? Predicate { get; init; } public abstract void Run(); } diff --git a/Common/Scheduler/SchedulerManager.cs b/Common/Scheduler/SchedulerManager.cs index 37a9126..f2ecf1a 100644 --- a/Common/Scheduler/SchedulerManager.cs +++ b/Common/Scheduler/SchedulerManager.cs @@ -18,8 +18,26 @@ private static void OnEverySecond(object? sender, ElapsedEventArgs e) { var now = TimeManager.GetNow(); foreach (var runner in Runners) - if (runner.Predicate != null && runner.Predicate(now)) - runner.Run(); + { + var condition = runner.Predicate != null && runner.Predicate(now); + + if (runner.OnRiseOnly == false) + { + if (condition) + { + runner.Run(); + } + } + else + { + if (runner.PreviousCondition is false && condition) + { + runner.Run(); + } + } + + runner.PreviousCondition = condition; + } } public static void RegisterDaily(TimeSpan time) where T : SchedulerJobPresenterBase, new() @@ -54,4 +72,13 @@ private static void OnEverySecond(object? sender, ElapsedEventArgs e) x.Second == datetime.Second }); } + + public static void RegisterOn(Predicate predicate) where T : SchedulerJobPresenterBase, new() + { + Runners.Add(new SchedulerJobRunner + { + Predicate = predicate, + OnRiseOnly = true + }); + } } diff --git a/Events/Slot/SlotConditionRefreshPresenter.cs b/Events/Slot/SlotConditionRefreshPresenter.cs new file mode 100644 index 0000000..d547491 --- /dev/null +++ b/Events/Slot/SlotConditionRefreshPresenter.cs @@ -0,0 +1,12 @@ +using Approvers.King.Common; + +namespace Approvers.King.Events; + +public class SlotConditionRefreshPresenter : SchedulerJobPresenterBase +{ + protected override async Task MainAsync() + { + SlotManager.Instance.RefreshCondition(); + await SlotManager.Instance.SaveAsync(); + } +} diff --git a/Program.cs b/Program.cs index d6c464b..d429df7 100644 --- a/Program.cs +++ b/Program.cs @@ -17,8 +17,12 @@ private static async Task BuildAsync(string[] args) await MasterManager.FetchAsync(); await GachaManager.Instance.LoadAsync(); + SlotManager.Instance.LoadMaster(); + await SlotManager.Instance.LoadAsync(); + SchedulerManager.Initialize(); + await DiscordManager.InitializeAsync(); if (GachaManager.Instance.IsTableEmpty) @@ -38,6 +42,7 @@ private static async Task BuildAsync(string[] args) TimeSpan.FromSeconds(1)); SchedulerManager.RegisterMonthly(TimeManager.MonthlyResetDay, TimeManager.DailyResetTime); + SchedulerManager.RegisterOn(x => x.Minute is 0); // 永久に待つ await Task.Delay(-1);