OSDN Git Service

基地航空隊の配備のコストを資材の変動に反映する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \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
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\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
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 \r
19 namespace KancolleSniffer\r
20 {\r
21     public class Sniffer\r
22     {\r
23         private bool _start;\r
24         private readonly ItemInfo _itemInfo = new ItemInfo();\r
25         private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
26         private readonly QuestInfo _questInfo = new QuestInfo();\r
27         private readonly MissionInfo _missionInfo = new MissionInfo();\r
28         private readonly ShipInfo _shipInfo;\r
29         private readonly ConditionTimer _conditionTimer;\r
30         private readonly DockInfo _dockInfo;\r
31         private readonly AkashiTimer _akashiTimer;\r
32         private readonly Achievement _achievement = new Achievement();\r
33         private readonly BattleInfo _battleInfo;\r
34         private readonly Logger _logger;\r
35         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
36         private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
37         private readonly Status _status = new Status();\r
38         private bool _saveState;\r
39         private readonly List<IHaveState> _haveState;\r
40 \r
41         [Flags]\r
42         public enum Update\r
43         {\r
44             None = 0,\r
45             Error = 1 << 0,\r
46             Start = 1 << 1,\r
47             Item = 1 << 2,\r
48             Ship = 1 << 3,\r
49             Timer = 1 << 4,\r
50             NDock = 1 << 5,\r
51             Mission = 1 << 6,\r
52             QuestList = 1 << 7,\r
53             Battle = 1 << 8,\r
54             All = (1 << 9) - 1\r
55         }\r
56 \r
57         public Sniffer()\r
58         {\r
59             _shipInfo = new ShipInfo(_itemInfo);\r
60             _conditionTimer = new ConditionTimer(_shipInfo);\r
61             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
62             _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo);\r
63             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
64             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
65             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
66         }\r
67 \r
68         private void SaveState()\r
69         {\r
70             if (!_saveState)\r
71                 return;\r
72             if (!_haveState.Any(x => x.NeedSave))\r
73                 return;\r
74             foreach (var x in _haveState)\r
75                 x.SaveState(_status);\r
76             _status.Save();\r
77         }\r
78 \r
79         public void LoadState()\r
80         {\r
81             _status.Load();\r
82             foreach (var x in _haveState)\r
83                 x.LoadState(_status);\r
84             _saveState = true;\r
85         }\r
86 \r
87         public Update Sniff(string url, string request, dynamic json)\r
88         {\r
89             if (!json.api_result())\r
90                 return Update.Error;\r
91             if ((int)json.api_result != 1)\r
92                 return Update.None;\r
93             var data = json.api_data() ? json.api_data : new object();\r
94 \r
95             if (url.EndsWith("api_start2"))\r
96             {\r
97                 return ApiStart(data);\r
98             }\r
99             if (!_start)\r
100                 return Update.None;\r
101             if (url.EndsWith("api_port/port"))\r
102                 return ApiPort(data);\r
103             if (url.Contains("member"))\r
104                 return ApiMember(url, json);\r
105             if (url.Contains("kousyou"))\r
106                 return ApiKousyou(url, request, data);\r
107             if (url.Contains("battle"))\r
108                 return ApiBattle(url, request, data);\r
109             return ApiOthers(url, request, data);\r
110         }\r
111 \r
112         private Update ApiStart(dynamic data)\r
113         {\r
114             _shipInfo.InspectMaster(data);\r
115             _missionInfo.InspectMaster(data.api_mst_mission);\r
116             _itemInfo.InspectMaster(data);\r
117             _exMapInfo.ResetIfNeeded();\r
118             _start = true;\r
119             return Update.Start;\r
120         }\r
121 \r
122         private Update ApiPort(dynamic data)\r
123         {\r
124             _itemInfo.InspectBasic(data.api_basic);\r
125             _materialInfo.InspectMaterial(data.api_material, true);\r
126             _logger.InspectBasic(data.api_basic);\r
127             _logger.InspectMaterial(data.api_material);\r
128             _shipInfo.InspectShip(data);\r
129             _shipInfo.ClearBadlyDamagedShips();\r
130             _conditionTimer.CalcRegenTime();\r
131             _missionInfo.InspectDeck(data.api_deck_port);\r
132             _dockInfo.InspectNDock(data.api_ndock);\r
133             _akashiTimer.Port();\r
134             _achievement.InspectBasic(data.api_basic);\r
135             if (data.api_parallel_quest_count()) // 昔のログにはないので\r
136                 _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
137             _battleInfo.CleanupResult();\r
138             _battleInfo.InBattle = false;\r
139             _shipInfo.ClearEscapedShips();\r
140             _miscTextInfo.ClearIfNeeded();\r
141             SaveState();\r
142             return Update.All;\r
143         }\r
144 \r
145         private Update ApiMember(string url, dynamic json)\r
146         {\r
147             var data = json.api_data() ? json.api_data : new object();\r
148 \r
149             if (url.EndsWith("api_get_member/require_info"))\r
150             {\r
151                 _itemInfo.InspectSlotItem(data.api_slot_item, true);\r
152                 _dockInfo.InspectKDock(data.api_kdock);\r
153                 return Update.Timer;\r
154             }\r
155             if (url.EndsWith("api_get_member/basic"))\r
156             {\r
157                 _itemInfo.InspectBasic(data);\r
158                 _logger.InspectBasic(data);\r
159                 return Update.None;\r
160             }\r
161             if (url.EndsWith("api_get_member/slot_item"))\r
162             {\r
163                 _itemInfo.InspectSlotItem(data, true);\r
164                 return Update.None;\r
165             }\r
166             if (url.EndsWith("api_get_member/kdock"))\r
167             {\r
168                 _dockInfo.InspectKDock(data);\r
169                 _logger.InspectKDock(data);\r
170                 return Update.Timer;\r
171             }\r
172             if (url.EndsWith("api_get_member/ndock"))\r
173             {\r
174                 _dockInfo.InspectNDock(data);\r
175                 _conditionTimer.CheckCond();\r
176                 _akashiTimer.CheckFleet();\r
177                 return Update.NDock | Update.Timer | Update.Ship;\r
178             }\r
179             if (url.EndsWith("api_get_member/questlist"))\r
180             {\r
181                 _questInfo.InspectQuestList(data);\r
182                 return Update.QuestList;\r
183             }\r
184             if (url.EndsWith("api_get_member/deck"))\r
185             {\r
186                 _shipInfo.InspectDeck(data);\r
187                 _missionInfo.InspectDeck(data);\r
188                 _akashiTimer.CheckFleet();\r
189                 return Update.Mission | Update.Timer;\r
190             }\r
191             if (url.EndsWith("api_get_member/ship2"))\r
192             {\r
193                 // ここだけjsonなので注意\r
194                 _shipInfo.InspectShip(json);\r
195                 _akashiTimer.CheckFleet();\r
196                 _battleInfo.InBattle = false;\r
197                 return Update.Item | Update.Ship | Update.Battle;\r
198             }\r
199             if (url.EndsWith("api_get_member/ship_deck"))\r
200             {\r
201                 _shipInfo.InspectShip(data);\r
202                 _akashiTimer.CheckFleet();\r
203                 _battleInfo.InBattle = false;\r
204                 return Update.Ship | Update.Battle;\r
205             }\r
206             if (url.EndsWith("api_get_member/ship3"))\r
207             {\r
208                 _shipInfo.InspectShip(data);\r
209                 _akashiTimer.CheckFleet();\r
210                 _conditionTimer.CheckCond();\r
211                 return Update.Ship;\r
212             }\r
213             if (url.EndsWith("api_get_member/material"))\r
214             {\r
215                 _materialInfo.InspectMaterial(data);\r
216                 return Update.Item;\r
217             }\r
218             if (url.EndsWith("api_get_member/mapinfo"))\r
219             {\r
220                 _exMapInfo.InspectMapInfo(data);\r
221                 _miscTextInfo.InspectMapInfo(data);\r
222                 return Update.Item;\r
223             }\r
224             if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
225             {\r
226                 _miscTextInfo.InspectPracticeEnemyInfo(data);\r
227                 return Update.Item;\r
228             }\r
229             if (url.EndsWith("api_get_member/preset_deck"))\r
230             {\r
231                 _shipInfo.InspectPresetDeck(data);\r
232                 return Update.None;\r
233             }\r
234             return Update.None;\r
235         }\r
236 \r
237         private Update ApiKousyou(string url, string request, dynamic data)\r
238         {\r
239             if (url.EndsWith("api_req_kousyou/createitem"))\r
240             {\r
241                 _itemInfo.InspectCreateItem(data);\r
242                 _materialInfo.InspectCreateIem(data);\r
243                 _logger.InspectCreateItem(request, data);\r
244                 return Update.Item;\r
245             }\r
246             if (url.EndsWith("api_req_kousyou/getship"))\r
247             {\r
248                 _itemInfo.InspectGetShip(data);\r
249                 _shipInfo.InspectShip(data);\r
250                 _dockInfo.InspectKDock(data.api_kdock);\r
251                 _conditionTimer.CheckCond();\r
252                 return Update.Item | Update.Timer;\r
253             }\r
254             if (url.EndsWith("api_req_kousyou/destroyship"))\r
255             {\r
256                 _shipInfo.InspectDestroyShip(request, data);\r
257                 _materialInfo.InspectDestroyShip(data);\r
258                 _conditionTimer.CheckCond();\r
259                 _akashiTimer.CheckFleet();\r
260                 return Update.Item | Update.Ship;\r
261             }\r
262             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
263             {\r
264                 _itemInfo.InspectDestroyItem(request, data);\r
265                 _materialInfo.InspectDestroyItem(data);\r
266                 return Update.Item;\r
267             }\r
268             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
269             {\r
270                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
271                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
272                 _itemInfo.InspectRemodelSlot(data);\r
273                 _materialInfo.InspectRemodelSlot(data);\r
274                 return Update.Item;\r
275             }\r
276             if (url.EndsWith("api_req_kousyou/createship"))\r
277             {\r
278                 _logger.InspectCreateShip(request);\r
279                 return Update.None;\r
280             }\r
281             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
282             {\r
283                 _dockInfo.InspectCreateShipSpeedChange(request);\r
284                 return Update.Timer;\r
285             }\r
286             return Update.None;\r
287         }\r
288 \r
289         private Update ApiBattle(string url, string request, dynamic data)\r
290         {\r
291             if (IsNormalBattleAPI(url))\r
292             {\r
293                 _battleInfo.InspectBattle(data, url);\r
294                 _logger.InspectBattle(data);\r
295                 return Update.Ship | Update.Battle;\r
296             }\r
297             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
298             {\r
299                 if (url.EndsWith("/battle"))\r
300                 {\r
301                     _shipInfo.InspectMapStart(request); // 演習を出撃中とみなす\r
302                     _conditionTimer.InvalidateCond();\r
303                     _miscTextInfo.ClearFlag = true;\r
304                 }\r
305                 _battleInfo.InspectBattle(data, url);\r
306                 return Update.Ship | Update.Battle | Update.Timer;\r
307             }\r
308             if (url.EndsWith("api_req_sortie/battleresult"))\r
309             {\r
310                 _battleInfo.InspectBattleResult(data);\r
311                 _exMapInfo.InspectBattleResult(data);\r
312                 _logger.InspectBattleResult(data);\r
313                 return Update.Ship;\r
314             }\r
315             if (url.EndsWith("api_req_practice/battle_result"))\r
316             {\r
317                 _battleInfo.InspectPracticeResult(data);\r
318                 return Update.Ship;\r
319             }\r
320             if (IsCombinedBattleAPI(url))\r
321             {\r
322                 _battleInfo.InspectCombinedBattle(data, url);\r
323                 _logger.InspectBattle(data);\r
324                 return Update.Ship | Update.Battle;\r
325             }\r
326             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
327             {\r
328                 _battleInfo.InspectCombinedBattleResult(data);\r
329                 _logger.InspectBattleResult(data);\r
330                 return Update.Ship;\r
331             }\r
332             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
333             {\r
334                 _battleInfo.CauseCombinedBattleEscape();\r
335                 return Update.Ship;\r
336             }\r
337             return Update.None;\r
338         }\r
339 \r
340         private bool IsNormalBattleAPI(string url)\r
341         {\r
342             return url.EndsWith("api_req_sortie/battle") ||\r
343                    url.EndsWith("api_req_sortie/airbattle") ||\r
344                    url.EndsWith("api_req_sortie/ld_airbattle") ||\r
345                    url.EndsWith("api_req_battle_midnight/battle") ||\r
346                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
347         }\r
348 \r
349         private bool IsCombinedBattleAPI(string url)\r
350         {\r
351             return url.EndsWith("api_req_combined_battle/battle") ||\r
352                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
353                    url.EndsWith("api_req_combined_battle/ld_airbattle") ||\r
354                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
355                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
356                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
357         }\r
358 \r
359         private Update ApiOthers(string url, string request, dynamic data)\r
360         {\r
361             if (url.EndsWith("api_req_hensei/change"))\r
362             {\r
363                 _shipInfo.InspectChange(request);\r
364                 _akashiTimer.InspectChange(request);\r
365                 return Update.Ship;\r
366             }\r
367             if (url.EndsWith("api_req_hensei/preset_select"))\r
368             {\r
369                 _shipInfo.InspectDeck(new[] {data});\r
370                 _akashiTimer.CheckFleet();\r
371                 return Update.Ship;\r
372             }\r
373             if (url.EndsWith("api_req_hensei/preset_register"))\r
374             {\r
375                 _shipInfo.InspectPresetRegister(data);\r
376                 return Update.None;\r
377             }\r
378             if (url.EndsWith("api_req_hensei/preset_delete"))\r
379             {\r
380                 _shipInfo.InspectPresetDelete(request);\r
381                 return Update.Timer;\r
382             }\r
383             if (url.EndsWith("api_req_hensei/combined"))\r
384             {\r
385                 _shipInfo.InspectCombined(request);\r
386                 return Update.Ship;\r
387             }\r
388             if (url.EndsWith("api_req_hokyu/charge"))\r
389             {\r
390                 _shipInfo.InspectCharge(data);\r
391                 _materialInfo.InspectCharge(data);\r
392                 return Update.Item | Update.Ship;\r
393             }\r
394             if (url.EndsWith("api_req_kaisou/powerup"))\r
395             {\r
396                 _shipInfo.InspectPowerup(request, data);\r
397                 _conditionTimer.CheckCond();\r
398                 _akashiTimer.CheckFleet();\r
399                 return Update.Item | Update.Ship;\r
400             }\r
401             if (url.EndsWith("api_req_kaisou/slot_exchange_index"))\r
402             {\r
403                 _shipInfo.InspectSlotExchange(request, data);\r
404                 return Update.Ship;\r
405             }\r
406             if (url.EndsWith("api_req_nyukyo/start"))\r
407             {\r
408                 _dockInfo.InspectNyukyo(request);\r
409                 _conditionTimer.CheckCond();\r
410                 _akashiTimer.CheckFleet();\r
411                 return Update.Item | Update.Ship;\r
412             }\r
413             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
414             {\r
415                 _dockInfo.InspectSpeedChange(request);\r
416                 _conditionTimer.CheckCond();\r
417                 return Update.NDock | Update.Timer | Update.Ship;\r
418             }\r
419             if (url.EndsWith("api_req_map/start"))\r
420             {\r
421                 _shipInfo.InspectMapStart(request); // 出撃中判定が必要なので_conditionTimerより前\r
422                 _conditionTimer.InvalidateCond();\r
423                 _exMapInfo.InspectMapStart(data);\r
424                 _logger.InspectMapStart(data);\r
425                 _miscTextInfo.ClearFlag = true;\r
426                 return Update.Timer | Update.Ship;\r
427             }\r
428             if (url.EndsWith("api_req_map/next"))\r
429             {\r
430                 _battleInfo.InspectMapNext(request);\r
431                 _exMapInfo.InspectMapNext(data);\r
432                 _logger.InspectMapNext(data);\r
433                 return Update.None;\r
434             }\r
435             if (url.EndsWith("api_req_mission/result"))\r
436             {\r
437                 _materialInfo.InspectMissionResult(data);\r
438                 _logger.InspectMissionResult(data);\r
439                 return Update.Item;\r
440             }\r
441             if (url.EndsWith("api_req_quest/clearitemget"))\r
442             {\r
443                 _questInfo.InspectClearItemGet(request);\r
444                 return Update.QuestList;\r
445             }\r
446             if (url.EndsWith("api_req_air_corps/supply"))\r
447             {\r
448                 _materialInfo.InspectAirCorpsSupply(data);\r
449                 return Update.Item;\r
450             }\r
451             if (url.EndsWith("api_req_air_corps/set_plane"))\r
452             {\r
453                 _materialInfo.InspectAirCorpsSetPlane(data);\r
454                 return Update.Item;\r
455             }\r
456             return Update.None;\r
457         }\r
458 \r
459         public NameAndTimer[] NDock => _dockInfo.NDock;\r
460 \r
461         public RingTimer[] KDock => _dockInfo.KDock;\r
462 \r
463         public ItemInfo Item => _itemInfo;\r
464 \r
465         public MaterialInfo Material => _materialInfo;\r
466 \r
467         public QuestStatus[] Quests => _questInfo.Quests;\r
468 \r
469         public NameAndTimer[] Missions => _missionInfo.Missions;\r
470 \r
471         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
472 \r
473         public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
474 \r
475         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
476 \r
477         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
478 \r
479         public int CombinedFleetType => _shipInfo.CombinedFleetType;\r
480 \r
481         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
482 \r
483         public int[] GetFighterPower(int fleet) => _shipInfo.GetFighterPower(fleet);\r
484 \r
485         public double GetContactTriggerRate(int fleet) => _shipInfo.GetContactTriggerRate(fleet);\r
486 \r
487         public double GetFleetLineOfSights(int fleet) => _shipInfo.GetLineOfSights(fleet);\r
488 \r
489         public ShipStatus[] DamagedShipList => _shipInfo.GetDamagedShipList(_dockInfo);\r
490 \r
491         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
492 \r
493         public string[] BadlyDamagedShips => _shipInfo.BadlyDamagedShips;\r
494 \r
495         public ItemStatus[] ItemList => _itemInfo.GetItemListWithOwner(ShipList);\r
496 \r
497         public AkashiTimer AkashiTimer => _akashiTimer;\r
498 \r
499         public Achievement Achievement => _achievement;\r
500 \r
501         public BattleInfo Battle => _battleInfo;\r
502 \r
503         public ExMapInfo ExMap => _exMapInfo;\r
504 \r
505         public string MiscText => _miscTextInfo.Text;\r
506 \r
507         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
508         {\r
509             _logger.SetWriter(writer, nowFunc);\r
510         }\r
511 \r
512         public void SkipMaster()\r
513         {\r
514             _start = true;\r
515         }\r
516 \r
517         public void EnableLog(LogType type)\r
518         {\r
519             _logger.EnableLog(type);\r
520         }\r
521 \r
522         public int MaterialLogInterval\r
523         {\r
524             set { _logger.MaterialLogInterval = value; }\r
525         }\r
526 \r
527         public string LogOutputDir\r
528         {\r
529             set { _logger.OutputDir = value; }\r
530         }\r
531     }\r
532 \r
533     public class NameAndTimer\r
534     {\r
535         public string Name { get; set; }\r
536         public RingTimer Timer { get; set; }\r
537 \r
538         public NameAndTimer()\r
539         {\r
540             Timer = new RingTimer();\r
541         }\r
542     }\r
543 \r
544     public class RingTimer\r
545     {\r
546         private readonly TimeSpan _spare;\r
547 \r
548         public TimeSpan Rest { get; private set; }\r
549 \r
550         public bool IsFinished => EndTime != DateTime.MinValue && Rest <= _spare;\r
551 \r
552         public DateTime EndTime { get; private set; }\r
553 \r
554         public bool NeedRing { get; set; }\r
555 \r
556         public RingTimer(int spare = 60)\r
557         {\r
558             _spare = TimeSpan.FromSeconds(spare);\r
559         }\r
560 \r
561         public void SetEndTime(double time)\r
562         {\r
563             SetEndTime((int)time == 0\r
564                 ? DateTime.MinValue\r
565                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
566         }\r
567 \r
568         public void SetEndTime(DateTime time)\r
569         {\r
570             EndTime = time;\r
571         }\r
572 \r
573         public void Update()\r
574         {\r
575             if (EndTime == DateTime.MinValue)\r
576             {\r
577                 Rest = TimeSpan.Zero;\r
578                 return;\r
579             }\r
580             var prev = Rest;\r
581             Rest = EndTime - DateTime.Now;\r
582             if (Rest < TimeSpan.Zero)\r
583                 Rest = TimeSpan.Zero;\r
584             if (prev > _spare && _spare >= Rest)\r
585                 NeedRing = true;\r
586         }\r
587 \r
588         public string ToString(bool finish = false)\r
589             => EndTime == DateTime.MinValue\r
590                 ? ""\r
591                 : finish ? EndTime.ToString(@"dd\ HH\:mm") : $@"{(int)Rest.TotalHours:d2}:{Rest:mm\:ss}";\r
592     }\r
593 }