OSDN Git Service

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