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 if (low >= max && progress != 100)
\r
160 var high = (int)Ceiling(max * next / 100.0);
\r
163 Now = low - Spec.Shift;
\r
168 Now = high - 1 - Spec.Shift;
\r
174 public override string ToString()
\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
183 public string ToToolTip()
\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
193 return string.Join(" ",
\r
194 new[] {"対潜警戒任務", "海峡警備行動", "長時間対潜警戒"}.Zip(NowArray, (mission, n) => n >= 1 ? mission + n : "")
\r
195 .Where(s => !string.IsNullOrEmpty(s)));
\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
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
208 public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;
\r
212 public class QuestCountList
\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
220 /// このテーブルは七四式電子観測儀を参考に作成した。
\r
221 /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs
\r
223 private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>
\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
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
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
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
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
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
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
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
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
296 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
298 public QuestCount GetCount(int id)
\r
300 if (_countDict.TryGetValue(id, out var value))
\r
302 if (QuestSpecs.TryGetValue(id, out var spec))
\r
304 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
305 return _countDict[id] = new QuestCount
\r
309 NowArray = nowArray,
\r
313 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
316 public void Remove(int id)
\r
318 _countDict.Remove(id);
\r
321 public void Remove(QuestInterval interval)
\r
324 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
326 _countDict.Remove(id);
\r
330 public IEnumerable<QuestCount> CountList
\r
332 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
337 foreach (var count in value)
\r
339 count.Spec = QuestSpecs[count.Id];
\r
340 _countDict[count.Id] = count;
\r
346 public class QuestInfo : IHaveState
\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
355 private readonly Color[] _color =
\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
362 public int AcceptMax { get; set; } = 5;
\r
364 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
366 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
368 _itemInfo = itemInfo;
\r
369 _battleInfo = battleInfo;
\r
370 if (nowFunc != null)
\r
371 _nowFunc = nowFunc;
\r
374 public void InspectQuestList(dynamic json)
\r
377 if (json.api_list == null)
\r
379 for (var i = 0; i < 2; i++)
\r
381 foreach (var entry in json.api_list)
\r
383 if (entry is double) // -1の場合がある。
\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
408 if (_quests.Remove(id))
\r
415 AddQuest(id, cat, name, detail, material, progress, true);
\r
419 if (_quests.Count <= AcceptMax)
\r
422 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
423 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
429 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
432 var count = _countList.GetCount(id);
\r
435 if (count.AdjustCount(progress))
\r
438 _quests[id] = new QuestStatus
\r
441 Category = category,
\r
444 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,
\r
446 Progress = progress,
\r
447 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
451 public void ClearQuests()
\r
456 private void ResetQuests()
\r
458 var now = _nowFunc();
\r
459 var daily = now.Date.AddHours(5);
\r
460 if (!(_lastReset < daily && daily <= now))
\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
480 private bool _boss;
\r
482 public void InspectMapStart(dynamic json)
\r
484 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
485 ago.Count.NowArray[0]++;
\r
486 InspectMapNext(json);
\r
489 public void InspectMapNext(dynamic json)
\r
491 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
492 _boss = (int)json.api_event_id == 5;
\r
494 if (_quests.TryGetValue(861, out var q861))
\r
496 if (_map == 16 && (int)json.api_event_id == 8)
\r
498 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
500 if (fleet.Count(s => s == 10 || s == 15) == 2)
\r
501 IncrementCount(q861.Count);
\r
506 public void InspectBattleResult(dynamic json)
\r
508 var rank = json.api_win_rank;
\r
509 foreach (var quest in _quests.Values)
\r
511 var count = quest.Count;
\r
512 switch (count.Spec)
\r
514 case QuestSortie sortie:
\r
515 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
516 IncrementCount(count);
\r
518 case QuestEnemyType enemyType:
\r
519 var num = enemyType.CountResult(
\r
520 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
522 AddCount(count, num);
\r
526 if (_quests.TryGetValue(214, out var ago))
\r
528 var array = ago.Count.NowArray;
\r
532 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
542 if (_quests.TryGetValue(249, out var q249))
\r
544 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
546 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
548 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
549 IncrementCount(q249.Count);
\r
552 if (_quests.TryGetValue(257, out var q257))
\r
554 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
556 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\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
562 if (_quests.TryGetValue(259, out var q259))
\r
564 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
566 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
570 143, 148, 546, // 武蔵
\r
571 80, 275, 541, // 長門
\r
573 26, 286, 411, // 扶桑
\r
574 27, 287, 412, // 山城
\r
578 if (fleet.Select(s => s.Id).Intersect(senkan).Count() == 3 &&
\r
579 fleet.Count(s => s.ShipType == 3) > 0)
\r
581 IncrementCount(q259.Count);
\r
585 if (_quests.TryGetValue(266, out var q266))
\r
587 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
589 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
591 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
592 IncrementCount(q266.Count);
\r
595 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
597 var array = opz.Count.NowArray;
\r
600 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
604 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
608 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
612 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
618 if (_quests.TryGetValue(862, out var q862))
\r
620 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\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
627 if (_quests.TryGetValue(873, out var q873))
\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
632 var array = q873.Count.NowArray;
\r
650 if (_quests.TryGetValue(875, out var q875))
\r
652 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\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
661 public void InspectPracticeResult(dynamic json)
\r
663 foreach (var quest in _quests.Values)
\r
665 var count = quest.Count;
\r
666 if (!(count.Spec is QuestPractice practice))
\r
668 if (practice.Check(json.api_win_rank))
\r
669 IncrementCount(count);
\r
673 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
675 public void InspectDeck(dynamic json)
\r
677 foreach (var entry in json)
\r
678 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
681 public void InspectMissionResult(string request, dynamic json)
\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
687 var mid = _missionId[deck - 1];
\r
688 foreach (var quest in _quests.Values)
\r
690 var count = quest.Count;
\r
691 if (!(count.Spec is QuestMission mission))
\r
693 if (mission.Check(mid))
\r
694 IncrementCount(count);
\r
696 if (_quests.TryGetValue(426, out var q426))
\r
698 var count = q426.Count;
\r
702 count.NowArray[0]++;
\r
705 count.NowArray[1]++;
\r
708 count.NowArray[2]++;
\r
711 count.NowArray[3]++;
\r
715 if (_quests.TryGetValue(428, out var q428))
\r
717 var count = q428.Count;
\r
721 count.NowArray[0]++;
\r
724 count.NowArray[1]++;
\r
727 count.NowArray[2]++;
\r
733 private void IncrementCount(QuestCount count)
\r
739 private void AddCount(QuestCount count, int value)
\r
741 count.Now += value;
\r
745 private void IncrementCount(int id)
\r
750 private void AddCount(int id, int value)
\r
752 if (_quests.TryGetValue(id, out var quest))
\r
754 quest.Count.Now += value;
\r
759 public void CountNyukyo() => IncrementCount(503);
\r
761 public void CountCharge() => IncrementCount(504);
\r
763 public void CountCreateItem()
\r
765 IncrementCount(605);
\r
766 IncrementCount(607);
\r
769 public void CountCreateShip()
\r
771 IncrementCount(606);
\r
772 IncrementCount(608);
\r
775 public void InspectDestroyShip(string request)
\r
777 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
780 public void CountRemodelSlot() => IncrementCount(619);
\r
782 public void InspectDestroyItem(string request, dynamic json)
\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
790 var count = quest.Count;
\r
791 if (!(count.Spec is QuestDestroyItem destroy))
\r
793 AddCount(count, items.Count(destroy.Check));
\r
795 if (_quests.TryGetValue(675, out var q675))
\r
797 q675.Count.NowArray[0] += items.Count(id => id == 6);
\r
798 q675.Count.NowArray[1] += items.Count(id => id == 21);
\r
801 if (_quests.TryGetValue(676, out var q676))
\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
808 if (_quests.TryGetValue(677, out var q677))
\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
817 public void InspectPowerup(dynamic json)
\r
819 if ((int)json.api_powerup_flag == 0)
\r
821 foreach (var quest in _quests.Values)
\r
823 var count = quest.Count;
\r
824 if (!(count.Spec is QuestPowerup))
\r
826 IncrementCount(count);
\r
830 public void InspectStop(string request)
\r
832 var values = HttpUtility.ParseQueryString(request);
\r
833 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
837 public void InspectClearItemGet(string request)
\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
846 public bool NeedSave { get; private set; }
\r
848 public void SaveState(Status status)
\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
858 public void LoadState(Status status)
\r
860 _lastReset = status.QuestLastReset;
\r
861 if (status.QuestCountList != null)
\r
862 _countList.CountList = status.QuestCountList;
\r
863 if (status.QuestList != null)
\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