OSDN Git Service

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