OSDN Git Service

明石が中破以上でもタイマーが進んでしまうのを直す
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / AkashiTimer.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\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.Collections.Generic;\r
20 using System.Linq;\r
21 using System.Web;\r
22 \r
23 namespace KancolleSniffer\r
24 {\r
25     public class AkashiTimer\r
26     {\r
27         private readonly ShipInfo _shipInfo;\r
28         private readonly DockInfo _dockInfo;\r
29         private readonly RepairStatus[] _repairStatuses = new RepairStatus[ShipInfo.FleetCount];\r
30         private DateTime _start;\r
31         private DateTime _prev;\r
32 \r
33         public class RepairSpan\r
34         {\r
35             public int Diff { get; set; }\r
36             public TimeSpan Span { get; set; }\r
37 \r
38             public RepairSpan(int diff, TimeSpan span)\r
39             {\r
40                 Diff = diff;\r
41                 Span = span;\r
42             }\r
43         }\r
44 \r
45         private class RepairStatus\r
46         {\r
47             private ShipStatus[] _target = new ShipStatus[0];\r
48             private int[] _deck = new int[0];\r
49 \r
50             public int[] Deck\r
51             {\r
52                 set { _deck = value; }\r
53             }\r
54 \r
55             public State State { get; set; }\r
56 \r
57             public bool IsRepaired(ShipStatus[] target) => _target.Zip(target, (a, b) => a.NowHp < b.NowHp).Any(x => x);\r
58 \r
59             public bool DeckChanged(IEnumerable<int> deck) => !_deck.SequenceEqual(deck);\r
60 \r
61             public void UpdateTarget(ShipStatus[] target)\r
62             {\r
63                 _target = target;\r
64             }\r
65 \r
66             public RepairSpan[] GetTimers(DateTime start, DateTime now)\r
67             {\r
68                 var spent = TimeSpan.FromSeconds((int)(now - start).TotalSeconds);\r
69                 return _target.Select(s =>\r
70                 {\r
71                     var damage = s.MaxHp - s.NowHp;\r
72                     if (damage == 0)\r
73                         return new RepairSpan(0, TimeSpan.MinValue);\r
74                     if (spent < TimeSpan.FromMinutes(20))\r
75                         return new RepairSpan(0, TimeSpan.FromMinutes(20) - spent);\r
76                     if (damage == 1)\r
77                         return new RepairSpan(1, TimeSpan.Zero);\r
78                     for (var d = 2; d <= damage; d++)\r
79                     {\r
80                         var sec = s.CalcRepairSec(d) + 60;\r
81                         if (sec <= 20 * 60)\r
82                             continue;\r
83                         if (TimeSpan.FromSeconds(sec) > spent)\r
84                             return new RepairSpan(d - 1, TimeSpan.FromSeconds(sec) - spent);\r
85                     }\r
86                     return new RepairSpan(damage, TimeSpan.Zero);\r
87                 }).ToArray();\r
88             }\r
89 \r
90             public Notice GetNotice(DateTime start, DateTime prev, DateTime now)\r
91             {\r
92                 var m20 = TimeSpan.FromMinutes(20);\r
93                 var proc = new List<string>();\r
94                 var comp = new List<string>();\r
95                 foreach (var s in _target)\r
96                 {\r
97                     var damage = s.MaxHp - s.NowHp;\r
98                     if (damage == 0)\r
99                         continue;\r
100                     if (damage == 1)\r
101                     {\r
102                         if (prev - start < m20 && now - start >= m20)\r
103                             comp.Add(s.Name);\r
104                         continue;\r
105                     }\r
106                     // スリープで時間が飛んだときに修理完了だけを表示するために、\r
107                     // 完全回復から減らしながら所要時間と経過時間と比較する。\r
108                     for (var d = damage; d >= 2; d--)\r
109                     {\r
110                         var sec = s.CalcRepairSec(d) + 60;\r
111                         if (sec <= 20 * 60)\r
112                         {\r
113                             if (d == damage && (prev - start < m20 && now - start >= m20))\r
114                                 comp.Add(s.Name);\r
115                             continue;\r
116                         }\r
117                         var span = TimeSpan.FromSeconds(sec);\r
118                         if (span <= prev - start || now - start < span)\r
119                             continue;\r
120                         if (d == damage)\r
121                             comp.Add(s.Name);\r
122                         else\r
123                             proc.Add(s.Name);\r
124                         break;\r
125                     }\r
126                 }\r
127                 return new Notice\r
128                 {\r
129                     Proceeded = proc.Count == 0 ? "" : string.Join(" ", proc),\r
130                     Completed = comp.Count == 0 ? "" : string.Join(" ", comp)\r
131                 };\r
132             }\r
133         }\r
134 \r
135         public class Notice\r
136         {\r
137             public string Proceeded { get; set; }\r
138             public string Completed { get; set; }\r
139         }\r
140 \r
141         public AkashiTimer(ShipInfo ship, DockInfo dock)\r
142         {\r
143             _shipInfo = ship;\r
144             _dockInfo = dock;\r
145             for (var i = 0; i < _repairStatuses.Length; i++)\r
146                 _repairStatuses[i] = new RepairStatus();\r
147         }\r
148 \r
149         private enum State\r
150         {\r
151             Continue = 0,\r
152             Reset = 1,\r
153         }\r
154 \r
155         public void Port()\r
156         {\r
157             CheckFleet();\r
158             var now = DateTime.Now;\r
159             var reset = _repairStatuses.Any(r => r.State == State.Reset);\r
160             if (_start == DateTime.MinValue || now - _start > TimeSpan.FromMinutes(20) || reset)\r
161                 _start = now;\r
162         }\r
163 \r
164         public void InspectChange(string request)\r
165         {\r
166             CheckFleet();\r
167             var values = HttpUtility.ParseQueryString(request);\r
168             if (int.Parse(values["api_ship_idx"]) == -1)\r
169                 return;\r
170             if (_repairStatuses.Any(r => r.State == State.Reset))\r
171                 _start = DateTime.Now;\r
172         }\r
173 \r
174         public void CheckFleet()\r
175         {\r
176             for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)\r
177                 CheckFleet(fleet);\r
178         }\r
179 \r
180         private void CheckFleet(int fleet)\r
181         {\r
182             var deck = _shipInfo.GetDeck(fleet).ToArray();\r
183             var repair = _repairStatuses[fleet];\r
184             var fs = _shipInfo.GetStatus(deck[0]);\r
185             repair.State = State.Continue;\r
186             if (!fs.Spec.IsRepairShip)\r
187             {\r
188                 repair.UpdateTarget(new ShipStatus[0]);\r
189                 repair.Deck = deck;\r
190                 return;\r
191             }\r
192             if (repair.DeckChanged(deck))\r
193             {\r
194                 repair.State = State.Reset;\r
195                 repair.Deck = deck;\r
196             }\r
197             var target = RepairTarget(deck);\r
198             if (repair.IsRepaired(target))\r
199                 repair.State = State.Reset;\r
200             repair.UpdateTarget(target);\r
201         }\r
202 \r
203         private ShipStatus[] RepairTarget(int[] deck)\r
204         {\r
205             var fs = _shipInfo.GetStatus(deck[0]);\r
206             if (fs.DamageLevel >= ShipStatus.Damage.Half)\r
207                 return new ShipStatus[0];\r
208             var cap = fs.Slot.Count(item => item.Spec.IsRepairFacility) + 2;\r
209             /*\r
210              * 泊地修理の条件を満たさない艦はMaxHp==NowHpのダミーを設定する。\r
211              * - 入渠中の艦娘は終わったときに回復扱いされないようNowHp=MaxHpに\r
212              * - 中破以上でNowHp=MaxHpにすると回復扱いされるのでNowHp=MaxHp=0に\r
213             */\r
214             return (from id in deck.Take(cap)\r
215                 let s = _shipInfo.GetStatus(id)\r
216                 let full = new ShipStatus {NowHp = s.MaxHp, MaxHp = s.MaxHp}\r
217                 let zero = new ShipStatus()\r
218                 select _dockInfo.InNDock(id) ? full : s.DamageLevel >= ShipStatus.Damage.Half ? zero : s).ToArray();\r
219         }\r
220 \r
221         public RepairSpan[] GetTimers(int fleet)\r
222             => _start == DateTime.MinValue ? new RepairSpan[0] : _repairStatuses[fleet].GetTimers(_start, DateTime.Now);\r
223 \r
224         public TimeSpan PresetDeckTimer\r
225         {\r
226             get\r
227             {\r
228                 if (_start == DateTime.MinValue)\r
229                     return TimeSpan.MinValue;\r
230                 var r = _start + TimeSpan.FromMinutes(20) - DateTime.Now;\r
231                 return r >= TimeSpan.Zero ? r : TimeSpan.Zero;\r
232             }\r
233         }\r
234 \r
235         public bool CheckReparing(int fleet) => GetTimers(fleet).Any(r => r.Span != TimeSpan.MinValue);\r
236 \r
237         public bool CheckReparing() => Enumerable.Range(0, ShipInfo.FleetCount).Any(CheckReparing);\r
238 \r
239         public Notice[] GetNotice()\r
240         {\r
241             var now = DateTime.Now;\r
242             var prev = _prev;\r
243             _prev = now;\r
244             if (prev == DateTime.MinValue || _start == DateTime.MinValue)\r
245                 return new Notice[0];\r
246             var r = _repairStatuses.Select(repair => repair.GetNotice(_start, prev, now)).ToArray();\r
247             var m20 = TimeSpan.FromMinutes(20);\r
248             if (prev - _start < m20 && now - _start >= m20)\r
249                 r[0].Proceeded = "20分経過しました。";\r
250             return r;\r
251         }\r
252     }\r
253 }