OSDN Git Service

HPのパーセント表示を切り替えるのをダブルクリックにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / 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 static System.Math;\r
22 \r
23 namespace KancolleSniffer\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 \r
50     public enum QuestInterval\r
51     {\r
52         Other,\r
53         Daily,\r
54         Weekly,\r
55         Monthly,\r
56         Quarterly\r
57     }\r
58 \r
59     public class QuestSpec\r
60     {\r
61         public QuestInterval Interval { get; set; }\r
62         public int Max { get; set; }\r
63         public int[] MaxArray { get; set; }\r
64         public bool AdjustCount { get; set; } = true;\r
65         public int Shift { get; set; }\r
66         public int[] Material { get; set; }\r
67     }\r
68 \r
69     public class QuestSortie : QuestSpec\r
70     {\r
71         public string Rank { get; set; }\r
72         public int[] Maps { get; set; }\r
73         public int[] ShipTypes { get; set; }\r
74 \r
75         public static int CompareRank(string a, string b)\r
76         {\r
77             const string ranks = "SABCDE";\r
78             return ranks.IndexOf(a, StringComparison.Ordinal) -\r
79                    ranks.IndexOf(b, StringComparison.Ordinal);\r
80         }\r
81 \r
82         public bool Check(string rank, int map, bool boss)\r
83         {\r
84             return (Rank == null || CompareRank(rank, Rank) <= 0) &&\r
85                    (Maps == null || Maps.Contains(map) && boss);\r
86         }\r
87     }\r
88 \r
89     public class QuestEnemyType : QuestSpec\r
90     {\r
91         public int[] EnemyType { get; set; } = new int[0];\r
92 \r
93         public int CountResult(IEnumerable<ShipStatus> enemyResult) =>\r
94             enemyResult.Count(ship => ship.NowHp == 0 && EnemyType.Contains(ship.Spec.ShipType));\r
95     }\r
96 \r
97     public class QuestPractice : QuestSpec\r
98     {\r
99         public bool Win { get; set; }\r
100         public bool Check(string rank) => !Win || QuestSortie.CompareRank(rank, "B") <= 0;\r
101     }\r
102 \r
103     public class QuestMission : QuestSpec\r
104     {\r
105         public int[] Ids { get; set; }\r
106         public bool Check(int id) => Ids == null || Ids.Contains(id);\r
107     }\r
108 \r
109     public class QuestDestroyItem : QuestSpec\r
110     {\r
111         public int[] Items { get; set; }\r
112         public bool Check(int id) => Items == null || Items.Contains(id);\r
113     }\r
114 \r
115     public class QuestPowerup : QuestSpec\r
116     {\r
117     }\r
118 \r
119     public class QuestCount\r
120     {\r
121         public int Id { get; set; }\r
122         public int Now { get; set; }\r
123         public int[] NowArray { get; set; }\r
124 \r
125         [XmlIgnore]\r
126         public QuestSpec Spec { get; set; }\r
127 \r
128         public bool AdjustCount(int progress)\r
129         {\r
130             if (!Spec.AdjustCount)\r
131                 return false;\r
132             if (NowArray != null)\r
133             {\r
134                 if (progress != 100)\r
135                     return false;\r
136                 NowArray = NowArray.Zip(Spec.MaxArray, Max).ToArray();\r
137                 return true;\r
138             }\r
139             var next = 0;\r
140             switch (progress)\r
141             {\r
142                 case 0:\r
143                     next = 50;\r
144                     break;\r
145                 case 50:\r
146                     next = 80;\r
147                     break;\r
148                 case 80:\r
149                     next = 100;\r
150                     break;\r
151                 case 100:\r
152                     next = 100000;\r
153                     break;\r
154             }\r
155             var now = Now + Spec.Shift;\r
156             var max = Spec.Max + Spec.Shift;\r
157             var low = (int)Ceiling(max * progress / 100.0);\r
158             if (low >= max && progress != 100)\r
159                 low = max - 1;\r
160             var high = (int)Ceiling(max * next / 100.0);\r
161             if (now < low)\r
162             {\r
163                 Now = low - Spec.Shift;\r
164                 return true;\r
165             }\r
166             if (now >= high)\r
167             {\r
168                 Now = high - 1 - Spec.Shift;\r
169                 return true;\r
170             }\r
171             return false;\r
172         }\r
173 \r
174         public override string ToString()\r
175         {\r
176             if (Id == 426 || Id == 854 || Id == 873)\r
177                 return $"{NowArray.Count(n => n >= 1)}/{Spec.MaxArray.Length}";\r
178             return NowArray != null\r
179                 ? string.Join(" ", NowArray.Zip(Spec.MaxArray, (n, m) => $"{n}/{m}"))\r
180                 : $"{Now}/{Spec.Max}";\r
181         }\r
182 \r
183         public string ToToolTip()\r
184         {\r
185             switch (Id)\r
186             {\r
187                 case 426:\r
188                     return string.Join(" ",\r
189                         new[] {"警備任務", "対潜警戒任務", "海上護衛任務", "強硬偵察任務"}\r
190                             .Zip(NowArray, (mission, n) => n >= 1 ? mission : "")\r
191                             .Where(s => !string.IsNullOrEmpty(s)));\r
192                 case 428:\r
193                     return string.Join(" ",\r
194                         new[] {"対潜警戒任務", "海峡警備行動", "長時間対潜警戒"}.Zip(NowArray, (mission, n) => n >= 1 ? mission + n : "")\r
195                             .Where(s => !string.IsNullOrEmpty(s)));\r
196                 case 854:\r
197                     return string.Join(" ",\r
198                         new[] {"2-4", "6-1", "6-3", "6-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")\r
199                             .Where(s => !string.IsNullOrEmpty(s)));\r
200                 case 873:\r
201                     return string.Join(" ",\r
202                         new[] {"3-1", "3-2", "3-3"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")\r
203                             .Where(s => !string.IsNullOrEmpty(s)));\r
204             }\r
205             return "";\r
206         }\r
207 \r
208         public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;\r
209     }\r
210 \r
211     // @formatter:off\r
212     public class QuestCountList\r
213     {\r
214         private const QuestInterval Daily = QuestInterval.Daily;\r
215         private const QuestInterval Weekly = QuestInterval.Weekly;\r
216         private const QuestInterval Monthly = QuestInterval.Monthly;\r
217         private const QuestInterval Quarterly = QuestInterval.Quarterly;\r
218 \r
219         /// <summary>\r
220         /// このテーブルは七四式電子観測儀を参考に作成した。\r
221         /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs\r
222         /// </summary>\r
223         private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>\r
224         {\r
225             {201, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 0, 1, 0}}}, // 201: 敵艦隊を撃滅せよ!\r
226             {216, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 1, 1, 0}}}, // 216: 敵艦隊主力を撃滅せよ!\r
227             {210, new QuestSortie {Interval = Daily, Max = 10, Material = new[] {0, 0, 1, 0}}}, // 210: 敵艦隊を10回邀撃せよ!\r
228             {211, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 211: 敵空母を3隻撃沈せよ!\r
229             {212, new QuestEnemyType {Interval = Daily, Max = 5, EnemyType = new[] {15}, Material = new[] {0, 0, 2, 0}}}, // 212: 敵輸送船団を叩け!\r
230             {218, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {15}, Material = new[] {0, 1, 1, 0}}}, // 218: 敵補給艦を3隻撃沈せよ!\r
231             {226, new QuestSortie {Interval = Daily, Max = 5, Rank = "B", Maps = new[] {21, 22, 23, 24, 25}, Material = new[] {1, 1, 0, 0}}}, // 226: 南西諸島海域の制海権を握れ!\r
232             {230, new QuestEnemyType {Interval = Daily, Max = 6, EnemyType = new[] {13}, Material = new[] {0, 1, 0, 0}}}, // 230: 敵潜水艦を制圧せよ!\r
233 \r
234             {213, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {15}, Material = new[] {0, 0, 3, 0}}}, // 213: 海上通商破壊作戦\r
235             {214, new QuestSpec {Interval = Weekly, MaxArray = new[] {36, 6, 24, 12}, Material = new[] {2, 0, 2, 0}}}, // 214: あ号作戦\r
236             {220, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 220: い号作戦\r
237             {221, new QuestEnemyType {Interval = Weekly, Max = 50, EnemyType = new[] {15}, Material = new[] {0, 3, 0, 0}}}, // 221: ろ号作戦\r
238             {228, new QuestEnemyType {Interval = Weekly, Max = 15, EnemyType = new[] {13}, Material = new[] {0, 2, 0, 1}}}, // 228: 海上護衛戦\r
239             {229, new QuestSortie {Interval = Weekly, Max = 12, Rank = "B", Maps = new[] {41, 42, 43, 44, 45}, Material = new[] {0, 0, 2, 0}}}, // 229: 敵東方艦隊を撃滅せよ!\r
240             {241, new QuestSortie {Interval = Weekly, Max = 5, Rank = "B", Maps = new[] {33, 34, 35}, Material = new[] {0, 0, 3, 3}}}, // 241: 敵北方艦隊主力を撃滅せよ!\r
241             {242, new QuestSortie {Interval = Weekly, Max = 1, Rank = "B", Maps = new[] {44}, Material = new[] {0, 1, 1, 0}}}, // 242: 敵東方中枢艦隊を撃破せよ!\r
242             {243, new QuestSortie {Interval = Weekly, Max = 2, Rank = "S", Maps = new[] {52}, Material = new[] {0, 0, 2, 2}}}, // 243: 南方海域珊瑚諸島沖の制空権を握れ!\r
243             {249, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 5, 0}}}, // 249: 「第五戦隊」出撃せよ!\r
244             {256, new QuestSortie {Interval = Monthly, Max = 3, Rank = "S", Maps = new[] {61}, Material = new[] {0, 0, 0, 0}}}, // 256: 「潜水艦隊」出撃せよ!\r
245             {257, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 3}}}, // 257: 「水雷戦隊」南西へ!\r
246             {259, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 3, 0, 4}}}, // 259: 「水上打撃部隊」南方へ!\r
247             {261, new QuestSortie {Interval = Weekly, Max = 3, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 0, 3}}}, // 261: 海上輸送路の安全確保に努めよ!\r
248             {265, new QuestSortie {Interval = Monthly, Max = 10, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 5, 3}}}, // 265: 海上護衛強化月間\r
249             {266, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 4, 2}}}, // 266: 「水上反撃部隊」突入せよ!\r
250 \r
251             {822, new QuestSortie {Interval = Quarterly, Max = 2, Rank = "S", Maps = new[] {24}, Material = new[] {0, 0, 0, 5}}}, // 822: 沖ノ島海域迎撃戦\r
252             {854, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 0, 4}}}, // 854: 戦果拡張任務!「Z作戦」前段作戦\r
253             {861, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 4, 0, 0}}}, // 861: 強行輸送艦隊、抜錨!\r
254             {862, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 8, 4}}}, // 862: 前線の航空偵察を実施せよ!\r
255             {873, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 873: 北方海域警備を実施せよ!\r
256             {875, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 0, 0}}}, // 875: 精鋭「三一駆」、鉄底海域に突入せよ!\r
257 \r
258             {303, new QuestPractice {Interval = Daily, Max = 3, Win = false, Material = new[] {1, 0, 0, 0}}}, // 303: 「演習」で練度向上!\r
259             {304, new QuestPractice {Interval = Daily, Max = 5, Win = true, Material = new[] {0, 0, 1, 0}}}, // 304: 「演習」で他提督を圧倒せよ!\r
260             {302, new QuestPractice {Interval = Weekly, Max = 20, Win = true, Material = new[] {0, 0, 2, 1}}}, // 302: 大規模演習\r
261             {311, new QuestPractice {Interval = Daily, Max = 7, Win = true, Material = new[] {0, 2, 0, 0}}}, // 311: 精鋭艦隊演習\r
262             {318, new QuestSpec {Interval = Daily, Max = 3, Material = new[] {0, 2, 2, 0}, AdjustCount = false}}, // 318: 給糧艦「伊良湖」の支援\r
263 \r
264             {402, new QuestMission {Interval = Daily, Max = 3, Material = new[] {0, 0, 1, 0}}}, // 402: 「遠征」を3回成功させよう!\r
265             {403, new QuestMission {Interval = Daily, Max = 10, Material = new[] {0, 0, 0, 0}}}, // 403: 「遠征」を10回成功させよう!\r
266             {404, new QuestMission {Interval = Weekly, Max = 30, Material = new[] {0, 0, 3, 0}}}, // 404: 大規模遠征作戦、発令!\r
267             {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 0, 0}}}, // 410: 南方への輸送作戦を成功させよ!\r
268             {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 2, 1}}}, // 411: 南方への鼠輸送を継続実施せよ!\r
269             {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}, Material = new[] {0, 0, 0, 0}}}, // 424: 輸送船団護衛を強化せよ!\r
270             {426, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 4, 0}}}, // 426: 海上通商航路の警戒を厳とせよ!\r
271             {428, new QuestSpec {Interval = Quarterly, MaxArray = new[] {2, 2, 2}, Material = new[] {0, 0, 0, 3}}}, // 428: 近海に侵入する敵潜を制圧せよ!\r
272 \r
273             {503, new QuestSpec {Interval = Daily, Max = 5, Material = new[] {0, 2, 0, 0}}}, // 503: 艦隊大整備!\r
274             {504, new QuestSpec {Interval = Daily, Max = 15, Material = new[] {1, 0, 1, 0}}}, // 504: 艦隊酒保祭り!\r
275 \r
276             {605, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {1, 0, 1, 0}}}, // 605: 新装備「開発」指令\r
277             {606, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 1, 1, 0}}}, // 606: 新造艦「建造」指令\r
278             {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {0, 0, 2, 0}}}, // 607: 装備「開発」集中強化!\r
279             {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {1, 0, 2, 0}}}, // 608: 艦娘「建造」艦隊強化!\r
280             {609, new QuestSpec {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 609: 軍縮条約対応!\r
281             {619, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 0, 0, 1}}}, // 619: 装備の改修強化\r
282 \r
283             {613, new QuestSpec {Interval = Weekly, Max = 24, Material = new[] {0, 0, 0, 0}}}, // 613: 資源の再利用\r
284             {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Items = new[] {21}, Material = new[] {0, 0, 2, 1}}}, // 638: 対空機銃量産\r
285             {663, new QuestDestroyItem {Interval = Quarterly, Max = 10, Items = new[] {3}, Material = new[] {0, 0, 3, 0}}}, // 663: 新型艤装の継続研究\r
286             {673, new QuestDestroyItem {Interval = Daily, Max = 4, Items = new[] {1}, Shift = 1, Material = new[] {0, 0, 1, 0}}}, // 673: 装備開発力の整備\r
287             {674, new QuestDestroyItem {Interval = Daily, Max = 3, Items = new[] {21}, Shift = 2, Material = new[] {0, 1, 1, 0}}}, // 674: 工廠環境の整備\r
288             {675, new QuestSpec {Interval = Quarterly, MaxArray = new[] {6, 4}, Material = new[] {0, 0, 0, 0}}}, // 675: 運用装備の統合整備\r
289             {676, new QuestSpec {Interval = Weekly, MaxArray = new[] {3, 3, 1}, Material = new[] {0, 1, 7, 0}}}, // 676: 装備開発力の集中整備\r
290             {677, new QuestSpec {Interval = Weekly, MaxArray = new[] {4, 2, 3}, Material = new[] {0, 5, 0, 0}}}, // 677: 継戦支援能力の整備\r
291             {678, new QuestSpec {Interval = Quarterly, MaxArray = new[] {3, 5}, Material = new[] {0, 0, 8, 0}}}, // 678: 主力艦上戦闘機の更新\r
292             {680, new QuestSpec {Interval = Quarterly, MaxArray = new[] {4, 4}, Material = new[] {0, 0, 6, 0}}}, // 680: 対空兵装の整備拡充\r
293 \r
294             {702, new QuestPowerup {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!\r
295             {703, new QuestPowerup {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!\r
296         };\r
297         // @formatter:on\r
298 \r
299         private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();\r
300 \r
301         public QuestCount GetCount(int id)\r
302         {\r
303             if (_countDict.TryGetValue(id, out var value))\r
304                 return value;\r
305             if (QuestSpecs.TryGetValue(id, out var spec))\r
306             {\r
307                 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();\r
308                 return _countDict[id] = new QuestCount\r
309                 {\r
310                     Id = id,\r
311                     Now = 0,\r
312                     NowArray = nowArray,\r
313                     Spec = spec\r
314                 };\r
315             }\r
316             return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};\r
317         }\r
318 \r
319         public void Remove(int id)\r
320         {\r
321             _countDict.Remove(id);\r
322         }\r
323 \r
324         public void Remove(QuestInterval interval)\r
325         {\r
326             foreach (var id in\r
327                 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())\r
328             {\r
329                 _countDict.Remove(id);\r
330             }\r
331         }\r
332 \r
333         public IEnumerable<QuestCount> CountList\r
334         {\r
335             get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));\r
336             set\r
337             {\r
338                 if (value == null)\r
339                     return;\r
340                 foreach (var count in value)\r
341                 {\r
342                     count.Spec = QuestSpecs[count.Id];\r
343                     _countDict[count.Id] = count;\r
344                 }\r
345             }\r
346         }\r
347     }\r
348 \r
349     public class QuestInfo : IHaveState\r
350     {\r
351         private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();\r
352         private readonly QuestCountList _countList = new QuestCountList();\r
353         private readonly ItemInfo _itemInfo;\r
354         private readonly BattleInfo _battleInfo;\r
355         private readonly Func<DateTime> _nowFunc = () => DateTime.Now;\r
356         private DateTime _lastReset;\r
357 \r
358         private readonly Color[] _color =\r
359         {\r
360             Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),\r
361             Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),\r
362             Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)\r
363         };\r
364 \r
365         public int AcceptMax { get; set; } = 5;\r
366 \r
367         public QuestStatus[] Quests => _quests.Values.ToArray();\r
368 \r
369         public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)\r
370         {\r
371             _itemInfo = itemInfo;\r
372             _battleInfo = battleInfo;\r
373             if (nowFunc != null)\r
374                 _nowFunc = nowFunc;\r
375         }\r
376 \r
377         public void InspectQuestList(dynamic json)\r
378         {\r
379             ResetQuests();\r
380             if (json.api_list == null)\r
381                 return;\r
382             for (var i = 0; i < 2; i++)\r
383             {\r
384                 foreach (var entry in json.api_list)\r
385                 {\r
386                     if (entry is double) // -1の場合がある。\r
387                         continue;\r
388 \r
389                     var id = (int)entry.api_no;\r
390                     var state = (int)entry.api_state;\r
391                     var progress = (int)entry.api_progress_flag;\r
392                     var cat = (int)entry.api_category;\r
393                     var name = (string)entry.api_title;\r
394                     var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");\r
395                     var material = (int[])entry.api_get_material;\r
396 \r
397                     switch (progress)\r
398                     {\r
399                         case 0:\r
400                             break;\r
401                         case 1:\r
402                             progress = 50;\r
403                             break;\r
404                         case 2:\r
405                             progress = 80;\r
406                             break;\r
407                     }\r
408                     switch (state)\r
409                     {\r
410                         case 1:\r
411                             if (_quests.Remove(id))\r
412                                 NeedSave = true;\r
413                             break;\r
414                         case 3:\r
415                             progress = 100;\r
416                             goto case 2;\r
417                         case 2:\r
418                             AddQuest(id, cat, name, detail, material, progress, true);\r
419                             break;\r
420                     }\r
421                 }\r
422                 if (_quests.Count <= AcceptMax)\r
423                     break;\r
424                 /*\r
425                  * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。\r
426                  * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。\r
427                  */\r
428                 _quests.Clear();\r
429             }\r
430         }\r
431 \r
432         private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,\r
433             bool adjustCount)\r
434         {\r
435             var count = _countList.GetCount(id);\r
436             if (adjustCount)\r
437             {\r
438                 if (count.AdjustCount(progress))\r
439                     NeedSave = true;\r
440             }\r
441             _quests[id] = new QuestStatus\r
442             {\r
443                 Id = id,\r
444                 Category = category,\r
445                 Name = name,\r
446                 Detail = detail,\r
447                 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,\r
448                 Count = count,\r
449                 Progress = progress,\r
450                 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor\r
451             };\r
452         }\r
453 \r
454         public void ClearQuests()\r
455         {\r
456             _quests.Clear();\r
457         }\r
458 \r
459         private void ResetQuests()\r
460         {\r
461             var now = _nowFunc();\r
462             var daily = now.Date.AddHours(5);\r
463             if (!(_lastReset < daily && daily <= now))\r
464                 return;\r
465             _quests.Clear(); // 前日に未消化のデイリーを消す。\r
466             _countList.Remove(QuestInterval.Daily);\r
467             var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);\r
468             if (_lastReset < weekly && weekly <= now)\r
469                 _countList.Remove(QuestInterval.Weekly);\r
470             var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);\r
471             if (_lastReset < monthly && monthly <= now)\r
472                 _countList.Remove(QuestInterval.Monthly);\r
473             var season = now.Month / 3;\r
474             var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);\r
475             if (_lastReset < quarterly && quarterly <= now)\r
476                 _countList.Remove(QuestInterval.Quarterly);\r
477             _lastReset = now;\r
478             NeedSave = true;\r
479         }\r
480 \r
481 \r
482         private int _map;\r
483         private bool _boss;\r
484 \r
485         public void InspectMapStart(dynamic json)\r
486         {\r
487             if (_quests.TryGetValue(214, out var ago)) // あ号\r
488                 ago.Count.NowArray[0]++;\r
489             InspectMapNext(json);\r
490         }\r
491 \r
492         public void InspectMapNext(dynamic json)\r
493         {\r
494             _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;\r
495             _boss = (int)json.api_event_id == 5;\r
496 \r
497             if (_quests.TryGetValue(861, out var q861))\r
498             {\r
499                 if (_map == 16 && (int)json.api_event_id == 8)\r
500                 {\r
501                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)\r
502                         .ToArray();\r
503                     if (fleet.Count(s => s == 10 || s == 22) == 2)\r
504                         IncrementCount(q861.Count);\r
505                 }\r
506             }\r
507         }\r
508 \r
509         public void InspectBattleResult(dynamic json)\r
510         {\r
511             var rank = json.api_win_rank;\r
512             foreach (var quest in _quests.Values)\r
513             {\r
514                 var count = quest.Count;\r
515                 switch (count.Spec)\r
516                 {\r
517                     case QuestSortie sortie:\r
518                         if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))\r
519                             IncrementCount(count);\r
520                         break;\r
521                     case QuestEnemyType enemyType:\r
522                         var num = enemyType.CountResult(\r
523                             _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));\r
524                         if (num > 0)\r
525                             AddCount(count, num);\r
526                         break;\r
527                 }\r
528             }\r
529             if (_quests.TryGetValue(214, out var ago))\r
530             {\r
531                 var array = ago.Count.NowArray;\r
532                 if (_boss)\r
533                 {\r
534                     array[2]++;\r
535                     if (QuestSortie.CompareRank(rank, "B") <= 0)\r
536                         array[3]++;\r
537                     NeedSave = true;\r
538                 }\r
539                 if (rank == "S")\r
540                 {\r
541                     array[1]++;\r
542                     NeedSave = true;\r
543                 }\r
544             }\r
545             if (_quests.TryGetValue(249, out var q249))\r
546             {\r
547                 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)\r
548                 {\r
549                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)\r
550                         .ToArray();\r
551                     if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)\r
552                         IncrementCount(q249.Count);\r
553                 }\r
554             }\r
555             if (_quests.TryGetValue(257, out var q257))\r
556             {\r
557                 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)\r
558                 {\r
559                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)\r
560                         .ToArray();\r
561                     if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))\r
562                         IncrementCount(q257.Count);\r
563                 }\r
564             }\r
565             if (_quests.TryGetValue(259, out var q259))\r
566             {\r
567                 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)\r
568                 {\r
569                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();\r
570                     var senkan = new[]\r
571                     {\r
572                         131, 136, // 大和\r
573                         143, 148, 546, // 武蔵\r
574                         80, 275, 541, // 長門\r
575                         81, 276, // 陸奥\r
576                         26, 286, 411, // 扶桑\r
577                         27, 287, 412, // 山城\r
578                         77, 82, // 伊勢\r
579                         87, 88 // 日向\r
580                     };\r
581                     if (fleet.Select(s => s.Id).Intersect(senkan).Count() == 3 &&\r
582                         fleet.Count(s => s.ShipType == 3) > 0)\r
583                     {\r
584                         IncrementCount(q259.Count);\r
585                     }\r
586                 }\r
587             }\r
588             if (_quests.TryGetValue(266, out var q266))\r
589             {\r
590                 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)\r
591                 {\r
592                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)\r
593                         .ToArray();\r
594                     if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))\r
595                         IncrementCount(q266.Count);\r
596                 }\r
597             }\r
598             if (_quests.TryGetValue(854, out var opz) && _boss)\r
599             {\r
600                 var array = opz.Count.NowArray;\r
601                 switch (_map)\r
602                 {\r
603                     case 24 when QuestSortie.CompareRank(rank, "A") <= 0:\r
604                         array[0]++;\r
605                         NeedSave = true;\r
606                         break;\r
607                     case 61 when QuestSortie.CompareRank(rank, "A") <= 0:\r
608                         array[1]++;\r
609                         NeedSave = true;\r
610                         break;\r
611                     case 63 when QuestSortie.CompareRank(rank, "A") <= 0:\r
612                         array[2]++;\r
613                         NeedSave = true;\r
614                         break;\r
615                     case 64 when QuestSortie.CompareRank(rank, "S") <= 0:\r
616                         array[3]++;\r
617                         NeedSave = true;\r
618                         break;\r
619                 }\r
620             }\r
621             if (_quests.TryGetValue(862, out var q862))\r
622             {\r
623                 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)\r
624                 {\r
625                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType).ToArray();\r
626                     if (fleet.Count(s => s == 3) == 2 && fleet.Count(s => s == 16) == 1)\r
627                         IncrementCount(q862.Count);\r
628                 }\r
629             }\r
630             if (_quests.TryGetValue(873, out var q873))\r
631             {\r
632                 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&\r
633                     _boss && QuestSortie.CompareRank(rank, "A") <= 0)\r
634                 {\r
635                     var array = q873.Count.NowArray;\r
636                     switch (_map)\r
637                     {\r
638                         case 31:\r
639                             array[0]++;\r
640                             NeedSave = true;\r
641                             break;\r
642                         case 32:\r
643                             array[1]++;\r
644                             NeedSave = true;\r
645                             break;\r
646                         case 33:\r
647                             array[2]++;\r
648                             NeedSave = true;\r
649                             break;\r
650                     }\r
651                 }\r
652             }\r
653             if (_quests.TryGetValue(875, out var q875))\r
654             {\r
655                 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)\r
656                 {\r
657                     var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();\r
658                     if (fleet.Contains(543) && fleet.Intersect(new []{344, 345, 359}).Any())\r
659                         IncrementCount(q875.Count);\r
660                 }\r
661             }\r
662         }\r
663 \r
664         public void InspectPracticeResult(dynamic json)\r
665         {\r
666             foreach (var quest in _quests.Values)\r
667             {\r
668                 var count = quest.Count;\r
669                 if (!(count.Spec is QuestPractice practice))\r
670                     continue;\r
671                 if (practice.Check(json.api_win_rank))\r
672                     IncrementCount(count);\r
673             }\r
674             if (_quests.TryGetValue(318, out var q318))\r
675             {\r
676                 if (QuestSortie.CompareRank(json.api_win_rank, "B") <= 0 &&\r
677                     _battleInfo.Result.Friend.Main.Count(s => s.Spec.ShipType == 3) >= 2)\r
678                 {\r
679                     IncrementCount(q318.Count);\r
680                 }\r
681             }\r
682         }\r
683 \r
684         private readonly int[] _missionId = new int[ShipInfo.FleetCount];\r
685 \r
686         public void InspectDeck(dynamic json)\r
687         {\r
688             foreach (var entry in json)\r
689                 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];\r
690         }\r
691 \r
692         public void InspectMissionResult(string request, dynamic json)\r
693         {\r
694             var values = HttpUtility.ParseQueryString(request);\r
695             var deck = int.Parse(values["api_deck_id"]);\r
696             if ((int)json.api_clear_result == 0)\r
697                 return;\r
698             var mid = _missionId[deck - 1];\r
699             foreach (var quest in _quests.Values)\r
700             {\r
701                 var count = quest.Count;\r
702                 if (!(count.Spec is QuestMission mission))\r
703                     continue;\r
704                 if (mission.Check(mid))\r
705                     IncrementCount(count);\r
706             }\r
707             if (_quests.TryGetValue(426, out var q426))\r
708             {\r
709                 var count = q426.Count;\r
710                 switch (mid)\r
711                 {\r
712                     case 3:\r
713                         count.NowArray[0]++;\r
714                         break;\r
715                     case 4:\r
716                         count.NowArray[1]++;\r
717                         break;\r
718                     case 5:\r
719                         count.NowArray[2]++;\r
720                         break;\r
721                     case 10:\r
722                         count.NowArray[3]++;\r
723                         break;\r
724                 }\r
725             }\r
726             if (_quests.TryGetValue(428, out var q428))\r
727             {\r
728                 var count = q428.Count;\r
729                 switch (mid)\r
730                 {\r
731                     case 4:\r
732                         count.NowArray[0]++;\r
733                         break;\r
734                     case 101:\r
735                         count.NowArray[1]++;\r
736                         break;\r
737                     case 102:\r
738                         count.NowArray[2]++;\r
739                         break;\r
740                 }\r
741             }\r
742         }\r
743 \r
744         private void IncrementCount(QuestCount count)\r
745         {\r
746             count.Now++;\r
747             NeedSave = true;\r
748         }\r
749 \r
750         private void AddCount(QuestCount count, int value)\r
751         {\r
752             count.Now += value;\r
753             NeedSave = true;\r
754         }\r
755 \r
756         private void IncrementCount(int id)\r
757         {\r
758             AddCount(id, 1);\r
759         }\r
760 \r
761         private void AddCount(int id, int value)\r
762         {\r
763             if (_quests.TryGetValue(id, out var quest))\r
764             {\r
765                 quest.Count.Now += value;\r
766                 NeedSave = true;\r
767             }\r
768         }\r
769 \r
770         public void CountNyukyo() => IncrementCount(503);\r
771 \r
772         public void CountCharge() => IncrementCount(504);\r
773 \r
774         public void CountCreateItem()\r
775         {\r
776             IncrementCount(605);\r
777             IncrementCount(607);\r
778         }\r
779 \r
780         public void CountCreateShip()\r
781         {\r
782             IncrementCount(606);\r
783             IncrementCount(608);\r
784         }\r
785 \r
786         public void InspectDestroyShip(string request)\r
787         {\r
788             AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);\r
789         }\r
790 \r
791         public void CountRemodelSlot() => IncrementCount(619);\r
792 \r
793         public void InspectDestroyItem(string request, dynamic json)\r
794         {\r
795             var values = HttpUtility.ParseQueryString(request);\r
796             var items = values["api_slotitem_ids"].Split(',')\r
797                 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();\r
798             IncrementCount(613); // 613: 資源の再利用\r
799             foreach (var quest in _quests.Values)\r
800             {\r
801                 var count = quest.Count;\r
802                 if (!(count.Spec is QuestDestroyItem destroy))\r
803                     continue;\r
804                 AddCount(count, items.Count(spec => destroy.Check(spec.Type)));\r
805             }\r
806             if (_quests.TryGetValue(675, out var q675))\r
807             {\r
808                 q675.Count.NowArray[0] += items.Count(spec => spec.Type == 6);\r
809                 q675.Count.NowArray[1] += items.Count(spec => spec.Type == 21);\r
810                 NeedSave = true;\r
811             }\r
812             if (_quests.TryGetValue(676, out var q676))\r
813             {\r
814                 q676.Count.NowArray[0] += items.Count(spec => spec.Type == 2);\r
815                 q676.Count.NowArray[1] += items.Count(spec => spec.Type == 4);\r
816                 q676.Count.NowArray[2] += items.Count(spec => spec.Type == 30);\r
817                 NeedSave = true;\r
818             }\r
819             if (_quests.TryGetValue(677, out var q677))\r
820             {\r
821                 q677.Count.NowArray[0] += items.Count(spec => spec.Type == 3);\r
822                 q677.Count.NowArray[1] += items.Count(spec => spec.Type == 10);\r
823                 q677.Count.NowArray[2] += items.Count(spec => spec.Type == 5);\r
824                 NeedSave = true;\r
825             }\r
826             if (_quests.TryGetValue(678, out var q678))\r
827             {\r
828                 q678.Count.NowArray[0] += items.Count(spec => spec.Id == 19);\r
829                 q678.Count.NowArray[1] += items.Count(spec => spec.Id == 20);\r
830                 NeedSave = true;\r
831             }\r
832             if (_quests.TryGetValue(680, out var q680))\r
833             {\r
834                 q680.Count.NowArray[0] += items.Count(spec => spec.Type == 21);\r
835                 q680.Count.NowArray[1] += items.Count(spec => spec.Type == 12 || spec.Type == 13);\r
836                 NeedSave = true;\r
837             }\r
838         }\r
839 \r
840         public void InspectPowerup(dynamic json)\r
841         {\r
842             if ((int)json.api_powerup_flag == 0)\r
843                 return;\r
844             foreach (var quest in _quests.Values)\r
845             {\r
846                 var count = quest.Count;\r
847                 if (!(count.Spec is QuestPowerup))\r
848                     continue;\r
849                 IncrementCount(count);\r
850             }\r
851         }\r
852 \r
853         public void InspectStop(string request)\r
854         {\r
855             var values = HttpUtility.ParseQueryString(request);\r
856             _quests.Remove(int.Parse(values["api_quest_id"]));\r
857             NeedSave = true;\r
858         }\r
859 \r
860         public void InspectClearItemGet(string request)\r
861         {\r
862             var values = HttpUtility.ParseQueryString(request);\r
863             var id = int.Parse(values["api_quest_id"]);\r
864             _countList.Remove(id);\r
865             _quests.Remove(id);\r
866             NeedSave = true;\r
867         }\r
868 \r
869         public bool NeedSave { get; private set; }\r
870 \r
871         public void SaveState(Status status)\r
872         {\r
873             NeedSave = false;\r
874             status.QuestLastReset = _lastReset;\r
875             if (_quests != null)\r
876                 status.QuestList = _quests.Values.ToArray();\r
877             if (_countList != null)\r
878                 status.QuestCountList = _countList.CountList.ToArray();\r
879         }\r
880 \r
881         public void LoadState(Status status)\r
882         {\r
883             _lastReset = status.QuestLastReset;\r
884             if (status.QuestCountList != null)\r
885                 _countList.CountList = status.QuestCountList;\r
886             if (status.QuestList != null)\r
887             {\r
888                 _quests.Clear();\r
889                 foreach (var q in status.QuestList)\r
890                     AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);\r
891             }\r
892         }\r
893     }\r
894 }