OSDN Git Service

AkashiTimer.GetTimersがnullを返さないようにする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / AkashiTimer.cs
1 // Copyright (C) 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\r
2 // \r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Linq;\r
20 \r
21 namespace KancolleSniffer\r
22 {\r
23     public class AkashiTimer\r
24     {\r
25         private readonly ShipInfo _shipInfo;\r
26         private readonly ItemInfo _itemInfo;\r
27         private readonly DockInfo _dockInfo;\r
28         private readonly RepairStatus[] _repairStatuses = new RepairStatus[ShipInfo.FleetCount];\r
29 \r
30         public class RepairTime\r
31         {\r
32             public int Diff { get; set; }\r
33             public DateTime Time { get; set; }\r
34 \r
35             public RepairTime(int diff, DateTime time)\r
36             {\r
37                 Diff = diff;\r
38                 Time = time;\r
39             }\r
40         }\r
41 \r
42         public class RepairSpan\r
43         {\r
44             public int Diff { get; set; }\r
45             public TimeSpan Span { get; set; }\r
46 \r
47             public RepairSpan(int diff, TimeSpan span)\r
48             {\r
49                 Diff = diff;\r
50                 Span = span;\r
51             }\r
52 \r
53             public RepairSpan(RepairTime time)\r
54             {\r
55                 Diff = time.Diff;\r
56                 Span = TimeSpan.FromSeconds(Math.Ceiling((time.Time - DateTime.Now).TotalSeconds));\r
57             }\r
58         }\r
59 \r
60         private class RepairStatus\r
61         {\r
62             private ShipStatus[] _target;\r
63             private RepairTime[][] _times = new RepairTime[0][];\r
64             private DateTime _prev;\r
65             public DateTime Start { get; set; }\r
66             public int[] Deck { private get; set; }\r
67 \r
68             public int TotalHp\r
69             {\r
70                 get { return _target == null ? 0 : _target.Sum(s => s.NowHp); }\r
71             }\r
72 \r
73             public void Invalidate()\r
74             {\r
75                 Start = DateTime.MinValue;\r
76                 Deck = null;\r
77                 _target = null;\r
78                 _times = new RepairTime[0][];\r
79             }\r
80 \r
81             public bool DeckChanged(int[] deck)\r
82             {\r
83                 return Deck != null && Deck.Where((t, i) => deck[i] != t).Any();\r
84             }\r
85 \r
86             public void UpdateTarget(ShipStatus[] target)\r
87             {\r
88                 _target = target;\r
89                 UpdateTimes();\r
90             }\r
91 \r
92             private void UpdateTimes()\r
93             {\r
94                 _times = (from s in _target\r
95                     let damage = s.MaxHp - s.NowHp\r
96                     let first = new RepairTime(0, Start.AddMinutes(20))\r
97                     select damage == 0\r
98                         ? null\r
99                         : new[] {first}.Concat(from d in Enumerable.Range(2, damage < 2 ? 0 : damage - 1)\r
100                             let span = s.CalcRepairTime(d) + TimeSpan.FromSeconds(30)\r
101                             where span.TotalSeconds > 20 * 60\r
102                             select new RepairTime(d - 1, Start + span)).ToArray()).ToArray();\r
103             }\r
104 \r
105             public RepairSpan[] GetTimers()\r
106             {\r
107                 return (from times in _times\r
108                     select times == null\r
109                         ? new RepairSpan(0, TimeSpan.MinValue)\r
110                         : (from t in times select new RepairSpan(t)).FirstOrDefault(s => s.Span > TimeSpan.Zero)\r
111                           ?? new RepairSpan(times.Last().Diff + 1, TimeSpan.Zero)\r
112                     ).ToArray();\r
113             }\r
114 \r
115             public string GetNotice()\r
116             {\r
117                 var now = DateTime.Now;\r
118                 if (Start == DateTime.MinValue)\r
119                     return "";\r
120                 var pr = _prev;\r
121                 _prev = now;\r
122                 if (pr == DateTime.MinValue)\r
123                     return "";\r
124                 var m20 = TimeSpan.FromMinutes(20);\r
125                 if (pr - Start < m20 && now - Start >= m20)\r
126                     return "20分経過しました。";\r
127                 var margin = TimeSpan.Zero;\r
128                 var msg = string.Join(" ", from e in _times.Zip(_target, (times, ship) => new {times, ship})\r
129                     where e.times != null && e.times.Any(rt => rt.Time - pr > margin && rt.Time - now <= margin)\r
130                     select e.ship.Name);\r
131                 return msg == "" ? "" : "修理進行: " + msg;\r
132             }\r
133         }\r
134 \r
135         public AkashiTimer(ShipInfo ship, ItemInfo item, DockInfo dock)\r
136         {\r
137             _shipInfo = ship;\r
138             _itemInfo = item;\r
139             _dockInfo = dock;\r
140             for (var i = 0; i < _repairStatuses.Length; i++)\r
141                 _repairStatuses[i] = new RepairStatus();\r
142         }\r
143 \r
144         public void SetTimer(bool port = false)\r
145         {\r
146             for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)\r
147                 SetTimer(fleet, port);\r
148         }\r
149 \r
150         private void SetTimer(int fleet, bool port)\r
151         {\r
152             var deck = _shipInfo.GetDeck(fleet);\r
153             var repair = _repairStatuses[fleet];\r
154             var fs = deck[0];\r
155             if (!_shipInfo[fs].Name.StartsWith("明石") || _dockInfo.InNDock(fs) || _shipInfo.InMission(fleet))\r
156             {\r
157                 repair.Invalidate();\r
158                 return;\r
159             }\r
160             if (repair.DeckChanged(deck))\r
161                 repair.Invalidate();\r
162             repair.Deck = deck.ToArray();\r
163             var cap = _shipInfo[fs].Slot.Count(item => _itemInfo[item].Name == "艦艇修理施設") + 2;\r
164             var target = (from id in deck.Take(cap) select IsRepairable(id) ? _shipInfo[id] : new ShipStatus()).ToArray();\r
165             var totalHp = target.Sum(s => s.NowHp);\r
166             if (totalHp == 0)\r
167             {\r
168                 repair.Invalidate();\r
169                 return;\r
170             }\r
171             if (totalHp == repair.TotalHp)\r
172                 return;\r
173             /*\r
174              * 母港に遷移したときに、耐久値が回復しているか修理開始から20分経過しているときに\r
175              * タイマーをリスタートする。\r
176              * \r
177              * Q. なぜ20分でリスタートするのか?\r
178              * \r
179              * A. 明石の修理は戦闘中も続くので、戦闘中に修理開始から20分経つと母港に戻った\r
180              *    ときに耐久値が回復する。最後の戦闘で損傷すると、損傷と回復が同時に耐久値に\r
181              *    反映されて回復が起こったことがわからない。耐久値の回復だけを基準にすると\r
182              *    リスタートできないので、20分経過していたらリスタートする。\r
183             */\r
184             if (repair.Start == DateTime.MinValue ||\r
185                 (port && (totalHp > repair.TotalHp || (DateTime.Now - repair.Start).TotalMinutes > 20)))\r
186                 repair.Start = DateTime.Now;\r
187             repair.UpdateTarget(target);\r
188         }\r
189 \r
190         private bool IsRepairable(int id)\r
191         {\r
192             var s = _shipInfo[id];\r
193             return !_dockInfo.InNDock(id) && s.NowHp < s.MaxHp && s.DamageLevel < ShipStatus.Damage.Half;\r
194         }\r
195 \r
196         public RepairSpan[] GetTimers(int fleet)\r
197         {\r
198             return _repairStatuses[fleet].GetTimers();\r
199         }\r
200 \r
201         public string[] GetNotice()\r
202         {\r
203             return _repairStatuses.Select(repair => repair.GetNotice()).ToArray();\r
204         }\r
205     }\r
206 }