OSDN Git Service

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