OSDN Git Service

出撃から戻ったときに装備数が更新されないのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
index 89236d3..2632c7e 100644 (file)
-// Copyright (C) 2013, 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\r
+// Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
 // \r
-// This program is part of KancolleSniffer.\r
+// Licensed under the Apache License, Version 2.0 (the "License");\r
+// you may not use this file except in compliance with the License.\r
+// You may obtain a copy of the License at\r
 //\r
-// KancolleSniffer is free software: you can redistribute it and/or modify\r
-// it under the terms of the GNU General Public License as published by\r
-// the Free Software Foundation, either version 3 of the License, or\r
-// (at your option) any later version.\r
+//    http://www.apache.org/licenses/LICENSE-2.0\r
 //\r
-// This program is distributed in the hope that it will be useful,\r
-// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-// GNU General Public License for more details.\r
-//\r
-// You should have received a copy of the GNU General Public License\r
-// along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+// Unless required by applicable law or agreed to in writing, software\r
+// distributed under the License is distributed on an "AS IS" BASIS,\r
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+// See the License for the specific language governing permissions and\r
+// limitations under the License.\r
 \r
 using System;\r
-using System.IO;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
 \r
 namespace KancolleSniffer\r
 {\r
     public class Sniffer\r
     {\r
         private bool _start;\r
-        private readonly ShipMaster _shipMaster = new ShipMaster();\r
         private readonly ItemInfo _itemInfo = new ItemInfo();\r
+        private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
         private readonly QuestInfo _questInfo = new QuestInfo();\r
         private readonly MissionInfo _missionInfo = new MissionInfo();\r
         private readonly ShipInfo _shipInfo;\r
+        private readonly ConditionTimer _conditionTimer;\r
         private readonly DockInfo _dockInfo;\r
         private readonly AkashiTimer _akashiTimer;\r
         private readonly Achievement _achievement = new Achievement();\r
         private readonly BattleInfo _battleInfo;\r
+        private readonly Logger _logger;\r
+        private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
+        private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
         private readonly Status _status = new Status();\r
+        private bool _saveState;\r
+        private readonly List<IHaveState> _haveState;\r
 \r
         [Flags]\r
         public enum Update\r
         {\r
             None = 0,\r
-            Start = 1,\r
-            Item = 2,\r
-            Ship = 4,\r
-            Timer = 8,\r
-            NDock = 16,\r
-            Mission = 32,\r
-            QuestList = 64,\r
-            Battle = 128,\r
-            All = 255\r
+            Error = 1 << 0,\r
+            Start = 1 << 1,\r
+            Item = 1 << 2,\r
+            Ship = 1 << 3,\r
+            Timer = 1 << 4,\r
+            NDock = 1 << 5,\r
+            Mission = 1 << 6,\r
+            QuestList = 1 << 7,\r
+            Battle = 1 << 8,\r
+            All = (1 << 9) - 1\r
         }\r
 \r
         public Sniffer()\r
         {\r
-            _shipInfo = new ShipInfo(_shipMaster, _itemInfo);\r
-            _dockInfo = new DockInfo(_shipInfo, _itemInfo);\r
-            _akashiTimer = new AkashiTimer(_shipInfo, _itemInfo, _dockInfo, _missionInfo);\r
-            _battleInfo = new BattleInfo(_shipMaster, _shipInfo, _itemInfo);\r
+            _shipInfo = new ShipInfo(_itemInfo);\r
+            _conditionTimer = new ConditionTimer(_shipInfo);\r
+            _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
+            _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo);\r
+            _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
+            _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
+            _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
         }\r
 \r
-        public void SaveState()\r
+        private void SaveState()\r
         {\r
-            _achievement.SaveState(_status);\r
-            _itemInfo.SaveState(_status);\r
+            if (!_saveState)\r
+                return;\r
+            if (!_haveState.Any(x => x.NeedSave))\r
+                return;\r
+            foreach (var x in _haveState)\r
+                x.SaveState(_status);\r
             _status.Save();\r
         }\r
 \r
         public void LoadState()\r
         {\r
             _status.Load();\r
-            _achievement.LoadState(_status);\r
-            _itemInfo.LoadSate(_status);\r
+            foreach (var x in _haveState)\r
+                x.LoadState(_status);\r
+            _saveState = true;\r
         }\r
 \r
         public Update Sniff(string url, string request, dynamic json)\r
         {\r
-            var data = json.IsDefined("api_data") ? json.api_data : new object();\r
-\r
-            if (LogFile != null)\r
-            {\r
-                File.AppendAllText(LogFile,\r
-                    string.Format("url: {0}\nrequest: {1}\nresponse: {2}\n", url, request, json.ToString()));                \r
-            }\r
+            if (!json.api_result())\r
+                return Update.Error;\r
+            if ((int)json.api_result != 1)\r
+                return Update.None;\r
+            var data = json.api_data() ? json.api_data : new object();\r
 \r
             if (url.EndsWith("api_start2"))\r
             {\r
-                _start = true;\r
-                _shipMaster.Inspect(data.api_mst_ship);\r
-                _missionInfo.InspectMaster(data.api_mst_mission);\r
-                _itemInfo.InspectMaster(data.api_mst_slotitem);\r
-                return Update.Start;\r
+                return ApiStart(data);\r
             }\r
             if (!_start)\r
                 return Update.None;\r
             if (url.EndsWith("api_port/port"))\r
+                return ApiPort(data);\r
+            if (url.Contains("member"))\r
+                return ApiMember(url, json);\r
+            if (url.Contains("kousyou"))\r
+                return ApiKousyou(url, request, data);\r
+            if (url.Contains("battle"))\r
+                return ApiBattle(url, request, data);\r
+            return ApiOthers(url, request, data);\r
+        }\r
+\r
+        private Update ApiStart(dynamic data)\r
+        {\r
+            _shipInfo.InspectMaster(data);\r
+            _missionInfo.InspectMaster(data.api_mst_mission);\r
+            _itemInfo.InspectMaster(data);\r
+            _exMapInfo.ResetIfNeeded();\r
+            _start = true;\r
+            return Update.Start;\r
+        }\r
+\r
+        private Update ApiPort(dynamic data)\r
+        {\r
+            _itemInfo.InspectBasic(data.api_basic);\r
+            _materialInfo.InspectMaterial(data.api_material, true);\r
+            _logger.InspectBasic(data.api_basic);\r
+            _logger.InspectMaterial(data.api_material);\r
+            _shipInfo.InspectShip(data);\r
+            _shipInfo.ClearBadlyDamagedShips();\r
+            _conditionTimer.CalcRegenTime();\r
+            _missionInfo.InspectDeck(data.api_deck_port);\r
+            _dockInfo.InspectNDock(data.api_ndock);\r
+            _akashiTimer.Port();\r
+            _achievement.InspectBasic(data.api_basic);\r
+            if (data.api_parallel_quest_count()) // 昔のログにはないので\r
+                _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
+            _battleInfo.CleanupResult();\r
+            _battleInfo.InBattle = false;\r
+            _shipInfo.ClearEscapedShips();\r
+            _miscTextInfo.ClearIfNeeded();\r
+            SaveState();\r
+            return Update.All;\r
+        }\r
+\r
+        private Update ApiMember(string url, dynamic json)\r
+        {\r
+            var data = json.api_data() ? json.api_data : new object();\r
+\r
+            if (url.EndsWith("api_get_member/require_info"))\r
             {\r
-                _itemInfo.InspectBasic(data.api_basic);\r
-                _itemInfo.InspectMaterial(data.api_material);\r
-                _shipInfo.InspectShip(data);\r
-                _missionInfo.InspectDeck(data.api_deck_port);\r
-                _dockInfo.InspectNDock(data.api_ndock);\r
-                _akashiTimer.SetTimer(true);\r
-                _achievement.InspectBasic(data.api_basic);\r
-                _battleInfo.InBattle = false;\r
-                _battleInfo.HasDamagedShip = false;\r
-                return Update.All;\r
+                _itemInfo.InspectSlotItem(data.api_slot_item, true);\r
+                _dockInfo.InspectKDock(data.api_kdock);\r
+                return Update.Timer;\r
             }\r
             if (url.EndsWith("api_get_member/basic"))\r
             {\r
                 _itemInfo.InspectBasic(data);\r
+                _logger.InspectBasic(data);\r
                 return Update.None;\r
             }\r
             if (url.EndsWith("api_get_member/slot_item"))\r
             {\r
                 _itemInfo.InspectSlotItem(data, true);\r
-                return Update.None;\r
+                return Update.Item;\r
             }\r
             if (url.EndsWith("api_get_member/kdock"))\r
             {\r
                 _dockInfo.InspectKDock(data);\r
+                _logger.InspectKDock(data);\r
                 return Update.Timer;\r
             }\r
             if (url.EndsWith("api_get_member/ndock"))\r
             {\r
                 _dockInfo.InspectNDock(data);\r
-                _akashiTimer.SetTimer();\r
+                _conditionTimer.CheckCond();\r
+                _akashiTimer.CheckFleet();\r
                 return Update.NDock | Update.Timer | Update.Ship;\r
             }\r
-            if (url.EndsWith("api_req_hensei/change"))\r
-            {\r
-                _shipInfo.InspectChange(request);\r
-                _akashiTimer.SetTimer();\r
-                return Update.Ship;\r
-            }\r
             if (url.EndsWith("api_get_member/questlist"))\r
             {\r
-                _questInfo.Inspect(data);\r
+                _questInfo.InspectQuestList(data);\r
                 return Update.QuestList;\r
             }\r
             if (url.EndsWith("api_get_member/deck"))\r
             {\r
+                _shipInfo.InspectDeck(data);\r
                 _missionInfo.InspectDeck(data);\r
-                _akashiTimer.SetTimer();\r
+                _akashiTimer.CheckFleet();\r
                 return Update.Mission | Update.Timer;\r
             }\r
             if (url.EndsWith("api_get_member/ship2"))\r
             {\r
                 // ここだけjsonなので注意\r
                 _shipInfo.InspectShip(json);\r
-                _akashiTimer.SetTimer();\r
+                _akashiTimer.CheckFleet();\r
                 _battleInfo.InBattle = false;\r
                 return Update.Item | Update.Ship | Update.Battle;\r
             }\r
+            if (url.EndsWith("api_get_member/ship_deck"))\r
+            {\r
+                _shipInfo.InspectShip(data);\r
+                _akashiTimer.CheckFleet();\r
+                _battleInfo.InBattle = false;\r
+                return Update.Ship | Update.Battle;\r
+            }\r
             if (url.EndsWith("api_get_member/ship3"))\r
             {\r
                 _shipInfo.InspectShip(data);\r
-                _akashiTimer.SetTimer();\r
+                _akashiTimer.CheckFleet();\r
+                _conditionTimer.CheckCond();\r
                 return Update.Ship;\r
             }\r
             if (url.EndsWith("api_get_member/material"))\r
             {\r
-                _itemInfo.InspectMaterial(data);\r
+                _materialInfo.InspectMaterial(data);\r
                 return Update.Item;\r
             }\r
-            if (url.EndsWith("api_req_hokyu/charge"))\r
+            if (url.EndsWith("api_get_member/mapinfo"))\r
             {\r
-                _shipInfo.InspectCharge(data);\r
-                return Update.Item | Update.Ship;\r
+                _exMapInfo.InspectMapInfo(data);\r
+                _miscTextInfo.InspectMapInfo(data);\r
+                return Update.Item;\r
+            }\r
+            if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
+            {\r
+                _miscTextInfo.InspectPracticeEnemyInfo(data);\r
+                return Update.Item;\r
             }\r
+            if (url.EndsWith("api_get_member/preset_deck"))\r
+            {\r
+                _shipInfo.InspectPresetDeck(data);\r
+                return Update.None;\r
+            }\r
+            return Update.None;\r
+        }\r
+\r
+        private Update ApiKousyou(string url, string request, dynamic data)\r
+        {\r
             if (url.EndsWith("api_req_kousyou/createitem"))\r
             {\r
                 _itemInfo.InspectCreateItem(data);\r
+                _materialInfo.InspectCreateIem(data);\r
+                _logger.InspectCreateItem(request, data);\r
                 return Update.Item;\r
             }\r
             if (url.EndsWith("api_req_kousyou/getship"))\r
@@ -176,158 +248,296 @@ namespace KancolleSniffer
                 _itemInfo.InspectGetShip(data);\r
                 _shipInfo.InspectShip(data);\r
                 _dockInfo.InspectKDock(data.api_kdock);\r
+                _conditionTimer.CheckCond();\r
                 return Update.Item | Update.Timer;\r
             }\r
             if (url.EndsWith("api_req_kousyou/destroyship"))\r
             {\r
                 _shipInfo.InspectDestroyShip(request, data);\r
-                _akashiTimer.SetTimer();\r
+                _materialInfo.InspectDestroyShip(data);\r
+                _conditionTimer.CheckCond();\r
+                _akashiTimer.CheckFleet();\r
                 return Update.Item | Update.Ship;\r
             }\r
             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
             {\r
                 _itemInfo.InspectDestroyItem(request, data);\r
+                _materialInfo.InspectDestroyItem(data);\r
                 return Update.Item;\r
             }\r
-            if (url.EndsWith("api_req_kaisou/powerup"))\r
+            if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
             {\r
-                _shipInfo.InspectPowerup(request, data);\r
-                return Update.Item | Update.Ship;\r
+                _logger.SetCurrentMaterial(_materialInfo.Current);\r
+                _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
+                _itemInfo.InspectRemodelSlot(data);\r
+                _materialInfo.InspectRemodelSlot(data);\r
+                return Update.Item;\r
             }\r
-            if (url.EndsWith("api_req_nyukyo/start"))\r
+            if (url.EndsWith("api_req_kousyou/createship"))\r
             {\r
-                _dockInfo.InspectNyukyo(request);\r
-                _akashiTimer.SetTimer();\r
-                return Update.Item | Update.Ship;\r
+                _logger.InspectCreateShip(request);\r
+                return Update.None;\r
             }\r
-            if (url.EndsWith("api_req_nyukyo/speedchange"))\r
+            if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
             {\r
-                _dockInfo.InspectSpeedChange(request);\r
-                return Update.NDock | Update.Timer | Update.Ship;\r
+                _dockInfo.InspectCreateShipSpeedChange(request);\r
+                return Update.Timer;\r
             }\r
+            return Update.None;\r
+        }\r
+\r
+        private Update ApiBattle(string url, string request, dynamic data)\r
+        {\r
             if (IsNormalBattleAPI(url))\r
             {\r
-                _battleInfo.InspectBattle(data);\r
-                return Update.Battle;\r
+                _battleInfo.InspectBattle(data, url);\r
+                _logger.InspectBattle(data);\r
+                return Update.Ship | Update.Battle;\r
+            }\r
+            if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
+            {\r
+                if (url.EndsWith("/battle"))\r
+                {\r
+                    _shipInfo.InspectMapStart(request); // 演習を出撃中とみなす\r
+                    _conditionTimer.InvalidateCond();\r
+                    _miscTextInfo.ClearFlag = true;\r
+                }\r
+                _battleInfo.InspectBattle(data, url);\r
+                return Update.Ship | Update.Battle | Update.Timer;\r
             }\r
-            if (url.EndsWith("api_req_sortie/battleresult") || url.EndsWith("api_req_practice/battleresult"))\r
+            if (url.EndsWith("api_req_sortie/battleresult"))\r
             {\r
-                _battleInfo.CauseDamage();\r
+                _battleInfo.InspectBattleResult(data);\r
+                _exMapInfo.InspectBattleResult(data);\r
+                _logger.InspectBattleResult(data);\r
                 return Update.Ship;\r
             }\r
-            if (url.EndsWith("api_req_mission/result"))\r
+            if (url.EndsWith("api_req_practice/battle_result"))\r
             {\r
-                _itemInfo.InspectMissionResult(data);\r
-                return Update.Item;\r
+                _battleInfo.InspectPracticeResult(data);\r
+                return Update.Ship;\r
             }\r
             if (IsCombinedBattleAPI(url))\r
             {\r
-                _battleInfo.InspectCombinedBattle(data);\r
-                return Update.Battle;\r
+                _battleInfo.InspectCombinedBattle(data, url);\r
+                _logger.InspectBattle(data);\r
+                return Update.Ship | Update.Battle;\r
             }\r
             if (url.EndsWith("api_req_combined_battle/battleresult"))\r
             {\r
-                _battleInfo.CauseDamageCombined();\r
+                _battleInfo.InspectCombinedBattleResult(data);\r
+                _logger.InspectBattleResult(data);\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_combined_battle/goback_port"))\r
+            {\r
+                _battleInfo.CauseCombinedBattleEscape();\r
                 return Update.Ship;\r
             }\r
             return Update.None;\r
         }\r
 \r
-        public bool IsBattleAPI(string url)\r
-        {\r
-            return IsNormalBattleAPI(url) || IsCombinedBattleAPI(url);\r
-        }\r
-\r
-        public bool IsNormalBattleAPI(string url)\r
+        private bool IsNormalBattleAPI(string url)\r
         {\r
-            return url.EndsWith("api_req_sortie/battle") || url.EndsWith("api_req_practice/battle") ||\r
-                   url.EndsWith("api_req_battle_midnight/sp_midnight") ||\r
-                   url.EndsWith("api_req_practice/midnight_battle");\r
+            return url.EndsWith("api_req_sortie/battle") ||\r
+                   url.EndsWith("api_req_sortie/airbattle") ||\r
+                   url.EndsWith("api_req_sortie/ld_airbattle") ||\r
+                   url.EndsWith("api_req_battle_midnight/battle") ||\r
+                   url.EndsWith("api_req_battle_midnight/sp_midnight");\r
         }\r
 \r
-        public bool IsCombinedBattleAPI(string url)\r
+        private bool IsCombinedBattleAPI(string url)\r
         {\r
-            return url.EndsWith("api_req_combined_battle/battle") || url.EndsWith("api_req_combined_battle/airbattle") ||\r
+            return url.EndsWith("api_req_combined_battle/battle") ||\r
+                   url.EndsWith("api_req_combined_battle/airbattle") ||\r
+                   url.EndsWith("api_req_combined_battle/ld_airbattle") ||\r
+                   url.EndsWith("api_req_combined_battle/battle_water") ||\r
                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
                    url.EndsWith("api_req_combined_battle/sp_midnight");\r
         }\r
 \r
-        public NameAndTimer[] NDock\r
+        private Update ApiOthers(string url, string request, dynamic data)\r
         {\r
-            get { return _dockInfo.NDock; }\r
+            if (url.EndsWith("api_req_hensei/change"))\r
+            {\r
+                _shipInfo.InspectChange(request);\r
+                _akashiTimer.InspectChange(request);\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_hensei/preset_select"))\r
+            {\r
+                _shipInfo.InspectDeck(new[] {data});\r
+                _akashiTimer.CheckFleet();\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_hensei/preset_register"))\r
+            {\r
+                _shipInfo.InspectPresetRegister(data);\r
+                return Update.None;\r
+            }\r
+            if (url.EndsWith("api_req_hensei/preset_delete"))\r
+            {\r
+                _shipInfo.InspectPresetDelete(request);\r
+                return Update.Timer;\r
+            }\r
+            if (url.EndsWith("api_req_hensei/combined"))\r
+            {\r
+                _shipInfo.InspectCombined(request);\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_hokyu/charge"))\r
+            {\r
+                _shipInfo.InspectCharge(data);\r
+                _materialInfo.InspectCharge(data);\r
+                return Update.Item | Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_kaisou/powerup"))\r
+            {\r
+                _shipInfo.InspectPowerup(request, data);\r
+                _conditionTimer.CheckCond();\r
+                _akashiTimer.CheckFleet();\r
+                return Update.Item | Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_kaisou/slot_exchange_index"))\r
+            {\r
+                _shipInfo.InspectSlotExchange(request, data);\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_kaisou/slot_deprive"))\r
+            {\r
+                _shipInfo.InspectSlotDeprive(data);\r
+                return Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_nyukyo/start"))\r
+            {\r
+                _dockInfo.InspectNyukyo(request);\r
+                _conditionTimer.CheckCond();\r
+                _akashiTimer.CheckFleet();\r
+                return Update.Item | Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_nyukyo/speedchange"))\r
+            {\r
+                _dockInfo.InspectSpeedChange(request);\r
+                _conditionTimer.CheckCond();\r
+                return Update.NDock | Update.Timer | Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_map/start"))\r
+            {\r
+                _shipInfo.InspectMapStart(request); // 出撃中判定が必要なので_conditionTimerより前\r
+                _conditionTimer.InvalidateCond();\r
+                _exMapInfo.InspectMapStart(data);\r
+                _logger.InspectMapStart(data);\r
+                _miscTextInfo.ClearFlag = true;\r
+                return Update.Timer | Update.Ship;\r
+            }\r
+            if (url.EndsWith("api_req_map/next"))\r
+            {\r
+                _battleInfo.InspectMapNext(request);\r
+                _exMapInfo.InspectMapNext(data);\r
+                _logger.InspectMapNext(data);\r
+                return Update.None;\r
+            }\r
+            if (url.EndsWith("api_req_mission/result"))\r
+            {\r
+                _materialInfo.InspectMissionResult(data);\r
+                _logger.InspectMissionResult(data);\r
+                return Update.Item;\r
+            }\r
+            if (url.EndsWith("api_req_quest/stop"))\r
+            {\r
+                _questInfo.InspectStop(request);\r
+                return Update.QuestList;\r
+            }\r
+            if (url.EndsWith("api_req_quest/clearitemget"))\r
+            {\r
+                _questInfo.InspectClearItemGet(request);\r
+                return Update.QuestList;\r
+            }\r
+            if (url.EndsWith("api_req_air_corps/supply"))\r
+            {\r
+                _materialInfo.InspectAirCorpsSupply(data);\r
+                return Update.Item;\r
+            }\r
+            if (url.EndsWith("api_req_air_corps/set_plane"))\r
+            {\r
+                _materialInfo.InspectAirCorpsSetPlane(data);\r
+                return Update.Item;\r
+            }\r
+            return Update.None;\r
         }\r
 \r
-        public RingTimer[] KDock\r
-        {\r
-            get { return _dockInfo.KDock; }\r
-        }\r
+        public NameAndTimer[] NDock => _dockInfo.NDock;\r
 \r
-        public ItemInfo Item\r
-        {\r
-            get { return _itemInfo; }\r
-        }\r
+        public RingTimer[] KDock => _dockInfo.KDock;\r
 \r
-        public QuestInfo.NameAndProgress[] Quests\r
-        {\r
-            get { return _questInfo.Quests; }\r
-        }\r
+        public ItemInfo Item => _itemInfo;\r
 \r
-        public NameAndTimer[] Missions\r
-        {\r
-            get { return _missionInfo.Missions; }\r
-        }\r
+        public MaterialInfo Material => _materialInfo;\r
 \r
-        public string[] GetConditionTimers(int fleet)\r
-        {\r
-            return _shipInfo.GetConditionTimers(fleet);\r
-        }\r
+        public QuestStatus[] Quests => _questInfo.Quests;\r
 \r
-        public ShipStatus[] GetShipStatuses(int fleet)\r
-        {\r
-            return _shipInfo.GetShipStatuses(fleet);\r
-        }\r
+        public NameAndTimer[] Missions => _missionInfo.Missions;\r
 \r
-        public ChargeStatus[] ChargeStatuses\r
-        {\r
-            get { return _shipInfo.ChargeStatuses; }\r
-        }\r
+        public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
 \r
-        public int GetAirSuperiority(int fleet)\r
-        {\r
-            return _shipInfo.GetAirSuperiority(fleet);\r
-        }\r
+        public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
 \r
-        public DamageStatus[] DamagedShipList\r
-        {\r
-            get { return _shipInfo.GetDamagedShipList(_dockInfo); }\r
-        }\r
+        public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
+\r
+        public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
+\r
+        public int CombinedFleetType => _shipInfo.CombinedFleetType;\r
+\r
+        public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
+\r
+        public int[] GetFighterPower(int fleet) => _shipInfo.GetFighterPower(fleet);\r
 \r
-        public ShipStatus[] ShipList\r
+        public double GetContactTriggerRate(int fleet) => _shipInfo.GetContactTriggerRate(fleet);\r
+\r
+        public double GetFleetLineOfSights(int fleet) => _shipInfo.GetLineOfSights(fleet);\r
+\r
+        public ShipStatus[] DamagedShipList => _shipInfo.GetDamagedShipList(_dockInfo);\r
+\r
+        public ShipStatus[] ShipList => _shipInfo.ShipList;\r
+\r
+        public string[] BadlyDamagedShips => _shipInfo.BadlyDamagedShips;\r
+\r
+        public ItemStatus[] ItemList => _itemInfo.GetItemListWithOwner(ShipList);\r
+\r
+        public AkashiTimer AkashiTimer => _akashiTimer;\r
+\r
+        public Achievement Achievement => _achievement;\r
+\r
+        public BattleInfo Battle => _battleInfo;\r
+\r
+        public ExMapInfo ExMap => _exMapInfo;\r
+\r
+        public string MiscText => _miscTextInfo.Text;\r
+\r
+        public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
         {\r
-            get { return _shipInfo.ShipList; }\r
+            _logger.SetWriter(writer, nowFunc);\r
         }\r
 \r
-        public AkashiTimer.RepairSpan[] GetAkashiTimers(int fleet)\r
+        public void SkipMaster()\r
         {\r
-            return _akashiTimer.GetTimers(fleet);\r
+            _start = true;\r
         }\r
 \r
-        public string[] GetAkashiTimerNotice()\r
+        public void EnableLog(LogType type)\r
         {\r
-            return _akashiTimer.GetNotice();\r
+            _logger.EnableLog(type);\r
         }\r
 \r
-        public Achievement Achievement\r
+        public int MaterialLogInterval\r
         {\r
-            get { return _achievement; }\r
+            set { _logger.MaterialLogInterval = value; }\r
         }\r
 \r
-        public BattleInfo Battle\r
+        public string LogOutputDir\r
         {\r
-            get { return _battleInfo; }\r
+            set { _logger.OutputDir = value; }\r
         }\r
-\r
-        public string LogFile { get; set; }\r
     }\r
 \r
     public class NameAndTimer\r
@@ -343,10 +553,16 @@ namespace KancolleSniffer
 \r
     public class RingTimer\r
     {\r
-        private DateTime _endTime;\r
-        private TimeSpan _rest;\r
         private readonly TimeSpan _spare;\r
 \r
+        public TimeSpan Rest { get; private set; }\r
+\r
+        public bool IsFinished => EndTime != DateTime.MinValue && Rest <= _spare;\r
+\r
+        public DateTime EndTime { get; private set; }\r
+\r
+        public bool NeedRing { get; set; }\r
+\r
         public RingTimer(int spare = 60)\r
         {\r
             _spare = TimeSpan.FromSeconds(spare);\r
@@ -361,33 +577,27 @@ namespace KancolleSniffer
 \r
         public void SetEndTime(DateTime time)\r
         {\r
-            _endTime = time;\r
-            if (_endTime == DateTime.MinValue)\r
-                IsFinished = false;\r
+            EndTime = time;\r
         }\r
 \r
         public void Update()\r
         {\r
-            if (_endTime == DateTime.MinValue)\r
+            if (EndTime == DateTime.MinValue)\r
             {\r
-                _rest = TimeSpan.Zero;\r
+                Rest = TimeSpan.Zero;\r
                 return;\r
             }\r
-            _rest = _endTime - DateTime.Now;\r
-            if (_rest < TimeSpan.Zero)\r
-                _rest = TimeSpan.Zero;\r
-            if (_rest > _spare || IsFinished)\r
-                return;\r
-            IsFinished = true;\r
-            NeedRing = true;\r
+            var prev = Rest;\r
+            Rest = EndTime - DateTime.Now;\r
+            if (Rest < TimeSpan.Zero)\r
+                Rest = TimeSpan.Zero;\r
+            if (prev > _spare && _spare >= Rest)\r
+                NeedRing = true;\r
         }\r
 \r
-        public bool IsFinished { get; private set; }\r
-        public bool NeedRing { get; set; }\r
-\r
-        public override string ToString()\r
-        {\r
-            return _rest.Days == 0 ? _rest.ToString(@"hh\:mm\:ss") : _rest.ToString(@"d\.hh\:mm");\r
-        }\r
+        public string ToString(bool finish = false)\r
+            => EndTime == DateTime.MinValue\r
+                ? ""\r
+                : finish ? EndTime.ToString(@"dd\ HH\:mm") : $@"{(int)Rest.TotalHours:d2}:{Rest:mm\:ss}";\r
     }\r
 }
\ No newline at end of file