1 // Copyright (C) 2017 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
17 using System.Drawing;
\r
19 using System.Text.RegularExpressions;
\r
20 using System.Windows.Forms;
\r
21 using KancolleSniffer.Model;
\r
22 using static System.Math;
\r
24 namespace KancolleSniffer.View
\r
26 public class BattleResultPanel : PanelWithToolTip
\r
28 private readonly List<ShipLabels> _friendLabels = new List<ShipLabels>();
\r
29 private readonly List<ShipLabels> _enemyLabels = new List<ShipLabels>();
\r
30 private readonly List<ShipLabel.Hp> _hpLabels = new List<ShipLabel.Hp>();
\r
31 private Label _phaseLabel, _rankLabel, _supportLabel, _cellLabel;
\r
32 private readonly InformationPanel _information;
\r
33 private CellInfo _cellInfo;
\r
34 private readonly BattleData _data = new BattleData();
\r
36 public Spoiler Spoilers { get; set; }
\r
38 private class BattleData
\r
40 private Result _day;
\r
41 private Result _night;
\r
42 public int[] Formation;
\r
43 public Range FighterPower;
\r
44 public EnemyFighterPower EnemyFighterPower;
\r
45 public int AirControlLevel;
\r
46 public int SupportType;
\r
47 public bool HaveDay => _day != null;
\r
48 public bool HaveNight => _night != null;
\r
52 public BattleInfo.BattleResult Damage;
\r
53 public BattleResultRank Rank;
\r
56 public Result GetResult(BattleState state)
\r
60 case BattleState.Day:
\r
61 case BattleState.AirRaid:
\r
63 case BattleState.SpNight:
\r
64 case BattleState.Night:
\r
71 public void SetData(BattleInfo battle)
\r
73 switch (battle.BattleState)
\r
75 case BattleState.Day:
\r
76 case BattleState.AirRaid:
\r
77 _day = new Result {Damage = battle.Result, Rank = battle.ResultRank};
\r
80 case BattleState.SpNight:
\r
82 goto case BattleState.Night;
\r
83 case BattleState.Night:
\r
84 _night = new Result {Damage = battle.Result, Rank = battle.ResultRank};
\r
87 if (battle.BattleState != BattleState.Night)
\r
88 SetNonStateData(battle);
\r
91 private void SetNonStateData(BattleInfo battle)
\r
93 Formation = battle.Formation;
\r
94 FighterPower = battle.FighterPower;
\r
95 EnemyFighterPower = battle.EnemyFighterPower;
\r
96 AirControlLevel = battle.AirControlLevel;
\r
97 SupportType = battle.SupportType;
\r
101 public BattleResultPanel()
\r
105 _information = new InformationPanel();
\r
106 Controls.Add(_information);
\r
110 public event Action HpLabelClick;
\r
112 private void HpLabelClickHandler(object sender, EventArgs ev)
\r
114 HpLabelClick?.Invoke();
\r
117 public void ToggleHpPercent()
\r
119 foreach (var label in _hpLabels)
\r
120 label.ToggleHpPercent();
\r
123 public void Update(Sniffer sniffer)
\r
125 var state = sniffer.Battle.BattleState;
\r
126 _cellInfo = sniffer.CellInfo;
\r
127 switch (sniffer.Battle.BattleState)
\r
129 case BattleState.None:
\r
130 if (!_data.HaveDay && !_data.HaveNight)
\r
134 UpdateCellInfo(_cellInfo);
\r
136 case BattleState.Result:
\r
137 case BattleState.Unknown:
\r
140 _data.SetData(sniffer.Battle);
\r
141 if ((Spoilers & Spoiler.BattleResult) != 0)
\r
143 ShowData(sniffer.Battle.BattleState);
\r
146 case BattleState.Day:
\r
147 case BattleState.AirRaid:
\r
150 case BattleState.Night:
\r
151 case BattleState.SpNight:
\r
163 private void PhaseLabelClick(object sender, EventArgs ev)
\r
165 switch (_phaseLabel.Text)
\r
170 ShowData(BattleState.Day);
\r
173 else if (_data.HaveNight)
\r
175 ShowData(BattleState.Night);
\r
180 if (_data.HaveNight)
\r
182 ShowData(BattleState.Night);
\r
189 ShowData(BattleState.Day);
\r
196 private void SetPhase(string phase)
\r
198 _phaseLabel.Text = phase;
\r
199 if (phase == "結果" || _data.HaveDay && _data.HaveNight)
\r
201 _phaseLabel.BorderStyle = BorderStyle.FixedSingle;
\r
202 _phaseLabel.Cursor = Cursors.Hand;
\r
206 _phaseLabel.BorderStyle = BorderStyle.None;
\r
207 _phaseLabel.Cursor = Cursors.Default;
\r
211 private Point _scrollPosition;
\r
213 private void ClearResult()
\r
215 _scrollPosition = AutoScrollPosition;
\r
216 SetPanelVisible(0);
\r
217 _information.Visible = false;
\r
218 _rankLabel.Text = "";
\r
219 _supportLabel.Text = "";
\r
222 private void ShowData(BattleState state)
\r
224 var result = _data.GetResult(state);
\r
225 ShowDamage(result.Damage);
\r
226 ShowResultRank(result.Rank);
\r
227 _information.Show(_data);
\r
228 ShowSupportType(_data.SupportType);
\r
229 UpdateCellInfo(_cellInfo);
\r
230 AutoScrollPosition = new Point(-_scrollPosition.X, -_scrollPosition.Y);
\r
233 private void ShowDamage(BattleInfo.BattleResult result)
\r
236 SetEachResult(_friendLabels, result.Friend);
\r
237 SetEachResult(_enemyLabels, result.Enemy);
\r
238 var lines = Max(Ships(result.Friend).Length, Ships(result.Enemy).Length);
\r
239 SetPanelVisible(lines);
\r
240 ResumeLayout(); // スクロールバーの有無を決定する
\r
241 AdjustPanelWidth(lines);
\r
244 private void SetEachResult(IReadOnlyList<ShipLabels> labelsList, BattleInfo.BattleResult.Combined fleet)
\r
246 var ships = Ships(fleet);
\r
247 for (var i = 0; i < ships.Length; i++)
\r
249 var labels = labelsList[i];
\r
250 if (i == fleet.Main.Length)
\r
252 labels.Name.Text = "護衛";
\r
256 var ship = ships[i];
\r
257 if (ShipMaster.IsEnemyId(ship.Spec.Id))
\r
259 labels.Hp.Set(ship);
\r
260 labels.Name.SetName(ShortenName(ship.Name));
\r
265 if (ship.Name.StartsWith("基地航空隊"))
\r
266 labels.Name.SetName(ship.Name.Substring(2));
\r
268 ToolTip.SetToolTip(labels.Name, GetEquipString(ship));
\r
270 for (var i = ships.Length; i < labelsList.Count; i++)
\r
271 labelsList[i].Reset();
\r
274 private static ShipStatus[] Ships(BattleInfo.BattleResult.Combined fleet)
\r
276 if (fleet.Guard.Length == 0)
\r
278 return fleet.Main.Concat(new[] {new ShipStatus()}.Concat(fleet.Guard)).ToArray();
\r
281 private static string GetEquipString(ShipStatus ship)
\r
284 (from i in Enumerable.Range(0, ship.Slot.Count)
\r
285 let item = ship.Slot[i]
\r
287 select item.Spec.Name +
\r
288 (item.Spec.IsAircraft && ship.OnSlot.Length > 0 && ship.Spec.MaxEq.Length > 0
\r
289 ? $"{ship.OnSlot[i]}/{ship.Spec.MaxEq[i]}"
\r
291 if (ship.SlotEx.Id > 0)
\r
292 result = result.Concat(new[] {ship.SlotEx.Spec.Name});
\r
293 return string.Join("\r\n", result);
\r
296 private static string ShortenName(string name)
\r
298 return new Regex(@"\(elite\)|\(flagship\)").Replace(name,
\r
299 match => match.Value == "(elite)" ? "(e)" : "(f)");
\r
302 private void ShowResultRank(BattleResultRank rank)
\r
304 var result = new[] {"完全S", "勝利S", "勝利A", "勝利B", "敗北C", "敗北D", "敗北E"};
\r
305 _rankLabel.Text = result[(int)rank];
\r
308 private void ShowSupportType(int support)
\r
310 _supportLabel.Text = new[] {"", "空支援", "砲支援", "雷支援", "潜支援"}[support];
\r
313 public void UpdateCellInfo(CellInfo cellInfo)
\r
315 _cellLabel.Text = (Spoilers & Spoiler.NextCell) == 0 ? cellInfo.Current : cellInfo.Next;
\r
316 _cellLabel.Location = new Point(ClientSize.Width - _cellLabel.Width - 2, _cellLabel.Location.Y);
\r
319 private const int LineHeight = 16;
\r
320 private const int LabelHeight = 12;
\r
321 private const int LabelMargin = (LineHeight - LabelHeight) / 2;
\r
322 private const int TopMargin = 4;
\r
324 private void CreateLabels()
\r
326 _phaseLabel = new Label
\r
328 Location = new Point(4, TopMargin),
\r
329 Size = new Size(31, LabelHeight + 2)
\r
331 _phaseLabel.Click += PhaseLabelClick;
\r
332 Controls.Add(_phaseLabel);
\r
333 _rankLabel = new Label
\r
335 Location = new Point(37, TopMargin),
\r
336 Size = new Size(42, LabelHeight)
\r
338 Controls.Add(_rankLabel);
\r
339 _supportLabel = new Label
\r
341 Location = new Point(77, TopMargin),
\r
344 Controls.Add(_supportLabel);
\r
345 _cellLabel = new Label
\r
347 Location = new Point(0, TopMargin),
\r
350 Controls.Add(_cellLabel);
\r
351 for (var i = 0; i < 13; i++)
\r
353 var friend = new ShipLabels
\r
355 Name = new ShipLabel.Name(new Point(1, LabelMargin), ShipNameWidth.BattleResult),
\r
356 Hp = new ShipLabel.Hp(new Point(101, 0), LineHeight),
\r
357 BackPanel = new Panel
\r
359 Location = new Point(0, LineHeight * i + 38),
\r
360 Size = new Size(0, LineHeight),
\r
361 BackColor = CustomColors.ColumnColors.DarkFirst(i),
\r
365 _friendLabels.Add(friend);
\r
366 friend.Arrange(this, CustomColors.ColumnColors.DarkFirst(i));
\r
367 _hpLabels.Add(friend.Hp);
\r
368 friend.Hp.Click += HpLabelClickHandler;
\r
369 var enemy = new ShipLabels
\r
371 Name = new ShipLabel.Name(new Point(164, LabelMargin), ShipNameWidth.Max),
\r
372 Hp = new ShipLabel.Hp
\r
374 Location = new Point(119, 0),
\r
376 MinimumSize = new Size(0, LineHeight),
\r
377 TextAlign = ContentAlignment.MiddleLeft
\r
380 _enemyLabels.Add(enemy);
\r
381 enemy.Arrange(friend.BackPanel, CustomColors.ColumnColors.DarkFirst(i));
\r
385 private void SetPanelVisible(int showPanels)
\r
387 for (var i = 0; i < _friendLabels.Count; i++)
\r
388 _friendLabels[i].BackPanel.Visible = i < showPanels;
\r
391 private void AdjustPanelWidth(int lines)
\r
393 var panelWidth = Max(ClientSize.Width, // スクロールバーの有無を反映した横幅
\r
394 _enemyLabels[0].Name.Location.X + _enemyLabels.Max(labels => labels.Name.Size.Width) - 1); // 敵の名前の右端
\r
395 for (var i = 0; i < lines; i++)
\r
396 _friendLabels[i].BackPanel.Width = panelWidth;
\r
399 protected override void OnMouseWheel(MouseEventArgs e)
\r
401 if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift)
\r
408 base.OnMouseWheel(e);
\r
411 private sealed class InformationPanel : PanelWithToolTip
\r
413 private readonly Formation _formation;
\r
414 private readonly FighterPower _fighterPower;
\r
416 public InformationPanel()
\r
418 _formation = new Formation
\r
422 Location = new Point(47, LabelMargin),
\r
423 Size = new Size(29, LabelHeight)
\r
427 Location = new Point(75, LabelMargin),
\r
428 Size = new Size(29, LabelHeight)
\r
432 Location = new Point(1, LabelMargin),
\r
433 Size = new Size(48, LabelHeight),
\r
434 TextAlign = ContentAlignment.MiddleCenter
\r
437 Controls.AddRange(_formation.Controls);
\r
438 _fighterPower = new FighterPower
\r
443 Location = new Point(162, LabelMargin),
\r
444 Size = new Size(23, LabelHeight),
\r
445 TextAlign = ContentAlignment.MiddleRight
\r
449 Location = new Point(183, LabelMargin),
\r
450 Size = new Size(23, LabelHeight),
\r
451 TextAlign = ContentAlignment.MiddleRight
\r
455 Location = new Point(110, LabelMargin),
\r
456 Size = new Size(53, LabelHeight)
\r
459 Controls.AddRange(_fighterPower.Controls);
\r
460 Location = new Point(0, 20);
\r
461 Size = new Size(206, 16);
\r
462 BackColor = CustomColors.ColumnColors.Bright;
\r
466 public void Show(BattleData data)
\r
472 private void SetData(BattleData data)
\r
474 _formation.SetFormation(data.Formation);
\r
475 _fighterPower.SetFighterPower(data);
\r
478 private class StateLabels
\r
480 public Label Friend;
\r
481 public Label Enemy;
\r
482 public Label State;
\r
484 public Control[] Controls => new Control[] {Friend, Enemy, State};
\r
487 private class Formation : StateLabels
\r
489 public void SetFormation(int[] formation)
\r
491 Friend.Text = FormationName(formation[0]);
\r
492 Enemy.Text = FormationName(formation[1]);
\r
493 State.Text = new[] {"同航戦", "反航戦", "T字有利", "T字不利"}[formation[2] - 1];
\r
496 private static string FormationName(int formation)
\r
526 private class FighterPower : StateLabels
\r
528 public ToolTip ToolTip { private get; set; }
\r
530 public void SetFighterPower(BattleData data)
\r
532 if (data.AirControlLevel == -1)
\r
534 foreach (var control in Controls)
\r
538 SetFriend(data.FighterPower);
\r
539 SetEnemy(data.EnemyFighterPower);
\r
540 SetAirControlLevel(data.AirControlLevel);
\r
543 private void SetFriend(Range fighterPower)
\r
545 Friend.Text = fighterPower.Min.ToString();
\r
546 ToolTip.SetToolTip(Friend, fighterPower.Diff ? fighterPower.RangeString : "");
\r
549 private void SetEnemy(EnemyFighterPower enemy)
\r
551 Enemy.Text = enemy.AirCombat + enemy.UnknownMark;
\r
552 ToolTip.SetToolTip(Enemy,
\r
553 enemy.AirCombat == enemy.Interception ? "" : "防空:" + enemy.Interception + enemy.UnknownMark);
\r
556 private void SetAirControlLevel(int level)
\r
559 new[] {"", "制空均衡", "制空確保", "航空優勢", "航空劣勢", "制空喪失"}[level + 1];
\r