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
293 {702, new QuestPowerup {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!
\r
294 {703, new QuestPowerup {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!
\r
298 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
300 public QuestCount GetCount(int id)
\r
302 if (_countDict.TryGetValue(id, out var value))
\r
304 if (QuestSpecs.TryGetValue(id, out var spec))
\r
306 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
307 return _countDict[id] = new QuestCount
\r
311 NowArray = nowArray,
\r
315 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
318 public void Remove(int id)
\r
320 _countDict.Remove(id);
\r
323 public void Remove(QuestInterval interval)
\r
326 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
328 _countDict.Remove(id);
\r
332 public IEnumerable<QuestCount> CountList
\r
334 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
339 foreach (var count in value)
\r
341 count.Spec = QuestSpecs[count.Id];
\r
342 _countDict[count.Id] = count;
\r
348 public class QuestInfo : IHaveState
\r
350 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
351 private readonly QuestCountList _countList = new QuestCountList();
\r
352 private readonly ItemInfo _itemInfo;
\r
353 private readonly BattleInfo _battleInfo;
\r
354 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
355 private DateTime _lastReset;
\r
357 private readonly Color[] _color =
\r
359 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
360 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
361 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
364 public int AcceptMax { get; set; } = 5;
\r
366 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
368 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
370 _itemInfo = itemInfo;
\r
371 _battleInfo = battleInfo;
\r
372 if (nowFunc != null)
\r
373 _nowFunc = nowFunc;
\r
376 public void InspectQuestList(dynamic json)
\r
379 if (json.api_list == null)
\r
381 for (var i = 0; i < 2; i++)
\r
383 foreach (var entry in json.api_list)
\r
385 if (entry is double) // -1の場合がある。
\r
388 var id = (int)entry.api_no;
\r
389 var state = (int)entry.api_state;
\r
390 var progress = (int)entry.api_progress_flag;
\r
391 var cat = (int)entry.api_category;
\r
392 var name = (string)entry.api_title;
\r
393 var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");
\r
394 var material = (int[])entry.api_get_material;
\r
410 if (_quests.Remove(id))
\r
417 AddQuest(id, cat, name, detail, material, progress, true);
\r
421 if (_quests.Count <= AcceptMax)
\r
424 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
425 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
431 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
434 var count = _countList.GetCount(id);
\r
437 if (count.AdjustCount(progress))
\r
440 _quests[id] = new QuestStatus
\r
443 Category = category,
\r
446 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,
\r
448 Progress = progress,
\r
449 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
453 public void ClearQuests()
\r
458 private void ResetQuests()
\r
460 var now = _nowFunc();
\r
461 var daily = now.Date.AddHours(5);
\r
462 if (!(_lastReset < daily && daily <= now))
\r
464 _quests.Clear(); // 前日に未消化のデイリーを消す。
\r
465 _countList.Remove(QuestInterval.Daily);
\r
466 var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);
\r
467 if (_lastReset < weekly && weekly <= now)
\r
468 _countList.Remove(QuestInterval.Weekly);
\r
469 var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);
\r
470 if (_lastReset < monthly && monthly <= now)
\r
471 _countList.Remove(QuestInterval.Monthly);
\r
472 var season = now.Month / 3;
\r
473 var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);
\r
474 if (_lastReset < quarterly && quarterly <= now)
\r
475 _countList.Remove(QuestInterval.Quarterly);
\r
482 private bool _boss;
\r
484 public void InspectMapStart(dynamic json)
\r
486 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
487 ago.Count.NowArray[0]++;
\r
488 InspectMapNext(json);
\r
491 public void InspectMapNext(dynamic json)
\r
493 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
494 _boss = (int)json.api_event_id == 5;
\r
496 if (_quests.TryGetValue(861, out var q861))
\r
498 if (_map == 16 && (int)json.api_event_id == 8)
\r
500 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
502 if (fleet.Count(s => s == 10 || s == 22) == 2)
\r
503 IncrementCount(q861.Count);
\r
508 public void InspectBattleResult(dynamic json)
\r
510 var rank = json.api_win_rank;
\r
511 foreach (var quest in _quests.Values)
\r
513 var count = quest.Count;
\r
514 switch (count.Spec)
\r
516 case QuestSortie sortie:
\r
517 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
518 IncrementCount(count);
\r
520 case QuestEnemyType enemyType:
\r
521 var num = enemyType.CountResult(
\r
522 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
524 AddCount(count, num);
\r
528 if (_quests.TryGetValue(214, out var ago))
\r
530 var array = ago.Count.NowArray;
\r
534 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
544 if (_quests.TryGetValue(249, out var q249))
\r
546 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
548 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
550 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
551 IncrementCount(q249.Count);
\r
554 if (_quests.TryGetValue(257, out var q257))
\r
556 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
558 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
560 if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))
\r
561 IncrementCount(q257.Count);
\r
564 if (_quests.TryGetValue(259, out var q259))
\r
566 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
568 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
572 143, 148, 546, // 武蔵
\r
573 80, 275, 541, // 長門
\r
575 26, 286, 411, // 扶桑
\r
576 27, 287, 412, // 山城
\r
580 if (fleet.Select(s => s.Id).Intersect(senkan).Count() == 3 &&
\r
581 fleet.Count(s => s.ShipType == 3) > 0)
\r
583 IncrementCount(q259.Count);
\r
587 if (_quests.TryGetValue(266, out var q266))
\r
589 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
591 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
593 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
594 IncrementCount(q266.Count);
\r
597 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
599 var array = opz.Count.NowArray;
\r
602 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
606 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
610 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
614 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
620 if (_quests.TryGetValue(862, out var q862))
\r
622 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
624 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType).ToArray();
\r
625 if (fleet.Count(s => s == 3) == 2 && fleet.Count(s => s == 16) == 1)
\r
626 IncrementCount(q862.Count);
\r
629 if (_quests.TryGetValue(873, out var q873))
\r
631 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&
\r
632 _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
634 var array = q873.Count.NowArray;
\r
652 if (_quests.TryGetValue(875, out var q875))
\r
654 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
656 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();
\r
657 if (fleet.Contains(543) && fleet.Intersect(new []{344, 345, 359}).Any())
\r
658 IncrementCount(q875.Count);
\r
663 public void InspectPracticeResult(dynamic json)
\r
665 foreach (var quest in _quests.Values)
\r
667 var count = quest.Count;
\r
668 if (!(count.Spec is QuestPractice practice))
\r
670 if (practice.Check(json.api_win_rank))
\r
671 IncrementCount(count);
\r
673 if (_quests.TryGetValue(318, out var q318))
\r
675 if (QuestSortie.CompareRank(json.api_win_rank, "B") <= 0 &&
\r
676 _battleInfo.Result.Friend.Main.Count(s => s.Spec.ShipType == 3) >= 2)
\r
678 IncrementCount(q318.Count);
\r
683 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
685 public void InspectDeck(dynamic json)
\r
687 foreach (var entry in json)
\r
688 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
691 public void InspectMissionResult(string request, dynamic json)
\r
693 var values = HttpUtility.ParseQueryString(request);
\r
694 var deck = int.Parse(values["api_deck_id"]);
\r
695 if ((int)json.api_clear_result == 0)
\r
697 var mid = _missionId[deck - 1];
\r
698 foreach (var quest in _quests.Values)
\r
700 var count = quest.Count;
\r
701 if (!(count.Spec is QuestMission mission))
\r
703 if (mission.Check(mid))
\r
704 IncrementCount(count);
\r
706 if (_quests.TryGetValue(426, out var q426))
\r
708 var count = q426.Count;
\r
712 count.NowArray[0]++;
\r
715 count.NowArray[1]++;
\r
718 count.NowArray[2]++;
\r
721 count.NowArray[3]++;
\r
725 if (_quests.TryGetValue(428, out var q428))
\r
727 var count = q428.Count;
\r
731 count.NowArray[0]++;
\r
734 count.NowArray[1]++;
\r
737 count.NowArray[2]++;
\r
743 private void IncrementCount(QuestCount count)
\r
749 private void AddCount(QuestCount count, int value)
\r
751 count.Now += value;
\r
755 private void IncrementCount(int id)
\r
760 private void AddCount(int id, int value)
\r
762 if (_quests.TryGetValue(id, out var quest))
\r
764 quest.Count.Now += value;
\r
769 public void CountNyukyo() => IncrementCount(503);
\r
771 public void CountCharge() => IncrementCount(504);
\r
773 public void CountCreateItem()
\r
775 IncrementCount(605);
\r
776 IncrementCount(607);
\r
779 public void CountCreateShip()
\r
781 IncrementCount(606);
\r
782 IncrementCount(608);
\r
785 public void InspectDestroyShip(string request)
\r
787 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
790 public void CountRemodelSlot() => IncrementCount(619);
\r
792 public void InspectDestroyItem(string request, dynamic json)
\r
794 var values = HttpUtility.ParseQueryString(request);
\r
795 var items = values["api_slotitem_ids"].Split(',')
\r
796 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();
\r
797 IncrementCount(613); // 613: 資源の再利用
\r
798 foreach (var quest in _quests.Values)
\r
800 var count = quest.Count;
\r
801 if (!(count.Spec is QuestDestroyItem destroy))
\r
803 AddCount(count, items.Count(spec => destroy.Check(spec.Type)));
\r
805 if (_quests.TryGetValue(675, out var q675))
\r
807 q675.Count.NowArray[0] += items.Count(spec => spec.Type == 6);
\r
808 q675.Count.NowArray[1] += items.Count(spec => spec.Type == 21);
\r
811 if (_quests.TryGetValue(676, out var q676))
\r
813 q676.Count.NowArray[0] += items.Count(spec => spec.Type == 2);
\r
814 q676.Count.NowArray[1] += items.Count(spec => spec.Type == 4);
\r
815 q676.Count.NowArray[2] += items.Count(spec => spec.Type == 30);
\r
818 if (_quests.TryGetValue(677, out var q677))
\r
820 q677.Count.NowArray[0] += items.Count(spec => spec.Type == 3);
\r
821 q677.Count.NowArray[1] += items.Count(spec => spec.Type == 10);
\r
822 q677.Count.NowArray[2] += items.Count(spec => spec.Type == 5);
\r
825 if (_quests.TryGetValue(678, out var q678))
\r
827 q678.Count.NowArray[0] += items.Count(spec => spec.Id == 19);
\r
828 q678.Count.NowArray[1] += items.Count(spec => spec.Id == 20);
\r
833 public void InspectPowerup(dynamic json)
\r
835 if ((int)json.api_powerup_flag == 0)
\r
837 foreach (var quest in _quests.Values)
\r
839 var count = quest.Count;
\r
840 if (!(count.Spec is QuestPowerup))
\r
842 IncrementCount(count);
\r
846 public void InspectStop(string request)
\r
848 var values = HttpUtility.ParseQueryString(request);
\r
849 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
853 public void InspectClearItemGet(string request)
\r
855 var values = HttpUtility.ParseQueryString(request);
\r
856 var id = int.Parse(values["api_quest_id"]);
\r
857 _countList.Remove(id);
\r
858 _quests.Remove(id);
\r
862 public bool NeedSave { get; private set; }
\r
864 public void SaveState(Status status)
\r
867 status.QuestLastReset = _lastReset;
\r
868 if (_quests != null)
\r
869 status.QuestList = _quests.Values.ToArray();
\r
870 if (_countList != null)
\r
871 status.QuestCountList = _countList.CountList.ToArray();
\r
874 public void LoadState(Status status)
\r
876 _lastReset = status.QuestLastReset;
\r
877 if (status.QuestCountList != null)
\r
878 _countList.CountList = status.QuestCountList;
\r
879 if (status.QuestList != null)
\r
882 foreach (var q in status.QuestList)
\r
883 AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);
\r