OSDN Git Service

明石が中破以上でもタイマーが進んでしまうのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / AkashiTimer.cs
index 6a756ed..e59a887 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\r
+// Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
 // \r
 // This program is part of KancolleSniffer.\r
 //\r
 using System;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
+using System.Web;\r
 \r
 namespace KancolleSniffer\r
 {\r
     public class AkashiTimer\r
     {\r
         private readonly ShipInfo _shipInfo;\r
-        private readonly ItemInfo _itemInfo;\r
         private readonly DockInfo _dockInfo;\r
         private readonly RepairStatus[] _repairStatuses = new RepairStatus[ShipInfo.FleetCount];\r
         private DateTime _start;\r
@@ -52,26 +52,17 @@ namespace KancolleSniffer
                 set { _deck = value; }\r
             }\r
 \r
-            public void Invalidate()\r
-            {\r
-                _target = new ShipStatus[0];\r
-            }\r
+            public State State { get; set; }\r
 \r
-            public bool DeckChanged(IEnumerable<int> deck)\r
-            {\r
-                return !_deck.SequenceEqual(deck);\r
-            }\r
+            public bool IsRepaired(ShipStatus[] target) => _target.Zip(target, (a, b) => a.NowHp < b.NowHp).Any(x => x);\r
+\r
+            public bool DeckChanged(IEnumerable<int> deck) => !_deck.SequenceEqual(deck);\r
 \r
             public void UpdateTarget(ShipStatus[] target)\r
             {\r
                 _target = target;\r
             }\r
 \r
-            public bool IsRepaired(ShipStatus[] target)\r
-            {\r
-                return _target.Zip(target, (a, b) => a.NowHp < b.NowHp).Any(x => x);\r
-            }\r
-\r
             public RepairSpan[] GetTimers(DateTime start, DateTime now)\r
             {\r
                 var spent = TimeSpan.FromSeconds((int)(now - start).TotalSeconds);\r
@@ -96,125 +87,167 @@ namespace KancolleSniffer
                 }).ToArray();\r
             }\r
 \r
-            public string GetNotice(DateTime start, DateTime prev, DateTime now)\r
+            public Notice GetNotice(DateTime start, DateTime prev, DateTime now)\r
             {\r
-                var msg = string.Join(" ", _target.Where(s =>\r
+                var m20 = TimeSpan.FromMinutes(20);\r
+                var proc = new List<string>();\r
+                var comp = new List<string>();\r
+                foreach (var s in _target)\r
                 {\r
                     var damage = s.MaxHp - s.NowHp;\r
-                    if (damage < 2)\r
-                        return false;\r
-                    for (var d = 2; d <= damage; d++)\r
+                    if (damage == 0)\r
+                        continue;\r
+                    if (damage == 1)\r
+                    {\r
+                        if (prev - start < m20 && now - start >= m20)\r
+                            comp.Add(s.Name);\r
+                        continue;\r
+                    }\r
+                    // スリープで時間が飛んだときに修理完了だけを表示するために、\r
+                    // 完全回復から減らしながら所要時間と経過時間と比較する。\r
+                    for (var d = damage; d >= 2; d--)\r
                     {\r
                         var sec = s.CalcRepairSec(d) + 60;\r
                         if (sec <= 20 * 60)\r
+                        {\r
+                            if (d == damage && (prev - start < m20 && now - start >= m20))\r
+                                comp.Add(s.Name);\r
                             continue;\r
+                        }\r
                         var span = TimeSpan.FromSeconds(sec);\r
-                        if (span > prev - start && span <= now - start)\r
-                            return true;\r
+                        if (span <= prev - start || now - start < span)\r
+                            continue;\r
+                        if (d == damage)\r
+                            comp.Add(s.Name);\r
+                        else\r
+                            proc.Add(s.Name);\r
+                        break;\r
                     }\r
-                    return false;\r
-                }).Select(s => s.Name));\r
-                return msg == "" ? "" : "修理進行: " + msg;\r
+                }\r
+                return new Notice\r
+                {\r
+                    Proceeded = proc.Count == 0 ? "" : string.Join(" ", proc),\r
+                    Completed = comp.Count == 0 ? "" : string.Join(" ", comp)\r
+                };\r
             }\r
         }\r
 \r
-        public AkashiTimer(ShipInfo ship, ItemInfo item, DockInfo dock)\r
+        public class Notice\r
+        {\r
+            public string Proceeded { get; set; }\r
+            public string Completed { get; set; }\r
+        }\r
+\r
+        public AkashiTimer(ShipInfo ship, DockInfo dock)\r
         {\r
             _shipInfo = ship;\r
-            _itemInfo = item;\r
             _dockInfo = dock;\r
             for (var i = 0; i < _repairStatuses.Length; i++)\r
                 _repairStatuses[i] = new RepairStatus();\r
         }\r
 \r
-        public void SetTimer(bool port = false)\r
+        private enum State\r
         {\r
-            var cont = false;\r
-            for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)\r
-            {\r
-                if (CheckFleet(fleet, port))\r
-                    cont = true;\r
-            }\r
-            if (!cont)\r
-            {\r
-                _start = DateTime.MinValue;\r
+            Continue = 0,\r
+            Reset = 1,\r
+        }\r
+\r
+        public void Port()\r
+        {\r
+            CheckFleet();\r
+            var now = DateTime.Now;\r
+            var reset = _repairStatuses.Any(r => r.State == State.Reset);\r
+            if (_start == DateTime.MinValue || now - _start > TimeSpan.FromMinutes(20) || reset)\r
+                _start = now;\r
+        }\r
+\r
+        public void InspectChange(string request)\r
+        {\r
+            CheckFleet();\r
+            var values = HttpUtility.ParseQueryString(request);\r
+            if (int.Parse(values["api_ship_idx"]) == -1)\r
                 return;\r
-            }\r
-            if (_start == DateTime.MinValue)\r
+            if (_repairStatuses.Any(r => r.State == State.Reset))\r
                 _start = DateTime.Now;\r
         }\r
 \r
-        private bool CheckFleet(int fleet, bool port)\r
+        public void CheckFleet()\r
         {\r
-            var deck = _shipInfo.GetDeck(fleet);\r
+            for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)\r
+                CheckFleet(fleet);\r
+        }\r
+\r
+        private void CheckFleet(int fleet)\r
+        {\r
+            var deck = _shipInfo.GetDeck(fleet).ToArray();\r
             var repair = _repairStatuses[fleet];\r
-            var fs = deck[0];\r
-            if (!_shipInfo[fs].Name.StartsWith("明石") || _dockInfo.InNDock(fs) || _shipInfo.InMission(fleet))\r
+            var fs = _shipInfo.GetStatus(deck[0]);\r
+            repair.State = State.Continue;\r
+            if (!fs.Spec.IsRepairShip)\r
             {\r
-                repair.Invalidate();\r
-                return false;\r
+                repair.UpdateTarget(new ShipStatus[0]);\r
+                repair.Deck = deck;\r
+                return;\r
             }\r
             if (repair.DeckChanged(deck))\r
             {\r
-                _start = DateTime.MinValue;\r
-                repair.Invalidate();\r
-            }\r
-            repair.Deck = deck.ToArray();\r
-            var cap = _shipInfo[fs].Slot.Count(item => _itemInfo[item].Name == "艦艇修理施設") + 2;\r
-            var target = deck.Take(cap).Select(id =>\r
-            {\r
-                /*\r
-                 * 修理できない艦娘のステータスをNowHp == MaxHpにする。\r
-                 * - 入渠中の艦娘\r
-                 *   入渠が終了して戻ったのを明石による回復と誤認しないためにHPを回復時の値に\r
-                 * - 中破以上の艦娘\r
-                 *   出撃中の損傷で小破以下から中破以上になったのを回復と誤認しないためにHPを0に\r
-                */\r
-                var s = _shipInfo[id];\r
-                var full = new ShipStatus {NowHp = s.MaxHp, MaxHp = s.MaxHp};\r
-                var zero = new ShipStatus();\r
-                return _dockInfo.InNDock(id)\r
-                    ? full\r
-                    : s.DamageLevel >= ShipStatus.Damage.Half ? zero : s;\r
-            }).ToArray();\r
-            var damage = target.Sum(s => s.MaxHp - s.NowHp);\r
-            if (damage == 0)\r
-            {\r
-                repair.Invalidate();\r
-                return false;\r
+                repair.State = State.Reset;\r
+                repair.Deck = deck;\r
             }\r
+            var target = RepairTarget(deck);\r
+            if (repair.IsRepaired(target))\r
+                repair.State = State.Reset;\r
+            repair.UpdateTarget(target);\r
+        }\r
+\r
+        private ShipStatus[] RepairTarget(int[] deck)\r
+        {\r
+            var fs = _shipInfo.GetStatus(deck[0]);\r
+            if (fs.DamageLevel >= ShipStatus.Damage.Half)\r
+                return new ShipStatus[0];\r
+            var cap = fs.Slot.Count(item => item.Spec.IsRepairFacility) + 2;\r
             /*\r
-             * 母港に遷移したときに、耐久値が回復しているか修理開始から20分経過している\r
-             * ときにタイマーをリスタートする。\r
-             *\r
-             * 泊地修理中に出撃して母港に戻ったときに、修理開始から20分以上経っていると\r
-             * HPが回復する。最後の戦闘の損傷と回復量が差し引きゼロだと泊地修理の進行が\r
-             * わからないので、20分経っていたらとにかくリスタートする。\r
+             * 泊地修理の条件を満たさない艦はMaxHp==NowHpのダミーを設定する。\r
+             * - 入渠中の艦娘は終わったときに回復扱いされないようNowHp=MaxHpに\r
+             * - 中破以上でNowHp=MaxHpにすると回復扱いされるのでNowHp=MaxHp=0に\r
             */\r
-            if (port && _start != DateTime.MinValue &&\r
-                (repair.IsRepaired(target) || DateTime.Now - _start > TimeSpan.FromMinutes(20)))\r
-                _start = DateTime.MinValue;\r
-            repair.UpdateTarget(target);\r
-            return true;\r
+            return (from id in deck.Take(cap)\r
+                let s = _shipInfo.GetStatus(id)\r
+                let full = new ShipStatus {NowHp = s.MaxHp, MaxHp = s.MaxHp}\r
+                let zero = new ShipStatus()\r
+                select _dockInfo.InNDock(id) ? full : s.DamageLevel >= ShipStatus.Damage.Half ? zero : s).ToArray();\r
         }\r
 \r
         public RepairSpan[] GetTimers(int fleet)\r
+            => _start == DateTime.MinValue ? new RepairSpan[0] : _repairStatuses[fleet].GetTimers(_start, DateTime.Now);\r
+\r
+        public TimeSpan PresetDeckTimer\r
         {\r
-            if (_start == DateTime.MinValue)\r
-                return new RepairSpan[0];\r
-            return _repairStatuses[fleet].GetTimers(_start, DateTime.Now);\r
+            get\r
+            {\r
+                if (_start == DateTime.MinValue)\r
+                    return TimeSpan.MinValue;\r
+                var r = _start + TimeSpan.FromMinutes(20) - DateTime.Now;\r
+                return r >= TimeSpan.Zero ? r : TimeSpan.Zero;\r
+            }\r
         }\r
 \r
-        public string[] GetNotice()\r
+        public bool CheckReparing(int fleet) => GetTimers(fleet).Any(r => r.Span != TimeSpan.MinValue);\r
+\r
+        public bool CheckReparing() => Enumerable.Range(0, ShipInfo.FleetCount).Any(CheckReparing);\r
+\r
+        public Notice[] GetNotice()\r
         {\r
             var now = DateTime.Now;\r
             var prev = _prev;\r
             _prev = now;\r
             if (prev == DateTime.MinValue || _start == DateTime.MinValue)\r
-                return new string[0];\r
-            if (prev - _start < TimeSpan.FromMinutes(20) && now - _start >= TimeSpan.FromMinutes(20))\r
-                return new[] {"20分経過しました。"};\r
-            return _repairStatuses.Select(repair => repair.GetNotice(_start, prev, now)).ToArray();\r
+                return new Notice[0];\r
+            var r = _repairStatuses.Select(repair => repair.GetNotice(_start, prev, now)).ToArray();\r
+            var m20 = TimeSpan.FromMinutes(20);\r
+            if (prev - _start < m20 && now - _start >= m20)\r
+                r[0].Proceeded = "20分経過しました。";\r
+            return r;\r
         }\r
     }\r
 }
\ No newline at end of file