1 // Copyright (C) 2013, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\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
7 // http://www.apache.org/licenses/LICENSE-2.0
\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
16 using System.Collections.Generic;
\r
17 using System.Drawing;
\r
19 using System.Windows.Forms;
\r
20 using System.Xml.Serialization;
\r
21 using static System.Math;
\r
23 namespace KancolleSniffer
\r
25 public class QuestStatus
\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
35 public QuestCount Count { get; set; }
\r
38 public Color Color { get; set; }
\r
40 public string ToToolTip() =>
\r
42 (Material == null || Material.All(x => x == 0)
\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
50 public enum QuestInterval
\r
59 public class QuestSpec
\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
69 public class QuestSortie : QuestSpec
\r
71 public string Rank { get; set; }
\r
72 public int[] Maps { get; set; }
\r
73 public int[] ShipTypes { get; set; }
\r
75 public static int CompareRank(string a, string b)
\r
77 const string ranks = "SABCDE";
\r
78 return ranks.IndexOf(a, StringComparison.Ordinal) -
\r
79 ranks.IndexOf(b, StringComparison.Ordinal);
\r
82 public bool Check(string rank, int map, bool boss)
\r
84 return (Rank == null || CompareRank(rank, Rank) <= 0) &&
\r
85 (Maps == null || Maps.Contains(map) && boss);
\r
89 public class QuestEnemyType : QuestSpec
\r
91 public int[] EnemyType { get; set; } = new int[0];
\r
93 public int CountResult(IEnumerable<ShipStatus> enemyResult) =>
\r
94 enemyResult.Count(ship => ship.NowHp == 0 && EnemyType.Contains(ship.Spec.ShipType));
\r
97 public class QuestPractice : QuestSpec
\r
99 public bool Win { get; set; }
\r
100 public bool Check(string rank) => !Win || QuestSortie.CompareRank(rank, "B") <= 0;
\r
103 public class QuestMission : QuestSpec
\r
105 public int[] Ids { get; set; }
\r
106 public bool Check(int id) => Ids == null || Ids.Contains(id);
\r
109 public class QuestDestroyItem : QuestSpec
\r
111 public int[] Items { get; set; }
\r
112 public bool Check(int id) => Items == null || Items.Contains(id);
\r
115 public class QuestPowerup : QuestSpec
\r
119 public class QuestCount
\r
121 public int Id { get; set; }
\r
122 public int Now { get; set; }
\r
123 public int[] NowArray { get; set; }
\r
126 public QuestSpec Spec { get; set; }
\r
128 public bool AdjustCount(int progress)
\r
130 if (!Spec.AdjustCount)
\r
132 if (NowArray != null)
\r
134 if (progress != 100)
\r
136 NowArray = NowArray.Zip(Spec.MaxArray, Max).ToArray();
\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 var high = (int)Ceiling(max * next / 100.0);
\r
161 Now = low - Spec.Shift;
\r
166 Now = high - 1 - Spec.Shift;
\r
172 public override string ToString()
\r
174 if (Id == 426 || Id == 854 || Id == 873)
\r
175 return $"{NowArray.Count(n => n >= 1)}/{Spec.MaxArray.Length}";
\r
176 return NowArray != null
\r
177 ? string.Join(" ", NowArray.Zip(Spec.MaxArray, (n, m) => $"{n}/{m}"))
\r
178 : $"{Now}/{Spec.Max}";
\r
181 public string ToToolTip()
\r
186 return string.Join(" ",
\r
187 new[] {"警備任務", "対潜警戒任務", "海上護衛任務", "強硬偵察任務"}
\r
188 .Zip(NowArray, (mission, n) => n >= 1 ? mission : "")
\r
189 .Where(s => !string.IsNullOrEmpty(s)));
\r
191 return string.Join(" ",
\r
192 new[] {"対潜警戒任務", "海峡警備行動", "長時間対潜警戒"}.Zip(NowArray, (mission, n) => n >= 1 ? mission + n : "")
\r
193 .Where(s => !string.IsNullOrEmpty(s)));
\r
195 return string.Join(" ",
\r
196 new[] {"2-4", "6-1", "6-3", "6-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
197 .Where(s => !string.IsNullOrEmpty(s)));
\r
199 return string.Join(" ",
\r
200 new[] {"3-1", "3-2", "3-3"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
201 .Where(s => !string.IsNullOrEmpty(s)));
\r
206 public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;
\r
210 public class QuestCountList
\r
212 private const QuestInterval Daily = QuestInterval.Daily;
\r
213 private const QuestInterval Weekly = QuestInterval.Weekly;
\r
214 private const QuestInterval Monthly = QuestInterval.Monthly;
\r
215 private const QuestInterval Quarterly = QuestInterval.Quarterly;
\r
218 /// このテーブルは七四式電子観測儀を参考に作成した。
\r
219 /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs
\r
221 private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>
\r
223 {201, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 0, 1, 0}}}, // 201: 敵艦隊を撃滅せよ!
\r
224 {216, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 1, 1, 0}}}, // 216: 敵艦隊主力を撃滅せよ!
\r
225 {210, new QuestSortie {Interval = Daily, Max = 10, Material = new[] {0, 0, 1, 0}}}, // 210: 敵艦隊を10回邀撃せよ!
\r
226 {211, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 211: 敵空母を3隻撃沈せよ!
\r
227 {212, new QuestEnemyType {Interval = Daily, Max = 5, EnemyType = new[] {15}, Material = new[] {0, 0, 2, 0}}}, // 212: 敵輸送船団を叩け!
\r
228 {218, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {15}, Material = new[] {0, 1, 1, 0}}}, // 218: 敵補給艦を3隻撃沈せよ!
\r
229 {226, new QuestSortie {Interval = Daily, Max = 5, Rank = "B", Maps = new[] {21, 22, 23, 24, 25}, Material = new[] {1, 1, 0, 0}}}, // 226: 南西諸島海域の制海権を握れ!
\r
230 {230, new QuestEnemyType {Interval = Daily, Max = 6, EnemyType = new[] {13}, Material = new[] {0, 1, 0, 0}}}, // 230: 敵潜水艦を制圧せよ!
\r
232 {213, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {15}, Material = new[] {0, 0, 3, 0}}}, // 213: 海上通商破壊作戦
\r
233 {214, new QuestSpec {Interval = Weekly, MaxArray = new[] {36, 6, 24, 12}, Material = new[] {2, 0, 2, 0}}}, // 214: あ号作戦
\r
234 {220, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 220: い号作戦
\r
235 {221, new QuestEnemyType {Interval = Weekly, Max = 50, EnemyType = new[] {15}, Material = new[] {0, 3, 0, 0}}}, // 221: ろ号作戦
\r
236 {228, new QuestEnemyType {Interval = Weekly, Max = 15, EnemyType = new[] {13}, Material = new[] {0, 2, 0, 1}}}, // 228: 海上護衛戦
\r
237 {229, new QuestSortie {Interval = Weekly, Max = 12, Rank = "B", Maps = new[] {41, 42, 43, 44, 45}, Material = new[] {0, 0, 2, 0}}}, // 229: 敵東方艦隊を撃滅せよ!
\r
238 {241, new QuestSortie {Interval = Weekly, Max = 5, Rank = "B", Maps = new[] {33, 34, 35}, Material = new[] {0, 0, 3, 3}}}, // 241: 敵北方艦隊主力を撃滅せよ!
\r
239 {242, new QuestSortie {Interval = Weekly, Max = 1, Rank = "B", Maps = new[] {44}, Material = new[] {0, 1, 1, 0}}}, // 242: 敵東方中枢艦隊を撃破せよ!
\r
240 {243, new QuestSortie {Interval = Weekly, Max = 2, Rank = "S", Maps = new[] {52}, Material = new[] {0, 0, 2, 2}}}, // 243: 南方海域珊瑚諸島沖の制空権を握れ!
\r
241 {249, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 5, 0}}}, // 249: 「第五戦隊」出撃せよ!
\r
242 {256, new QuestSortie {Interval = Monthly, Max = 3, Rank = "S", Maps = new[] {61}, Material = new[] {0, 0, 0, 0}}}, // 256: 「潜水艦隊」出撃せよ!
\r
243 {257, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 3}}}, // 257: 「水雷戦隊」南西へ!
\r
244 {259, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 3, 0, 4}}}, // 259: 「水上打撃部隊」南方へ!
\r
245 {261, new QuestSortie {Interval = Weekly, Max = 3, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 0, 3}}}, // 261: 海上輸送路の安全確保に努めよ!
\r
246 {265, new QuestSortie {Interval = Monthly, Max = 10, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 5, 3}}}, // 265: 海上護衛強化月間
\r
247 {266, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 4, 2}}}, // 266: 「水上反撃部隊」突入せよ!
\r
249 {822, new QuestSortie {Interval = Quarterly, Max = 2, Rank = "S", Maps = new[] {24}, Material = new[] {0, 0, 0, 5}}}, // 822: 沖ノ島海域迎撃戦
\r
250 {854, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 0, 4}}}, // 854: 戦果拡張任務!「Z作戦」前段作戦
\r
251 {861, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 4, 0, 0}}}, // 861: 強行輸送艦隊、抜錨!
\r
252 {862, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 8, 4}}}, // 862: 前線の航空偵察を実施せよ!
\r
253 {873, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 873: 北方海域警備を実施せよ!
\r
254 {875, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 0, 0}}}, // 875: 精鋭「三一駆」、鉄底海域に突入せよ!
\r
256 {303, new QuestPractice {Interval = Daily, Max = 3, Win = false, Material = new[] {1, 0, 0, 0}}}, // 303: 「演習」で練度向上!
\r
257 {304, new QuestPractice {Interval = Daily, Max = 5, Win = true, Material = new[] {0, 0, 1, 0}}}, // 304: 「演習」で他提督を圧倒せよ!
\r
258 {302, new QuestPractice {Interval = Weekly, Max = 20, Win = true, Material = new[] {0, 0, 2, 1}}}, // 302: 大規模演習
\r
259 {311, new QuestPractice {Interval = Daily, Max = 7, Win = true, Material = new[] {0, 2, 0, 0}}}, // 311: 精鋭艦隊演習
\r
261 {402, new QuestMission {Interval = Daily, Max = 3, Material = new[] {0, 0, 1, 0}}}, // 402: 「遠征」を3回成功させよう!
\r
262 {403, new QuestMission {Interval = Daily, Max = 10, Material = new[] {0, 0, 0, 0}}}, // 403: 「遠征」を10回成功させよう!
\r
263 {404, new QuestMission {Interval = Weekly, Max = 30, Material = new[] {0, 0, 3, 0}}}, // 404: 大規模遠征作戦、発令!
\r
264 {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 0, 0}}}, // 410: 南方への輸送作戦を成功させよ!
\r
265 {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 2, 1}}}, // 411: 南方への鼠輸送を継続実施せよ!
\r
266 {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}, Material = new[] {0, 0, 0, 0}}}, // 424: 輸送船団護衛を強化せよ!
\r
267 {426, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 4, 0}}}, // 426: 海上通商航路の警戒を厳とせよ!
\r
268 {428, new QuestSpec {Interval = Quarterly, MaxArray = new[] {2, 2, 2}, Material = new[] {0, 0, 0, 3}}}, // 428: 近海に侵入する敵潜を制圧せよ!
\r
270 {503, new QuestSpec {Interval = Daily, Max = 5, Material = new[] {0, 2, 0, 0}}}, // 503: 艦隊大整備!
\r
271 {504, new QuestSpec {Interval = Daily, Max = 15, Material = new[] {1, 0, 1, 0}}}, // 504: 艦隊酒保祭り!
\r
273 {605, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {1, 0, 1, 0}}}, // 605: 新装備「開発」指令
\r
274 {606, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 1, 1, 0}}}, // 606: 新造艦「建造」指令
\r
275 {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {0, 0, 2, 0}}}, // 607: 装備「開発」集中強化!
\r
276 {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {1, 0, 2, 0}}}, // 608: 艦娘「建造」艦隊強化!
\r
277 {609, new QuestSpec {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 609: 軍縮条約対応!
\r
278 {619, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 0, 0, 1}}}, // 619: 装備の改修強化
\r
280 {613, new QuestSpec {Interval = Weekly, Max = 24, Material = new[] {0, 0, 0, 0}}}, // 613: 資源の再利用
\r
281 {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Items = new[] {21}, Material = new[] {0, 0, 2, 1}}}, // 638: 対空機銃量産
\r
282 {663, new QuestDestroyItem {Interval = Quarterly, Max = 10, Items = new[] {3}, Material = new[] {0, 0, 3, 0}}}, // 663: 新型艤装の継続研究
\r
283 {673, new QuestDestroyItem {Interval = Daily, Max = 4, Items = new[] {1}, Shift = 1, Material = new[] {0, 0, 1, 0}}}, // 673: 装備開発力の整備
\r
284 {674, new QuestDestroyItem {Interval = Daily, Max = 3, Items = new[] {21}, Shift = 2, Material = new[] {0, 1, 1, 0}}}, // 674: 工廠環境の整備
\r
285 {675, new QuestSpec {Interval = Quarterly, MaxArray = new[] {6, 4}, Material = new[] {0, 0, 0, 0}}}, // 675: 運用装備の統合整備
\r
286 {676, new QuestSpec {Interval = Weekly, MaxArray = new[] {3, 3, 1}, Material = new[] {0, 1, 7, 0}}}, // 676: 装備開発力の集中整備
\r
287 {677, new QuestSpec {Interval = Weekly, MaxArray = new[] {4, 2, 3}, Material = new[] {0, 5, 0, 0}}}, // 677: 継戦支援能力の整備
\r
289 {702, new QuestPowerup {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!
\r
290 {703, new QuestPowerup {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!
\r
294 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
296 public QuestCount GetCount(int id)
\r
298 if (_countDict.TryGetValue(id, out var value))
\r
300 if (QuestSpecs.TryGetValue(id, out var spec))
\r
302 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
303 return _countDict[id] = new QuestCount
\r
307 NowArray = nowArray,
\r
311 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
314 public void Remove(int id)
\r
316 _countDict.Remove(id);
\r
319 public void Remove(QuestInterval interval)
\r
322 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
324 _countDict.Remove(id);
\r
328 public IEnumerable<QuestCount> CountList
\r
330 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
335 foreach (var count in value)
\r
337 count.Spec = QuestSpecs[count.Id];
\r
338 _countDict[count.Id] = count;
\r
344 public class QuestInfo : IHaveState
\r
346 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
347 private readonly QuestCountList _countList = new QuestCountList();
\r
348 private readonly ItemInfo _itemInfo;
\r
349 private readonly BattleInfo _battleInfo;
\r
350 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
351 private DateTime _lastReset;
\r
353 private readonly Color[] _color =
\r
355 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
356 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
357 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
360 public int AcceptMax { get; set; } = 5;
\r
362 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
364 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
366 _itemInfo = itemInfo;
\r
367 _battleInfo = battleInfo;
\r
368 if (nowFunc != null)
\r
369 _nowFunc = nowFunc;
\r
372 public void InspectQuestList(dynamic json)
\r
375 if (json.api_list == null)
\r
377 for (var i = 0; i < 2; i++)
\r
379 foreach (var entry in json.api_list)
\r
381 if (entry is double) // -1の場合がある。
\r
384 var id = (int)entry.api_no;
\r
385 var state = (int)entry.api_state;
\r
386 var progress = (int)entry.api_progress_flag;
\r
387 var cat = (int)entry.api_category;
\r
388 var name = (string)entry.api_title;
\r
389 var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");
\r
390 var material = (int[])entry.api_get_material;
\r
406 if (_quests.Remove(id))
\r
413 AddQuest(id, cat, name, detail, material, progress, true);
\r
417 if (_quests.Count <= AcceptMax)
\r
420 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
421 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
427 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
430 var count = _countList.GetCount(id);
\r
433 count.AdjustCount(progress);
\r
436 _quests[id] = new QuestStatus
\r
439 Category = category,
\r
442 Material = material?.Concat(count.Spec.Material).ToArray(),
\r
444 Progress = progress,
\r
445 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
449 public void ClearQuests()
\r
454 private void ResetQuests()
\r
456 var now = _nowFunc();
\r
457 var daily = now.Date.AddHours(5);
\r
458 if (!(_lastReset < daily && daily <= now))
\r
460 _quests.Clear(); // 前日に未消化のデイリーを消す。
\r
461 _countList.Remove(QuestInterval.Daily);
\r
462 var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);
\r
463 if (_lastReset < weekly && weekly <= now)
\r
464 _countList.Remove(QuestInterval.Weekly);
\r
465 var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);
\r
466 if (_lastReset < monthly && monthly <= now)
\r
467 _countList.Remove(QuestInterval.Monthly);
\r
468 var season = now.Month / 3;
\r
469 var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);
\r
470 if (_lastReset < quarterly && quarterly <= now)
\r
471 _countList.Remove(QuestInterval.Quarterly);
\r
478 private bool _boss;
\r
480 public void InspectMapStart(dynamic json)
\r
482 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
483 ago.Count.NowArray[0]++;
\r
484 InspectMapNext(json);
\r
487 public void InspectMapNext(dynamic json)
\r
489 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
490 _boss = (int)json.api_event_id == 5;
\r
492 if (_quests.TryGetValue(861, out var q861))
\r
494 if (_map == 16 && (int)json.api_event_id == 8)
\r
496 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
498 if (fleet.Count(s => s == 10 || s == 15) == 2)
\r
499 IncrementCount(q861.Count);
\r
504 public void InspectBattleResult(dynamic json)
\r
506 var rank = json.api_win_rank;
\r
507 foreach (var quest in _quests.Values)
\r
509 var count = quest.Count;
\r
510 switch (count.Spec)
\r
512 case QuestSortie sortie:
\r
513 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
514 IncrementCount(count);
\r
516 case QuestEnemyType enemyType:
\r
517 var num = enemyType.CountResult(
\r
518 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
520 AddCount(count, num);
\r
524 if (_quests.TryGetValue(214, out var ago))
\r
526 var array = ago.Count.NowArray;
\r
530 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
540 if (_quests.TryGetValue(249, out var q249))
\r
542 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
544 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
546 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
547 IncrementCount(q249.Count);
\r
550 if (_quests.TryGetValue(257, out var q257))
\r
552 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
554 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
556 if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))
\r
557 IncrementCount(q257.Count);
\r
560 if (_quests.TryGetValue(259, out var q259))
\r
562 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
564 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
568 143, 148, 546, // 武蔵
\r
569 80, 275, 541, // 長門
\r
571 26, 286, 411, // 扶桑
\r
572 27, 287, 412, // 山城
\r
576 if (fleet.Select(s => s.Id).Intersect(senkan).Count() == 3 &&
\r
577 fleet.Count(s => s.ShipType == 3) > 0)
\r
579 IncrementCount(q259.Count);
\r
583 if (_quests.TryGetValue(266, out var q266))
\r
585 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
587 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
589 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
590 IncrementCount(q266.Count);
\r
593 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
595 var array = opz.Count.NowArray;
\r
598 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
602 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
606 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
610 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
616 if (_quests.TryGetValue(862, out var q862))
\r
618 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
620 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType).ToArray();
\r
621 if (fleet.Count(s => s == 3) == 2 && fleet.Count(s => s == 16) == 1)
\r
622 IncrementCount(q862.Count);
\r
625 if (_quests.TryGetValue(873, out var q873))
\r
627 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&
\r
628 _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
630 var array = q873.Count.NowArray;
\r
648 if (_quests.TryGetValue(875, out var q875))
\r
650 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
652 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();
\r
653 if (fleet.Contains(543) && fleet.Intersect(new []{344, 345, 359}).Any())
\r
654 IncrementCount(q875.Count);
\r
659 public void InspectPracticeResult(dynamic json)
\r
661 foreach (var quest in _quests.Values)
\r
663 var count = quest.Count;
\r
664 if (!(count.Spec is QuestPractice practice))
\r
666 if (practice.Check(json.api_win_rank))
\r
667 IncrementCount(count);
\r
671 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
673 public void InspectDeck(dynamic json)
\r
675 foreach (var entry in json)
\r
676 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
679 public void InspectMissionResult(string request, dynamic json)
\r
681 var values = HttpUtility.ParseQueryString(request);
\r
682 var deck = int.Parse(values["api_deck_id"]);
\r
683 if ((int)json.api_clear_result == 0)
\r
685 var mid = _missionId[deck - 1];
\r
686 foreach (var quest in _quests.Values)
\r
688 var count = quest.Count;
\r
689 if (!(count.Spec is QuestMission mission))
\r
691 if (mission.Check(mid))
\r
692 IncrementCount(count);
\r
694 if (_quests.TryGetValue(426, out var q426))
\r
696 var count = q426.Count;
\r
700 count.NowArray[0]++;
\r
703 count.NowArray[1]++;
\r
706 count.NowArray[2]++;
\r
709 count.NowArray[3]++;
\r
713 if (_quests.TryGetValue(428, out var q428))
\r
715 var count = q428.Count;
\r
719 count.NowArray[0]++;
\r
722 count.NowArray[1]++;
\r
725 count.NowArray[2]++;
\r
731 private void IncrementCount(QuestCount count)
\r
737 private void AddCount(QuestCount count, int value)
\r
739 count.Now += value;
\r
743 private void IncrementCount(int id)
\r
748 private void AddCount(int id, int value)
\r
750 if (_quests.TryGetValue(id, out var quest))
\r
752 quest.Count.Now += value;
\r
757 public void CountNyukyo() => IncrementCount(503);
\r
759 public void CountCharge() => IncrementCount(504);
\r
761 public void CountCreateItem()
\r
763 IncrementCount(605);
\r
764 IncrementCount(607);
\r
767 public void CountCreateShip()
\r
769 IncrementCount(606);
\r
770 IncrementCount(608);
\r
773 public void InspectDestroyShip(string request)
\r
775 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
778 public void CountRemodelSlot() => IncrementCount(619);
\r
780 public void InspectDestroyItem(string request, dynamic json)
\r
782 var values = HttpUtility.ParseQueryString(request);
\r
783 var items = values["api_slotitem_ids"].Split(',')
\r
784 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec.Type).ToArray();
\r
785 IncrementCount(613); // 613: 資源の再利用
\r
786 foreach (var quest in _quests.Values)
\r
788 var count = quest.Count;
\r
789 if (!(count.Spec is QuestDestroyItem destroy))
\r
791 AddCount(count, items.Count(destroy.Check));
\r
793 if (_quests.TryGetValue(675, out var q675))
\r
795 q675.Count.NowArray[0] += items.Count(id => id == 6);
\r
796 q675.Count.NowArray[1] += items.Count(id => id == 21);
\r
799 if (_quests.TryGetValue(676, out var q676))
\r
801 q676.Count.NowArray[0] += items.Count(id => id == 2);
\r
802 q676.Count.NowArray[1] += items.Count(id => id == 4);
\r
803 q676.Count.NowArray[2] += items.Count(id => id == 30);
\r
806 if (_quests.TryGetValue(677, out var q677))
\r
808 q677.Count.NowArray[0] += items.Count(id => id == 3);
\r
809 q677.Count.NowArray[1] += items.Count(id => id == 10);
\r
810 q677.Count.NowArray[2] += items.Count(id => id == 5);
\r
815 public void InspectPowerup(dynamic json)
\r
817 if ((int)json.api_powerup_flag == 0)
\r
819 foreach (var quest in _quests.Values)
\r
821 var count = quest.Count;
\r
822 if (!(count.Spec is QuestPowerup))
\r
824 IncrementCount(count);
\r
828 public void InspectStop(string request)
\r
830 var values = HttpUtility.ParseQueryString(request);
\r
831 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
835 public void InspectClearItemGet(string request)
\r
837 var values = HttpUtility.ParseQueryString(request);
\r
838 var id = int.Parse(values["api_quest_id"]);
\r
839 _countList.Remove(id);
\r
840 _quests.Remove(id);
\r
844 public bool NeedSave { get; private set; }
\r
846 public void SaveState(Status status)
\r
849 status.QuestLastReset = _lastReset;
\r
850 if (_quests != null)
\r
851 status.QuestList = _quests.Values.ToArray();
\r
852 if (_countList != null)
\r
853 status.QuestCountList = _countList.CountList.ToArray();
\r
856 public void LoadState(Status status)
\r
858 _lastReset = status.QuestLastReset;
\r
859 if (status.QuestCountList != null)
\r
860 _countList.CountList = status.QuestCountList;
\r
861 if (status.QuestList != null)
\r
864 foreach (var q in status.QuestList)
\r
865 AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);
\r