1 // Copyright (C) 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>
\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
21 namespace KancolleSniffer
\r
23 public class AkashiTimer
\r
25 private readonly ShipInfo _shipInfo;
\r
26 private readonly ItemInfo _itemInfo;
\r
27 private readonly DockInfo _dockInfo;
\r
28 private readonly MissionInfo _missionInfo;
\r
29 private readonly RepairStatus[] _repairStatuses = new RepairStatus[ShipInfo.FleetCount];
\r
31 public class RepairTime
\r
33 public int Diff { get; set; }
\r
34 public DateTime Time { get; set; }
\r
36 public RepairTime(int diff, DateTime time)
\r
43 public class RepairSpan
\r
45 public int Diff { get; set; }
\r
46 public TimeSpan Span { get; set; }
\r
48 public RepairSpan(int diff, TimeSpan span)
\r
54 public RepairSpan(RepairTime time)
\r
57 Span = TimeSpan.FromSeconds(Math.Ceiling((time.Time - DateTime.Now).TotalSeconds));
\r
61 private class RepairStatus
\r
63 private readonly ShipInfo _shipInfo;
\r
64 private readonly DockInfo _dockInfo;
\r
65 private ShipStatus[] _target;
\r
66 private RepairTime[][] _times;
\r
67 private DateTime _prev;
\r
68 public DateTime Start { get; set; }
\r
69 public int[] Deck { private get; set; }
\r
73 get { return _target == null ? 0 : _target.Sum(s => s.NowHp); }
\r
76 public RepairStatus(ShipInfo ship, DockInfo dock)
\r
82 public void Invalidate()
\r
84 Start = DateTime.MinValue;
\r
90 public bool DeckChanged(int[] deck)
\r
92 return Deck != null && Deck.Where((t, i) => deck[i] != t).Any();
\r
95 public void UpdateTarget(ShipStatus[] target)
\r
101 private void UpdateTimes()
\r
103 _times = (from s in _target
\r
104 let damage = s.MaxHp - s.NowHp
\r
105 let first = new RepairTime(0, Start.AddMinutes(20))
\r
108 : new[] {first}.Concat(from d in Enumerable.Range(2, damage < 2 ? 0 : damage - 1)
\r
109 let span = s.RepairTime(d) + TimeSpan.FromSeconds(30)
\r
110 where span.TotalSeconds > 20 * 60
\r
111 select new RepairTime(d - 1, Start + span)).ToArray()).ToArray();
\r
114 public RepairSpan[] GetTimers()
\r
116 if (_times == null)
\r
118 return (from e in _times.Zip(Deck, (times, id) => new { times, id })
\r
119 select e.times == null || _dockInfo.InNDock(e.id)
\r
120 ? new RepairSpan(0, TimeSpan.MinValue)
\r
121 : (from t in e.times select new RepairSpan(t)).FirstOrDefault(s => s.Span > TimeSpan.Zero)
\r
122 ?? new RepairSpan(e.times.Last().Diff + 1, TimeSpan.Zero)
\r
126 public string GetNotice()
\r
128 var now = DateTime.Now;
\r
129 if (Start == DateTime.MinValue)
\r
133 if (pr == DateTime.MinValue)
\r
135 var m20 = TimeSpan.FromMinutes(20);
\r
136 if (pr - Start < m20 && now - Start >= m20)
\r
137 return "20分経過しました。";
\r
138 var margin = TimeSpan.Zero;
\r
139 var msg = string.Join(" ", from e in _times.Zip(Deck, (times, id) => new {times, id})
\r
141 e.times != null && !_dockInfo.InNDock(e.id) &&
\r
142 e.times.Any(rt => rt.Time - pr > margin && rt.Time - now <= margin)
\r
143 select _shipInfo[e.id].Name);
\r
144 return msg == "" ? "" : "修理進行: " + msg;
\r
148 public AkashiTimer(ShipInfo ship, ItemInfo item, DockInfo dock, MissionInfo mission)
\r
153 _missionInfo = mission;
\r
154 for (var i = 0; i < _repairStatuses.Length; i++)
\r
155 _repairStatuses[i] = new RepairStatus(ship, dock);
\r
158 public void SetTimer(bool port = false)
\r
160 for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)
\r
161 SetTimer(fleet, port);
\r
164 private void SetTimer(int fleet, bool port)
\r
166 var deck = _shipInfo.GetDeck(fleet);
\r
167 var repair = _repairStatuses[fleet];
\r
169 if (!_shipInfo[fs].Name.StartsWith("明石") || _dockInfo.InNDock(fs) || _missionInfo.InMission(fleet))
\r
171 repair.Invalidate();
\r
174 if (repair.DeckChanged(deck))
\r
175 repair.Invalidate();
\r
176 repair.Deck = deck.ToArray();
\r
177 var cap = _shipInfo[fs].Slot.Count(item => _itemInfo[item].Name == "艦艇修理施設") + 2;
\r
178 var target = (from id in deck.Take(cap) select IsRepairable(id) ? _shipInfo[id] : new ShipStatus()).ToArray();
\r
179 var totalHp = target.Sum(s => s.NowHp);
\r
182 repair.Invalidate();
\r
185 if (totalHp == repair.TotalHp)
\r
188 * 母港に遷移したときに、耐久値が回復しているか修理開始から20分経過しているときに
\r
191 * Q. なぜ20分でリスタートするのか?
\r
193 * A. 明石の修理は戦闘中も続くので、戦闘中に修理開始から20分経つと母港に戻った
\r
194 * ときに耐久値が回復する。最後の戦闘で損傷すると、損傷と回復が同時に耐久値に
\r
195 * 反映されて回復が起こったことがわからない。耐久値の回復だけを基準にすると
\r
196 * リスタートできないので、20分経過していたらリスタートする。
\r
198 if (repair.Start == DateTime.MinValue ||
\r
199 (port && (totalHp > repair.TotalHp || (DateTime.Now - repair.Start).TotalMinutes > 20)))
\r
200 repair.Start = DateTime.Now;
\r
201 repair.UpdateTarget(target);
\r
204 private bool IsRepairable(int id)
\r
206 var s = _shipInfo[id];
\r
207 return !_dockInfo.InNDock(id) && s.NowHp < s.MaxHp && s.DamageLevel < ShipStatus.Damage.Half;
\r
210 public RepairSpan[] GetTimers(int fleet)
\r
212 return _repairStatuses[fleet].GetTimers();
\r
215 public string[] GetNotice()
\r
217 return _repairStatuses.Select(repair => repair.GetNotice()).ToArray();
\r