OSDN Git Service

「継戦支援能力の整備」のカウンターを実装する
[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 QuestCount Count { get; set; }\r
31         public int Progress { get; set; }\r
32         public Color Color { get; set; }\r
33     }\r
34 \r
35     public enum QuestInterval\r
36     {\r
37         Other,\r
38         Daily,\r
39         Weekly,\r
40         Monthly,\r
41         Quarterly\r
42     }\r
43 \r
44     public class QuestSpec\r
45     {\r
46         public QuestInterval Interval { get; set; }\r
47         public int Max { get; set; }\r
48         public int[] MaxArray { get; set; }\r
49         public bool AdjustCount { get; set; } = true;\r
50         public int Shift { get; set; }\r
51     }\r
52 \r
53     public class QuestSortie : QuestSpec\r
54     {\r
55         public string Rank { get; set; }\r
56         public int[] Maps { get; set; }\r
57         public int[] ShipTypes { get; set; }\r
58 \r
59         public static int CompareRank(string a, string b)\r
60         {\r
61             const string ranks = "SABCDE";\r
62             return ranks.IndexOf(a, StringComparison.Ordinal) -\r
63                    ranks.IndexOf(b, StringComparison.Ordinal);\r
64         }\r
65 \r
66         public bool Check(string rank, int map, bool boss)\r
67         {\r
68             return (Rank == null || CompareRank(rank, Rank) <= 0) &&\r
69                    (Maps == null || Maps.Contains(map) && boss);\r
70         }\r
71     }\r
72 \r
73     public class QuestEnemyType : QuestSpec\r
74     {\r
75         public int[] EnemyType { get; set; } = new int[0];\r
76 \r
77         public int CountResult(IEnumerable<ShipStatus> enemyResult) =>\r
78             enemyResult.Count(ship => ship.NowHp == 0 && EnemyType.Contains(ship.Spec.ShipType));\r
79     }\r
80 \r
81     public class QuestPractice : QuestSpec\r
82     {\r
83         public bool Win { get; set; }\r
84         public bool Check(string rank) => !Win || QuestSortie.CompareRank(rank, "B") <= 0;\r
85     }\r
86 \r
87     public class QuestMission : QuestSpec\r
88     {\r
89         public int[] Ids { get; set; }\r
90         public bool Check(int id) => Ids == null || Ids.Contains(id);\r
91     }\r
92 \r
93     public class QuestDestroyItem : QuestSpec\r
94     {\r
95         public int[] Items { get; set; }\r
96         public bool Check(int id) => Items == null || Items.Contains(id);\r
97     }\r
98 \r
99     public class QuestPowerup : QuestSpec\r
100     {\r
101     }\r
102 \r
103     public class QuestCount\r
104     {\r
105         public int Id { get; set; }\r
106         public int Now { get; set; }\r
107         public int[] NowArray { get; set; }\r
108 \r
109         [XmlIgnore]\r
110         public QuestSpec Spec { get; set; }\r
111 \r
112         public bool AdjustCount(int progress)\r
113         {\r
114             if (!Spec.AdjustCount)\r
115                 return false;\r
116             if (NowArray != null)\r
117             {\r
118                 if (progress != 100)\r
119                     return false;\r
120                 NowArray = NowArray.Zip(Spec.MaxArray, Max).ToArray();\r
121                 return true;\r
122             }\r
123             var next = 0;\r
124             switch (progress)\r
125             {\r
126                 case 0:\r
127                     next = 50;\r
128                     break;\r
129                 case 50:\r
130                     next = 80;\r
131                     break;\r
132                 case 80:\r
133                     next = 100;\r
134                     break;\r
135                 case 100:\r
136                     next = 100000;\r
137                     break;\r
138             }\r
139             var now = Now + Spec.Shift;\r
140             var max = Spec.Max + Spec.Shift;\r
141             var low = (int)Ceiling(max * progress / 100.0);\r
142             var high = (int)Ceiling(max * next / 100.0);\r
143             if (now < low)\r
144             {\r
145                 Now = low - Spec.Shift;\r
146                 return true;\r
147             }\r
148             if (now >= high)\r
149             {\r
150                 Now = high - 1 - Spec.Shift;\r
151                 return true;\r
152             }\r
153             return false;\r
154         }\r
155 \r
156         public override string ToString()\r
157         {\r
158             if (Id == 854)\r
159                 return $"{NowArray.Count(n => n == 1)}/{Spec.MaxArray.Length}";\r
160             return NowArray != null\r
161                 ? string.Join(" ", NowArray.Zip(Spec.MaxArray, (n, m) => $"{n}/{m}"))\r
162                 : $"{Now}/{Spec.Max}";\r
163         }\r
164 \r
165         public string ToToolTip()\r
166         {\r
167             return Id != 854\r
168                 ? ""\r
169                 : string.Join(" ",\r
170                     new[] {"2-4", "6-1", "6-3", "6-4"}.Zip(NowArray, (map, flag) => flag == 1 ? map : "")\r
171                         .Where(s => !string.IsNullOrEmpty(s)));\r
172         }\r
173 \r
174         public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;\r
175     }\r
176 \r
177     // @formatter:off\r
178     public class QuestCountList\r
179     {\r
180         private const QuestInterval Daily = QuestInterval.Daily;\r
181         private const QuestInterval Weekly = QuestInterval.Weekly;\r
182         private const QuestInterval Monthly = QuestInterval.Monthly;\r
183         private const QuestInterval Quarterly = QuestInterval.Quarterly;\r
184 \r
185         /// <summary>\r
186         /// このテーブルは七四式電子観測儀を参考に作成した。\r
187         /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs\r
188         /// </summary>\r
189         private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>\r
190         {\r
191             {201, new QuestSortie {Interval = Daily, Max = 1, Rank = "B"}}, // 201: 敵艦隊を撃滅せよ!\r
192             {216, new QuestSortie {Interval = Daily, Max = 1, Rank = "B"}}, // 216: 敵艦隊主力を撃滅せよ!\r
193             {210, new QuestSortie {Interval = Daily, Max = 10}}, // 210: 敵艦隊を10回邀撃せよ!\r
194             {211, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {7, 11}}}, // 211: 敵空母を3隻撃沈せよ!\r
195             {212, new QuestEnemyType {Interval = Daily, Max = 5, EnemyType = new[] {15}}}, // 212: 敵輸送船団を叩け!\r
196             {218, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {15}}}, // 218: 敵補給艦を3隻撃沈せよ!\r
197             {226, new QuestSortie {Interval = Daily, Max = 5, Rank = "B", Maps = new[] {21, 22, 23, 24, 25}}}, // 226: 南西諸島海域の制海権を握れ!\r
198             {230, new QuestEnemyType {Interval = Daily, Max = 6, EnemyType = new[] {13}}}, // 230: 敵潜水艦を制圧せよ!\r
199 \r
200             {213, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {15}}}, // 213: 海上通商破壊作戦\r
201             {214, new QuestSpec {Interval = Weekly, MaxArray = new[] {36, 6, 24, 12}}}, // 214: あ号作戦\r
202             {220, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {7, 11}}}, // 220: い号作戦\r
203             {221, new QuestEnemyType {Interval = Weekly, Max = 50, EnemyType = new[] {15}}}, // 221: ろ号作戦\r
204             {228, new QuestEnemyType {Interval = Weekly, Max = 15, EnemyType = new[] {13}}}, // 228: 海上護衛戦\r
205             {229, new QuestSortie {Interval = Weekly, Max = 12, Rank = "B", Maps = new[] {41, 42, 43, 44, 45}}}, // 229: 敵東方艦隊を撃滅せよ!\r
206             {241, new QuestSortie {Interval = Weekly, Max = 5, Rank = "B", Maps = new[] {33, 34, 35}}}, // 241: 敵北方艦隊主力を撃滅せよ!\r
207             {242, new QuestSortie {Interval = Weekly, Max = 1, Rank = "B", Maps = new[] {44}}}, // 242: 敵東方中枢艦隊を撃破せよ!\r
208             {243, new QuestSortie {Interval = Weekly, Max = 2, Rank = "B", Maps = new[] {52}}}, // 243: 南方海域珊瑚諸島沖の制空権を握れ!\r
209             {256, new QuestSortie {Interval = Monthly, Max = 3, Rank = "S", Maps = new[] {61}}}, // 256: 「潜水艦隊」出撃せよ!\r
210             {261, new QuestSortie {Interval = Weekly, Max = 3, Rank = "A", Maps = new[] {15}}}, // 261: 海上輸送路の安全確保に努めよ!\r
211             {265, new QuestSortie {Interval = Monthly, Max = 10, Rank = "A", Maps = new[] {15}}}, // 265: 海上護衛強化月間\r
212 \r
213             {822, new QuestSortie {Interval = Quarterly, Max = 2, Rank = "S", Maps = new[] {24}}}, // 822: 沖ノ島海域迎撃戦\r
214             {854, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}}}, // 854: 戦果拡張任務!「Z作戦」前段作戦\r
215 \r
216             {303, new QuestPractice {Interval = Daily, Max = 3, Win = false}}, // 303: 「演習」で練度向上!\r
217             {304, new QuestPractice {Interval = Daily, Max = 5, Win = true}}, // 304: 「演習」で他提督を圧倒せよ!\r
218             {302, new QuestPractice {Interval = Weekly, Max = 20, Win = true}}, // 302: 大規模演習\r
219             {311, new QuestPractice {Interval = Daily, Max = 7, Win = true}}, // 311: 精鋭艦隊演習\r
220 \r
221             {402, new QuestMission {Interval = Daily, Max = 3}}, // 402: 「遠征」を3回成功させよう!\r
222             {403, new QuestMission {Interval = Daily, Max = 10}}, // 403: 「遠征」を10回成功させよう!\r
223             {404, new QuestMission {Interval = Weekly, Max = 30}}, // 404: 大規模遠征作戦、発令!\r
224             {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}}}, // 410: 南方への輸送作戦を成功させよ!\r
225             {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}}}, // 411: 南方への鼠輸送を継続実施せよ!\r
226             {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}}}, // 424: 輸送船団護衛を強化せよ!\r
227 \r
228             {503, new QuestSpec {Interval = Daily, Max = 5}}, // 503: 艦隊大整備!\r
229             {504, new QuestSpec {Interval = Daily, Max = 15}}, // 504: 艦隊酒保祭り!\r
230 \r
231             {605, new QuestSpec {Interval = Daily, Max = 1}}, // 605: 新装備「開発」指令\r
232             {606, new QuestSpec {Interval = Daily, Max = 1}}, // 606: 新造艦「建造」指令\r
233             {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1}}, // 607: 装備「開発」集中強化!\r
234             {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1}}, // 608: 艦娘「建造」艦隊強化!\r
235             {609, new QuestSpec {Interval = Daily, Max = 2}}, // 609: 軍縮条約対応!\r
236             {619, new QuestSpec {Interval = Daily, Max = 1}}, // 619: 装備の改修強化\r
237 \r
238             {613, new QuestSpec {Interval = Weekly, Max = 24}}, // 613: 資源の再利用\r
239             {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Items = new[] {21}}}, // 638: 対空機銃量産\r
240             {673, new QuestDestroyItem {Interval = Daily, Max = 4, Items = new[] {1}, Shift = 1}}, // 673: 装備開発力の整備\r
241             {674, new QuestDestroyItem {Interval = Daily, Max = 3, Items = new[] {21}, Shift = 2}}, // 674: 工廠環境の整備\r
242             {675, new QuestSpec {Interval = Quarterly, MaxArray = new[] {6, 4}}}, // 675: 運用装備の統合整備\r
243             {676, new QuestSpec {Interval = Weekly, MaxArray = new[] {3, 3, 1}}}, // 676: 装備開発力の集中整備\r
244             {677, new QuestSpec {Interval = Weekly, MaxArray = new[] {4, 2, 3}}}, // 677: 継戦支援能力の整備\r
245 \r
246             {702, new QuestPowerup {Interval = Daily, Max = 2}}, // 702: 艦の「近代化改修」を実施せよ!\r
247             {703, new QuestPowerup {Interval = Weekly, Max = 15}} // 703: 「近代化改修」を進め、戦備を整えよ!\r
248         };\r
249         // @formatter:on\r
250 \r
251         private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();\r
252 \r
253         public QuestCount GetCount(int id)\r
254         {\r
255             if (_countDict.TryGetValue(id, out var value))\r
256                 return value;\r
257             if (QuestSpecs.TryGetValue(id, out var spec))\r
258             {\r
259                 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();\r
260                 return _countDict[id] = new QuestCount\r
261                 {\r
262                     Id = id,\r
263                     Now = 0,\r
264                     NowArray = nowArray,\r
265                     Spec = spec\r
266                 };\r
267             }\r
268             return new QuestCount {Spec = new QuestSpec {AdjustCount = false}};\r
269         }\r
270 \r
271         public void Remove(int id)\r
272         {\r
273             _countDict.Remove(id);\r
274         }\r
275 \r
276         public void Remove(QuestInterval interval)\r
277         {\r
278             foreach (var id in\r
279                 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())\r
280             {\r
281                 _countDict.Remove(id);\r
282             }\r
283         }\r
284 \r
285         public IEnumerable<QuestCount> CountList\r
286         {\r
287             get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));\r
288             set\r
289             {\r
290                 if (value == null)\r
291                     return;\r
292                 foreach (var count in value)\r
293                 {\r
294                     count.Spec = QuestSpecs[count.Id];\r
295                     _countDict[count.Id] = count;\r
296                 }\r
297             }\r
298         }\r
299     }\r
300 \r
301     public class QuestInfo : IHaveState\r
302     {\r
303         private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();\r
304         private readonly QuestCountList _countList = new QuestCountList();\r
305         private readonly ItemInfo _itemInfo;\r
306         private readonly BattleInfo _battleInfo;\r
307         private readonly Func<DateTime> _nowFunc = () => DateTime.Now;\r
308         private DateTime _lastReset;\r
309 \r
310         private readonly Color[] _color =\r
311         {\r
312             Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),\r
313             Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),\r
314             Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)\r
315         };\r
316 \r
317         public int AcceptMax { get; set; } = 5;\r
318 \r
319         public QuestStatus[] Quests => _quests.Values.ToArray();\r
320 \r
321         public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)\r
322         {\r
323             _itemInfo = itemInfo;\r
324             _battleInfo = battleInfo;\r
325             if (nowFunc != null)\r
326                 _nowFunc = nowFunc;\r
327         }\r
328 \r
329         public void InspectQuestList(dynamic json)\r
330         {\r
331             ResetQuests();\r
332             if (json.api_list == null)\r
333                 return;\r
334             for (var i = 0; i < 2; i++)\r
335             {\r
336                 foreach (var entry in json.api_list)\r
337                 {\r
338                     if (entry is double) // -1の場合がある。\r
339                         continue;\r
340 \r
341                     var id = (int)entry.api_no;\r
342                     var state = (int)entry.api_state;\r
343                     var progress = (int)entry.api_progress_flag;\r
344                     var cat = (int)entry.api_category;\r
345                     var name = (string)entry.api_title;\r
346 \r
347                     switch (progress)\r
348                     {\r
349                         case 0:\r
350                             break;\r
351                         case 1:\r
352                             progress = 50;\r
353                             break;\r
354                         case 2:\r
355                             progress = 80;\r
356                             break;\r
357                     }\r
358                     switch (state)\r
359                     {\r
360                         case 1:\r
361                             _quests.Remove(id);\r
362                             break;\r
363                         case 3:\r
364                             progress = 100;\r
365                             goto case 2;\r
366                         case 2:\r
367                             var count = _countList.GetCount(id);\r
368                             if (count.AdjustCount(progress))\r
369                                 NeedSave = true;\r
370                             _quests[id] = new QuestStatus\r
371                             {\r
372                                 Id = id,\r
373                                 Category = cat,\r
374                                 Name = name,\r
375                                 Count = count,\r
376                                 Progress = progress,\r
377                                 Color = cat <= _color.Length ? _color[cat - 1] : Control.DefaultBackColor\r
378                             };\r
379                             break;\r
380                     }\r
381                 }\r
382                 if (_quests.Count <= AcceptMax)\r
383                     break;\r
384                 /*\r
385                  * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。\r
386                  * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。\r
387                  */\r
388                 _quests.Clear();\r
389             }\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 (!(_lastReset < daily && daily <= now))\r
397                 return;\r
398             _quests.Clear(); // 前日に未消化のデイリーを消す。\r
399             _countList.Remove(QuestInterval.Daily);\r
400             var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);\r
401             if (_lastReset < weekly && weekly <= now)\r
402                 _countList.Remove(QuestInterval.Weekly);\r
403             var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);\r
404             if (_lastReset < monthly && monthly <= now)\r
405                 _countList.Remove(QuestInterval.Monthly);\r
406             var season = now.Month / 3;\r
407             var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);\r
408             if (_lastReset < quarterly && quarterly <= now)\r
409                 _countList.Remove(QuestInterval.Quarterly);\r
410             _lastReset = now;\r
411         }\r
412 \r
413         private bool _boss;\r
414         private int _map;\r
415 \r
416         public void InspectMapStart(dynamic json)\r
417         {\r
418             if (_quests.TryGetValue(214, out var ago)) // あ号\r
419                 ago.Count.NowArray[0]++;\r
420             InspectMapNext(json);\r
421         }\r
422 \r
423         public void InspectMapNext(dynamic json)\r
424         {\r
425             _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;\r
426             _boss = (int)json.api_event_id == 5;\r
427         }\r
428 \r
429         public void InspectBattleResult(dynamic json)\r
430         {\r
431             var rank = json.api_win_rank;\r
432             foreach (var quest in _quests.Values)\r
433             {\r
434                 var count = quest.Count;\r
435                 switch (count.Spec)\r
436                 {\r
437                     case QuestSortie sortie:\r
438                         if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))\r
439                             IncrementCount(count);\r
440                         break;\r
441                     case QuestEnemyType enemyType:\r
442                         var num = enemyType.CountResult(\r
443                             _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));\r
444                         if (num > 0)\r
445                             AddCount(count, num);\r
446                         break;\r
447                 }\r
448             }\r
449             if (_quests.TryGetValue(214, out var ago))\r
450             {\r
451                 var array = ago.Count.NowArray;\r
452                 if (_boss)\r
453                 {\r
454                     array[2]++;\r
455                     if (QuestSortie.CompareRank(rank, "B") <= 0)\r
456                         array[3]++;\r
457                     NeedSave = true;\r
458                 }\r
459                 if (rank == "S")\r
460                 {\r
461                     array[1]++;\r
462                     NeedSave = true;\r
463                 }\r
464             }\r
465             if (_quests.TryGetValue(854, out var opz) && _boss)\r
466             {\r
467                 var array = opz.Count.NowArray;\r
468                 switch (_map)\r
469                 {\r
470                     case 24 when QuestSortie.CompareRank(rank, "A") <= 0:\r
471                         array[0]++;\r
472                         NeedSave = true;\r
473                         break;\r
474                     case 61 when QuestSortie.CompareRank(rank, "A") <= 0:\r
475                         array[1]++;\r
476                         NeedSave = true;\r
477                         break;\r
478                     case 63 when QuestSortie.CompareRank(rank, "A") <= 0:\r
479                         array[2]++;\r
480                         NeedSave = true;\r
481                         break;\r
482                     case 64 when QuestSortie.CompareRank(rank, "S") <= 0:\r
483                         array[3]++;\r
484                         NeedSave = true;\r
485                         break;\r
486                 }\r
487             }\r
488         }\r
489 \r
490         public void InspectPracticeResult(dynamic json)\r
491         {\r
492             foreach (var quest in _quests.Values)\r
493             {\r
494                 var count = quest.Count;\r
495                 if (!(count.Spec is QuestPractice practice))\r
496                     continue;\r
497                 if (practice.Check(json.api_win_rank))\r
498                     IncrementCount(count);\r
499             }\r
500         }\r
501 \r
502         private readonly int[] _missionId = new int[ShipInfo.FleetCount];\r
503 \r
504         public void InspectDeck(dynamic json)\r
505         {\r
506             foreach (var entry in json)\r
507                 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];\r
508         }\r
509 \r
510         public void InspectMissionResult(string request, dynamic json)\r
511         {\r
512             var values = HttpUtility.ParseQueryString(request);\r
513             var deck = int.Parse(values["api_deck_id"]);\r
514             if ((int)json.api_clear_result == 0)\r
515                 return;\r
516             foreach (var quest in _quests.Values)\r
517             {\r
518                 var count = quest.Count;\r
519                 if (!(count.Spec is QuestMission mission))\r
520                     continue;\r
521                 if (mission.Check(_missionId[deck - 1]))\r
522                     IncrementCount(count);\r
523             }\r
524         }\r
525 \r
526         private void IncrementCount(QuestCount count)\r
527         {\r
528             count.Now++;\r
529             NeedSave = true;\r
530         }\r
531 \r
532         private void AddCount(QuestCount count, int value)\r
533         {\r
534             count.Now += value;\r
535             NeedSave = true;\r
536         }\r
537 \r
538         private void IncrementCount(int id)\r
539         {\r
540             AddCount(id, 1);\r
541         }\r
542 \r
543         private void AddCount(int id, int value)\r
544         {\r
545             if (_quests.TryGetValue(id, out var quest))\r
546             {\r
547                 quest.Count.Now += value;\r
548                 NeedSave = true;\r
549             }\r
550         }\r
551 \r
552         public void CountNyukyo() => IncrementCount(503);\r
553 \r
554         public void CountCharge() => IncrementCount(504);\r
555 \r
556         public void CountCreateItem()\r
557         {\r
558             IncrementCount(605);\r
559             IncrementCount(607);\r
560         }\r
561 \r
562         public void CountCreateShip()\r
563         {\r
564             IncrementCount(606);\r
565             IncrementCount(608);\r
566         }\r
567 \r
568         public void InspectDestroyShip(string request)\r
569         {\r
570             AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);\r
571         }\r
572 \r
573         public void CountRemodelSlot() => IncrementCount(619);\r
574 \r
575         public void InspectDestroyItem(string request, dynamic json)\r
576         {\r
577             var values = HttpUtility.ParseQueryString(request);\r
578             var items = values["api_slotitem_ids"].Split(',')\r
579                 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec.Type).ToArray();\r
580             IncrementCount(613); // 613: 資源の再利用\r
581             foreach (var quest in _quests.Values)\r
582             {\r
583                 var count = quest.Count;\r
584                 if (!(count.Spec is QuestDestroyItem destroy))\r
585                     continue;\r
586                 AddCount(count, items.Count(destroy.Check));\r
587             }\r
588             if (_quests.TryGetValue(675, out var q675))\r
589             {\r
590                 q675.Count.NowArray[0] += items.Count(id => id == 6);\r
591                 q675.Count.NowArray[1] += items.Count(id => id == 21);\r
592                 NeedSave = true;\r
593             }\r
594             if (_quests.TryGetValue(676, out var q676))\r
595             {\r
596                 q676.Count.NowArray[0] += items.Count(id => id == 2);\r
597                 q676.Count.NowArray[1] += items.Count(id => id == 4);\r
598                 q676.Count.NowArray[2] += items.Count(id => id == 30);\r
599                 NeedSave = true;\r
600             }\r
601             if (_quests.TryGetValue(677, out var q677))\r
602             {\r
603                 q677.Count.NowArray[0] += items.Count(id => id == 3);\r
604                 q677.Count.NowArray[1] += items.Count(id => id == 10);\r
605                 q677.Count.NowArray[2] += items.Count(id => id == 5);\r
606                 NeedSave = true;\r
607             }\r
608         }\r
609 \r
610         public void InspectPowerup(dynamic json)\r
611         {\r
612             if ((int)json.api_powerup_flag == 0)\r
613                 return;\r
614             foreach (var quest in _quests.Values)\r
615             {\r
616                 var count = quest.Count;\r
617                 if (!(count.Spec is QuestPowerup))\r
618                     continue;\r
619                 IncrementCount(count);\r
620             }\r
621         }\r
622 \r
623         public void InspectStop(string request)\r
624         {\r
625             var values = HttpUtility.ParseQueryString(request);\r
626             _quests.Remove(int.Parse(values["api_quest_id"]));\r
627         }\r
628 \r
629         public void InspectClearItemGet(string request)\r
630         {\r
631             var values = HttpUtility.ParseQueryString(request);\r
632             var id = int.Parse(values["api_quest_id"]);\r
633             _countList.Remove(id);\r
634             _quests.Remove(id);\r
635             NeedSave = true;\r
636         }\r
637 \r
638         public bool NeedSave { get; private set; }\r
639 \r
640         public void SaveState(Status status)\r
641         {\r
642             status.QuestLastReset = _lastReset;\r
643             if (_countList == null)\r
644                 return;\r
645             status.QuestCountList = _countList.CountList.ToArray();\r
646         }\r
647 \r
648         public void LoadState(Status status)\r
649         {\r
650             _countList.CountList = status.QuestCountList;\r
651             _lastReset = status.QuestLastReset;\r
652         }\r
653     }\r
654 }