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 KancolleSniffer.Util;
\r
22 using static System.Math;
\r
24 namespace KancolleSniffer.Model
\r
26 public class QuestStatus
\r
28 public int Id { get; set; }
\r
29 public int Category { get; set; }
\r
30 public string Name { get; set; }
\r
31 public string Detail { get; set; }
\r
32 public int[] Material { get; set; }
\r
33 public int Progress { get; set; }
\r
36 public QuestCount Count { get; set; }
\r
39 public Color Color { get; set; }
\r
41 public string ToToolTip() =>
\r
43 (Material == null || Material.All(x => x == 0)
\r
45 : "\r\n" + string.Join(" ",
\r
46 new[] {"燃", "弾", "鋼", "ボ", "建造", "修復", "開発", "改修"}
\r
47 .Zip(Material, (m, num) => num == 0 ? "" : m + num)
\r
48 .Where(s => !string.IsNullOrEmpty(s))));
\r
51 public enum QuestInterval
\r
60 public class QuestSpec
\r
62 public QuestInterval Interval { get; set; }
\r
63 public int Max { get; set; }
\r
64 public int[] MaxArray { get; set; }
\r
65 public bool AdjustCount { get; set; } = true;
\r
66 public int Shift { get; set; }
\r
67 public int[] Material { get; set; }
\r
70 public class QuestSortie : QuestSpec
\r
72 public string Rank { get; set; }
\r
73 public int[] Maps { get; set; }
\r
74 public int[] ShipTypes { get; set; }
\r
76 public static int CompareRank(string a, string b)
\r
78 const string ranks = "SABCDE";
\r
79 return ranks.IndexOf(a, StringComparison.Ordinal) -
\r
80 ranks.IndexOf(b, StringComparison.Ordinal);
\r
83 public bool Check(string rank, int map, bool boss)
\r
85 return (Rank == null || CompareRank(rank, Rank) <= 0) &&
\r
86 (Maps == null || Maps.Contains(map) && boss);
\r
90 public class QuestEnemyType : QuestSpec
\r
92 public int[] EnemyType { get; set; } = new int[0];
\r
94 public int CountResult(IEnumerable<ShipStatus> enemyResult) =>
\r
95 enemyResult.Count(ship => ship.NowHp == 0 && EnemyType.Contains(ship.Spec.ShipType));
\r
98 public class QuestPractice : QuestSpec
\r
100 public bool Win { get; set; }
\r
101 public bool Check(string rank) => !Win || QuestSortie.CompareRank(rank, "B") <= 0;
\r
104 public class QuestMission : QuestSpec
\r
106 public int[] Ids { get; set; }
\r
107 public bool Check(int id) => Ids == null || Ids.Contains(id);
\r
110 public class QuestDestroyItem : QuestSpec
\r
112 public int[] Types { get; set; }
\r
113 public int[] Ids { get; set; }
\r
115 public bool Count(QuestCount count, ItemSpec[] specs)
\r
117 if (count.NowArray == null)
\r
119 var num = specs.Count(spec => Types?.Contains(spec.Type) ?? (Ids?.Contains(spec.Id) ?? true));
\r
123 if (Types == null && Ids == null)
\r
125 var result = false;
\r
126 for (var i = 0; i < count.NowArray.Length; i++)
\r
128 var num = specs.Count(spec => Types != null ? Types[i] == spec.Type : Ids[i] == spec.Id);
\r
129 count.NowArray[i] += num;
\r
137 public class QuestPowerup : QuestSpec
\r
141 public class QuestCount
\r
143 public int Id { get; set; }
\r
144 public int Now { get; set; }
\r
145 public int[] NowArray { get; set; }
\r
148 public QuestSpec Spec { get; set; }
\r
150 public bool AdjustCount(int progress)
\r
152 if (!Spec.AdjustCount)
\r
154 if (NowArray != null)
\r
156 if (progress != 100)
\r
158 NowArray = NowArray.Zip(Spec.MaxArray, Max).ToArray();
\r
177 var now = Now + Spec.Shift;
\r
178 var max = Spec.Max + Spec.Shift;
\r
179 var low = (int)Ceiling(max * progress / 100.0);
\r
180 if (low >= max && progress != 100)
\r
182 var high = (int)Ceiling(max * next / 100.0);
\r
185 Now = low - Spec.Shift;
\r
190 Now = high - 1 - Spec.Shift;
\r
196 public override string ToString()
\r
198 if (Id == 426 || Id == 854 || Id == 873 || Id == 888)
\r
199 return $"{NowArray.Count(n => n >= 1)}/{Spec.MaxArray.Length}";
\r
200 return NowArray != null
\r
201 ? string.Join(" ", NowArray.Zip(Spec.MaxArray, (n, m) => $"{n}/{m}"))
\r
202 : $"{Now}/{Spec.Max}";
\r
205 public string ToToolTip()
\r
210 return string.Join(" ",
\r
211 new[] {"警備任務", "対潜警戒任務", "海上護衛任務", "強硬偵察任務"}
\r
212 .Zip(NowArray, (mission, n) => n >= 1 ? mission : "")
\r
213 .Where(s => !string.IsNullOrEmpty(s)));
\r
215 return string.Join(" ",
\r
216 new[] {"対潜警戒任務", "海峡警備行動", "長時間対潜警戒"}.Zip(NowArray, (mission, n) => n >= 1 ? mission + n : "")
\r
217 .Where(s => !string.IsNullOrEmpty(s)));
\r
219 return string.Join(" ",
\r
220 new[] {"2-4", "6-1", "6-3", "6-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
221 .Where(s => !string.IsNullOrEmpty(s)));
\r
223 return string.Join(" ",
\r
224 new[] {"3-1", "3-2", "3-3"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
225 .Where(s => !string.IsNullOrEmpty(s)));
\r
227 return string.Join(" ",
\r
228 new[] {"5-1", "5-3", "5-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
229 .Where(s => !string.IsNullOrEmpty(s)));
\r
234 public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;
\r
238 public class QuestCountList
\r
240 private const QuestInterval Daily = QuestInterval.Daily;
\r
241 private const QuestInterval Weekly = QuestInterval.Weekly;
\r
242 private const QuestInterval Monthly = QuestInterval.Monthly;
\r
243 private const QuestInterval Quarterly = QuestInterval.Quarterly;
\r
246 /// このテーブルは七四式電子観測儀を参考に作成した。
\r
247 /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs
\r
249 private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>
\r
251 {201, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 0, 1, 0}}}, // 201: 敵艦隊を撃滅せよ!
\r
252 {216, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 1, 1, 0}}}, // 216: 敵艦隊主力を撃滅せよ!
\r
253 {210, new QuestSortie {Interval = Daily, Max = 10, Material = new[] {0, 0, 1, 0}}}, // 210: 敵艦隊を10回邀撃せよ!
\r
254 {211, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 211: 敵空母を3隻撃沈せよ!
\r
255 {212, new QuestEnemyType {Interval = Daily, Max = 5, EnemyType = new[] {15}, Material = new[] {0, 0, 2, 0}}}, // 212: 敵輸送船団を叩け!
\r
256 {218, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {15}, Material = new[] {0, 1, 1, 0}}}, // 218: 敵補給艦を3隻撃沈せよ!
\r
257 {226, new QuestSortie {Interval = Daily, Max = 5, Rank = "B", Maps = new[] {21, 22, 23, 24, 25}, Material = new[] {1, 1, 0, 0}}}, // 226: 南西諸島海域の制海権を握れ!
\r
258 {230, new QuestEnemyType {Interval = Daily, Max = 6, EnemyType = new[] {13}, Material = new[] {0, 1, 0, 0}}}, // 230: 敵潜水艦を制圧せよ!
\r
260 {213, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {15}, Material = new[] {0, 0, 3, 0}}}, // 213: 海上通商破壊作戦
\r
261 {214, new QuestSpec {Interval = Weekly, MaxArray = new[] {36, 6, 24, 12}, Material = new[] {2, 0, 2, 0}}}, // 214: あ号作戦
\r
262 {220, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 220: い号作戦
\r
263 {221, new QuestEnemyType {Interval = Weekly, Max = 50, EnemyType = new[] {15}, Material = new[] {0, 3, 0, 0}}}, // 221: ろ号作戦
\r
264 {228, new QuestEnemyType {Interval = Weekly, Max = 15, EnemyType = new[] {13}, Material = new[] {0, 2, 0, 1}}}, // 228: 海上護衛戦
\r
265 {229, new QuestSortie {Interval = Weekly, Max = 12, Rank = "B", Maps = new[] {41, 42, 43, 44, 45}, Material = new[] {0, 0, 2, 0}}}, // 229: 敵東方艦隊を撃滅せよ!
\r
266 {241, new QuestSortie {Interval = Weekly, Max = 5, Rank = "B", Maps = new[] {33, 34, 35}, Material = new[] {0, 0, 3, 3}}}, // 241: 敵北方艦隊主力を撃滅せよ!
\r
267 {242, new QuestSortie {Interval = Weekly, Max = 1, Rank = "B", Maps = new[] {44}, Material = new[] {0, 1, 1, 0}}}, // 242: 敵東方中枢艦隊を撃破せよ!
\r
268 {243, new QuestSortie {Interval = Weekly, Max = 2, Rank = "S", Maps = new[] {52}, Material = new[] {0, 0, 2, 2}}}, // 243: 南方海域珊瑚諸島沖の制空権を握れ!
\r
269 {249, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 5, 0}}}, // 249: 「第五戦隊」出撃せよ!
\r
270 {256, new QuestSortie {Interval = Monthly, Max = 3, Rank = "S", Maps = new[] {61}, Material = new[] {0, 0, 0, 0}}}, // 256: 「潜水艦隊」出撃せよ!
\r
271 {257, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 3}}}, // 257: 「水雷戦隊」南西へ!
\r
272 {259, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 3, 0, 4}}}, // 259: 「水上打撃部隊」南方へ!
\r
273 {261, new QuestSortie {Interval = Weekly, Max = 3, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 0, 3}}}, // 261: 海上輸送路の安全確保に努めよ!
\r
274 {264, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 2}}}, // 264: 「空母機動部隊」西へ!
\r
275 {265, new QuestSortie {Interval = Monthly, Max = 10, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 5, 3}}}, // 265: 海上護衛強化月間
\r
276 {266, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 4, 2}}}, // 266: 「水上反撃部隊」突入せよ!
\r
278 {822, new QuestSortie {Interval = Quarterly, Max = 2, Rank = "S", Maps = new[] {24}, Material = new[] {0, 0, 0, 5}}}, // 822: 沖ノ島海域迎撃戦
\r
279 {854, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 0, 4}}}, // 854: 戦果拡張任務!「Z作戦」前段作戦
\r
280 {861, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 4, 0, 0}}}, // 861: 強行輸送艦隊、抜錨!
\r
281 {862, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 8, 4}}}, // 862: 前線の航空偵察を実施せよ!
\r
282 {873, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 873: 北方海域警備を実施せよ!
\r
283 {875, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 0, 0}}}, // 875: 精鋭「三一駆」、鉄底海域に突入せよ!
\r
284 {888, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 888: 新編成「三川艦隊」、鉄底海峡に突入せよ!
\r
286 {303, new QuestPractice {Interval = Daily, Max = 3, Win = false, Material = new[] {1, 0, 0, 0}}}, // 303: 「演習」で練度向上!
\r
287 {304, new QuestPractice {Interval = Daily, Max = 5, Win = true, Material = new[] {0, 0, 1, 0}}}, // 304: 「演習」で他提督を圧倒せよ!
\r
288 {302, new QuestPractice {Interval = Weekly, Max = 20, Win = true, Material = new[] {0, 0, 2, 1}}}, // 302: 大規模演習
\r
289 {311, new QuestPractice {Interval = Daily, Max = 7, Win = true, Material = new[] {0, 2, 0, 0}}}, // 311: 精鋭艦隊演習
\r
290 {315, new QuestPractice {Interval = Daily, Max = 8, Win = true, Material = new[] {0, 0, 0, 0}}}, // 315: 春季大演習
\r
291 {318, new QuestSpec {Interval = Daily, Max = 3, Material = new[] {0, 2, 2, 0}, AdjustCount = false}}, // 318: 給糧艦「伊良湖」の支援
\r
293 {402, new QuestMission {Interval = Daily, Max = 3, Material = new[] {0, 0, 1, 0}}}, // 402: 「遠征」を3回成功させよう!
\r
294 {403, new QuestMission {Interval = Daily, Max = 10, Material = new[] {0, 0, 0, 0}}}, // 403: 「遠征」を10回成功させよう!
\r
295 {404, new QuestMission {Interval = Weekly, Max = 30, Material = new[] {0, 0, 3, 0}}}, // 404: 大規模遠征作戦、発令!
\r
296 {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 0, 0}}}, // 410: 南方への輸送作戦を成功させよ!
\r
297 {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 2, 1}}}, // 411: 南方への鼠輸送を継続実施せよ!
\r
298 {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}, Material = new[] {0, 0, 0, 0}}}, // 424: 輸送船団護衛を強化せよ!
\r
299 {426, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 4, 0}}}, // 426: 海上通商航路の警戒を厳とせよ!
\r
300 {428, new QuestSpec {Interval = Quarterly, MaxArray = new[] {2, 2, 2}, Material = new[] {0, 0, 0, 3}}}, // 428: 近海に侵入する敵潜を制圧せよ!
\r
302 {503, new QuestSpec {Interval = Daily, Max = 5, Material = new[] {0, 2, 0, 0}}}, // 503: 艦隊大整備!
\r
303 {504, new QuestSpec {Interval = Daily, Max = 15, Material = new[] {1, 0, 1, 0}}}, // 504: 艦隊酒保祭り!
\r
305 {605, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {1, 0, 1, 0}}}, // 605: 新装備「開発」指令
\r
306 {606, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 1, 1, 0}}}, // 606: 新造艦「建造」指令
\r
307 {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {0, 0, 2, 0}}}, // 607: 装備「開発」集中強化!
\r
308 {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {1, 0, 2, 0}}}, // 608: 艦娘「建造」艦隊強化!
\r
309 {609, new QuestSpec {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 609: 軍縮条約対応!
\r
310 {619, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 0, 0, 1}}}, // 619: 装備の改修強化
\r
312 {613, new QuestSpec {Interval = Weekly, Max = 24, Material = new[] {0, 0, 0, 0}}}, // 613: 資源の再利用
\r
313 {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Types = new[] {21}, Material = new[] {0, 0, 2, 1}}}, // 638: 対空機銃量産
\r
314 {643, new QuestDestroyItem {Interval = Quarterly, Max = 2, Ids = new[] {20}, Material = new[] {0, 0, 2, 0}, AdjustCount = false}}, // 643: 主力「陸攻」の調達
\r
315 {645, new QuestDestroyItem {Interval = Monthly, Max = 1, Ids = new[] {35}, Material = new[] {0, 0, 0, 0}, AdjustCount = false}}, // 645: 「洋上補給」物資の調達
\r
316 {663, new QuestDestroyItem {Interval = Quarterly, Max = 10, Types = new[] {3}, Material = new[] {0, 0, 3, 0}}}, // 663: 新型艤装の継続研究
\r
317 {673, new QuestDestroyItem {Interval = Daily, Max = 4, Types = new[] {1}, Shift = 1, Material = new[] {0, 0, 1, 0}}}, // 673: 装備開発力の整備
\r
318 {674, new QuestDestroyItem {Interval = Daily, Max = 3, Types = new[] {21}, Shift = 2, Material = new[] {0, 1, 1, 0}}}, // 674: 工廠環境の整備
\r
319 {675, new QuestDestroyItem {Interval = Quarterly, MaxArray = new[] {6, 4}, Types = new[] {6, 21}, Material = new[] {0, 0, 0, 0}}}, // 675: 運用装備の統合整備
\r
320 {676, new QuestDestroyItem {Interval = Weekly, MaxArray = new[] {3, 3, 1}, Types = new[] {2, 4, 30}, Material = new[] {0, 1, 7, 0}}}, // 676: 装備開発力の集中整備
\r
321 {677, new QuestDestroyItem {Interval = Weekly, MaxArray = new[] {4, 2, 3}, Types = new[] {3, 10, 5}, Material = new[] {0, 5, 0, 0}}}, // 677: 継戦支援能力の整備
\r
322 {678, new QuestDestroyItem {Interval = Quarterly, MaxArray = new[] {3, 5}, Ids = new[] {19, 20}, Material = new[] {0, 0, 8, 0}}}, // 678: 主力艦上戦闘機の更新
\r
323 {680, new QuestSpec {Interval = Quarterly, MaxArray = new[] {4, 4}, Material = new[] {0, 0, 6, 0}}}, // 680: 対空兵装の整備拡充
\r
325 {702, new QuestPowerup {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!
\r
326 {703, new QuestPowerup {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!
\r
330 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
332 public QuestCount GetCount(int id)
\r
334 if (_countDict.TryGetValue(id, out var value))
\r
336 if (QuestSpecs.TryGetValue(id, out var spec))
\r
338 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
339 return _countDict[id] = new QuestCount
\r
343 NowArray = nowArray,
\r
347 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
350 public void Remove(int id)
\r
352 _countDict.Remove(id);
\r
355 public void Remove(QuestInterval interval)
\r
358 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
360 _countDict.Remove(id);
\r
364 public IEnumerable<QuestCount> CountList
\r
366 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
371 foreach (var count in value)
\r
373 count.Spec = QuestSpecs[count.Id];
\r
374 _countDict[count.Id] = count;
\r
380 public class QuestInfo : IHaveState
\r
382 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
383 private readonly QuestCountList _countList = new QuestCountList();
\r
384 private readonly ItemInfo _itemInfo;
\r
385 private readonly BattleInfo _battleInfo;
\r
386 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
387 private DateTime _lastReset;
\r
389 private readonly Color[] _color =
\r
391 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
392 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
393 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
396 public int AcceptMax { get; set; } = 5;
\r
398 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
400 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
402 _itemInfo = itemInfo;
\r
403 _battleInfo = battleInfo;
\r
404 if (nowFunc != null)
\r
405 _nowFunc = nowFunc;
\r
408 public void InspectQuestList(dynamic json)
\r
411 if (json.api_list == null)
\r
413 for (var i = 0; i < 2; i++)
\r
415 foreach (var entry in json.api_list)
\r
417 if (entry is double) // -1の場合がある。
\r
420 var id = (int)entry.api_no;
\r
421 var state = (int)entry.api_state;
\r
422 var progress = (int)entry.api_progress_flag;
\r
423 var cat = (int)entry.api_category;
\r
424 var name = (string)entry.api_title;
\r
425 var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");
\r
426 var material = (int[])entry.api_get_material;
\r
442 if (_quests.Remove(id))
\r
449 AddQuest(id, cat, name, detail, material, progress, true);
\r
453 if (_quests.Count <= AcceptMax)
\r
456 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
457 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
463 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
466 var count = _countList.GetCount(id);
\r
469 if (count.AdjustCount(progress))
\r
472 _quests[id] = new QuestStatus
\r
475 Category = category,
\r
478 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,
\r
480 Progress = progress,
\r
481 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
485 public void ClearQuests()
\r
490 private void ResetQuests()
\r
492 var now = _nowFunc();
\r
493 var daily = now.Date.AddHours(5);
\r
494 if (!(_lastReset < daily && daily <= now))
\r
496 RemoveQuest(QuestInterval.Daily);
\r
497 _countList.Remove(QuestInterval.Daily);
\r
498 var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);
\r
499 if (_lastReset < weekly && weekly <= now)
\r
501 RemoveQuest(QuestInterval.Weekly);
\r
502 _countList.Remove(QuestInterval.Weekly);
\r
504 var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);
\r
505 if (_lastReset < monthly && monthly <= now)
\r
507 RemoveQuest(QuestInterval.Monthly);
\r
508 _countList.Remove(QuestInterval.Monthly);
\r
510 var season = now.Month / 3;
\r
511 var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);
\r
512 if (_lastReset < quarterly && quarterly <= now)
\r
514 RemoveQuest(QuestInterval.Quarterly);
\r
515 _countList.Remove(QuestInterval.Quarterly);
\r
521 private void RemoveQuest(QuestInterval interval)
\r
524 (from kv in _quests where kv.Value.Count.Spec.Interval == interval select kv.Key).ToArray())
\r
525 _quests.Remove(id);
\r
529 private bool _boss;
\r
531 public void InspectMapStart(dynamic json)
\r
533 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
534 ago.Count.NowArray[0]++;
\r
535 InspectMapNext(json);
\r
538 public void InspectMapNext(dynamic json)
\r
540 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
541 _boss = (int)json.api_event_id == 5;
\r
543 if (_quests.TryGetValue(861, out var q861))
\r
545 if (_map == 16 && (int)json.api_event_id == 8)
\r
547 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
549 if (fleet.Count(s => s == 10 || s == 22) == 2)
\r
550 IncrementCount(q861.Count);
\r
555 public void InspectBattleResult(dynamic json)
\r
557 var rank = json.api_win_rank;
\r
558 foreach (var quest in _quests.Values)
\r
560 var count = quest.Count;
\r
561 switch (count.Spec)
\r
563 case QuestSortie sortie:
\r
564 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
565 IncrementCount(count);
\r
567 case QuestEnemyType enemyType:
\r
568 var num = enemyType.CountResult(
\r
569 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
571 AddCount(count, num);
\r
575 if (_quests.TryGetValue(214, out var ago))
\r
577 var array = ago.Count.NowArray;
\r
581 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
591 if (_quests.TryGetValue(249, out var q249))
\r
593 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
595 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
597 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
598 IncrementCount(q249.Count);
\r
601 if (_quests.TryGetValue(257, out var q257))
\r
603 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
605 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
607 if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))
\r
608 IncrementCount(q257.Count);
\r
611 if (_quests.TryGetValue(259, out var q259))
\r
613 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
615 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
623 if (fleet.Select(s => s.ShipClass).Count(c => ctype.Contains(c)) == 3 &&
\r
624 fleet.Count(s => s.ShipType == 3) > 0)
\r
626 IncrementCount(q259.Count);
\r
630 if (_quests.TryGetValue(264, out var q264))
\r
632 if (_map == 42 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
634 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec)
\r
636 if (fleet.Count(spec => spec.ShipType == 2) >= 2 &&
\r
637 fleet.Count(spec => spec.IsAircraftCarrier) >= 2)
\r
638 IncrementCount(q264.Count);
\r
641 if (_quests.TryGetValue(266, out var q266))
\r
643 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
645 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
647 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
648 IncrementCount(q266.Count);
\r
651 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
653 var array = opz.Count.NowArray;
\r
656 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
660 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
664 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
668 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
674 if (_quests.TryGetValue(862, out var q862))
\r
676 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
678 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
680 if (fleet.Count(s => s == 3) >= 2 && fleet.Count(s => s == 16) >= 1)
\r
681 IncrementCount(q862.Count);
\r
684 if (_quests.TryGetValue(873, out var q873))
\r
686 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&
\r
687 _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
689 var array = q873.Count.NowArray;
\r
707 if (_quests.TryGetValue(875, out var q875))
\r
709 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
711 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();
\r
712 if (fleet.Contains(543) && fleet.Intersect(new[] {344, 345, 359}).Any())
\r
713 IncrementCount(q875.Count);
\r
716 if (_quests.TryGetValue(888, out var q888))
\r
718 if (!_boss || QuestSortie.CompareRank(rank, "S") != 0)
\r
720 var fleet = from ship in _battleInfo.Result.Friend.Main where ship.NowHp > 0 select ship.Spec.Id;
\r
723 69, 272, 427, // 鳥海
\r
725 123, 295, 142, // 衣笠
\r
726 59, 262, 416, // 古鷹
\r
727 60, 263, 417, // 加古
\r
728 51, 213, 477, // 天龍
\r
731 if (fleet.Intersect(member).Count() < 4)
\r
733 var array = q888.Count.NowArray;
\r
752 private int _questFleet;
\r
754 public void StartPractice(string request)
\r
756 var values = HttpUtility.ParseQueryString(request);
\r
757 _questFleet = int.Parse(values["api_deck_id"]) - 1;
\r
760 public void InspectPracticeResult(dynamic json)
\r
762 foreach (var quest in _quests.Values)
\r
764 var count = quest.Count;
\r
765 if (!(count.Spec is QuestPractice practice))
\r
767 if (practice.Check(json.api_win_rank))
\r
768 IncrementCount(count);
\r
770 if (_quests.TryGetValue(318, out var q318))
\r
772 if (_questFleet == 0 && QuestSortie.CompareRank(json.api_win_rank, "B") <= 0 &&
\r
773 _battleInfo.Result.Friend.Main.Count(s => s.Spec.ShipType == 3) >= 2)
\r
775 IncrementCount(q318.Count);
\r
780 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
782 public void InspectDeck(dynamic json)
\r
784 foreach (var entry in json)
\r
785 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
788 public void InspectMissionResult(string request, dynamic json)
\r
790 var values = HttpUtility.ParseQueryString(request);
\r
791 var deck = int.Parse(values["api_deck_id"]);
\r
792 if ((int)json.api_clear_result == 0)
\r
794 var mid = _missionId[deck - 1];
\r
795 foreach (var quest in _quests.Values)
\r
797 var count = quest.Count;
\r
798 if (!(count.Spec is QuestMission mission))
\r
800 if (mission.Check(mid))
\r
801 IncrementCount(count);
\r
803 if (_quests.TryGetValue(426, out var q426))
\r
805 var count = q426.Count;
\r
809 count.NowArray[0]++;
\r
812 count.NowArray[1]++;
\r
815 count.NowArray[2]++;
\r
818 count.NowArray[3]++;
\r
822 if (_quests.TryGetValue(428, out var q428))
\r
824 var count = q428.Count;
\r
828 count.NowArray[0]++;
\r
831 count.NowArray[1]++;
\r
834 count.NowArray[2]++;
\r
840 private void IncrementCount(QuestCount count)
\r
846 private void AddCount(QuestCount count, int value)
\r
848 count.Now += value;
\r
852 private void IncrementCount(int id)
\r
857 private void AddCount(int id, int value)
\r
859 if (_quests.TryGetValue(id, out var quest))
\r
861 quest.Count.Now += value;
\r
866 public void CountNyukyo() => IncrementCount(503);
\r
868 public void CountCharge() => IncrementCount(504);
\r
870 public void CountCreateItem()
\r
872 IncrementCount(605);
\r
873 IncrementCount(607);
\r
876 public void CountCreateShip()
\r
878 IncrementCount(606);
\r
879 IncrementCount(608);
\r
882 public void InspectDestroyShip(string request)
\r
884 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
887 public void CountRemodelSlot() => IncrementCount(619);
\r
889 public void InspectDestroyItem(string request, dynamic json)
\r
891 var values = HttpUtility.ParseQueryString(request);
\r
892 var items = values["api_slotitem_ids"].Split(',')
\r
893 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();
\r
894 IncrementCount(613); // 613: 資源の再利用
\r
895 foreach (var quest in _quests.Values)
\r
897 var count = quest.Count;
\r
898 if (!(count.Spec is QuestDestroyItem destroy))
\r
900 if (destroy.Count(count, items))
\r
903 if (_quests.TryGetValue(680, out var q680))
\r
905 q680.Count.NowArray[0] += items.Count(spec => spec.Type == 21);
\r
906 q680.Count.NowArray[1] += items.Count(spec => spec.Type == 12 || spec.Type == 13);
\r
911 public void InspectPowerup(dynamic json)
\r
913 if ((int)json.api_powerup_flag == 0)
\r
915 foreach (var quest in _quests.Values)
\r
917 var count = quest.Count;
\r
918 if (!(count.Spec is QuestPowerup))
\r
920 IncrementCount(count);
\r
924 public void InspectStop(string request)
\r
926 var values = HttpUtility.ParseQueryString(request);
\r
927 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
931 public void InspectClearItemGet(string request)
\r
933 var values = HttpUtility.ParseQueryString(request);
\r
934 var id = int.Parse(values["api_quest_id"]);
\r
935 _countList.Remove(id);
\r
936 _quests.Remove(id);
\r
940 public bool NeedSave { get; private set; }
\r
942 public void SaveState(Status status)
\r
945 status.QuestLastReset = _lastReset;
\r
946 if (_quests != null)
\r
947 status.QuestList = _quests.Values.ToArray();
\r
948 if (_countList != null)
\r
949 status.QuestCountList = _countList.CountList.ToArray();
\r
952 public void LoadState(Status status)
\r
954 _lastReset = status.QuestLastReset;
\r
955 if (status.QuestCountList != null)
\r
956 _countList.CountList = status.QuestCountList;
\r
957 if (status.QuestList != null)
\r
960 foreach (var q in status.QuestList)
\r
961 AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);
\r