OSDN Git Service

泊地修理が20分経過したときと修理が進行したときに通知する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / AkashiTimer.cs
1 // Copyright (C) 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\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.Linq;\r
20 \r
21 namespace KancolleSniffer\r
22 {\r
23     public class AkashiTimer\r
24     {\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
30 \r
31         public class RepairTime\r
32         {\r
33             public int Diff { get; set; }\r
34             public DateTime Time { get; set; }\r
35 \r
36             public RepairTime(int diff, DateTime time)\r
37             {\r
38                 Diff = diff;\r
39                 Time = time;\r
40             }\r
41         }\r
42 \r
43         public class RepairSpan\r
44         {\r
45             public int Diff { get; set; }\r
46             public TimeSpan Span { get; set; }\r
47 \r
48             public RepairSpan(int diff, TimeSpan span)\r
49             {\r
50                 Diff = diff;\r
51                 Span = span;\r
52             }\r
53 \r
54             public RepairSpan(RepairTime time)\r
55             {\r
56                 Diff = time.Diff;\r
57                 Span = TimeSpan.FromSeconds(Math.Ceiling((time.Time - DateTime.Now).TotalSeconds));\r
58             }\r
59         }\r
60 \r
61         private class RepairStatus\r
62         {\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
70 \r
71             public int TotalHp\r
72             {\r
73                 get { return _target == null ? 0 : _target.Sum(s => s.NowHp); }\r
74             }\r
75 \r
76             public RepairStatus(ShipInfo ship, DockInfo dock)\r
77             {\r
78                 _shipInfo = ship;\r
79                 _dockInfo = dock;\r
80             }\r
81 \r
82             public void Invalidate()\r
83             {\r
84                 Start = DateTime.MinValue;\r
85                 Deck = null;\r
86                 _target = null;\r
87                 _times = null;\r
88             }\r
89 \r
90             public bool DeckChanged(int[] deck)\r
91             {\r
92                 return Deck != null && Deck.Where((t, i) => deck[i] != t).Any();\r
93             }\r
94 \r
95             public void UpdateTarget(ShipStatus[] target)\r
96             {\r
97                 _target = target;\r
98                 UpdateTimes();\r
99             }\r
100 \r
101             private void UpdateTimes()\r
102             {\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
106                     select damage == 0\r
107                         ? null\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
112             }\r
113 \r
114             public RepairSpan[] GetTimers()\r
115             {\r
116                 if (_times == null)\r
117                     return 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
123                     ).ToArray();\r
124             }\r
125 \r
126             public string GetNotice()\r
127             {\r
128                 var now = DateTime.Now;\r
129                 if (Start == DateTime.MinValue)\r
130                     return "";\r
131                 var pr = _prev;\r
132                 _prev = now;\r
133                 if (pr == DateTime.MinValue)\r
134                     return "";\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
140                     where\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
145             }\r
146         }\r
147 \r
148         public AkashiTimer(ShipInfo ship, ItemInfo item, DockInfo dock, MissionInfo mission)\r
149         {\r
150             _shipInfo = ship;\r
151             _itemInfo = item;\r
152             _dockInfo = dock;\r
153             _missionInfo = mission;\r
154             for (var i = 0; i < _repairStatuses.Length; i++)\r
155                 _repairStatuses[i] = new RepairStatus(ship, dock);\r
156         }\r
157 \r
158         public void SetTimer(bool port = false)\r
159         {\r
160             for (var fleet = 0; fleet < ShipInfo.FleetCount; fleet++)\r
161                 SetTimer(fleet, port);\r
162         }\r
163 \r
164         private void SetTimer(int fleet, bool port)\r
165         {\r
166             var deck = _shipInfo.GetDeck(fleet);\r
167             var repair = _repairStatuses[fleet];\r
168             var fs = deck[0];\r
169             if (!_shipInfo[fs].Name.StartsWith("明石") || _dockInfo.InNDock(fs) || _missionInfo.InMission(fleet))\r
170             {\r
171                 repair.Invalidate();\r
172                 return;\r
173             }\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
180             if (totalHp == 0)\r
181             {\r
182                 repair.Invalidate();\r
183                 return;\r
184             }\r
185             if (totalHp == repair.TotalHp)\r
186                 return;\r
187             /*\r
188              * 母港に遷移したときに、耐久値が回復しているか修理開始から20分経過しているときに\r
189              * タイマーをリスタートする。\r
190              * \r
191              * Q. なぜ20分でリスタートするのか?\r
192              * \r
193              * A. 明石の修理は戦闘中も続くので、戦闘中に修理開始から20分経つと母港に戻った\r
194              *    ときに耐久値が回復する。最後の戦闘で損傷すると、損傷と回復が同時に耐久値に\r
195              *    反映されて回復が起こったことがわからない。耐久値の回復だけを基準にすると\r
196              *    リスタートできないので、20分経過していたらリスタートする。\r
197             */\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
202         }\r
203 \r
204         private bool IsRepairable(int id)\r
205         {\r
206             var s = _shipInfo[id];\r
207             return !_dockInfo.InNDock(id) && s.NowHp < s.MaxHp && s.DamageLevel < ShipStatus.Damage.Half;\r
208         }\r
209 \r
210         public RepairSpan[] GetTimers(int fleet)\r
211         {\r
212             return _repairStatuses[fleet].GetTimers();\r
213         }\r
214 \r
215         public string[] GetNotice()\r
216         {\r
217              return _repairStatuses.Select(repair => repair.GetNotice()).ToArray();\r
218         }\r
219     }\r
220 }