1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\r
3 // This program is part of KancolleSniffer.
\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
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
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
19 using System.Collections.Generic;
\r
23 namespace KancolleSniffer
\r
25 public class AkashiTimer
\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
33 public class RepairSpan
\r
35 public int Diff { get; set; }
\r
36 public TimeSpan Span { get; set; }
\r
38 public RepairSpan(int diff, TimeSpan span)
\r
45 private class RepairStatus
\r
47 private ShipStatus[] _target = new ShipStatus[0];
\r
48 private int[] _deck = new int[0];
\r
52 set { _deck = value; }
\r
55 public State State { get; set; }
\r
57 public bool IsRepaired(ShipStatus[] target) => _target.Zip(target, (a, b) => a.NowHp < b.NowHp).Any(x => x);
\r
59 public bool DeckChanged(IEnumerable<int> deck) => !_deck.SequenceEqual(deck);
\r
61 public void UpdateTarget(ShipStatus[] target)
\r
66 public RepairSpan[] GetTimers(DateTime start, DateTime now)
\r
68 var spent = TimeSpan.FromSeconds((int)(now - start).TotalSeconds);
\r
69 return _target.Select(s =>
\r
71 var damage = s.MaxHp - s.NowHp;
\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
77 return new RepairSpan(1, TimeSpan.Zero);
\r
78 for (var d = 2; d <= damage; d++)
\r
80 var sec = s.CalcRepairSec(d) + 60;
\r
83 if (TimeSpan.FromSeconds(sec) > spent)
\r
84 return new RepairSpan(d - 1, TimeSpan.FromSeconds(sec) - spent);
\r
86 return new RepairSpan(damage, TimeSpan.Zero);
\r
90 public Notice GetNotice(DateTime start, DateTime prev, DateTime now)
\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
97 var damage = s.MaxHp - s.NowHp;
\r
102 if (prev - start < m20 && now - start >= m20)
\r
106 // スリープで時間が飛んだときに修理完了だけを表示するために、
\r
107 // 完全回復から減らしながら所要時間と経過時間と比較する。
\r
108 for (var d = damage; d >= 2; d--)
\r
110 var sec = s.CalcRepairSec(d) + 60;
\r
111 if (sec <= 20 * 60)
\r
113 if (d == damage && (prev - start < m20 && now - start >= m20))
\r
117 var span = TimeSpan.FromSeconds(sec);
\r
118 if (span <= prev - start || now - start < span)
\r
129 Proceeded = proc.Count == 0 ? "" : string.Join(" ", proc),
\r
130 Completed = comp.Count == 0 ? "" : string.Join(" ", comp)
\r
135 public class Notice
\r
137 public string Proceeded { get; set; }
\r
138 public string Completed { get; set; }
\r
141 public AkashiTimer(ShipInfo ship, DockInfo dock)
\r
145 for (var i = 0; i < _repairStatuses.Length; i++)
\r
146 _repairStatuses[i] = new RepairStatus();
\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
164 public void InspectChange(string request)
\r
167 var values = HttpUtility.ParseQueryString(request);
\r
168 if (int.Parse(values["api_ship_idx"]) == -1)
\r
170 if (_repairStatuses.Any(r => r.State == State.Reset))
\r
171 _start = DateTime.Now;
\r
174 public void CheckFleet()
\r
176 for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)
\r
180 private void CheckFleet(int fleet)
\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
188 repair.UpdateTarget(new ShipStatus[0]);
\r
189 repair.Deck = deck;
\r
192 if (repair.DeckChanged(deck))
\r
194 repair.State = State.Reset;
\r
195 repair.Deck = deck;
\r
197 var target = RepairTarget(deck);
\r
198 if (repair.IsRepaired(target))
\r
199 repair.State = State.Reset;
\r
200 repair.UpdateTarget(target);
\r
203 private ShipStatus[] RepairTarget(int[] deck)
\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
210 * 泊地修理の条件を満たさない艦はMaxHp==NowHpのダミーを設定する。
\r
211 * - 入渠中の艦娘は終わったときに回復扱いされないようNowHp=MaxHpに
\r
212 * - 中破以上でNowHp=MaxHpにすると回復扱いされるのでNowHp=MaxHp=0に
\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
221 public RepairSpan[] GetTimers(int fleet)
\r
222 => _start == DateTime.MinValue ? new RepairSpan[0] : _repairStatuses[fleet].GetTimers(_start, DateTime.Now);
\r
224 public TimeSpan PresetDeckTimer
\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
235 public bool CheckReparing(int fleet) => GetTimers(fleet).Any(r => r.Span != TimeSpan.MinValue);
\r
237 public bool CheckReparing() => Enumerable.Range(0, ShipInfo.FleetCount).Any(CheckReparing);
\r
239 public Notice[] GetNotice()
\r
241 var now = DateTime.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