OSDN Git Service

バージョン12.11の準備
[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         Yearly1,\r
66         Yearly2,\r
67         Yearly3,\r
68         Yearly5,\r
69         Yearly8,\r
70         Yearly9,\r
71         Yearly10,\r
72         Yearly11\r
73     }\r
74 \r
75     public class QuestInfo : IHaveState\r
76     {\r
77         private readonly QuestCountList _countList;\r
78         private readonly Func<DateTime> _nowFunc = () => DateTime.Now;\r
79         private DateTime _now;\r
80         private DateTime _lastReset;\r
81         private IEnumerable<QuestStatus> _clearedQuest = new List<QuestStatus>();\r
82 \r
83         private readonly Color[] _color =\r
84         {\r
85             Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),\r
86             Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),\r
87             Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41), Color.FromArgb(232, 57, 41)\r
88         };\r
89 \r
90         public SortedDictionary<int, QuestStatus> QuestDictionary { get; } = new SortedDictionary<int, QuestStatus>();\r
91 \r
92         public QuestStatus[] Quests => QuestDictionary.Values.ToArray();\r
93 \r
94         public QuestInfo(QuestCountList countList, Func<DateTime> nowFunc = null)\r
95         {\r
96             _countList = countList;\r
97             if (nowFunc != null)\r
98                 _nowFunc = nowFunc;\r
99         }\r
100 \r
101         public void GetNotifications(out string[] notify, out string[] stop)\r
102         {\r
103             var cleared = QuestDictionary.Values.Where(q => q.Count.Cleared).ToArray();\r
104             notify = cleared.Except(_clearedQuest, new QuestComparer()).Select(q => q.Name).ToArray();\r
105             stop = _clearedQuest.Except(cleared, new QuestComparer()).Select(q => q.Name).ToArray();\r
106             _clearedQuest = cleared;\r
107         }\r
108 \r
109         private class QuestComparer : IEqualityComparer<QuestStatus>\r
110         {\r
111             public bool Equals(QuestStatus x, QuestStatus y)\r
112             {\r
113                 return x?.Id == y?.Id;\r
114             }\r
115 \r
116             public int GetHashCode(QuestStatus obj)\r
117             {\r
118                 return obj.Id;\r
119             }\r
120         }\r
121 \r
122         private readonly int[] _progress = {0, 50, 80};\r
123 \r
124         public void InspectQuestList(string request, dynamic json)\r
125         {\r
126             ResetCounts();\r
127             var values = HttpUtility.ParseQueryString(request);\r
128             if (values["api_tab_id"] == "0")\r
129                 QuestDictionary.Clear();\r
130             if (json.api_list == null)\r
131                 return;\r
132             foreach (var entry in json.api_list)\r
133             {\r
134                 if (entry is double) // -1の場合がある。\r
135                     continue;\r
136                 var quest = new QuestStatus\r
137                 {\r
138                     Id = (int)entry.api_no,\r
139                     Category = (int)entry.api_category,\r
140                     Progress = _progress[(int)entry.api_progress_flag],\r
141                     Name = (string)entry.api_title,\r
142                     Detail = ((string)entry.api_detail).Replace("<br>", "\r\n"),\r
143                     Material = (int[])entry.api_get_material\r
144                 };\r
145                 var state = (int)entry.api_state;\r
146                 switch (state)\r
147                 {\r
148                     case 3:\r
149                         quest.Progress = 100;\r
150                         goto case 2;\r
151                     case 2:\r
152                         AdjustQuest(quest);\r
153                         SetQuest(quest);\r
154                         break;\r
155                 }\r
156             }\r
157         }\r
158 \r
159         private void AdjustQuest(QuestStatus quest)\r
160         {\r
161             quest.Count = _countList.GetCount(quest.Id);\r
162             if (quest.Count.AdjustCount(quest.Progress))\r
163                 NeedSave = true;\r
164             quest.Material = quest.Material.Concat(quest.Count.Spec.Material).ToArray();\r
165             if (!QuestDictionary.ContainsKey(quest.Id))\r
166                 NeedSave = true;\r
167         }\r
168 \r
169         private void SetQuest(QuestStatus quest)\r
170         {\r
171             quest.Count = _countList.GetCount(quest.Id);\r
172             quest.Color = quest.Category <= _color.Length ? _color[quest.Category - 1] : Control.DefaultBackColor;\r
173             QuestDictionary[quest.Id] = quest;\r
174         }\r
175 \r
176         private void ResetCounts()\r
177         {\r
178             _now = _nowFunc();\r
179             if (!CrossBoundary(QuestInterval.Daily))\r
180                 return;\r
181             foreach (var interval in (QuestInterval[])typeof(QuestInterval).GetEnumValues())\r
182             {\r
183                 if (!CrossBoundary(interval))\r
184                     continue;\r
185                 _countList.Remove(interval);\r
186             }\r
187             _lastReset = _now;\r
188             NeedSave = true;\r
189         }\r
190 \r
191         private DateTime LastMorning => _now.Date.AddDays(_now.Hour < 5 ? -1 : 0).AddHours(5);\r
192 \r
193         private bool CrossBoundary(QuestInterval interval)\r
194         {\r
195             return interval switch\r
196             {\r
197                 QuestInterval.Other => false,\r
198                 QuestInterval.Daily => CrossBoundary(LastMorning),\r
199                 QuestInterval.Weekly => CrossBoundary(LastMonday.AddHours(5)),\r
200                 QuestInterval.Monthly => CrossBoundary(new DateTime(_now.Year, _now.Month, 1, 5, 0, 0)),\r
201                 QuestInterval.Quarterly => CrossBoundary(QuarterlyBoundary.AddHours(5)),\r
202                 QuestInterval.Yearly1 => CrossBoundary(new DateTime(_now.Year, 1, 1, 5, 0, 0)),\r
203                 QuestInterval.Yearly2 => CrossBoundary(new DateTime(_now.Year, 2, 1, 5, 0, 0)),\r
204                 QuestInterval.Yearly3 => CrossBoundary(new DateTime(_now.Year, 3, 1, 5, 0, 0)),\r
205                 QuestInterval.Yearly5 => CrossBoundary(new DateTime(_now.Year, 5, 1, 5, 0, 0)),\r
206                 QuestInterval.Yearly8 => CrossBoundary(new DateTime(_now.Year, 8, 1, 5, 0, 0)),\r
207                 QuestInterval.Yearly9 => CrossBoundary(new DateTime(_now.Year, 9, 1, 5, 0, 0)),\r
208                 QuestInterval.Yearly10 => CrossBoundary(new DateTime(_now.Year, 10, 1, 5, 0, 0)),\r
209                 QuestInterval.Yearly11 => CrossBoundary(new DateTime(_now.Year, 11, 1, 5, 0, 0)),\r
210                 _ => false\r
211             };\r
212         }\r
213 \r
214         private DateTime LastMonday => _now.Date.AddDays(-((6 + (int)_now.DayOfWeek) % 7));\r
215 \r
216         private DateTime QuarterlyBoundary =>\r
217             _now.Month / 3 == 0\r
218                 ? new DateTime(_now.Year - 1, 12, 1)\r
219                 : new DateTime(_now.Year, _now.Month / 3 * 3, 1);\r
220 \r
221         private bool CrossBoundary(DateTime boundary)\r
222         {\r
223             return _lastReset < boundary && boundary <= _now;\r
224         }\r
225 \r
226         public void InspectStop(string request)\r
227         {\r
228             var values = HttpUtility.ParseQueryString(request);\r
229             QuestDictionary.Remove(int.Parse(values["api_quest_id"]));\r
230             NeedSave = true;\r
231         }\r
232 \r
233         public void InspectClearItemGet(string request)\r
234         {\r
235             var values = HttpUtility.ParseQueryString(request);\r
236             var id = int.Parse(values["api_quest_id"]);\r
237             _countList.Remove(id);\r
238             QuestDictionary.Remove(id);\r
239             NeedSave = true;\r
240         }\r
241 \r
242         public bool NeedSave { get; set; }\r
243 \r
244         public void SaveState(Status status)\r
245         {\r
246             NeedSave = false;\r
247             status.QuestLastReset = _lastReset;\r
248             if (QuestDictionary != null)\r
249                 status.QuestList = QuestDictionary.Values.ToArray();\r
250             if (_countList != null)\r
251                 status.QuestCountList = _countList.NonZeroCountList.ToArray();\r
252         }\r
253 \r
254         public void LoadState(Status status)\r
255         {\r
256             _lastReset = status.QuestLastReset;\r
257             if (status.QuestCountList != null)\r
258                 _countList.SetCountList(status.QuestCountList);\r
259             if (status.QuestList != null)\r
260             {\r
261                 QuestDictionary.Clear();\r
262                 foreach (var quest in status.QuestList)\r
263                     SetQuest(quest);\r
264             }\r
265         }\r
266     }\r
267 }