OSDN Git Service

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