OSDN Git Service

工廠フル稼働!新兵装を開発せよ!のカウンターを実装する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Model / QuestInfo.cs
1 // Copyright (C) 2013, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Drawing;\r
18 using System.Linq;\r
19 using System.Windows.Forms;\r
20 using System.Xml.Serialization;\r
21 using KancolleSniffer.Util;\r
22 \r
23 namespace KancolleSniffer.Model\r
24 {\r
25     public class QuestStatus\r
26     {\r
27         public int Id { get; set; }\r
28         public int Category { get; set; }\r
29         public string Name { get; set; }\r
30         public string Detail { get; set; }\r
31         public int[] Material { get; set; }\r
32         public int Progress { get; set; }\r
33 \r
34         [XmlIgnore]\r
35         public QuestCount Count { get; set; }\r
36 \r
37         [XmlIgnore]\r
38         public Color Color { get; set; }\r
39 \r
40         public string ToToolTip() =>\r
41             Detail +\r
42             (Material == null || Material.All(x => x == 0)\r
43                 ? ""\r
44                 : "\r\n" + string.Join(" ",\r
45                       new[] {"燃", "弾", "鋼", "ボ", "建造", "修復", "開発", "改修"}\r
46                           .Zip(Material, (m, num) => num == 0 ? "" : m + num)\r
47                           .Where(s => !string.IsNullOrEmpty(s))));\r
48 \r
49         public QuestStatus Clone()\r
50         {\r
51             var clone = (QuestStatus)MemberwiseClone();\r
52             clone.Count = Count.Clone();\r
53             return clone;\r
54         }\r
55     }\r
56 \r
57     public enum QuestInterval\r
58     {\r
59         // ReSharper disable once UnusedMember.Global\r
60         Other,\r
61         Daily,\r
62         Weekly,\r
63         Monthly,\r
64         Quarterly,\r
65         Yearly2,\r
66         Yearly3,\r
67         Yearly5,\r
68         Yearly8,\r
69         Yearly9,\r
70         Yearly10,\r
71         Yearly11\r
72     }\r
73 \r
74     public class QuestInfo : IHaveState\r
75     {\r
76         private readonly QuestCountList _countList;\r
77         private readonly Func<DateTime> _nowFunc = () => DateTime.Now;\r
78         private DateTime _now;\r
79         private DateTime _lastReset;\r
80         private IEnumerable<QuestStatus> _clearedQuest = new List<QuestStatus>();\r
81 \r
82         private readonly Color[] _color =\r
83         {\r
84             Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),\r
85             Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),\r
86             Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41), Color.FromArgb(232, 57, 41)\r
87         };\r
88 \r
89         public SortedDictionary<int, QuestStatus> QuestDictionary { get; } = new SortedDictionary<int, QuestStatus>();\r
90 \r
91         public QuestStatus[] Quests => QuestDictionary.Values.ToArray();\r
92 \r
93         public QuestInfo(QuestCountList countList, Func<DateTime> nowFunc = null)\r
94         {\r
95             _countList = countList;\r
96             if (nowFunc != null)\r
97                 _nowFunc = nowFunc;\r
98         }\r
99 \r
100         public void GetNotifications(out string[] notify, out string[] stop)\r
101         {\r
102             var cleared = QuestDictionary.Values.Where(q => q.Count.Cleared).ToArray();\r
103             notify = cleared.Except(_clearedQuest, new QuestComparer()).Select(q => q.Name).ToArray();\r
104             stop = _clearedQuest.Except(cleared, new QuestComparer()).Select(q => q.Name).ToArray();\r
105             _clearedQuest = cleared;\r
106         }\r
107 \r
108         private class QuestComparer : IEqualityComparer<QuestStatus>\r
109         {\r
110             public bool Equals(QuestStatus x, QuestStatus y)\r
111             {\r
112                 return x?.Id == y?.Id;\r
113             }\r
114 \r
115             public int GetHashCode(QuestStatus obj)\r
116             {\r
117                 return obj.Id;\r
118             }\r
119         }\r
120 \r
121         private readonly int[] _progress = {0, 50, 80};\r
122 \r
123         public void InspectQuestList(string request, dynamic json)\r
124         {\r
125             ResetCounts();\r
126             var values = HttpUtility.ParseQueryString(request);\r
127             if (values["api_tab_id"] == "0")\r
128                 QuestDictionary.Clear();\r
129             if (json.api_list == null)\r
130                 return;\r
131             foreach (var entry in json.api_list)\r
132             {\r
133                 if (entry is double) // -1の場合がある。\r
134                     continue;\r
135                 var quest = new QuestStatus\r
136                 {\r
137                     Id = (int)entry.api_no,\r
138                     Category = (int)entry.api_category,\r
139                     Progress = _progress[(int)entry.api_progress_flag],\r
140                     Name = (string)entry.api_title,\r
141                     Detail = ((string)entry.api_detail).Replace("<br>", "\r\n"),\r
142                     Material = (int[])entry.api_get_material\r
143                 };\r
144                 var state = (int)entry.api_state;\r
145                 switch (state)\r
146                 {\r
147                     case 3:\r
148                         quest.Progress = 100;\r
149                         goto case 2;\r
150                     case 2:\r
151                         AdjustQuest(quest);\r
152                         SetQuest(quest);\r
153                         break;\r
154                 }\r
155             }\r
156         }\r
157 \r
158         private void AdjustQuest(QuestStatus quest)\r
159         {\r
160             quest.Count = _countList.GetCount(quest.Id);\r
161             if (quest.Count.AdjustCount(quest.Progress))\r
162                 NeedSave = true;\r
163             quest.Material = quest.Material.Concat(quest.Count.Spec.Material).ToArray();\r
164             if (!QuestDictionary.ContainsKey(quest.Id))\r
165                 NeedSave = true;\r
166         }\r
167 \r
168         private void SetQuest(QuestStatus quest)\r
169         {\r
170             quest.Count = _countList.GetCount(quest.Id);\r
171             quest.Color = quest.Category <= _color.Length ? _color[quest.Category - 1] : Control.DefaultBackColor;\r
172             QuestDictionary[quest.Id] = quest;\r
173         }\r
174 \r
175         private void ResetCounts()\r
176         {\r
177             _now = _nowFunc();\r
178             if (!CrossBoundary(QuestInterval.Daily))\r
179                 return;\r
180             foreach (var interval in (QuestInterval[])typeof(QuestInterval).GetEnumValues())\r
181             {\r
182                 if (!CrossBoundary(interval))\r
183                     continue;\r
184                 _countList.Remove(interval);\r
185             }\r
186             _lastReset = _now;\r
187             NeedSave = true;\r
188         }\r
189 \r
190         private DateTime LastMorning => _now.Date.AddDays(_now.Hour < 5 ? -1 : 0).AddHours(5);\r
191 \r
192         private bool CrossBoundary(QuestInterval interval)\r
193         {\r
194             return interval switch\r
195             {\r
196                 QuestInterval.Other => false,\r
197                 QuestInterval.Daily => CrossBoundary(LastMorning),\r
198                 QuestInterval.Weekly => CrossBoundary(LastMonday.AddHours(5)),\r
199                 QuestInterval.Monthly => CrossBoundary(new DateTime(_now.Year, _now.Month, 1, 5, 0, 0)),\r
200                 QuestInterval.Quarterly => CrossBoundary(QuarterlyBoundary.AddHours(5)),\r
201                 QuestInterval.Yearly2 => CrossBoundary(new DateTime(_now.Year, 2, 1, 5, 0, 0)),\r
202                 QuestInterval.Yearly3 => CrossBoundary(new DateTime(_now.Year, 3, 1, 5, 0, 0)),\r
203                 QuestInterval.Yearly5 => CrossBoundary(new DateTime(_now.Year, 5, 1, 5, 0, 0)),\r
204                 QuestInterval.Yearly8 => CrossBoundary(new DateTime(_now.Year, 8, 1, 5, 0, 0)),\r
205                 QuestInterval.Yearly9 => CrossBoundary(new DateTime(_now.Year, 9, 1, 5, 0, 0)),\r
206                 QuestInterval.Yearly10 => CrossBoundary(new DateTime(_now.Year, 10, 1, 5, 0, 0)),\r
207                 QuestInterval.Yearly11 => CrossBoundary(new DateTime(_now.Year, 11, 1, 5, 0, 0)),\r
208                 _ => false\r
209             };\r
210         }\r
211 \r
212         private DateTime LastMonday => _now.Date.AddDays(-((6 + (int)_now.DayOfWeek) % 7));\r
213 \r
214         private DateTime QuarterlyBoundary =>\r
215             _now.Month / 3 == 0\r
216                 ? new DateTime(_now.Year - 1, 12, 1)\r
217                 : new DateTime(_now.Year, _now.Month / 3 * 3, 1);\r
218 \r
219         private bool CrossBoundary(DateTime boundary)\r
220         {\r
221             return _lastReset < boundary && boundary <= _now;\r
222         }\r
223 \r
224         public void InspectStop(string request)\r
225         {\r
226             var values = HttpUtility.ParseQueryString(request);\r
227             QuestDictionary.Remove(int.Parse(values["api_quest_id"]));\r
228             NeedSave = true;\r
229         }\r
230 \r
231         public void InspectClearItemGet(string request)\r
232         {\r
233             var values = HttpUtility.ParseQueryString(request);\r
234             var id = int.Parse(values["api_quest_id"]);\r
235             _countList.Remove(id);\r
236             QuestDictionary.Remove(id);\r
237             NeedSave = true;\r
238         }\r
239 \r
240         public bool NeedSave { get; set; }\r
241 \r
242         public void SaveState(Status status)\r
243         {\r
244             NeedSave = false;\r
245             status.QuestLastReset = _lastReset;\r
246             if (QuestDictionary != null)\r
247                 status.QuestList = QuestDictionary.Values.ToArray();\r
248             if (_countList != null)\r
249                 status.QuestCountList = _countList.NonZeroCountList.ToArray();\r
250         }\r
251 \r
252         public void LoadState(Status status)\r
253         {\r
254             _lastReset = status.QuestLastReset;\r
255             if (status.QuestCountList != null)\r
256                 _countList.SetCountList(status.QuestCountList);\r
257             if (status.QuestList != null)\r
258             {\r
259                 QuestDictionary.Clear();\r
260                 foreach (var quest in status.QuestList)\r
261                     SetQuest(quest);\r
262             }\r
263         }\r
264     }\r
265 }