OSDN Git Service

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