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
262 {318, new QuestSpec {Interval = Daily, Max = 3, Material = new[] {0, 2, 2, 0}, AdjustCount = false}}, // 318: 給糧艦「伊良湖」の支援
\r
264 {402, new QuestMission {Interval = Daily, Max = 3, Material = new[] {0, 0, 1, 0}}}, // 402: 「遠征」を3回成功させよう!
\r
265 {403, new QuestMission {Interval = Daily, Max = 10, Material = new[] {0, 0, 0, 0}}}, // 403: 「遠征」を10回成功させよう!
\r
266 {404, new QuestMission {Interval = Weekly, Max = 30, Material = new[] {0, 0, 3, 0}}}, // 404: 大規模遠征作戦、発令!
\r
267 {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 0, 0}}}, // 410: 南方への輸送作戦を成功させよ!
\r
268 {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 2, 1}}}, // 411: 南方への鼠輸送を継続実施せよ!
\r
269 {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}, Material = new[] {0, 0, 0, 0}}}, // 424: 輸送船団護衛を強化せよ!
\r
270 {426, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 4, 0}}}, // 426: 海上通商航路の警戒を厳とせよ!
\r
271 {428, new QuestSpec {Interval = Quarterly, MaxArray = new[] {2, 2, 2}, Material = new[] {0, 0, 0, 3}}}, // 428: 近海に侵入する敵潜を制圧せよ!
\r
273 {503, new QuestSpec {Interval = Daily, Max = 5, Material = new[] {0, 2, 0, 0}}}, // 503: 艦隊大整備!
\r
274 {504, new QuestSpec {Interval = Daily, Max = 15, Material = new[] {1, 0, 1, 0}}}, // 504: 艦隊酒保祭り!
\r
276 {605, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {1, 0, 1, 0}}}, // 605: 新装備「開発」指令
\r
277 {606, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 1, 1, 0}}}, // 606: 新造艦「建造」指令
\r
278 {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {0, 0, 2, 0}}}, // 607: 装備「開発」集中強化!
\r
279 {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {1, 0, 2, 0}}}, // 608: 艦娘「建造」艦隊強化!
\r
280 {609, new QuestSpec {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 609: 軍縮条約対応!
\r
281 {619, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 0, 0, 1}}}, // 619: 装備の改修強化
\r
283 {613, new QuestSpec {Interval = Weekly, Max = 24, Material = new[] {0, 0, 0, 0}}}, // 613: 資源の再利用
\r
284 {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Items = new[] {21}, Material = new[] {0, 0, 2, 1}}}, // 638: 対空機銃量産
\r
285 {663, new QuestDestroyItem {Interval = Quarterly, Max = 10, Items = new[] {3}, Material = new[] {0, 0, 3, 0}}}, // 663: 新型艤装の継続研究
\r
286 {673, new QuestDestroyItem {Interval = Daily, Max = 4, Items = new[] {1}, Shift = 1, Material = new[] {0, 0, 1, 0}}}, // 673: 装備開発力の整備
\r
287 {674, new QuestDestroyItem {Interval = Daily, Max = 3, Items = new[] {21}, Shift = 2, Material = new[] {0, 1, 1, 0}}}, // 674: 工廠環境の整備
\r
288 {675, new QuestSpec {Interval = Quarterly, MaxArray = new[] {6, 4}, Material = new[] {0, 0, 0, 0}}}, // 675: 運用装備の統合整備
\r
289 {676, new QuestSpec {Interval = Weekly, MaxArray = new[] {3, 3, 1}, Material = new[] {0, 1, 7, 0}}}, // 676: 装備開発力の集中整備
\r
290 {677, new QuestSpec {Interval = Weekly, MaxArray = new[] {4, 2, 3}, Material = new[] {0, 5, 0, 0}}}, // 677: 継戦支援能力の整備
\r
291 {678, new QuestSpec {Interval = Quarterly, MaxArray = new[] {3, 5}, Material = new[] {0, 0, 8, 0}}}, // 678: 主力艦上戦闘機の更新
\r
292 {680, new QuestSpec {Interval = Quarterly, MaxArray = new[] {4, 4}, Material = new[] {0, 0, 6, 0}}}, // 680: 対空兵装の整備拡充
\r
294 {702, new QuestPowerup {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!
\r
295 {703, new QuestPowerup {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!
\r
299 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
301 public QuestCount GetCount(int id)
\r
303 if (_countDict.TryGetValue(id, out var value))
\r
305 if (QuestSpecs.TryGetValue(id, out var spec))
\r
307 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
308 return _countDict[id] = new QuestCount
\r
312 NowArray = nowArray,
\r
316 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
319 public void Remove(int id)
\r
321 _countDict.Remove(id);
\r
324 public void Remove(QuestInterval interval)
\r
327 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
329 _countDict.Remove(id);
\r
333 public IEnumerable<QuestCount> CountList
\r
335 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
340 foreach (var count in value)
\r
342 count.Spec = QuestSpecs[count.Id];
\r
343 _countDict[count.Id] = count;
\r
349 public class QuestInfo : IHaveState
\r
351 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
352 private readonly QuestCountList _countList = new QuestCountList();
\r
353 private readonly ItemInfo _itemInfo;
\r
354 private readonly BattleInfo _battleInfo;
\r
355 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
356 private DateTime _lastReset;
\r
358 private readonly Color[] _color =
\r
360 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
361 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
362 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
365 public int AcceptMax { get; set; } = 5;
\r
367 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
369 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
371 _itemInfo = itemInfo;
\r
372 _battleInfo = battleInfo;
\r
373 if (nowFunc != null)
\r
374 _nowFunc = nowFunc;
\r
377 public void InspectQuestList(dynamic json)
\r
380 if (json.api_list == null)
\r
382 for (var i = 0; i < 2; i++)
\r
384 foreach (var entry in json.api_list)
\r
386 if (entry is double) // -1の場合がある。
\r
389 var id = (int)entry.api_no;
\r
390 var state = (int)entry.api_state;
\r
391 var progress = (int)entry.api_progress_flag;
\r
392 var cat = (int)entry.api_category;
\r
393 var name = (string)entry.api_title;
\r
394 var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");
\r
395 var material = (int[])entry.api_get_material;
\r
411 if (_quests.Remove(id))
\r
418 AddQuest(id, cat, name, detail, material, progress, true);
\r
422 if (_quests.Count <= AcceptMax)
\r
425 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
426 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
432 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
435 var count = _countList.GetCount(id);
\r
438 if (count.AdjustCount(progress))
\r
441 _quests[id] = new QuestStatus
\r
444 Category = category,
\r
447 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,
\r
449 Progress = progress,
\r
450 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
454 public void ClearQuests()
\r
459 private void ResetQuests()
\r
461 var now = _nowFunc();
\r
462 var daily = now.Date.AddHours(5);
\r
463 if (!(_lastReset < daily && daily <= now))
\r
465 _quests.Clear(); // 前日に未消化のデイリーを消す。
\r
466 _countList.Remove(QuestInterval.Daily);
\r
467 var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);
\r
468 if (_lastReset < weekly && weekly <= now)
\r
469 _countList.Remove(QuestInterval.Weekly);
\r
470 var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);
\r
471 if (_lastReset < monthly && monthly <= now)
\r
472 _countList.Remove(QuestInterval.Monthly);
\r
473 var season = now.Month / 3;
\r
474 var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);
\r
475 if (_lastReset < quarterly && quarterly <= now)
\r
476 _countList.Remove(QuestInterval.Quarterly);
\r
483 private bool _boss;
\r
485 public void InspectMapStart(dynamic json)
\r
487 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
488 ago.Count.NowArray[0]++;
\r
489 InspectMapNext(json);
\r
492 public void InspectMapNext(dynamic json)
\r
494 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
495 _boss = (int)json.api_event_id == 5;
\r
497 if (_quests.TryGetValue(861, out var q861))
\r
499 if (_map == 16 && (int)json.api_event_id == 8)
\r
501 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
503 if (fleet.Count(s => s == 10 || s == 22) == 2)
\r
504 IncrementCount(q861.Count);
\r
509 public void InspectBattleResult(dynamic json)
\r
511 var rank = json.api_win_rank;
\r
512 foreach (var quest in _quests.Values)
\r
514 var count = quest.Count;
\r
515 switch (count.Spec)
\r
517 case QuestSortie sortie:
\r
518 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
519 IncrementCount(count);
\r
521 case QuestEnemyType enemyType:
\r
522 var num = enemyType.CountResult(
\r
523 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
525 AddCount(count, num);
\r
529 if (_quests.TryGetValue(214, out var ago))
\r
531 var array = ago.Count.NowArray;
\r
535 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
545 if (_quests.TryGetValue(249, out var q249))
\r
547 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
549 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
551 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
552 IncrementCount(q249.Count);
\r
555 if (_quests.TryGetValue(257, out var q257))
\r
557 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
559 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
561 if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))
\r
562 IncrementCount(q257.Count);
\r
565 if (_quests.TryGetValue(259, out var q259))
\r
567 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
569 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
573 143, 148, 546, // 武蔵
\r
574 80, 275, 541, // 長門
\r
576 26, 286, 411, // 扶桑
\r
577 27, 287, 412, // 山城
\r
581 if (fleet.Select(s => s.Id).Intersect(senkan).Count() == 3 &&
\r
582 fleet.Count(s => s.ShipType == 3) > 0)
\r
584 IncrementCount(q259.Count);
\r
588 if (_quests.TryGetValue(266, out var q266))
\r
590 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
592 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
594 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
595 IncrementCount(q266.Count);
\r
598 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
600 var array = opz.Count.NowArray;
\r
603 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
607 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
611 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
615 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
621 if (_quests.TryGetValue(862, out var q862))
\r
623 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
625 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType).ToArray();
\r
626 if (fleet.Count(s => s == 3) == 2 && fleet.Count(s => s == 16) == 1)
\r
627 IncrementCount(q862.Count);
\r
630 if (_quests.TryGetValue(873, out var q873))
\r
632 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&
\r
633 _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
635 var array = q873.Count.NowArray;
\r
653 if (_quests.TryGetValue(875, out var q875))
\r
655 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
657 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();
\r
658 if (fleet.Contains(543) && fleet.Intersect(new []{344, 345, 359}).Any())
\r
659 IncrementCount(q875.Count);
\r
664 public void InspectPracticeResult(dynamic json)
\r
666 foreach (var quest in _quests.Values)
\r
668 var count = quest.Count;
\r
669 if (!(count.Spec is QuestPractice practice))
\r
671 if (practice.Check(json.api_win_rank))
\r
672 IncrementCount(count);
\r
674 if (_quests.TryGetValue(318, out var q318))
\r
676 if (QuestSortie.CompareRank(json.api_win_rank, "B") <= 0 &&
\r
677 _battleInfo.Result.Friend.Main.Count(s => s.Spec.ShipType == 3) >= 2)
\r
679 IncrementCount(q318.Count);
\r
684 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
686 public void InspectDeck(dynamic json)
\r
688 foreach (var entry in json)
\r
689 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
692 public void InspectMissionResult(string request, dynamic json)
\r
694 var values = HttpUtility.ParseQueryString(request);
\r
695 var deck = int.Parse(values["api_deck_id"]);
\r
696 if ((int)json.api_clear_result == 0)
\r
698 var mid = _missionId[deck - 1];
\r
699 foreach (var quest in _quests.Values)
\r
701 var count = quest.Count;
\r
702 if (!(count.Spec is QuestMission mission))
\r
704 if (mission.Check(mid))
\r
705 IncrementCount(count);
\r
707 if (_quests.TryGetValue(426, out var q426))
\r
709 var count = q426.Count;
\r
713 count.NowArray[0]++;
\r
716 count.NowArray[1]++;
\r
719 count.NowArray[2]++;
\r
722 count.NowArray[3]++;
\r
726 if (_quests.TryGetValue(428, out var q428))
\r
728 var count = q428.Count;
\r
732 count.NowArray[0]++;
\r
735 count.NowArray[1]++;
\r
738 count.NowArray[2]++;
\r
744 private void IncrementCount(QuestCount count)
\r
750 private void AddCount(QuestCount count, int value)
\r
752 count.Now += value;
\r
756 private void IncrementCount(int id)
\r
761 private void AddCount(int id, int value)
\r
763 if (_quests.TryGetValue(id, out var quest))
\r
765 quest.Count.Now += value;
\r
770 public void CountNyukyo() => IncrementCount(503);
\r
772 public void CountCharge() => IncrementCount(504);
\r
774 public void CountCreateItem()
\r
776 IncrementCount(605);
\r
777 IncrementCount(607);
\r
780 public void CountCreateShip()
\r
782 IncrementCount(606);
\r
783 IncrementCount(608);
\r
786 public void InspectDestroyShip(string request)
\r
788 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
791 public void CountRemodelSlot() => IncrementCount(619);
\r
793 public void InspectDestroyItem(string request, dynamic json)
\r
795 var values = HttpUtility.ParseQueryString(request);
\r
796 var items = values["api_slotitem_ids"].Split(',')
\r
797 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();
\r
798 IncrementCount(613); // 613: 資源の再利用
\r
799 foreach (var quest in _quests.Values)
\r
801 var count = quest.Count;
\r
802 if (!(count.Spec is QuestDestroyItem destroy))
\r
804 AddCount(count, items.Count(spec => destroy.Check(spec.Type)));
\r
806 if (_quests.TryGetValue(675, out var q675))
\r
808 q675.Count.NowArray[0] += items.Count(spec => spec.Type == 6);
\r
809 q675.Count.NowArray[1] += items.Count(spec => spec.Type == 21);
\r
812 if (_quests.TryGetValue(676, out var q676))
\r
814 q676.Count.NowArray[0] += items.Count(spec => spec.Type == 2);
\r
815 q676.Count.NowArray[1] += items.Count(spec => spec.Type == 4);
\r
816 q676.Count.NowArray[2] += items.Count(spec => spec.Type == 30);
\r
819 if (_quests.TryGetValue(677, out var q677))
\r
821 q677.Count.NowArray[0] += items.Count(spec => spec.Type == 3);
\r
822 q677.Count.NowArray[1] += items.Count(spec => spec.Type == 10);
\r
823 q677.Count.NowArray[2] += items.Count(spec => spec.Type == 5);
\r
826 if (_quests.TryGetValue(678, out var q678))
\r
828 q678.Count.NowArray[0] += items.Count(spec => spec.Id == 19);
\r
829 q678.Count.NowArray[1] += items.Count(spec => spec.Id == 20);
\r
832 if (_quests.TryGetValue(680, out var q680))
\r
834 q680.Count.NowArray[0] += items.Count(spec => spec.Type == 21);
\r
835 q680.Count.NowArray[1] += items.Count(spec => spec.Type == 12 || spec.Type == 13);
\r
840 public void InspectPowerup(dynamic json)
\r
842 if ((int)json.api_powerup_flag == 0)
\r
844 foreach (var quest in _quests.Values)
\r
846 var count = quest.Count;
\r
847 if (!(count.Spec is QuestPowerup))
\r
849 IncrementCount(count);
\r
853 public void InspectStop(string request)
\r
855 var values = HttpUtility.ParseQueryString(request);
\r
856 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
860 public void InspectClearItemGet(string request)
\r
862 var values = HttpUtility.ParseQueryString(request);
\r
863 var id = int.Parse(values["api_quest_id"]);
\r
864 _countList.Remove(id);
\r
865 _quests.Remove(id);
\r
869 public bool NeedSave { get; private set; }
\r
871 public void SaveState(Status status)
\r
874 status.QuestLastReset = _lastReset;
\r
875 if (_quests != null)
\r
876 status.QuestList = _quests.Values.ToArray();
\r
877 if (_countList != null)
\r
878 status.QuestCountList = _countList.CountList.ToArray();
\r
881 public void LoadState(Status status)
\r
883 _lastReset = status.QuestLastReset;
\r
884 if (status.QuestCountList != null)
\r
885 _countList.CountList = status.QuestCountList;
\r
886 if (status.QuestList != null)
\r
889 foreach (var q in status.QuestList)
\r
890 AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);
\r