1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\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
7 // http://www.apache.org/licenses/LICENSE-2.0
\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
16 using System.Collections.Generic;
\r
18 using KancolleSniffer.Util;
\r
20 namespace KancolleSniffer.Model
\r
22 public class AkashiTimer : Sniffer.IPort
\r
24 private readonly ShipInfo _shipInfo;
\r
25 private readonly DockInfo _dockInfo;
\r
26 private readonly PresetDeck _presetDeck;
\r
27 private readonly RepairStatus[] _repairStatuses = new RepairStatus[ShipInfo.FleetCount];
\r
28 private DateTime _start;
\r
29 private readonly Func<DateTime> _nowFunc;
\r
31 public class RepairSpan
\r
33 public int Diff { get; }
\r
34 public TimeSpan Span { get; }
\r
36 public RepairSpan(int diff, TimeSpan span)
\r
43 private class RepairStatus
\r
45 private IReadOnlyList<ShipStatus> _target = new ShipStatus[0];
\r
46 private IReadOnlyList<int> _deck = new int[0];
\r
47 private TimeSpan FirstRepairTime => TimeSpan.FromMinutes(20);
\r
49 private bool PassedFirstRepairTime(DateTime start, TimeStep step) =>
\r
50 step.Prev - start < FirstRepairTime && step.Now - start >= FirstRepairTime;
\r
52 private TimeSpan RepairTime(ShipStatus ship, int damage) =>
\r
53 TimeSpan.FromMinutes(Math.Ceiling(ship.RepairTime.TotalMinutes / (ship.MaxHp - ship.NowHp) * damage));
\r
56 public IReadOnlyList<int> Deck
\r
58 set => _deck = value;
\r
61 public State State { get; set; }
\r
63 public bool IsRepaired(ShipStatus[] target) => _target.Zip(target, (a, b) => a.NowHp < b.NowHp).Any(x => x);
\r
65 public bool DeckChanged(IEnumerable<int> deck) => !_deck.SequenceEqual(deck);
\r
67 public void UpdateTarget(ShipStatus[] target)
\r
72 public RepairSpan[] GetTimers(DateTime start, DateTime now)
\r
74 var spent = TimeSpan.FromSeconds((int)(now - start).TotalSeconds);
\r
75 return _target.Select(s =>
\r
77 var damage = s.MaxHp - s.NowHp;
\r
79 return new RepairSpan(0, TimeSpan.MinValue);
\r
80 if (spent < FirstRepairTime)
\r
81 return new RepairSpan(0, FirstRepairTime - spent);
\r
83 return new RepairSpan(1, TimeSpan.Zero);
\r
84 for (var d = 2; d <= damage; d++)
\r
86 var span = RepairTime(s, d);
\r
87 if (span <= FirstRepairTime)
\r
90 return new RepairSpan(d - 1, span - spent);
\r
92 return new RepairSpan(damage, TimeSpan.Zero);
\r
96 public Notice GetNotice(DateTime start, TimeStep step)
\r
98 var proc = new List<string>();
\r
99 var comp = new List<string>();
\r
100 foreach (var s in _target)
\r
102 var damage = s.MaxHp - s.NowHp;
\r
107 if (PassedFirstRepairTime(start, step))
\r
111 // スリープで時間が飛んだときに修理完了だけを表示するために、
\r
112 // 完全回復から減らしながら所要時間と経過時間と比較する。
\r
113 for (var d = damage; d >= 2; d--)
\r
115 var span = RepairTime(s, d);
\r
116 if (span <= FirstRepairTime)
\r
118 if (d == damage && PassedFirstRepairTime(start, step))
\r
122 if (span <= step.Prev - start || step.Now - start < span)
\r
133 Proceeded = proc.Count == 0 ? "" : string.Join(" ", proc),
\r
134 Completed = comp.Count == 0 ? "" : string.Join(" ", comp)
\r
139 public class Notice
\r
141 public string Proceeded { get; set; }
\r
142 public string Completed { get; set; }
\r
145 public AkashiTimer(ShipInfo ship, DockInfo dock, PresetDeck preset, Func<DateTime> nowFunc = null)
\r
149 _presetDeck = preset;
\r
150 _nowFunc = nowFunc ?? (() => DateTime.Now);
\r
151 for (var i = 0; i < _repairStatuses.Length; i++)
\r
152 _repairStatuses[i] = new RepairStatus();
\r
164 var now = _nowFunc();
\r
165 var reset = _repairStatuses.Any(r => r.State == State.Reset);
\r
166 if (_start == DateTime.MinValue || now - _start > TimeSpan.FromMinutes(20) || reset)
\r
170 public void InspectChange(string request)
\r
173 var values = HttpUtility.ParseQueryString(request);
\r
174 if (int.Parse(values["api_ship_id"]) == -2)
\r
176 if (_repairStatuses.Any(r => r.State == State.Reset))
\r
177 _start = _nowFunc();
\r
180 public void CheckFleet()
\r
182 foreach (var fleet in _shipInfo.Fleets)
\r
186 private void CheckFleet(Fleet fleet)
\r
188 var deck = fleet.Deck;
\r
189 var ships = fleet.Ships;
\r
190 var repair = _repairStatuses[fleet.Number];
\r
191 repair.State = State.Continue;
\r
192 if (!ships[0].Spec.IsRepairShip)
\r
194 repair.UpdateTarget(new ShipStatus[0]);
\r
195 repair.Deck = deck;
\r
198 if (repair.DeckChanged(deck))
\r
200 repair.State = State.Reset;
\r
201 repair.Deck = deck;
\r
203 var target = RepairTarget(ships);
\r
204 if (repair.IsRepaired(target))
\r
205 repair.State = State.Reset;
\r
206 repair.UpdateTarget(target);
\r
209 private ShipStatus[] RepairTarget(IReadOnlyList<ShipStatus> ships)
\r
212 if (!fs.Spec.IsRepairShip || _dockInfo.InNDock(fs.Id) || fs.DamageLevel >= ShipStatus.Damage.Half)
\r
213 return new ShipStatus[0];
\r
214 var cap = fs.Slot.Count(item => item.Spec.IsRepairFacility) + 2;
\r
216 * 泊地修理の条件を満たさない艦はMaxHp==NowHpのダミーを設定する。
\r
217 * - 入渠中の艦娘は終わったときに回復扱いされないようNowHp=MaxHpに
\r
218 * - 中破以上でNowHp=MaxHpにすると回復扱いされるのでNowHp=MaxHp=0に
\r
220 return (from ship in ships.Take(cap)
\r
221 let s = (ShipStatus)ship.Clone()
\r
222 let full = new ShipStatus {NowHp = s.MaxHp, MaxHp = s.MaxHp}
\r
223 let zero = new ShipStatus()
\r
224 select _dockInfo.InNDock(s.Id) ? full : s.DamageLevel >= ShipStatus.Damage.Half ? zero : s).ToArray();
\r
227 public RepairSpan[] GetTimers(int fleet, DateTime now)
\r
228 => _start == DateTime.MinValue ? new RepairSpan[0] : _repairStatuses[fleet].GetTimers(_start, now);
\r
230 public TimeSpan GetPresetDeckTimer(DateTime now)
\r
232 if (_start == DateTime.MinValue)
\r
233 return TimeSpan.MinValue;
\r
234 var r = TimeSpan.FromMinutes(20) - TimeSpan.FromSeconds((int)(now - _start).TotalSeconds);
\r
235 return r >= TimeSpan.Zero ? r : TimeSpan.Zero;
\r
238 public bool CheckRepairing(int fleet, DateTime now) =>
\r
239 GetTimers(fleet, now).Any(r => r.Span != TimeSpan.MinValue);
\r
241 public bool CheckRepairing(DateTime now) =>
\r
242 Enumerable.Range(0, ShipInfo.FleetCount).Any(fleet => CheckRepairing(fleet, now));
\r
244 public bool CheckPresetRepairing()
\r
245 => _presetDeck.Decks.Where(deck => deck != null)
\r
246 .Any(deck => RepairTarget(deck.Select(id => _shipInfo.GetShip(id)).ToArray())
\r
247 .Any(s => s.NowHp < s.MaxHp));
\r
249 public Notice[] GetNotice(TimeStep step)
\r
251 if (step.Prev == DateTime.MinValue || _start == DateTime.MinValue)
\r
252 return new Notice[0];
\r
253 var r = _repairStatuses.Select(repair => repair.GetNotice(_start, step)).ToArray();
\r
254 var m20 = TimeSpan.FromMinutes(20);
\r
255 if (step.Prev - _start < m20 && step.Now - _start >= m20)
\r
256 r[0].Proceeded = "20分経過しました。";
\r