OSDN Git Service

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