OSDN Git Service

ItemInfoの抽象度を上げる
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Collections.Generic;\r
20 using System.Linq;\r
21 \r
22 namespace KancolleSniffer\r
23 {\r
24     public class Sniffer\r
25     {\r
26         private bool _start;\r
27         private readonly ItemInfo _itemInfo = new ItemInfo();\r
28         private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
29         private readonly QuestInfo _questInfo = new QuestInfo();\r
30         private readonly MissionInfo _missionInfo = new MissionInfo();\r
31         private readonly ShipInfo _shipInfo;\r
32         private readonly ConditionTimer _conditionTimer;\r
33         private readonly DockInfo _dockInfo;\r
34         private readonly AkashiTimer _akashiTimer;\r
35         private readonly Achievement _achievement = new Achievement();\r
36         private readonly BattleInfo _battleInfo;\r
37         private readonly Logger _logger;\r
38         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
39         private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
40         private readonly Status _status = new Status();\r
41         private bool _saveState;\r
42         private readonly List<IHaveState> _haveState;\r
43 \r
44         [Flags]\r
45         public enum Update\r
46         {\r
47             None = 0,\r
48             Error = 1 << 0,\r
49             Start = 1 << 1,\r
50             Item = 1 << 2,\r
51             Ship = 1 << 3,\r
52             Timer = 1 << 4,\r
53             NDock = 1 << 5,\r
54             Mission = 1 << 6,\r
55             QuestList = 1 << 7,\r
56             Battle = 1 << 8,\r
57             All = (1 << 9) - 1\r
58         }\r
59 \r
60         public Sniffer()\r
61         {\r
62             _shipInfo = new ShipInfo(_itemInfo);\r
63             _conditionTimer = new ConditionTimer(_shipInfo);\r
64             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
65             _akashiTimer = new AkashiTimer(_shipInfo, _itemInfo, _dockInfo);\r
66             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
67             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
68             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
69         }\r
70 \r
71         private void SaveState()\r
72         {\r
73             if (!_saveState)\r
74                 return;\r
75             if (!_haveState.Any(x => x.NeedSave))\r
76                 return;\r
77             foreach (var x in _haveState)\r
78                 x.SaveState(_status);\r
79             _status.Save();\r
80         }\r
81 \r
82         public void LoadState()\r
83         {\r
84             _status.Load();\r
85             foreach (var x in _haveState)\r
86                 x.LoadState(_status);\r
87             _saveState = true;\r
88         }\r
89 \r
90         public Update Sniff(string url, string request, dynamic json)\r
91         {\r
92             if (!json.api_result() || (int)json.api_result != 1)\r
93                 return Update.Error;\r
94             var data = json.api_data() ? json.api_data : new object();\r
95 \r
96             if (url.EndsWith("api_start2"))\r
97             {\r
98                 _shipInfo.InspectMaster(data);\r
99                 _missionInfo.InspectMaster(data.api_mst_mission);\r
100                 _itemInfo.InspectMaster(data);\r
101                 _exMapInfo.ResetIfNeeded();\r
102                 _start = true;\r
103                 return Update.Start;\r
104             }\r
105             if (!_start)\r
106                 return Update.None;\r
107             if (url.EndsWith("api_port/port"))\r
108             {\r
109                 _itemInfo.InspectBasic(data.api_basic);\r
110                 _materialInfo.InspectMaterial(data.api_material, true);\r
111                 _logger.InspectBasic(data.api_basic);\r
112                 _logger.InspectMaterial(data.api_material);\r
113                 _shipInfo.InspectShip(data);\r
114                 _conditionTimer.CalcRegenTime();\r
115                 _missionInfo.InspectDeck(data.api_deck_port);\r
116                 _dockInfo.InspectNDock(data.api_ndock);\r
117                 _akashiTimer.SetTimer(true);\r
118                 _achievement.InspectBasic(data.api_basic);\r
119                 if (data.api_parallel_quest_count()) // 昔のログにはないので\r
120                     _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
121                 _battleInfo.CleanupResult();\r
122                 _battleInfo.InBattle = false;\r
123                 _battleInfo.HasDamagedShip = false;\r
124                 _shipInfo.ClearEscapedShips();\r
125                 _miscTextInfo.ClearIfNeeded();\r
126                 SaveState();\r
127                 return Update.All;\r
128             }\r
129             if (url.EndsWith("api_get_member/basic"))\r
130             {\r
131                 _itemInfo.InspectBasic(data);\r
132                 _logger.InspectBasic(data);\r
133                 return Update.None;\r
134             }\r
135             if (url.EndsWith("api_get_member/slot_item"))\r
136             {\r
137                 _itemInfo.InspectSlotItem(data, true);\r
138                 return Update.None;\r
139             }\r
140             if (url.EndsWith("api_get_member/kdock"))\r
141             {\r
142                 _dockInfo.InspectKDock(data);\r
143                 _logger.InspectKDock(data);\r
144                 return Update.Timer;\r
145             }\r
146             if (url.EndsWith("api_get_member/ndock"))\r
147             {\r
148                 _dockInfo.InspectNDock(data);\r
149                 _conditionTimer.CheckCond();\r
150                 _akashiTimer.SetTimer();\r
151                 return Update.NDock | Update.Timer | Update.Ship;\r
152             }\r
153             if (url.EndsWith("api_req_hensei/change"))\r
154             {\r
155                 _shipInfo.InspectChange(request);\r
156                 _akashiTimer.SetTimer();\r
157                 return Update.Ship;\r
158             }\r
159             if (url.EndsWith("api_get_member/questlist"))\r
160             {\r
161                 _questInfo.Inspect(data);\r
162                 return Update.QuestList;\r
163             }\r
164             if (url.EndsWith("api_get_member/deck"))\r
165             {\r
166                 _shipInfo.InspectDeck(data);\r
167                 _missionInfo.InspectDeck(data);\r
168                 _akashiTimer.SetTimer();\r
169                 return Update.Mission | Update.Timer;\r
170             }\r
171             if (url.EndsWith("api_get_member/ship2"))\r
172             {\r
173                 // ここだけjsonなので注意\r
174                 _shipInfo.InspectShip(json);\r
175                 _akashiTimer.SetTimer();\r
176                 _battleInfo.InBattle = false;\r
177                 return Update.Item | Update.Ship | Update.Battle;\r
178             }\r
179             if (url.EndsWith("api_get_member/ship_deck"))\r
180             {\r
181                 _shipInfo.InspectShip(data);\r
182                 _akashiTimer.SetTimer();\r
183                 _battleInfo.InBattle = false;\r
184                 return Update.Ship | Update.Battle;\r
185             }\r
186             if (url.EndsWith("api_get_member/ship3"))\r
187             {\r
188                 _shipInfo.InspectShip(data);\r
189                 _akashiTimer.SetTimer();\r
190                 _conditionTimer.CheckCond();\r
191                 return Update.Ship;\r
192             }\r
193             if (url.EndsWith("api_get_member/material"))\r
194             {\r
195                 _materialInfo.InspectMaterial(data);\r
196                 return Update.Item;\r
197             }\r
198             if (url.EndsWith("api_req_hokyu/charge"))\r
199             {\r
200                 _shipInfo.InspectCharge(data);\r
201                 _materialInfo.InspectCharge(data);\r
202                 return Update.Item | Update.Ship;\r
203             }\r
204             if (url.EndsWith("api_req_kousyou/createitem"))\r
205             {\r
206                 _itemInfo.InspectCreateItem(data);\r
207                 _materialInfo.InspectCreateIem(data);\r
208                 _logger.InspectCreateItem(request, data);\r
209                 return Update.Item;\r
210             }\r
211             if (url.EndsWith("api_req_kousyou/getship"))\r
212             {\r
213                 _itemInfo.InspectGetShip(data);\r
214                 _shipInfo.InspectShip(data);\r
215                 _dockInfo.InspectKDock(data.api_kdock);\r
216                 _conditionTimer.CheckCond();\r
217                 return Update.Item | Update.Timer;\r
218             }\r
219             if (url.EndsWith("api_req_kousyou/destroyship"))\r
220             {\r
221                 _shipInfo.InspectDestroyShip(request, data);\r
222                 _materialInfo.InspectDestroyShip(data);\r
223                 _conditionTimer.CheckCond();\r
224                 _akashiTimer.SetTimer();\r
225                 return Update.Item | Update.Ship;\r
226             }\r
227             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
228             {\r
229                 _itemInfo.InspectDestroyItem(request, data);\r
230                 _materialInfo.InspectDestroyItem(data);\r
231                 return Update.Item;\r
232             }\r
233             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
234             {\r
235                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
236                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
237                 _itemInfo.InspectRemodelSlot(data);\r
238                 _materialInfo.InspectRemodelSlot(data);\r
239                 return Update.Item;\r
240             }\r
241             if (url.EndsWith("api_req_kousyou/createship"))\r
242             {\r
243                 _logger.InspectCreateShip(request);\r
244                 return Update.None;\r
245             }\r
246             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
247             {\r
248                 _dockInfo.InspectCreateShipSpeedChange(request);\r
249                 return Update.Timer;\r
250             }\r
251             if (url.EndsWith("api_req_kaisou/powerup"))\r
252             {\r
253                 _shipInfo.InspectPowerup(request, data);\r
254                 _conditionTimer.CheckCond();\r
255                 _akashiTimer.SetTimer();\r
256                 return Update.Item | Update.Ship;\r
257             }\r
258             if (url.EndsWith("api_req_nyukyo/start"))\r
259             {\r
260                 _dockInfo.InspectNyukyo(request);\r
261                 _conditionTimer.CheckCond();\r
262                 _akashiTimer.SetTimer();\r
263                 return Update.Item | Update.Ship;\r
264             }\r
265             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
266             {\r
267                 _dockInfo.InspectSpeedChange(request);\r
268                 _conditionTimer.CheckCond();\r
269                 return Update.NDock | Update.Timer | Update.Ship;\r
270             }\r
271             if (IsNormalBattleAPI(url))\r
272             {\r
273                 _battleInfo.InspectBattle(data);\r
274                 _logger.InspectBattle(data);\r
275                 return Update.Ship | Update.Battle;\r
276             }\r
277             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
278             {\r
279                 if (url.EndsWith("/battle"))\r
280                 {\r
281                     _shipInfo.StartSortie(request); // 演習を出撃中とみなす\r
282                     _conditionTimer.InvalidateCond();\r
283                     _miscTextInfo.ClearFlag = true;\r
284                 }\r
285                 _battleInfo.InspectBattle(data);\r
286                 return Update.Ship | Update.Battle | Update.Timer;\r
287             }\r
288             if (url.EndsWith("api_req_sortie/battleresult"))\r
289             {\r
290                 _battleInfo.InspectBattleResult(json);\r
291                 _exMapInfo.InspectBattleResult(data);\r
292                 _logger.InspectBattleResult(data);\r
293                 return Update.Ship;\r
294             }\r
295             if (url.EndsWith("api_req_practice/battle_result"))\r
296             {\r
297                 _battleInfo.InspectPracticeResult(json);\r
298                 return Update.Ship;\r
299             }\r
300             if (IsCombinedBattleAPI(url))\r
301             {\r
302                 _battleInfo.InspectCombinedBattle(data, url.EndsWith("battle_water"));\r
303                 _logger.InspectBattle(data);\r
304                 return Update.Ship | Update.Battle;\r
305             }\r
306             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
307             {\r
308                 _battleInfo.InspectCombinedBattleResult(data);\r
309                 _logger.InspectBattleResult(data);\r
310                 return Update.Ship;\r
311             }\r
312             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
313             {\r
314                 _battleInfo.CauseCombinedBattleEscape();\r
315                 return Update.Ship;\r
316             }\r
317             if (url.EndsWith("api_req_map/start"))\r
318             {\r
319                 _shipInfo.StartSortie(request);\r
320                 _conditionTimer.InvalidateCond();\r
321                 _exMapInfo.InspectMapStart(data);\r
322                 _logger.InspectMapStart(data);\r
323                 _miscTextInfo.ClearFlag = true;\r
324                 return Update.Timer;\r
325             }\r
326             if (url.EndsWith("api_req_map/next"))\r
327             {\r
328                 _exMapInfo.InspectMapNext(data);\r
329                 _logger.InspectMapNext(data);\r
330                 return Update.None;\r
331             }\r
332             if (url.EndsWith("api_req_mission/result"))\r
333             {\r
334                 _materialInfo.InspectMissionResult(data);\r
335                 _logger.InspectMissionResult(data);\r
336                 return Update.Item;\r
337             }\r
338             if (url.EndsWith("api_get_member/mapinfo"))\r
339             {\r
340                 _exMapInfo.InspectMapInfo(data);\r
341                 _miscTextInfo.InspectMapInfo(data);\r
342                 return Update.Item;\r
343             }\r
344             if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
345             {\r
346                 _miscTextInfo.InspectPracticeEnemyInfo(data);\r
347                 return Update.Item;\r
348             }\r
349             return Update.None;\r
350         }\r
351 \r
352         private bool IsNormalBattleAPI(string url)\r
353         {\r
354             return url.EndsWith("api_req_sortie/battle") ||\r
355                    url.EndsWith("api_req_sortie/airbattle") ||\r
356                    url.EndsWith("api_req_battle_midnight/battle") ||\r
357                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
358         }\r
359 \r
360         private bool IsCombinedBattleAPI(string url)\r
361         {\r
362             return url.EndsWith("api_req_combined_battle/battle") ||\r
363                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
364                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
365                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
366                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
367         }\r
368 \r
369         public NameAndTimer[] NDock => _dockInfo.NDock;\r
370 \r
371         public RingTimer[] KDock => _dockInfo.KDock;\r
372 \r
373         public ItemInfo Item => _itemInfo;\r
374 \r
375         public MaterialInfo Material => _materialInfo;\r
376 \r
377         public QuestStatus[] Quests => _questInfo.Quests;\r
378 \r
379         public NameAndTimer[] Missions => _missionInfo.Missions;\r
380 \r
381         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
382 \r
383         public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
384 \r
385         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
386 \r
387         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
388 \r
389         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
390 \r
391         public int GetFighterPower(int fleet, bool withBonus) => _shipInfo.GetFighterPower(fleet, withBonus);\r
392 \r
393         public double GetFleetLineOfSights(int fleet) => _shipInfo.GetLineOfSights(fleet);\r
394 \r
395         public ShipStatus[] DamagedShipList => _shipInfo.GetDamagedShipList(_dockInfo);\r
396 \r
397         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
398 \r
399         public ItemStatus[] ItemList => _itemInfo.GetItemListWithOwner(ShipList);\r
400 \r
401         public AkashiTimer.RepairSpan[] GetAkashiTimers(int fleet) => _akashiTimer.GetTimers(fleet);\r
402 \r
403         public AkashiTimer.Notice[] GetAkashiTimerNotice() => _akashiTimer.GetNotice();\r
404 \r
405         public Achievement Achievement => _achievement;\r
406 \r
407         public BattleInfo Battle => _battleInfo;\r
408 \r
409         public ExMapInfo ExMap => _exMapInfo;\r
410 \r
411         public string MiscText => _miscTextInfo.Text;\r
412 \r
413         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
414         {\r
415             _logger.SetWriter(writer, nowFunc);\r
416         }\r
417 \r
418         public void SkipMaster()\r
419         {\r
420             _start = true;\r
421         }\r
422 \r
423         public void EnableLog(LogType type)\r
424         {\r
425             _logger.EnableLog(type);\r
426         }\r
427 \r
428         public int MaterialLogInterval\r
429         {\r
430             set { _logger.MaterialLogInterval = value; }\r
431         }\r
432 \r
433         public string LogOutputDir\r
434         {\r
435             set { _logger.OutputDir = value; }\r
436         }\r
437     }\r
438 \r
439     public class NameAndTimer\r
440     {\r
441         public string Name { get; set; }\r
442         public RingTimer Timer { get; set; }\r
443 \r
444         public NameAndTimer()\r
445         {\r
446             Timer = new RingTimer();\r
447         }\r
448     }\r
449 \r
450     public class RingTimer\r
451     {\r
452         private DateTime _endTime;\r
453         private readonly TimeSpan _spare;\r
454 \r
455         public TimeSpan Rest { get; private set; }\r
456 \r
457         public bool IsFinished => _endTime != DateTime.MinValue && Rest <= _spare;\r
458 \r
459         public bool NeedRing { get; set; }\r
460 \r
461         public RingTimer(int spare = 60)\r
462         {\r
463             _spare = TimeSpan.FromSeconds(spare);\r
464         }\r
465 \r
466         public void SetEndTime(double time)\r
467         {\r
468             SetEndTime((int)time == 0\r
469                 ? DateTime.MinValue\r
470                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
471         }\r
472 \r
473         public void SetEndTime(DateTime time)\r
474         {\r
475             _endTime = time;\r
476         }\r
477 \r
478         public void Update()\r
479         {\r
480             if (_endTime == DateTime.MinValue)\r
481             {\r
482                 Rest = TimeSpan.Zero;\r
483                 return;\r
484             }\r
485             var prev = Rest;\r
486             Rest = _endTime - DateTime.Now;\r
487             if (Rest < TimeSpan.Zero)\r
488                 Rest = TimeSpan.Zero;\r
489             if (prev > _spare && _spare >= Rest)\r
490                 NeedRing = true;\r
491         }\r
492     }\r
493 }