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
53 // ReSharper disable once UnusedMember.Global
\r
61 public class QuestSpec
\r
63 public QuestInterval Interval { get; set; }
\r
64 public int Max { get; set; }
\r
65 public int[] MaxArray { get; set; }
\r
66 public bool AdjustCount { get; set; } = true;
\r
67 public int Shift { get; set; }
\r
68 public int[] Material { get; set; }
\r
71 public class QuestSortie : QuestSpec
\r
73 public string Rank { get; set; }
\r
74 public int[] Maps { 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
231 return string.Join(" ",
\r
232 new[] {"艦戦", "艦爆", "艦攻", "水偵"}.Zip(NowArray, (type, n) => n >= 1 ? type + n : "")
\r
233 .Where(s => !string.IsNullOrEmpty(s)));
\r
238 public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ?? Now >= Spec.Max;
\r
242 public class QuestCountList
\r
244 private const QuestInterval Daily = QuestInterval.Daily;
\r
245 private const QuestInterval Weekly = QuestInterval.Weekly;
\r
246 private const QuestInterval Monthly = QuestInterval.Monthly;
\r
247 private const QuestInterval Quarterly = QuestInterval.Quarterly;
\r
250 /// このテーブルは七四式電子観測儀を参考に作成した。
\r
251 /// https://github.com/andanteyk/ElectronicObserver/blob/develop/ElectronicObserver/Data/Quest/QuestProgressManager.cs
\r
253 private static readonly Dictionary<int, QuestSpec> QuestSpecs = new Dictionary<int, QuestSpec>
\r
255 {201, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 0, 1, 0}}}, // 201: 敵艦隊を撃滅せよ!
\r
256 {216, new QuestSortie {Interval = Daily, Max = 1, Rank = "B", Material = new[] {0, 1, 1, 0}}}, // 216: 敵艦隊主力を撃滅せよ!
\r
257 {210, new QuestSortie {Interval = Daily, Max = 10, Material = new[] {0, 0, 1, 0}}}, // 210: 敵艦隊を10回邀撃せよ!
\r
258 {211, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {7, 11}, Material = new[] {0, 2, 0, 0}}}, // 211: 敵空母を3隻撃沈せよ!
\r
259 {212, new QuestEnemyType {Interval = Daily, Max = 5, EnemyType = new[] {15}, Material = new[] {0, 0, 2, 0}}}, // 212: 敵輸送船団を叩け!
\r
260 {218, new QuestEnemyType {Interval = Daily, Max = 3, EnemyType = new[] {15}, Material = new[] {0, 1, 1, 0}}}, // 218: 敵補給艦を3隻撃沈せよ!
\r
261 {226, new QuestSortie {Interval = Daily, Max = 5, Rank = "B", Maps = new[] {21, 22, 23, 24, 25}, Material = new[] {1, 1, 0, 0}}}, // 226: 南西諸島海域の制海権を握れ!
\r
262 {230, new QuestEnemyType {Interval = Daily, Max = 6, EnemyType = new[] {13}, Material = new[] {0, 1, 0, 0}}}, // 230: 敵潜水艦を制圧せよ!
\r
264 {213, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {15}, Material = new[] {0, 0, 3, 0}}}, // 213: 海上通商破壊作戦
\r
265 {214, new QuestSpec {Interval = Weekly, MaxArray = new[] {36, 6, 24, 12}, Material = new[] {2, 0, 2, 0}}}, // 214: あ号作戦
\r
266 {220, new QuestEnemyType {Interval = Weekly, Max = 20, EnemyType = new[] {7, 11}, Material = new[] {0, 0, 2, 0}}}, // 220: い号作戦
\r
267 {221, new QuestEnemyType {Interval = Weekly, Max = 50, EnemyType = new[] {15}, Material = new[] {0, 3, 0, 0}}}, // 221: ろ号作戦
\r
268 {228, new QuestEnemyType {Interval = Weekly, Max = 15, EnemyType = new[] {13}, Material = new[] {0, 2, 0, 1}}}, // 228: 海上護衛戦
\r
269 {229, new QuestSortie {Interval = Weekly, Max = 12, Rank = "B", Maps = new[] {41, 42, 43, 44, 45}, Material = new[] {0, 0, 2, 0}}}, // 229: 敵東方艦隊を撃滅せよ!
\r
270 {241, new QuestSortie {Interval = Weekly, Max = 5, Rank = "B", Maps = new[] {33, 34, 35}, Material = new[] {0, 0, 3, 3}}}, // 241: 敵北方艦隊主力を撃滅せよ!
\r
271 {242, new QuestSortie {Interval = Weekly, Max = 1, Rank = "B", Maps = new[] {44}, Material = new[] {0, 1, 1, 0}}}, // 242: 敵東方中枢艦隊を撃破せよ!
\r
272 {243, new QuestSortie {Interval = Weekly, Max = 2, Rank = "S", Maps = new[] {52}, Material = new[] {0, 0, 2, 2}}}, // 243: 南方海域珊瑚諸島沖の制空権を握れ!
\r
273 {249, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 5, 0}}}, // 249: 「第五戦隊」出撃せよ!
\r
274 {256, new QuestSortie {Interval = Monthly, Max = 3, Rank = "S", Maps = new[] {61}, Material = new[] {0, 0, 0, 0}}}, // 256: 「潜水艦隊」出撃せよ!
\r
275 {257, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 3}}}, // 257: 「水雷戦隊」南西へ!
\r
276 {259, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 3, 0, 4}}}, // 259: 「水上打撃部隊」南方へ!
\r
277 {261, new QuestSortie {Interval = Weekly, Max = 3, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 0, 3}}}, // 261: 海上輸送路の安全確保に努めよ!
\r
278 {264, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 0, 2}}}, // 264: 「空母機動部隊」西へ!
\r
279 {265, new QuestSortie {Interval = Monthly, Max = 10, Rank = "A", Maps = new[] {15}, Material = new[] {0, 0, 5, 3}}}, // 265: 海上護衛強化月間
\r
280 {266, new QuestSpec {Interval = Monthly, Max = 1, Material = new[] {0, 0, 4, 2}}}, // 266: 「水上反撃部隊」突入せよ!
\r
282 {822, new QuestSortie {Interval = Quarterly, Max = 2, Rank = "S", Maps = new[] {24}, Material = new[] {0, 0, 0, 5}}}, // 822: 沖ノ島海域迎撃戦
\r
283 {854, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 0, 4}}}, // 854: 戦果拡張任務!「Z作戦」前段作戦
\r
284 {861, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 4, 0, 0}}}, // 861: 強行輸送艦隊、抜錨!
\r
285 {862, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 8, 4}}}, // 862: 前線の航空偵察を実施せよ!
\r
286 {873, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 873: 北方海域警備を実施せよ!
\r
287 {875, new QuestSpec {Interval = Quarterly, Max = 2, Material = new[] {0, 0, 0, 0}}}, // 875: 精鋭「三一駆」、鉄底海域に突入せよ!
\r
288 {888, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1}, Material = new[] {0, 0, 0, 0}}}, // 888: 新編成「三川艦隊」、鉄底海峡に突入せよ!
\r
290 {303, new QuestPractice {Interval = Daily, Max = 3, Win = false, Material = new[] {1, 0, 0, 0}}}, // 303: 「演習」で練度向上!
\r
291 {304, new QuestPractice {Interval = Daily, Max = 5, Win = true, Material = new[] {0, 0, 1, 0}}}, // 304: 「演習」で他提督を圧倒せよ!
\r
292 {302, new QuestPractice {Interval = Weekly, Max = 20, Win = true, Material = new[] {0, 0, 2, 1}}}, // 302: 大規模演習
\r
293 {311, new QuestPractice {Interval = Daily, Max = 7, Win = true, Material = new[] {0, 2, 0, 0}}}, // 311: 精鋭艦隊演習
\r
294 {315, new QuestPractice {Interval = Daily, Max = 8, Win = true, Material = new[] {0, 0, 0, 0}}}, // 315: 春季大演習
\r
295 {318, new QuestSpec {Interval = Daily, Max = 3, Material = new[] {0, 2, 2, 0}, AdjustCount = false}}, // 318: 給糧艦「伊良湖」の支援
\r
297 {402, new QuestMission {Interval = Daily, Max = 3, Material = new[] {0, 0, 1, 0}}}, // 402: 「遠征」を3回成功させよう!
\r
298 {403, new QuestMission {Interval = Daily, Max = 10, Material = new[] {0, 0, 0, 0}}}, // 403: 「遠征」を10回成功させよう!
\r
299 {404, new QuestMission {Interval = Weekly, Max = 30, Material = new[] {0, 0, 3, 0}}}, // 404: 大規模遠征作戦、発令!
\r
300 {410, new QuestMission {Interval = Weekly, Max = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 0, 0}}}, // 410: 南方への輸送作戦を成功させよ!
\r
301 {411, new QuestMission {Interval = Weekly, Max = 6, Shift = 1, Ids = new[] {37, 38}, Material = new[] {0, 0, 2, 1}}}, // 411: 南方への鼠輸送を継続実施せよ!
\r
302 {424, new QuestMission {Interval = Monthly, Max = 4, Shift = 1, Ids = new[] {5}, Material = new[] {0, 0, 0, 0}}}, // 424: 輸送船団護衛を強化せよ!
\r
303 {426, new QuestSpec {Interval = Quarterly, MaxArray = new[] {1, 1, 1, 1}, Material = new[] {0, 0, 4, 0}}}, // 426: 海上通商航路の警戒を厳とせよ!
\r
304 {428, new QuestSpec {Interval = Quarterly, MaxArray = new[] {2, 2, 2}, Material = new[] {0, 0, 0, 3}}}, // 428: 近海に侵入する敵潜を制圧せよ!
\r
306 {503, new QuestSpec {Interval = Daily, Max = 5, Material = new[] {0, 2, 0, 0}}}, // 503: 艦隊大整備!
\r
307 {504, new QuestSpec {Interval = Daily, Max = 15, Material = new[] {1, 0, 1, 0}}}, // 504: 艦隊酒保祭り!
\r
309 {605, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {1, 0, 1, 0}}}, // 605: 新装備「開発」指令
\r
310 {606, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 1, 1, 0}}}, // 606: 新造艦「建造」指令
\r
311 {607, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {0, 0, 2, 0}}}, // 607: 装備「開発」集中強化!
\r
312 {608, new QuestSpec {Interval = Daily, Max = 3, Shift = 1, Material = new[] {1, 0, 2, 0}}}, // 608: 艦娘「建造」艦隊強化!
\r
313 {609, new QuestSpec {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 609: 軍縮条約対応!
\r
314 {619, new QuestSpec {Interval = Daily, Max = 1, Material = new[] {0, 0, 0, 1}}}, // 619: 装備の改修強化
\r
316 {613, new QuestSpec {Interval = Weekly, Max = 24, Material = new[] {0, 0, 0, 0}}}, // 613: 資源の再利用
\r
317 {638, new QuestDestroyItem {Interval = Weekly, Max = 6, Types = new[] {21}, Material = new[] {0, 0, 2, 1}}}, // 638: 対空機銃量産
\r
318 {643, new QuestDestroyItem {Interval = Quarterly, Max = 2, Ids = new[] {20}, Material = new[] {0, 0, 2, 0}, AdjustCount = false}}, // 643: 主力「陸攻」の調達
\r
319 {645, new QuestDestroyItem {Interval = Monthly, Max = 1, Ids = new[] {35}, Material = new[] {0, 0, 0, 0}, AdjustCount = false}}, // 645: 「洋上補給」物資の調達
\r
320 {663, new QuestDestroyItem {Interval = Quarterly, Max = 10, Types = new[] {3}, Material = new[] {0, 0, 3, 0}}}, // 663: 新型艤装の継続研究
\r
321 {673, new QuestDestroyItem {Interval = Daily, Max = 4, Types = new[] {1}, Shift = 1, Material = new[] {0, 0, 1, 0}}}, // 673: 装備開発力の整備
\r
322 {674, new QuestDestroyItem {Interval = Daily, Max = 3, Types = new[] {21}, Shift = 2, Material = new[] {0, 1, 1, 0}}}, // 674: 工廠環境の整備
\r
323 {675, new QuestDestroyItem {Interval = Quarterly, MaxArray = new[] {6, 4}, Types = new[] {6, 21}, Material = new[] {0, 0, 0, 0}}}, // 675: 運用装備の統合整備
\r
324 {676, new QuestDestroyItem {Interval = Weekly, MaxArray = new[] {3, 3, 1}, Types = new[] {2, 4, 30}, Material = new[] {0, 1, 7, 0}}}, // 676: 装備開発力の集中整備
\r
325 {677, new QuestDestroyItem {Interval = Weekly, MaxArray = new[] {4, 2, 3}, Types = new[] {3, 10, 5}, Material = new[] {0, 5, 0, 0}}}, // 677: 継戦支援能力の整備
\r
326 {678, new QuestDestroyItem {Interval = Quarterly, MaxArray = new[] {3, 5}, Ids = new[] {19, 20}, Material = new[] {0, 0, 8, 0}}}, // 678: 主力艦上戦闘機の更新
\r
327 {680, new QuestSpec {Interval = Quarterly, MaxArray = new[] {4, 4}, Material = new[] {0, 0, 6, 0}}}, // 680: 対空兵装の整備拡充
\r
328 {688, new QuestDestroyItem {Interval = Quarterly, MaxArray = new[] {3, 3, 3, 3}, Types = new[] {6, 7, 8, 10}, Material = new[] {0, 0, 0, 0}}}, // 688: 航空戦力の強化
\r
330 {702, new QuestPowerUp {Interval = Daily, Max = 2, Material = new[] {0, 1, 0, 0}}}, // 702: 艦の「近代化改修」を実施せよ!
\r
331 {703, new QuestPowerUp {Interval = Weekly, Max = 15, Material = new[] {1, 0, 2, 0}}} // 703: 「近代化改修」を進め、戦備を整えよ!
\r
335 private readonly Dictionary<int, QuestCount> _countDict = new Dictionary<int, QuestCount>();
\r
337 public QuestCount GetCount(int id)
\r
339 if (_countDict.TryGetValue(id, out var value))
\r
341 if (QuestSpecs.TryGetValue(id, out var spec))
\r
343 var nowArray = spec.MaxArray?.Select(x => 0).ToArray();
\r
344 return _countDict[id] = new QuestCount
\r
348 NowArray = nowArray,
\r
352 return new QuestCount {Spec = new QuestSpec {Material = new int[0], AdjustCount = false}};
\r
355 public void Remove(int id)
\r
357 _countDict.Remove(id);
\r
360 public void Remove(QuestInterval interval)
\r
363 _countDict.Where(pair => pair.Value.Spec.Interval == interval).Select(pair => pair.Key).ToArray())
\r
365 _countDict.Remove(id);
\r
369 public IEnumerable<QuestCount> CountList
\r
371 get => _countDict.Values.Where(c => c.Now > 0 || (c.NowArray?.Any(n => n > 0) ?? false));
\r
376 foreach (var count in value)
\r
378 count.Spec = QuestSpecs[count.Id];
\r
379 _countDict[count.Id] = count;
\r
385 public class QuestInfo : IHaveState
\r
387 private readonly SortedDictionary<int, QuestStatus> _quests = new SortedDictionary<int, QuestStatus>();
\r
388 private readonly QuestCountList _countList = new QuestCountList();
\r
389 private readonly ItemInfo _itemInfo;
\r
390 private readonly BattleInfo _battleInfo;
\r
391 private readonly Func<DateTime> _nowFunc = () => DateTime.Now;
\r
392 private DateTime _lastReset;
\r
394 private readonly Color[] _color =
\r
396 Color.FromArgb(60, 141, 76), Color.FromArgb(232, 57, 41), Color.FromArgb(136, 204, 120),
\r
397 Color.FromArgb(52, 147, 185), Color.FromArgb(220, 198, 126), Color.FromArgb(168, 111, 76),
\r
398 Color.FromArgb(200, 148, 231), Color.FromArgb(232, 57, 41)
\r
401 public int AcceptMax { get; set; } = 5;
\r
403 public QuestStatus[] Quests => _quests.Values.ToArray();
\r
405 public QuestInfo(ItemInfo itemInfo, BattleInfo battleInfo, Func<DateTime> nowFunc = null)
\r
407 _itemInfo = itemInfo;
\r
408 _battleInfo = battleInfo;
\r
409 if (nowFunc != null)
\r
410 _nowFunc = nowFunc;
\r
413 public void InspectQuestList(dynamic json)
\r
416 if (json.api_list == null)
\r
418 for (var i = 0; i < 2; i++)
\r
420 foreach (var entry in json.api_list)
\r
422 if (entry is double) // -1の場合がある。
\r
425 var id = (int)entry.api_no;
\r
426 var state = (int)entry.api_state;
\r
427 var progress = (int)entry.api_progress_flag;
\r
428 var cat = (int)entry.api_category;
\r
429 var name = (string)entry.api_title;
\r
430 var detail = ((string)entry.api_detail).Replace("<br>", "\r\n");
\r
431 var material = (int[])entry.api_get_material;
\r
447 if (_quests.Remove(id))
\r
454 AddQuest(id, cat, name, detail, material, progress, true);
\r
458 if (_quests.Count <= AcceptMax)
\r
461 * ほかのPCで任務を達成した場合、任務が消えずに受領した任務の数が_questCountを超えることがある。
\r
462 * その場合はいったん任務をクリアして、現在のページの任務だけを登録し直す。
\r
468 private void AddQuest(int id, int category, string name, string detail, int[] material, int progress,
\r
471 var count = _countList.GetCount(id);
\r
474 if (count.AdjustCount(progress))
\r
477 _quests[id] = new QuestStatus
\r
480 Category = category,
\r
483 Material = adjustCount ? material?.Concat(count.Spec.Material).ToArray() : material,
\r
485 Progress = progress,
\r
486 Color = category <= _color.Length ? _color[category - 1] : Control.DefaultBackColor
\r
490 public void ClearQuests()
\r
495 private void ResetQuests()
\r
497 var now = _nowFunc();
\r
498 var daily = now.Date.AddHours(5);
\r
499 if (!(_lastReset < daily && daily <= now))
\r
501 RemoveQuest(QuestInterval.Daily);
\r
502 _countList.Remove(QuestInterval.Daily);
\r
503 var weekly = now.Date.AddDays(-((6 + (int)now.DayOfWeek) % 7)).AddHours(5);
\r
504 if (_lastReset < weekly && weekly <= now)
\r
506 RemoveQuest(QuestInterval.Weekly);
\r
507 _countList.Remove(QuestInterval.Weekly);
\r
509 var monthly = new DateTime(now.Year, now.Month, 1, 5, 0, 0);
\r
510 if (_lastReset < monthly && monthly <= now)
\r
512 RemoveQuest(QuestInterval.Monthly);
\r
513 _countList.Remove(QuestInterval.Monthly);
\r
515 var season = now.Month / 3;
\r
516 var quarterly = new DateTime(now.Year - (season == 0 ? 1 : 0), (season == 0 ? 12 : season * 3), 1, 5, 0, 0);
\r
517 if (_lastReset < quarterly && quarterly <= now)
\r
519 RemoveQuest(QuestInterval.Quarterly);
\r
520 _countList.Remove(QuestInterval.Quarterly);
\r
526 private void RemoveQuest(QuestInterval interval)
\r
529 (from kv in _quests where kv.Value.Count.Spec.Interval == interval select kv.Key).ToArray())
\r
530 _quests.Remove(id);
\r
534 private bool _boss;
\r
536 public void InspectMapStart(dynamic json)
\r
538 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
539 ago.Count.NowArray[0]++;
\r
540 InspectMapNext(json);
\r
543 public void InspectMapNext(dynamic json)
\r
545 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
546 _boss = (int)json.api_event_id == 5;
\r
548 if (_quests.TryGetValue(861, out var q861))
\r
550 if (_map == 16 && (int)json.api_event_id == 8)
\r
552 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
554 if (fleet.Count(s => s == 10 || s == 22) == 2)
\r
555 IncrementCount(q861.Count);
\r
560 public void InspectBattleResult(dynamic json)
\r
562 var rank = json.api_win_rank;
\r
563 foreach (var quest in _quests.Values)
\r
565 var count = quest.Count;
\r
566 switch (count.Spec)
\r
568 case QuestSortie sortie:
\r
569 if (count.Id == 216 && !_boss || sortie.Check(rank, _map, _boss))
\r
570 IncrementCount(count);
\r
572 case QuestEnemyType enemyType:
\r
573 var num = enemyType.CountResult(
\r
574 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
576 AddCount(count, num);
\r
580 if (_quests.TryGetValue(214, out var ago))
\r
582 var array = ago.Count.NowArray;
\r
586 if (QuestSortie.CompareRank(rank, "B") <= 0)
\r
596 if (_quests.TryGetValue(249, out var q249))
\r
598 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
600 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id)
\r
602 if (fleet.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
603 IncrementCount(q249.Count);
\r
606 if (_quests.TryGetValue(257, out var q257))
\r
608 if (_map == 14 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
610 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
612 if (fleet[0] == 3 && fleet.Count(s => s == 3) <= 3 && fleet.All(s => s == 2 || s == 3))
\r
613 IncrementCount(q257.Count);
\r
616 if (_quests.TryGetValue(259, out var q259))
\r
618 if (_map == 51 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
620 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec).ToArray();
\r
621 // ReSharper disable once IdentifierTypo
\r
629 if (fleet.Select(s => s.ShipClass).Count(c => ctype.Contains(c)) == 3 &&
\r
630 fleet.Count(s => s.ShipType == 3) > 0)
\r
632 IncrementCount(q259.Count);
\r
636 if (_quests.TryGetValue(264, out var q264))
\r
638 if (_map == 42 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
640 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec)
\r
642 if (fleet.Count(spec => spec.ShipType == 2) >= 2 &&
\r
643 fleet.Count(spec => spec.IsAircraftCarrier) >= 2)
\r
644 IncrementCount(q264.Count);
\r
647 if (_quests.TryGetValue(266, out var q266))
\r
649 if (_map == 25 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
651 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
653 if (fleet[0] == 2 && fleet.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
654 IncrementCount(q266.Count);
\r
657 if (_quests.TryGetValue(854, out var opz) && _boss)
\r
659 var array = opz.Count.NowArray;
\r
662 case 24 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
666 case 61 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
670 case 63 when QuestSortie.CompareRank(rank, "A") <= 0:
\r
674 case 64 when QuestSortie.CompareRank(rank, "S") <= 0:
\r
680 if (_quests.TryGetValue(862, out var q862))
\r
682 if (_map == 63 && _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
684 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.ShipType)
\r
686 if (fleet.Count(s => s == 3) >= 2 && fleet.Count(s => s == 16) >= 1)
\r
687 IncrementCount(q862.Count);
\r
690 if (_quests.TryGetValue(873, out var q873))
\r
692 if (_battleInfo.Result.Friend.Main.Count(s => s.NowHp > 0 && s.Spec.ShipType == 3) >= 1 &&
\r
693 _boss && QuestSortie.CompareRank(rank, "A") <= 0)
\r
695 var array = q873.Count.NowArray;
\r
713 if (_quests.TryGetValue(875, out var q875))
\r
715 if (_map == 54 && _boss && QuestSortie.CompareRank(rank, "S") == 0)
\r
717 var fleet = _battleInfo.Result.Friend.Main.Where(s => s.NowHp > 0).Select(s => s.Spec.Id).ToArray();
\r
718 if (fleet.Contains(543) && fleet.Intersect(new[] {344, 345, 359}).Any())
\r
719 IncrementCount(q875.Count);
\r
722 if (_quests.TryGetValue(888, out var q888))
\r
724 if (!_boss || QuestSortie.CompareRank(rank, "S") != 0)
\r
726 var fleet = from ship in _battleInfo.Result.Friend.Main where ship.NowHp > 0 select ship.Spec.Id;
\r
729 69, 272, 427, // 鳥海
\r
731 123, 295, 142, // 衣笠
\r
732 59, 262, 416, // 古鷹
\r
733 60, 263, 417, // 加古
\r
734 51, 213, 477, // 天龍
\r
737 if (fleet.Intersect(member).Count() < 4)
\r
739 var array = q888.Count.NowArray;
\r
758 private int _questFleet;
\r
760 public void StartPractice(string request)
\r
762 var values = HttpUtility.ParseQueryString(request);
\r
763 _questFleet = int.Parse(values["api_deck_id"]) - 1;
\r
766 public void InspectPracticeResult(dynamic json)
\r
768 foreach (var quest in _quests.Values)
\r
770 var count = quest.Count;
\r
771 if (!(count.Spec is QuestPractice practice))
\r
773 if (practice.Check(json.api_win_rank))
\r
774 IncrementCount(count);
\r
776 if (_quests.TryGetValue(318, out var q318))
\r
778 if (_questFleet == 0 && QuestSortie.CompareRank(json.api_win_rank, "B") <= 0 &&
\r
779 _battleInfo.Result.Friend.Main.Count(s => s.Spec.ShipType == 3) >= 2)
\r
781 IncrementCount(q318.Count);
\r
786 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
788 public void InspectDeck(dynamic json)
\r
790 foreach (var entry in json)
\r
791 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
794 public void InspectMissionResult(string request, dynamic json)
\r
796 var values = HttpUtility.ParseQueryString(request);
\r
797 var deck = int.Parse(values["api_deck_id"]);
\r
798 if ((int)json.api_clear_result == 0)
\r
800 var mid = _missionId[deck - 1];
\r
801 foreach (var quest in _quests.Values)
\r
803 var count = quest.Count;
\r
804 if (!(count.Spec is QuestMission mission))
\r
806 if (mission.Check(mid))
\r
807 IncrementCount(count);
\r
809 if (_quests.TryGetValue(426, out var q426))
\r
811 var count = q426.Count;
\r
815 count.NowArray[0]++;
\r
818 count.NowArray[1]++;
\r
821 count.NowArray[2]++;
\r
824 count.NowArray[3]++;
\r
828 if (_quests.TryGetValue(428, out var q428))
\r
830 var count = q428.Count;
\r
834 count.NowArray[0]++;
\r
837 count.NowArray[1]++;
\r
840 count.NowArray[2]++;
\r
846 private void IncrementCount(QuestCount count)
\r
852 private void AddCount(QuestCount count, int value)
\r
854 count.Now += value;
\r
858 private void IncrementCount(int id)
\r
863 private void AddCount(int id, int value)
\r
865 if (_quests.TryGetValue(id, out var quest))
\r
867 quest.Count.Now += value;
\r
872 public void CountNyukyo() => IncrementCount(503);
\r
874 public void CountCharge() => IncrementCount(504);
\r
876 public void CountCreateItem()
\r
878 IncrementCount(605);
\r
879 IncrementCount(607);
\r
882 public void CountCreateShip()
\r
884 IncrementCount(606);
\r
885 IncrementCount(608);
\r
888 public void InspectDestroyShip(string request)
\r
890 AddCount(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
893 public void CountRemodelSlot() => IncrementCount(619);
\r
895 public void InspectDestroyItem(string request, dynamic json)
\r
897 var values = HttpUtility.ParseQueryString(request);
\r
898 var items = values["api_slotitem_ids"].Split(',')
\r
899 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();
\r
900 IncrementCount(613); // 613: 資源の再利用
\r
901 foreach (var quest in _quests.Values)
\r
903 var count = quest.Count;
\r
904 if (!(count.Spec is QuestDestroyItem destroy))
\r
906 if (destroy.Count(count, items))
\r
909 if (_quests.TryGetValue(680, out var q680))
\r
911 q680.Count.NowArray[0] += items.Count(spec => spec.Type == 21);
\r
912 q680.Count.NowArray[1] += items.Count(spec => spec.Type == 12 || spec.Type == 13);
\r
917 public void InspectPowerUp(dynamic json)
\r
919 if ((int)json.api_powerup_flag == 0)
\r
921 foreach (var quest in _quests.Values)
\r
923 var count = quest.Count;
\r
924 if (!(count.Spec is QuestPowerUp))
\r
926 IncrementCount(count);
\r
930 public void InspectStop(string request)
\r
932 var values = HttpUtility.ParseQueryString(request);
\r
933 _quests.Remove(int.Parse(values["api_quest_id"]));
\r
937 public void InspectClearItemGet(string request)
\r
939 var values = HttpUtility.ParseQueryString(request);
\r
940 var id = int.Parse(values["api_quest_id"]);
\r
941 _countList.Remove(id);
\r
942 _quests.Remove(id);
\r
946 public bool NeedSave { get; private set; }
\r
948 public void SaveState(Status status)
\r
951 status.QuestLastReset = _lastReset;
\r
952 if (_quests != null)
\r
953 status.QuestList = _quests.Values.ToArray();
\r
954 if (_countList != null)
\r
955 status.QuestCountList = _countList.CountList.ToArray();
\r
958 public void LoadState(Status status)
\r
960 _lastReset = status.QuestLastReset;
\r
961 if (status.QuestCountList != null)
\r
962 _countList.CountList = status.QuestCountList;
\r
963 if (status.QuestList != null)
\r
966 foreach (var q in status.QuestList)
\r
967 AddQuest(q.Id, q.Category, q.Name, q.Detail, q.Material, q.Progress, false);
\r