1 // Copyright (C) 2019 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
18 using System.Xml.Serialization;
\r
19 using KancolleSniffer.Util;
\r
21 namespace KancolleSniffer.Model
\r
23 public class QuestCount
\r
25 public int Id { get; set; }
\r
26 public int Now { get; set; }
\r
27 public int[] NowArray { get; set; }
\r
30 public QuestSpec Spec { get; set; }
\r
32 public bool AdjustCount(int progress)
\r
34 if (!Spec.AdjustCount)
\r
36 if (NowArray != null)
\r
38 if (progress != 100)
\r
40 NowArray = NowArray.Zip(Spec.MaxArray, Math.Max).ToArray();
\r
59 var now = Now + Spec.Shift;
\r
60 var max = Spec.Max + Spec.Shift;
\r
61 var low = (int)Math.Ceiling(max * progress / 100.0);
\r
62 if (low >= max && progress != 100)
\r
64 var high = (int)Math.Ceiling(max * next / 100.0);
\r
67 Now = low - Spec.Shift;
\r
72 Now = high - 1 - Spec.Shift;
\r
78 public override string ToString()
\r
80 if (Id == 280 || Id == 426 || Id == 854 || Id == 872 || Id == 873 || Id == 888 || Id == 894)
\r
81 return $"{NowArray.Count(n => n >= 1)}/{Spec.MaxArray.Length}";
\r
82 return NowArray != null
\r
83 ? string.Join(" ", NowArray.Zip(Spec.MaxArray, (n, m) => $"{n}/{m}"))
\r
84 : $"{Now}/{Spec.Max}";
\r
87 public QuestCount Clone()
\r
89 var clone = (QuestCount)MemberwiseClone();
\r
90 if (NowArray != null)
\r
91 clone.NowArray = (int[])NowArray.Clone();
\r
95 public bool Equals(QuestCount other)
\r
99 if (NowArray == null)
\r
100 return Now == other.Now;
\r
101 return NowArray.SequenceEqual(other.NowArray);
\r
104 public string ToToolTip()
\r
109 return string.Join(" ",
\r
110 new[] {"1-2", "1-3", "1-4", "2-1"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
111 .Where(s => !string.IsNullOrEmpty(s)));
\r
113 return string.Join(" ",
\r
114 new[] {"警備任務", "対潜警戒任務", "海上護衛任務", "強硬偵察任務"}
\r
115 .Zip(NowArray, (mission, n) => n >= 1 ? mission : "")
\r
116 .Where(s => !string.IsNullOrEmpty(s)));
\r
118 return string.Join(" ",
\r
119 new[] {"対潜警戒任務", "海峡警備行動", "長時間対潜警戒"}.Zip(NowArray, (mission, n) => n >= 1 ? mission + n : "")
\r
120 .Where(s => !string.IsNullOrEmpty(s)));
\r
122 return string.Join(" ",
\r
123 new[] {"2-4", "6-1", "6-3", "6-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
124 .Where(s => !string.IsNullOrEmpty(s)));
\r
126 return string.Join(" ",
\r
127 new[] {"7-2M", "5-5", "6-2", "6-5"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
128 .Where(s => !string.IsNullOrEmpty(s)));
\r
130 return string.Join(" ",
\r
131 new[] {"3-1", "3-2", "3-3"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
132 .Where(s => !string.IsNullOrEmpty(s)));
\r
134 return string.Join(" ",
\r
135 new[] {"5-1", "5-3", "5-4"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
136 .Where(s => !string.IsNullOrEmpty(s)));
\r
138 return string.Join(" ",
\r
139 new[] {"艦戦", "艦爆", "艦攻", "水偵"}.Zip(NowArray, (type, n) => n >= 1 ? type + n : "")
\r
140 .Where(s => !string.IsNullOrEmpty(s)));
\r
142 return string.Join(" ",
\r
143 new[] {"1-5", "7-1", "7-2G", "7-2M"}.Zip(NowArray, (map, n) => n >= 1 ? $"{map}:{n}" : "")
\r
144 .Where(s => !string.IsNullOrEmpty(s)));
\r
146 return string.Join(" ",
\r
147 new[] {"1-3", "1-4", "2-1", "2-2", "2-3"}.Zip(NowArray, (map, n) => n >= 1 ? map : "")
\r
148 .Where(s => !string.IsNullOrEmpty(s)));
\r
153 public bool Cleared => NowArray?.Zip(Spec.MaxArray, (n, m) => n >= m).All(x => x) ??
\r
154 Spec.Max != 0 && Now >= Spec.Max;
\r
157 public class QuestCounter
\r
159 private readonly QuestInfo _questInfo;
\r
160 private readonly ItemInfo _itemInfo;
\r
161 private readonly BattleInfo _battleInfo;
\r
162 private readonly SortedDictionary<int, QuestStatus> _quests;
\r
165 private bool _boss;
\r
167 private class ResultShipSpecs
\r
169 public ResultShipSpecs(BattleInfo battleInfo)
\r
171 Specs = battleInfo.Result?.Friend.Main.Where(s => s.NowHp > 0).Select(ship => ship.Spec).ToArray() ??
\r
173 Ids = Specs.Select(spec => spec.Id).ToArray();
\r
174 Types = Specs.Select(spec => spec.ShipType).ToArray();
\r
175 Classes = Specs.Select(spec => spec.ShipClass).ToArray();
\r
176 Flagship = Specs.FirstOrDefault();
\r
177 FlagshipType = Types.FirstOrDefault();
\r
180 public ShipSpec[] Specs { get; }
\r
181 public int[] Ids { get; }
\r
182 public int[] Types { get; }
\r
183 public int[] Classes { get; }
\r
184 public ShipSpec Flagship { get; }
\r
185 public int FlagshipType { get; }
\r
188 public QuestCounter(QuestInfo questInfo, ItemInfo itemInfo, BattleInfo battleInfo)
\r
190 _questInfo = questInfo;
\r
191 _quests = questInfo.QuestDictionary;
\r
192 _itemInfo = itemInfo;
\r
193 _battleInfo = battleInfo;
\r
196 private bool NeedSave
\r
198 set => _questInfo.NeedSave = value;
\r
201 public void InspectMapStart(dynamic json)
\r
203 if (_quests.TryGetValue(214, out var ago)) // あ号
\r
204 ago.Count.NowArray[0]++;
\r
205 InspectMapNext(json);
\r
208 public void InspectMapNext(dynamic json)
\r
210 _map = (int)json.api_maparea_id * 10 + (int)json.api_mapinfo_no;
\r
211 _cell = json.api_no() ? (int)json.api_no : 0;
\r
212 _boss = (int)json.api_event_id == 5;
\r
214 if (_quests.TryGetValue(861, out var q861) && _map == 16 && (int)json.api_event_id == 8)
\r
216 if (new ResultShipSpecs(_battleInfo).Types.Count(s => s == 10 || s == 22) == 2)
\r
217 Increment(q861.Count);
\r
223 private readonly string _rank;
\r
225 public Rank(string rank)
\r
230 public bool S => QuestSortie.CompareRank(_rank, "S") == 0;
\r
231 public bool A => QuestSortie.CompareRank(_rank, "A") <= 0;
\r
232 public bool B => QuestSortie.CompareRank(_rank, "B") <= 0;
\r
235 public void InspectBattleResult(dynamic json)
\r
237 var rawRak = json.api_win_rank;
\r
238 var rank = new Rank(rawRak);
\r
239 var specs = new ResultShipSpecs(_battleInfo);
\r
240 foreach (var quest in _quests.Values)
\r
242 var count = quest.Count;
\r
243 switch (count.Spec)
\r
245 case QuestSortie sortie:
\r
246 if (count.Id == 216 && !_boss || sortie.Check(rawRak, _map, _boss))
\r
249 case QuestEnemyType enemyType:
\r
250 var num = enemyType.CountResult(
\r
251 _battleInfo.Result.Enemy.Main.Concat(_battleInfo.Result.Enemy.Guard));
\r
260 IncrementNth(count, 1);
\r
263 IncrementNth(count, 2);
\r
265 IncrementNth(count, 3);
\r
269 if (_map == 25 && _boss && rank.S &&
\r
270 specs.Ids.Intersect(new[] {62, 63, 64, 265, 266, 268, 319, 192, 194}).Count() == 3)
\r
276 if (_map == 14 && _boss && rank.S &&
\r
277 specs.FlagshipType == 3 &&
\r
278 specs.Types.Count(s => s == 3) <= 3 &&
\r
279 specs.Types.All(s => s == 2 || s == 3))
\r
285 if (_map == 51 && _boss && rank.S &&
\r
286 specs.Types.Count(type => type == 3) > 0 &&
\r
287 specs.Classes.Count(c => new[]
\r
293 }.Contains(c)) == 3)
\r
299 if (_map == 42 && _boss && rank.S &&
\r
300 specs.Types.Count(type => type == 2) >= 2 &&
\r
301 specs.Specs.Count(spec => spec.IsAircraftCarrier) >= 2)
\r
307 if (_map == 25 && _boss && rank.S &&
\r
308 specs.FlagshipType == 2 &&
\r
309 specs.Types.OrderBy(x => x).SequenceEqual(new[] {2, 2, 2, 2, 3, 5}))
\r
315 if (_boss && rank.S &&
\r
316 specs.Types.Count(type => type == 1 || type == 2) >= 3 &&
\r
317 specs.Types.Intersect(new[] {3, 4, 7, 21}).Any())
\r
322 IncrementNth(count, 0);
\r
325 IncrementNth(count, 1);
\r
328 IncrementNth(count, 2);
\r
331 IncrementNth(count, 3);
\r
341 case 24 when rank.A:
\r
342 IncrementNth(count, 0);
\r
344 case 61 when rank.A:
\r
345 IncrementNth(count, 1);
\r
347 case 63 when rank.A:
\r
348 IncrementNth(count, 2);
\r
350 case 64 when rank.S:
\r
351 IncrementNth(count, 3);
\r
357 if (_map == 63 && _boss && rank.A &&
\r
358 specs.Types.Count(s => s == 3) >= 2 &&
\r
359 specs.Types.Count(s => s == 16) >= 1)
\r
365 if (_boss && rank.S)
\r
371 IncrementNth(count, 0);
\r
374 IncrementNth(count, 1);
\r
377 IncrementNth(count, 2);
\r
380 IncrementNth(count, 3);
\r
386 if (_boss && rank.A &&
\r
387 specs.Types.Count(type => type == 3) >= 1)
\r
392 IncrementNth(count, 0);
\r
395 IncrementNth(count, 1);
\r
398 IncrementNth(count, 2);
\r
404 if (_map == 54 && _boss && rank.S &&
\r
405 specs.Ids.Contains(543) &&
\r
406 specs.Ids.Intersect(new[] {344, 345, 359}).Any())
\r
412 if (_boss && rank.S &&
\r
413 specs.Ids.Intersect(new[]
\r
415 69, 272, 427, // 鳥海
\r
417 123, 295, 142, // 衣笠
\r
418 59, 262, 416, // 古鷹
\r
419 60, 263, 417, // 加古
\r
420 51, 213, 477, // 天龍
\r
427 IncrementNth(count, 0);
\r
430 IncrementNth(count, 1);
\r
433 IncrementNth(count, 2);
\r
439 if (_boss && rank.S)
\r
444 IncrementNth(count, 0);
\r
447 IncrementNth(count, 1);
\r
452 IncrementNth(count, 2);
\r
455 IncrementNth(count, 3);
\r
462 if (_boss && rank.S &&
\r
463 specs.Specs.Any(spec => spec.IsAircraftCarrier))
\r
468 IncrementNth(count, 0);
\r
471 IncrementNth(count, 1);
\r
474 IncrementNth(count, 2);
\r
477 IncrementNth(count, 3);
\r
480 IncrementNth(count, 4);
\r
489 private int _questFleet;
\r
491 public void StartPractice(string request)
\r
493 var values = HttpUtility.ParseQueryString(request);
\r
494 _questFleet = int.Parse(values["api_deck_id"]) - 1;
\r
497 public void InspectPracticeResult(dynamic json)
\r
499 var rank = new Rank(json.api_win_rank);
\r
500 var specs = new ResultShipSpecs(_battleInfo);
\r
501 foreach (var quest in _quests.Values)
\r
503 var count = quest.Count;
\r
504 if (count.Spec is QuestPractice practice)
\r
506 if (practice.Check(json.api_win_rank))
\r
513 if (_questFleet == 0 && rank.B &&
\r
514 specs.Types.Count(type => type == 3) >= 2)
\r
521 specs.Flagship.IsAircraftCarrier &&
\r
522 specs.Specs.Count(spec => spec.IsAircraftCarrier) >= 2 &&
\r
523 specs.Types.Count(type => type == 2) >= 2)
\r
532 private readonly int[] _missionId = new int[ShipInfo.FleetCount];
\r
534 public void InspectDeck(dynamic json)
\r
536 foreach (var entry in json)
\r
537 _missionId[(int)entry.api_id - 1] = (int)entry.api_mission[1];
\r
540 public void InspectMissionResult(string request, dynamic json)
\r
542 var values = HttpUtility.ParseQueryString(request);
\r
543 var deck = int.Parse(values["api_deck_id"]);
\r
544 if ((int)json.api_clear_result == 0)
\r
546 var mid = _missionId[deck - 1];
\r
547 foreach (var quest in _quests.Values)
\r
549 var count = quest.Count;
\r
550 if (count.Spec is QuestMission mission)
\r
552 if (mission.Check(mid))
\r
562 IncrementNth(count, 0);
\r
565 IncrementNth(count, 1);
\r
568 IncrementNth(count, 2);
\r
571 IncrementNth(count, 3);
\r
579 IncrementNth(count, 0);
\r
582 IncrementNth(count, 1);
\r
585 IncrementNth(count, 2);
\r
593 public void CountNyukyo() => Increment(503);
\r
595 public void CountCharge() => Increment(504);
\r
597 public void InspectCreateItem(string request)
\r
599 var values = HttpUtility.ParseQueryString(request);
\r
600 var count = values["api_multiple_flag"] == "1" ? 3 : 1;
\r
605 public void CountCreateShip()
\r
611 public void InspectDestroyShip(string request)
\r
613 Add(609, HttpUtility.ParseQueryString(request)["api_ship_id"].Split(',').Length);
\r
616 public void CountRemodelSlot() => Increment(619);
\r
618 public void InspectDestroyItem(string request, dynamic json)
\r
620 var values = HttpUtility.ParseQueryString(request);
\r
621 var items = values["api_slotitem_ids"].Split(',')
\r
622 .Select(id => _itemInfo.GetStatus(int.Parse(id)).Spec).ToArray();
\r
623 Increment(613); // 613: 資源の再利用
\r
624 foreach (var quest in _quests.Values)
\r
626 var count = quest.Count;
\r
627 if (count.Spec is QuestDestroyItem destroy)
\r
629 if (destroy.Count(count, items))
\r
633 if (quest.Id == 680)
\r
635 count.NowArray[0] += items.Count(spec => spec.Type == 21);
\r
636 count.NowArray[1] += items.Count(spec => spec.Type == 12 || spec.Type == 13);
\r
642 public void InspectPowerUp(dynamic json)
\r
644 if ((int)json.api_powerup_flag == 0)
\r
646 foreach (var quest in _quests.Values)
\r
648 var count = quest.Count;
\r
649 if (!(count.Spec is QuestPowerUp))
\r
655 public void Increment(QuestCount count)
\r
660 public void Add(QuestCount count, int value)
\r
662 count.Now += value;
\r
666 public void Increment(int id)
\r
671 public void Add(int id, int value)
\r
673 if (!_quests.TryGetValue(id, out var quest))
\r
675 quest.Count.Now += value;
\r
679 public void IncrementNth(QuestCount count, int n)
\r
681 count.NowArray[n]++;
\r