OSDN Git Service

cc4174325f9e1261a5406e4c42ea8c302ab45033
[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.Globalization;\r
18 using System.Linq;\r
19 \r
20 namespace KancolleSniffer\r
21 {\r
22     public class Sniffer\r
23     {\r
24         private bool _start;\r
25         private readonly ItemInfo _itemInfo = new ItemInfo();\r
26         private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
27         private readonly QuestInfo _questInfo = new QuestInfo();\r
28         private readonly MissionInfo _missionInfo = new MissionInfo();\r
29         private readonly ShipInfo _shipInfo;\r
30         private readonly ConditionTimer _conditionTimer;\r
31         private readonly DockInfo _dockInfo;\r
32         private readonly AkashiTimer _akashiTimer;\r
33         private readonly Achievement _achievement = new Achievement();\r
34         private readonly BattleInfo _battleInfo;\r
35         private readonly Logger _logger;\r
36         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
37         private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
38         private readonly BaseAirCoprs _baseAirCoprs;\r
39         private readonly PresetDeck _presetDeck = new PresetDeck();\r
40         private readonly Status _status = new Status();\r
41         private bool _saveState;\r
42         private readonly List<IHaveState> _haveState;\r
43 \r
44         public interface IRepeatingTimerController\r
45         {\r
46             void Stop(string key);\r
47             void Stop(string key, int fleet);\r
48             void Suspend();\r
49             void Resume();\r
50         }\r
51 \r
52         public IRepeatingTimerController RepeatingTimerController { get; set; }\r
53 \r
54         [Flags]\r
55         public enum Update\r
56         {\r
57             None = 0,\r
58             Error = 1 << 0,\r
59             Start = 1 << 1,\r
60             Item = 1 << 2,\r
61             Ship = 1 << 3,\r
62             Timer = 1 << 4,\r
63             NDock = 1 << 5,\r
64             Mission = 1 << 6,\r
65             QuestList = 1 << 7,\r
66             Battle = 1 << 8,\r
67             All = (1 << 9) - 1\r
68         }\r
69 \r
70         public Sniffer(bool start = false)\r
71         {\r
72             _start = start;\r
73             _shipInfo = new ShipInfo(_itemInfo);\r
74             _conditionTimer = new ConditionTimer(_shipInfo);\r
75             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
76             _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo, _presetDeck);\r
77             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
78             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
79             _baseAirCoprs = new BaseAirCoprs(_itemInfo);\r
80             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
81         }\r
82 \r
83         private void SaveState()\r
84         {\r
85             if (!_saveState)\r
86                 return;\r
87             if (!_haveState.Any(x => x.NeedSave))\r
88                 return;\r
89             foreach (var x in _haveState)\r
90                 x.SaveState(_status);\r
91             _status.Save();\r
92         }\r
93 \r
94         public void LoadState()\r
95         {\r
96             _status.Load();\r
97             foreach (var x in _haveState)\r
98                 x.LoadState(_status);\r
99             _saveState = true;\r
100         }\r
101 \r
102         public Update Sniff(string url, string request, dynamic json)\r
103         {\r
104             if (!json.api_result())\r
105                 return Update.Error;\r
106             if ((int)json.api_result != 1)\r
107                 return Update.None;\r
108             var data = json.api_data() ? json.api_data : new object();\r
109 \r
110             if (url.EndsWith("api_start2"))\r
111             {\r
112                 return ApiStart(data);\r
113             }\r
114             if (!_start)\r
115                 return Update.None;\r
116 \r
117             if (url.EndsWith("api_port/port"))\r
118                 return ApiPort(data);\r
119             if (url.Contains("member"))\r
120                 return ApiMember(url, json);\r
121             if (url.Contains("kousyou"))\r
122                 return ApiKousyou(url, request, data);\r
123             if (url.Contains("battle") || url.Contains("sortie"))\r
124                 return ApiBattle(url, request, data);\r
125             return ApiOthers(url, request, data);\r
126         }\r
127 \r
128         private Update ApiStart(dynamic data)\r
129         {\r
130             _shipInfo.InspectMaster(data);\r
131             _missionInfo.InspectMaster(data.api_mst_mission);\r
132             _itemInfo.InspectMaster(data);\r
133             _exMapInfo.ResetIfNeeded();\r
134             _start = true;\r
135             return Update.Start;\r
136         }\r
137 \r
138         private Update ApiPort(dynamic data)\r
139         {\r
140             _itemInfo.InspectBasic(data.api_basic);\r
141             _materialInfo.InspectMaterialPort(data.api_material);\r
142             _logger.InspectBasic(data.api_basic);\r
143             _logger.InspectMaterial(data.api_material);\r
144             _shipInfo.InspectShip(data);\r
145             _shipInfo.ClearBadlyDamagedShips();\r
146             _conditionTimer.CalcRegenTime();\r
147             _missionInfo.InspectDeck(data.api_deck_port);\r
148             _dockInfo.InspectNDock(data.api_ndock);\r
149             _akashiTimer.Port();\r
150             _achievement.InspectBasic(data.api_basic);\r
151             if (data.api_parallel_quest_count()) // 昔のログにはないので\r
152                 _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
153             if (data.api_event_object())\r
154                 _baseAirCoprs.InspectEventObject(data.api_event_object);\r
155             if (data.api_plane_info())\r
156                 _baseAirCoprs.InspectPlaneInfo(data.api_plane_info);\r
157             _battleInfo.CleanupResult();\r
158             _battleInfo.BattleState = BattleState.None;\r
159             _shipInfo.ClearEscapedShips();\r
160             _miscTextInfo.ClearIfNeeded();\r
161             SaveState();\r
162             RepeatingTimerController?.Resume();\r
163             foreach (var s in new[] {"遠征終了", "入渠終了", "疲労回復", "泊地修理"})\r
164                 RepeatingTimerController?.Stop(s);\r
165             return Update.All;\r
166         }\r
167 \r
168         private Update ApiMember(string url, dynamic json)\r
169         {\r
170             var data = json.api_data() ? json.api_data : new object();\r
171 \r
172             if (url.EndsWith("api_get_member/require_info"))\r
173             {\r
174                 _itemInfo.InspectSlotItem(data.api_slot_item, true);\r
175                 _dockInfo.InspectKDock(data.api_kdock);\r
176                 return Update.Timer;\r
177             }\r
178             if (url.EndsWith("api_get_member/basic"))\r
179             {\r
180                 _itemInfo.InspectBasic(data);\r
181                 _logger.InspectBasic(data);\r
182                 return Update.None;\r
183             }\r
184             if (url.EndsWith("api_get_member/slot_item"))\r
185             {\r
186                 _itemInfo.InspectSlotItem(data, true);\r
187                 return Update.Item;\r
188             }\r
189             if (url.EndsWith("api_get_member/kdock"))\r
190             {\r
191                 _dockInfo.InspectKDock(data);\r
192                 _logger.InspectKDock(data);\r
193                 return Update.Timer;\r
194             }\r
195             if (url.EndsWith("api_get_member/ndock"))\r
196             {\r
197                 _dockInfo.InspectNDock(data);\r
198                 _conditionTimer.CheckCond();\r
199                 _akashiTimer.CheckFleet();\r
200                 RepeatingTimerController?.Stop("入渠終了");\r
201                 return Update.NDock | Update.Timer | Update.Ship;\r
202             }\r
203             if (url.EndsWith("api_get_member/questlist"))\r
204             {\r
205                 _questInfo.InspectQuestList(data);\r
206                 return Update.QuestList;\r
207             }\r
208             if (url.EndsWith("api_get_member/deck"))\r
209             {\r
210                 _shipInfo.InspectDeck(data);\r
211                 _missionInfo.InspectDeck(data);\r
212                 _akashiTimer.CheckFleet();\r
213                 return Update.Mission | Update.Timer;\r
214             }\r
215             if (url.EndsWith("api_get_member/ship2"))\r
216             {\r
217                 // ここだけjsonなので注意\r
218                 _shipInfo.InspectShip(json);\r
219                 _akashiTimer.CheckFleet();\r
220                 _battleInfo.BattleState = BattleState.None;\r
221                 return Update.Item | Update.Ship | Update.Battle;\r
222             }\r
223             if (url.EndsWith("api_get_member/ship_deck"))\r
224             {\r
225                 _shipInfo.InspectShip(data);\r
226                 _akashiTimer.CheckFleet();\r
227                 _battleInfo.BattleState = BattleState.None;\r
228                 return Update.Ship | Update.Battle;\r
229             }\r
230             if (url.EndsWith("api_get_member/ship3"))\r
231             {\r
232                 _shipInfo.InspectShip(data);\r
233                 _akashiTimer.CheckFleet();\r
234                 _conditionTimer.CheckCond();\r
235                 return Update.Ship;\r
236             }\r
237             if (url.EndsWith("api_get_member/material"))\r
238             {\r
239                 _materialInfo.InspectMaterial(data);\r
240                 return Update.Item;\r
241             }\r
242             if (url.EndsWith("api_get_member/mapinfo"))\r
243             {\r
244                 _exMapInfo.InspectMapInfo(data);\r
245                 _miscTextInfo.InspectMapInfo(data);\r
246                 if (data.api_air_base())\r
247                     _baseAirCoprs.Inspect(data.api_air_base);\r
248                 return Update.Item;\r
249             }\r
250             if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
251             {\r
252                 _miscTextInfo.InspectPracticeEnemyInfo(data);\r
253                 return Update.Item;\r
254             }\r
255             if (url.EndsWith("api_get_member/preset_deck"))\r
256             {\r
257                 _presetDeck.Inspect(data);\r
258                 return Update.None;\r
259             }\r
260             if (url.EndsWith("api_get_member/base_air_corps"))\r
261             {\r
262                 _baseAirCoprs.Inspect(data);\r
263                 return Update.Ship;\r
264             }\r
265             return Update.None;\r
266         }\r
267 \r
268         private Update ApiKousyou(string url, string request, dynamic data)\r
269         {\r
270             if (url.EndsWith("api_req_kousyou/createitem"))\r
271             {\r
272                 _itemInfo.InspectCreateItem(data);\r
273                 _materialInfo.InspectCreateIem(data);\r
274                 _logger.InspectCreateItem(request, data);\r
275                 return Update.Item;\r
276             }\r
277             if (url.EndsWith("api_req_kousyou/getship"))\r
278             {\r
279                 _itemInfo.InspectGetShip(data);\r
280                 _shipInfo.InspectShip(data);\r
281                 _dockInfo.InspectKDock(data.api_kdock);\r
282                 _conditionTimer.CheckCond();\r
283                 RepeatingTimerController?.Stop("建造完了");\r
284                 return Update.Item | Update.Timer;\r
285             }\r
286             if (url.EndsWith("api_req_kousyou/destroyship"))\r
287             {\r
288                 _shipInfo.InspectDestroyShip(request, data);\r
289                 _materialInfo.InspectDestroyShip(data);\r
290                 _conditionTimer.CheckCond();\r
291                 _akashiTimer.CheckFleet();\r
292                 return Update.Item | Update.Ship;\r
293             }\r
294             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
295             {\r
296                 _itemInfo.InspectDestroyItem(request, data);\r
297                 _materialInfo.InspectDestroyItem(data);\r
298                 return Update.Item;\r
299             }\r
300             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
301             {\r
302                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
303                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
304                 _itemInfo.InspectRemodelSlot(data);\r
305                 _materialInfo.InspectRemodelSlot(data);\r
306                 return Update.Item;\r
307             }\r
308             if (url.EndsWith("api_req_kousyou/createship"))\r
309             {\r
310                 _logger.InspectCreateShip(request);\r
311                 return Update.None;\r
312             }\r
313             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
314             {\r
315                 _dockInfo.InspectCreateShipSpeedChange(request);\r
316                 return Update.Timer;\r
317             }\r
318             return Update.None;\r
319         }\r
320 \r
321         private Update ApiBattle(string url, string request, dynamic data)\r
322         {\r
323             if (IsNormalBattleAPI(url) || IsCombinedBattleAPI(url))\r
324             {\r
325                 _battleInfo.InspectBattle(url, request, data);\r
326                 _logger.InspectBattle(data);\r
327                 return Update.Ship | Update.Battle;\r
328             }\r
329             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
330             {\r
331                 if (url.EndsWith("/battle"))\r
332                 {\r
333                     _shipInfo.InspectMapStart(request); // 演習を出撃中とみなす\r
334                     _conditionTimer.InvalidateCond();\r
335                     _miscTextInfo.ClearFlag = true;\r
336                     RepeatingTimerController?.Suspend();\r
337                 }\r
338                 _battleInfo.InspectBattle(url, request, data);\r
339                 return Update.Ship | Update.Battle | Update.Timer;\r
340             }\r
341             if (url.EndsWith("api_req_sortie/battleresult") || url.EndsWith("api_req_combined_battle/battleresult"))\r
342             {\r
343                 _battleInfo.InspectBattleResult(data);\r
344                 _exMapInfo.InspectBattleResult(data);\r
345                 _logger.InspectBattleResult(data);\r
346                 return Update.Ship;\r
347             }\r
348             if (url.EndsWith("api_req_practice/battle_result"))\r
349             {\r
350                 _battleInfo.InspectPracticeResult(data);\r
351                 return Update.Ship;\r
352             }\r
353             if (url.EndsWith("/goback_port"))\r
354             {\r
355                 _battleInfo.CauseEscape();\r
356                 return Update.Ship;\r
357             }\r
358             _battleInfo.BattleState = BattleState.Unknown;\r
359             return Update.None;\r
360         }\r
361 \r
362         private bool IsNormalBattleAPI(string url)\r
363         {\r
364             return url.EndsWith("api_req_sortie/battle") ||\r
365                    url.EndsWith("api_req_sortie/airbattle") ||\r
366                    url.EndsWith("api_req_sortie/ld_airbattle") ||\r
367                    url.EndsWith("api_req_battle_midnight/battle") ||\r
368                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
369         }\r
370 \r
371         private bool IsCombinedBattleAPI(string url)\r
372         {\r
373             return url.EndsWith("api_req_combined_battle/battle") ||\r
374                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
375                    url.EndsWith("api_req_combined_battle/ld_airbattle") ||\r
376                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
377                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
378                    url.EndsWith("api_req_combined_battle/sp_midnight") ||\r
379                    url.EndsWith("api_req_combined_battle/ec_battle") ||\r
380                    url.EndsWith("api_req_combined_battle/ec_midnight_battle") ||\r
381                    url.EndsWith("api_req_combined_battle/ec_night_to_day") ||\r
382                    url.EndsWith("api_req_combined_battle/each_battle") ||\r
383                    url.EndsWith("api_req_combined_battle/each_battle_water");\r
384         }\r
385 \r
386         private Update ApiOthers(string url, string request, dynamic data)\r
387         {\r
388             if (url.EndsWith("api_req_hensei/change"))\r
389             {\r
390                 _shipInfo.InspectChange(request);\r
391                 _akashiTimer.InspectChange(request);\r
392                 return Update.Ship;\r
393             }\r
394             if (url.EndsWith("api_req_hensei/preset_select"))\r
395             {\r
396                 _shipInfo.InspectDeck(new[] {data});\r
397                 _akashiTimer.CheckFleet();\r
398                 return Update.Ship;\r
399             }\r
400             if (url.EndsWith("api_req_hensei/preset_register"))\r
401             {\r
402                 _presetDeck.InspectRegister(data);\r
403                 return Update.None;\r
404             }\r
405             if (url.EndsWith("api_req_hensei/preset_delete"))\r
406             {\r
407                 _presetDeck.InspectDelete(request);\r
408                 return Update.Timer;\r
409             }\r
410             if (url.EndsWith("api_req_hensei/combined"))\r
411             {\r
412                 _shipInfo.InspectCombined(request);\r
413                 return Update.Ship;\r
414             }\r
415             if (url.EndsWith("api_req_hokyu/charge"))\r
416             {\r
417                 _shipInfo.InspectCharge(data);\r
418                 _materialInfo.InspectCharge(data);\r
419                 return Update.Item | Update.Ship;\r
420             }\r
421             if (url.EndsWith("api_req_kaisou/powerup"))\r
422             {\r
423                 _shipInfo.InspectPowerup(request, data);\r
424                 _conditionTimer.CheckCond();\r
425                 _akashiTimer.CheckFleet();\r
426                 return Update.Item | Update.Ship;\r
427             }\r
428             if (url.EndsWith("api_req_kaisou/slot_exchange_index"))\r
429             {\r
430                 _shipInfo.InspectSlotExchange(request, data);\r
431                 return Update.Ship;\r
432             }\r
433             if (url.EndsWith("api_req_kaisou/slot_deprive"))\r
434             {\r
435                 _shipInfo.InspectSlotDeprive(data);\r
436                 return Update.Ship;\r
437             }\r
438             if (url.EndsWith("api_req_nyukyo/start"))\r
439             {\r
440                 _dockInfo.InspectNyukyo(request);\r
441                 _conditionTimer.CheckCond();\r
442                 _akashiTimer.CheckFleet();\r
443                 var ndock = HttpUtility.ParseQueryString(request)["api_ndock_id"];\r
444                 if (ndock != null && int.TryParse(ndock, out int id))\r
445                     RepeatingTimerController?.Stop("入渠終了", id - 1);\r
446                 return Update.Item | Update.Ship;\r
447             }\r
448             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
449             {\r
450                 _dockInfo.InspectSpeedChange(request);\r
451                 _conditionTimer.CheckCond();\r
452                 return Update.NDock | Update.Timer | Update.Item | Update.Ship;\r
453             }\r
454             if (url.EndsWith("api_req_map/start"))\r
455             {\r
456                 _shipInfo.InspectMapStart(request); // 出撃中判定が必要なので_conditionTimerより前\r
457                 _conditionTimer.InvalidateCond();\r
458                 _exMapInfo.InspectMapStart(data);\r
459                 _battleInfo.InspectMapStart(data);\r
460                 _logger.InspectMapStart(data);\r
461                 _miscTextInfo.ClearFlag = true;\r
462                 RepeatingTimerController?.Suspend();\r
463                 return Update.Timer | Update.Ship;\r
464             }\r
465             if (url.EndsWith("api_req_map/next"))\r
466             {\r
467                 _exMapInfo.InspectMapNext(data);\r
468                 _battleInfo.InspectMapNext(data);\r
469                 _logger.InspectMapNext(data);\r
470                 return Update.None;\r
471             }\r
472             if (url.EndsWith("api_req_mission/start"))\r
473             {\r
474                 var deck = HttpUtility.ParseQueryString(request)["api_deck_id"];\r
475                 if (deck != null && int.TryParse(deck, out int id))\r
476                     RepeatingTimerController?.Stop("遠征終了", id - 1);\r
477                 return Update.None;\r
478             }\r
479             if (url.EndsWith("api_req_mission/result"))\r
480             {\r
481                 _materialInfo.InspectMissionResult(data);\r
482                 _logger.InspectMissionResult(data);\r
483                 return Update.Item;\r
484             }\r
485             if (url.EndsWith("api_req_quest/stop"))\r
486             {\r
487                 _questInfo.InspectStop(request);\r
488                 return Update.QuestList;\r
489             }\r
490             if (url.EndsWith("api_req_quest/clearitemget"))\r
491             {\r
492                 _questInfo.InspectClearItemGet(request);\r
493                 _logger.InspectClearItemGet(data);\r
494                 return Update.QuestList;\r
495             }\r
496             if (url.EndsWith("api_req_air_corps/supply"))\r
497             {\r
498                 _materialInfo.InspectAirCorpsSupply(data);\r
499                 _baseAirCoprs.InspectSupply(request, data);\r
500                 return Update.Item;\r
501             }\r
502             if (url.EndsWith("api_req_air_corps/set_plane"))\r
503             {\r
504                 _materialInfo.InspectAirCorpsSetPlane(data);\r
505                 _baseAirCoprs.InspectSetPlane(request, data);\r
506                 return Update.Item | Update.Ship;\r
507             }\r
508             if (url.EndsWith("api_req_air_corps/set_action"))\r
509             {\r
510                 _baseAirCoprs.InspectSetAction(request);\r
511                 return Update.Ship;\r
512             }\r
513             if (url.EndsWith("api_req_air_corps/expand_base"))\r
514             {\r
515                 _baseAirCoprs.InspectExpandBase(request, data);\r
516                 return Update.Ship;\r
517             }\r
518             return Update.None;\r
519         }\r
520 \r
521         public NameAndTimer[] NDock => _dockInfo.NDock;\r
522 \r
523         public RingTimer[] KDock => _dockInfo.KDock;\r
524 \r
525         public ItemInfo Item => _itemInfo;\r
526 \r
527         public MaterialInfo Material => _materialInfo;\r
528 \r
529         public QuestStatus[] Quests => _questInfo.Quests;\r
530 \r
531         public NameAndTimer[] Missions => _missionInfo.Missions;\r
532 \r
533         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
534 \r
535         public int[] GetConditionNotice(DateTime prev, DateTime now) => _conditionTimer.GetNotice(prev, now);\r
536 \r
537         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
538 \r
539         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
540 \r
541         public ShipInfo.ShipStatusPair[] BattleResultStatusDiff => _shipInfo.BattleResultDiff;\r
542 \r
543         public bool IsBattleResultStatusError => _shipInfo.IsBattleResultError;\r
544 \r
545         public ShipStatus[] BattleStartStatus => _shipInfo.BattleStartStatus;\r
546 \r
547         public int CombinedFleetType => _shipInfo.CombinedFleetType;\r
548 \r
549         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
550 \r
551         public int[] GetFighterPower(int fleet) => _shipInfo.GetFighterPower(fleet);\r
552 \r
553         public double GetContactTriggerRate(int fleet) => _shipInfo.GetContactTriggerRate(fleet);\r
554 \r
555         public double GetFleetLineOfSights(int fleet, int factor) => _shipInfo.GetLineOfSights(fleet, factor);\r
556 \r
557         public ShipStatus[] RepairList => _shipInfo.GetRepairList(_dockInfo);\r
558 \r
559         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
560 \r
561         public string[] BadlyDamagedShips => _shipInfo.BadlyDamagedShips;\r
562 \r
563         public double GetDaihatsuBonus(int fleet) => _shipInfo.GetDaihatsuBonus(fleet);\r
564 \r
565         public double GetTransportPoint(int fleet) => _shipInfo.GetTransportPoint(fleet);\r
566 \r
567         public ItemStatus[] ItemList\r
568         {\r
569             get\r
570             {\r
571                 _itemInfo.ClearHolder();\r
572                 _shipInfo.SetItemHolder();\r
573                 _baseAirCoprs.SetItemHolder();\r
574                 return _itemInfo.ItemList;\r
575             }\r
576         }\r
577 \r
578         public AkashiTimer AkashiTimer => _akashiTimer;\r
579 \r
580         public Achievement Achievement => _achievement;\r
581 \r
582         public BattleInfo Battle => _battleInfo;\r
583 \r
584         public ExMapInfo ExMap => _exMapInfo;\r
585 \r
586         public string MiscText => _miscTextInfo.Text;\r
587 \r
588         public BaseAirCoprs.BaseInfo[] BaseAirCorps => _baseAirCoprs.AllAirCorps;\r
589 \r
590         public bool UseOldEnemyId\r
591         {\r
592             set => _shipInfo.UseOldEnemyId = value;\r
593         }\r
594 \r
595         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
596         {\r
597             _logger.SetWriter(writer, nowFunc);\r
598         }\r
599 \r
600         public void SkipMaster()\r
601         {\r
602             _start = true;\r
603         }\r
604 \r
605         public void EnableLog(LogType type)\r
606         {\r
607             _logger.EnableLog(type);\r
608         }\r
609 \r
610         public int MaterialLogInterval\r
611         {\r
612             set => _logger.MaterialLogInterval = value;\r
613         }\r
614 \r
615         public string LogOutputDir\r
616         {\r
617             set => _logger.OutputDir = value;\r
618         }\r
619 \r
620         public void FlashLog()\r
621         {\r
622             _logger.FlashLog();\r
623         }\r
624     }\r
625 \r
626     public class NameAndTimer\r
627     {\r
628         public string Name { get; set; }\r
629         public RingTimer Timer { get; set; }\r
630 \r
631         public NameAndTimer()\r
632         {\r
633             Timer = new RingTimer();\r
634         }\r
635     }\r
636 \r
637     public class RingTimer\r
638     {\r
639         private readonly TimeSpan _spare;\r
640         private bool _finished;\r
641 \r
642         public bool IsFinished(DateTime now) => EndTime != DateTime.MinValue && EndTime - now < _spare || _finished;\r
643 \r
644         public DateTime EndTime { get; private set; }\r
645 \r
646         public RingTimer(int spare = 60)\r
647         {\r
648             _spare = TimeSpan.FromSeconds(spare);\r
649         }\r
650 \r
651         public void SetEndTime(double time)\r
652         {\r
653             SetEndTime((int)time == 0\r
654                 ? DateTime.MinValue\r
655                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
656         }\r
657 \r
658         public void SetEndTime(DateTime time)\r
659         {\r
660             EndTime = time;\r
661             _finished = false;\r
662         }\r
663 \r
664         public void Finish()\r
665         {\r
666             _finished = true;\r
667         }\r
668 \r
669         public bool CheckRing(DateTime prev, DateTime now)\r
670         {\r
671             return EndTime != DateTime.MinValue && prev != DateTime.MinValue &&\r
672                        prev < EndTime -_spare && EndTime - _spare <= now;\r
673         }\r
674 \r
675         public string ToString(DateTime now, bool endTime = false)\r
676         {\r
677             if (EndTime == DateTime.MinValue && !_finished)\r
678                 return "";\r
679             if (endTime)\r
680                 return EndTime.ToString(@"dd\ HH\:mm", CultureInfo.InvariantCulture);\r
681             var rest = _finished || EndTime - now < TimeSpan.Zero ? TimeSpan.Zero : EndTime - now;\r
682             return $"{(int)rest.TotalHours:d2}:" + rest.ToString(@"mm\:ss", CultureInfo.InvariantCulture);\r
683         }\r
684     }\r
685 }