--- /dev/null
+// Copyright (C) 2014 Kazuhiro Fujieda <fujieda@users.sourceforge.jp>\r
+// \r
+// This program is part of KancolleSniffer.\r
+//\r
+// KancolleSniffer is free software: you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation, either version 3 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, see <http://www.gnu.org/licenses/>.\r
+\r
+using System.Linq;\r
+\r
+namespace KancolleSniffer\r
+{\r
+ public class BattleInfo\r
+ {\r
+ private readonly ShipMaster _shipMaster;\r
+ private readonly ShipInfo _shipInfo;\r
+ public bool InBattle { get; set; }\r
+ public string Formation { get; private set; }\r
+ public int DelayInFormation { get; private set; }\r
+\r
+ private struct Delay\r
+ {\r
+ public const int Basic = 7200;\r
+ public const int Tau = 200;\r
+ public const int SearchAirSuccess = 5700;\r
+ public const int SearchAirFailure = 4900;\r
+ public const int SearchSuccess = 5400;\r
+ public const int Submarine = 2500;\r
+ public const int AirFight = 8700;\r
+ public const int AirFightBoth = 2700;\r
+ public const int Support = 9800;\r
+ public const int OpeningAttack = 3500;\r
+ public const int Cutin = 4900;\r
+ }\r
+\r
+ public BattleInfo(ShipMaster shipMaster, ShipInfo shipInfo)\r
+ {\r
+ _shipMaster = shipMaster;\r
+ _shipInfo = shipInfo;\r
+ }\r
+\r
+ public void InspectBattle(dynamic json)\r
+ {\r
+ InBattle = true;\r
+ var delay = Delay.Basic;\r
+ switch ((int)json.api_formation[2])\r
+ {\r
+ case 1:\r
+ Formation = "同航戦";\r
+ break;\r
+ case 2:\r
+ Formation = "反航戦";\r
+ break;\r
+ case 3:\r
+ Formation = "T字有利";\r
+ delay += Delay.Tau;\r
+ break;\r
+ case 4:\r
+ Formation = "T字不利";\r
+ delay += Delay.Tau;\r
+ break;\r
+ }\r
+ var subm = (SubmarineFlags)CheckSubmarine(json);\r
+ delay += SearchDelay(json) + SupportDelay(json) + CutinDelay(json) + subm.SubmarineDelay();\r
+ if (!subm.PreventOpeningAttack)\r
+ delay += OpeningAttackDelay(json);\r
+ if (!subm.PreventAirFight)\r
+ delay += AirFightDelay(json);\r
+ DelayInFormation = delay;\r
+ }\r
+\r
+ private int SearchDelay(dynamic json)\r
+ {\r
+ switch ((int)json.api_search[0])\r
+ {\r
+ case 1: // 索敵機による索敵成功\r
+ case 2: // 索敵機未帰還あり\r
+ return Delay.SearchAirSuccess;\r
+ case 3: // 索敵機未帰還\r
+ case 4: // 索敵機による索敵失敗\r
+ return Delay.SearchAirFailure;\r
+ case 5: // 索敵力による索敵成功\r
+ return Delay.SearchSuccess;\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ private int OpeningAttackDelay(dynamic json)\r
+ {\r
+ return json.api_opening_flag == 1 ? Delay.OpeningAttack : 0;\r
+ }\r
+\r
+ private class SubmarineFlags\r
+ {\r
+ public bool[] Friend;\r
+ public bool[] Enemy;\r
+\r
+ public int SubmarineDelay()\r
+ {\r
+ return Friend.Any(x => x) || Enemy.Any(x => x) ? Delay.Submarine : 0; // どちらかに潜水艦 \r
+ }\r
+\r
+ public bool PreventAirFight\r
+ {\r
+ get { return Friend.All(x => x) || Enemy.All(x => x); } // 一方がすべて潜水艦\r
+ }\r
+\r
+ public bool PreventOpeningAttack\r
+ {\r
+ get { return Friend.All(x => x) && Enemy.All(x => x); } // 双方すべて潜水艦\r
+ }\r
+ }\r
+\r
+ private SubmarineFlags CheckSubmarine(dynamic json)\r
+ {\r
+ return new SubmarineFlags\r
+ {\r
+ Friend = (from status in _shipInfo.GetShipStatuses((int)json.api_dock_id - 1)\r
+ select _shipMaster[status.ShipId].IsSubmarine).ToArray(),\r
+ Enemy = (from id in (int[])json.api_ship_ke\r
+ where id != -1\r
+ select _shipMaster[id].IsSubmarine).ToArray()\r
+ };\r
+ }\r
+\r
+ private int AirFightDelay(dynamic json)\r
+ {\r
+ // 僚艦が偵察機だけだと航空戦に参加しないので、\r
+ // 艦載機の配備ではなく遭遇戦の状況を調べる。\r
+ var f = json.api_kouku.api_stage1.api_f_count > 0;\r
+ var e = json.api_kouku.api_stage1.api_e_count > 0;\r
+ var result = 0;\r
+ if (f || e) // 艦載機発艦\r
+ result += Delay.AirFight;\r
+ if (f && e) // 双方とも\r
+ result += Delay.AirFightBoth;\r
+ return result;\r
+ }\r
+\r
+ private int SupportDelay(dynamic json)\r
+ {\r
+ return json.api_support_flag() && json.api_support_flag == 1 ? Delay.Support : 0;\r
+ }\r
+\r
+ private int CutinDelay(dynamic json)\r
+ {\r
+ var cutin = 0;\r
+ var maxHps = (int[])json.api_nowhps;\r
+ var nowHps = (int[])json.api_nowhps;\r
+ var planeFrom = (int[][])json.api_kouku.api_plane_from;\r
+ if ((planeFrom[0][0] != -1 || planeFrom[1][0] != -1) && // どちらかに艦載機あり\r
+ json.api_kouku.api_stage3 != null) // 敵艦載機が全滅しているとnull\r
+ {\r
+ // 航空戦による中破大破の判定\r
+ var damages = (int[])json.api_kouku.api_stage3.api_fdam;\r
+ var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();\r
+ if (IsCutinShown(nowHps, newHps, maxHps))\r
+ cutin++;\r
+ nowHps = newHps;\r
+ }\r
+ if ((int)json.api_opening_flag != 0)\r
+ {\r
+ // 開幕雷撃による中破大破の判定\r
+ var damages = (int[])json.api_opening_atack.api_fdam;\r
+ var newHps = nowHps.Zip(damages, (hp, dmg) => hp - dmg).ToArray();\r
+ if (IsCutinShown(nowHps, newHps, maxHps))\r
+ cutin++;\r
+ }\r
+ return Delay.Cutin * cutin;\r
+ }\r
+\r
+ private bool IsCutinShown(int[] nowHps, int[] newHps, int[] maxHps)\r
+ {\r
+ for (var i = 1; i < ShipInfo.MemberCount + 1; i++)\r
+ {\r
+ if (ShipStatus.CalcDamage(nowHps[i], maxHps[i]) <= ShipStatus.Damage.Small &&\r
+ ShipStatus.CalcDamage(newHps[i], maxHps[i]) >= ShipStatus.Damage.Half)\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ }\r
+}
\ No newline at end of file
<ItemGroup>\r
<Compile Include="Achievement.cs" />\r
<Compile Include="AkashiTimer.cs" />\r
+ <Compile Include="BattleInfo.cs" />\r
<Compile Include="Config.cs" />\r
<Compile Include="ConfigDialog.cs">\r
<SubType>Form</SubType>\r
this.components = new System.ComponentModel.Container();\r
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));\r
this.panelHeadquarters = new System.Windows.Forms.Panel();\r
+ this.labelLogin = new System.Windows.Forms.Label();\r
+ this.labelResetAchievement = new System.Windows.Forms.Label();\r
this.labelAchievement = new System.Windows.Forms.Label();\r
this.label28 = new System.Windows.Forms.Label();\r
this.labelNumOfBuckets = new System.Windows.Forms.Label();\r
this.label2 = new System.Windows.Forms.Label();\r
this.labelNumOfShips = new System.Windows.Forms.Label();\r
this.label1 = new System.Windows.Forms.Label();\r
- this.labelLogin = new System.Windows.Forms.Label();\r
this.panelFleet1 = new System.Windows.Forms.Panel();\r
this.label19 = new System.Windows.Forms.Label();\r
this.labelAkashiTimer = new System.Windows.Forms.Label();\r
this.labelFleet3 = new System.Windows.Forms.Label();\r
this.labelCheckFleet2 = new System.Windows.Forms.Label();\r
this.labelFleet2 = new System.Windows.Forms.Label();\r
- this.labelResetAchievement = new System.Windows.Forms.Label();\r
+ this.panelBattleInfo = new System.Windows.Forms.Panel();\r
+ this.label29 = new System.Windows.Forms.Label();\r
+ this.labelFormation = new System.Windows.Forms.Label();\r
this.panelHeadquarters.SuspendLayout();\r
this.panelFleet1.SuspendLayout();\r
this.panelDock.SuspendLayout();\r
this.panel3.SuspendLayout();\r
this.contextMenuStripToolTip.SuspendLayout();\r
this.panel4.SuspendLayout();\r
+ this.panelBattleInfo.SuspendLayout();\r
this.SuspendLayout();\r
// \r
// panelHeadquarters\r
this.panelHeadquarters.Size = new System.Drawing.Size(245, 35);\r
this.panelHeadquarters.TabIndex = 0;\r
// \r
+ // labelLogin\r
+ // \r
+ this.labelLogin.AutoSize = true;\r
+ this.labelLogin.Font = new System.Drawing.Font("MS UI Gothic", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));\r
+ this.labelLogin.Location = new System.Drawing.Point(6, 4);\r
+ this.labelLogin.Name = "labelLogin";\r
+ this.labelLogin.Size = new System.Drawing.Size(203, 26);\r
+ this.labelLogin.TabIndex = 23;\r
+ this.labelLogin.Text = "艦これにログインしてください。\r\nログイン中ならログインし直してください。";\r
+ // \r
+ // labelResetAchievement\r
+ // \r
+ this.labelResetAchievement.AutoSize = true;\r
+ this.labelResetAchievement.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;\r
+ this.labelResetAchievement.Location = new System.Drawing.Point(192, 16);\r
+ this.labelResetAchievement.Name = "labelResetAchievement";\r
+ this.labelResetAchievement.Size = new System.Drawing.Size(15, 14);\r
+ this.labelResetAchievement.TabIndex = 8;\r
+ this.labelResetAchievement.Text = "↺";\r
+ this.labelResetAchievement.Click += new System.EventHandler(this.labelResetAchievement_Click);\r
+ // \r
// labelAchievement\r
// \r
this.labelAchievement.Location = new System.Drawing.Point(148, 19);\r
this.label1.TabIndex = 0;\r
this.label1.Text = "艦娘数";\r
// \r
- // labelLogin\r
- // \r
- this.labelLogin.AutoSize = true;\r
- this.labelLogin.Font = new System.Drawing.Font("MS UI Gothic", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));\r
- this.labelLogin.Location = new System.Drawing.Point(6, 4);\r
- this.labelLogin.Name = "labelLogin";\r
- this.labelLogin.Size = new System.Drawing.Size(203, 26);\r
- this.labelLogin.TabIndex = 23;\r
- this.labelLogin.Text = "艦これにログインしてください。\r\nログイン中ならログインし直してください。";\r
- // \r
// panelFleet1\r
// \r
this.panelFleet1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;\r
+ this.panelFleet1.Controls.Add(this.panelBattleInfo);\r
this.panelFleet1.Controls.Add(this.label19);\r
this.panelFleet1.Controls.Add(this.labelAkashiTimer);\r
this.panelFleet1.Controls.Add(this.labelAirSuperiority);\r
this.labelFleet2.Text = "第二艦隊";\r
this.labelFleet2.Click += new System.EventHandler(this.labelFleet_Click);\r
// \r
- // labelResetAchievement\r
+ // panelBattleInfo\r
// \r
- this.labelResetAchievement.AutoSize = true;\r
- this.labelResetAchievement.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;\r
- this.labelResetAchievement.Location = new System.Drawing.Point(192, 16);\r
- this.labelResetAchievement.Name = "labelResetAchievement";\r
- this.labelResetAchievement.Size = new System.Drawing.Size(15, 14);\r
- this.labelResetAchievement.TabIndex = 8;\r
- this.labelResetAchievement.Text = "↺";\r
- this.labelResetAchievement.Click += new System.EventHandler(this.labelResetAchievement_Click);\r
+ this.panelBattleInfo.Controls.Add(this.labelFormation);\r
+ this.panelBattleInfo.Controls.Add(this.label29);\r
+ this.panelBattleInfo.Location = new System.Drawing.Point(66, 119);\r
+ this.panelBattleInfo.Name = "panelBattleInfo";\r
+ this.panelBattleInfo.Size = new System.Drawing.Size(175, 12);\r
+ this.panelBattleInfo.TabIndex = 40;\r
+ this.panelBattleInfo.Visible = false;\r
+ // \r
+ // label29\r
+ // \r
+ this.label29.AutoSize = true;\r
+ this.label29.Location = new System.Drawing.Point(1, 0);\r
+ this.label29.Name = "label29";\r
+ this.label29.Size = new System.Drawing.Size(41, 12);\r
+ this.label29.TabIndex = 0;\r
+ this.label29.Text = "戦闘中";\r
+ // \r
+ // labelFormation\r
+ // \r
+ this.labelFormation.Location = new System.Drawing.Point(44, 0);\r
+ this.labelFormation.Name = "labelFormation";\r
+ this.labelFormation.Size = new System.Drawing.Size(48, 12);\r
+ this.labelFormation.TabIndex = 1;\r
// \r
// MainForm\r
// \r
this.contextMenuStripToolTip.ResumeLayout(false);\r
this.panel4.ResumeLayout(false);\r
this.panel4.PerformLayout();\r
+ this.panelBattleInfo.ResumeLayout(false);\r
+ this.panelBattleInfo.PerformLayout();\r
this.ResumeLayout(false);\r
this.PerformLayout();\r
\r
private System.Windows.Forms.Label labelAchievement;\r
private System.Windows.Forms.Label label28;\r
private System.Windows.Forms.Label labelResetAchievement;\r
+ private System.Windows.Forms.Panel panelBattleInfo;\r
+ private System.Windows.Forms.Label labelFormation;\r
+ private System.Windows.Forms.Label label29;\r
}\r
}\r
\r
action += UpdateQuestList;\r
if ((update & Sniffer.Update.Ship) != 0)\r
action += UpdateShipInfo;\r
+ if ((update & Sniffer.Update.Battle) != 0)\r
+ action += UpdateBattleInfo;\r
Invoke(action);\r
}\r
\r
UpdateAkashiTimer();\r
}\r
\r
+ private void UpdateBattleInfo()\r
+ {\r
+ labelFormation.Text = "";\r
+ panelBattleInfo.Visible = _sniffer.Battle.InBattle;\r
+ if (!_sniffer.Battle.InBattle)\r
+ return;\r
+ var timer = new Timer {Interval = _sniffer.Battle.DelayInFormation};\r
+ timer.Tick += (sender, args) =>\r
+ {\r
+ labelFormation.Text = _sniffer.Battle.Formation;\r
+ timer.Stop();\r
+ };\r
+ timer.Start();\r
+ }\r
+\r
private void UpdateChargeInfo()\r
{\r
var fuel = new[] {labelFuel1, labelFuel2, labelFuel3, labelFuel4};\r
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w\r
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0\r
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAQ\r
- CwAAAk1TRnQBSQFMAgEBBQEAASABAAEgAQABDgEAAQ8BAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo\r
+ CwAAAk1TRnQBSQFMAgEBBQEAASgBAAEoAQABDgEAAQ8BAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo\r
AwABOAMAAR4DAAEBAQABCAUAAZABBhgAAYACAAGAAwACgAEAAYADAAGAAQABgAEAAoACAAPAAQABwAHc\r
AcABAAHwAcoBpgEAATMFAAEzAQABMwEAATMBAAIzAgADFgEAAxwBAAMiAQADKQEAA1UBAANNAQADQgEA\r
AzkBAAGAAXwB/wEAAlAB/wEAAZMBAAHWAQAB/wHsAcwBAAHGAdYB7wEAAdYC5wEAAZABqQGtAgAB/wEz\r
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w\r
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0\r
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA0\r
- CwAAAk1TRnQBSQFMAgEBBQEAASABAAEgAQABDgEAAQ4BAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo\r
+ CwAAAk1TRnQBSQFMAgEBBQEAASgBAAEoAQABDgEAAQ4BAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo\r
AwABOAMAARwDAAEBAQABCAUAASABBhgAAYACAAGAAwACgAEAAYADAAGAAQABgAEAAoACAAPAAQABwAHc\r
AcABAAHwAcoBpgEAATMFAAEzAQABMwEAATMBAAIzAgADFgEAAxwBAAMiAQADKQEAA1UBAANNAQADQgEA\r
AzkBAAGAAXwB/wEAAlAB/wEAAZMBAAHWAQAB/wHsAcwBAAHGAdYB7wEAAdYC5wEAAZABqQGtAgAB/wEz\r
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w\r
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0\r
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABm\r
- BwAAAk1TRnQBSQFMAwEBAAFYAQABWAEAAQcBAAEPAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA\r
+ BwAAAk1TRnQBSQFMAwEBAAFgAQABYAEAAQcBAAEPAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA\r
ARwDAAEPAwABAQEAAQgFAAGkAQEYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA\r
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5\r
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA\r
{\r
Name = entry.api_name,\r
FuelMax = (int)entry.api_fuel_max,\r
- BullMax = (int)entry.api_bull_max\r
- }; \r
+ BullMax = (int)entry.api_bull_max,\r
+ ShipType = (int)entry.api_stype,\r
+ };\r
}\r
}\r
\r
get\r
{\r
ShipSpec spec;\r
- return _shipSpecs.TryGetValue(id, out spec) ? spec : new ShipSpec { Name = "不明" }; \r
+ return _shipSpecs.TryGetValue(id, out spec) ? spec : new ShipSpec {Name = "不明"};\r
}\r
}\r
}\r
public string Name { get; set; }\r
public int FuelMax { get; set; }\r
public int BullMax { get; set; }\r
+ public int ShipType { get; set; }\r
+\r
+ public bool IsSubmarine\r
+ {\r
+ get { return ShipType == 13 || ShipType == 14; }\r
+ }\r
}\r
}
\ No newline at end of file
private readonly DockInfo _dockInfo;\r
private readonly AkashiTimer _akashiTimer;\r
private readonly Achievement _achievement = new Achievement();\r
+ private readonly BattleInfo _battleInfo;\r
private readonly Status _status = new Status();\r
\r
[Flags]\r
NDock = 16,\r
Mission = 32,\r
QuestList = 64,\r
- All = 127\r
+ Battle = 128,\r
+ All = 255\r
}\r
\r
public Sniffer()\r
_shipInfo = new ShipInfo(_shipMaster, _itemInfo);\r
_dockInfo = new DockInfo(_shipInfo);\r
_akashiTimer = new AkashiTimer(_shipInfo, _itemInfo, _dockInfo, _missionInfo);\r
+ _battleInfo = new BattleInfo(_shipMaster, _shipInfo);\r
}\r
\r
public void SaveState()\r
_dockInfo.InspectNDock(data.api_ndock);\r
_akashiTimer.SetTimer(true);\r
_achievement.InspectBasic(data.api_basic);\r
+ _battleInfo.InBattle = false;\r
return Update.All;\r
}\r
if (url.EndsWith("api_get_member/basic"))\r
// ここだけjsonなので注意\r
_shipInfo.InspectShip(json);\r
_akashiTimer.SetTimer();\r
- return Update.Item | Update.Ship;\r
+ _battleInfo.InBattle = false;\r
+ return Update.Item | Update.Ship | Update.Battle;\r
}\r
if (url.EndsWith("api_get_member/ship3"))\r
{\r
_shipInfo.InspectNyukyo(request);\r
return Update.Item | Update.Ship;\r
}\r
+ if (url.EndsWith("api_req_sortie/battle") || url.EndsWith("api_req_practice/battle"))\r
+ {\r
+ _battleInfo.InspectBattle(data);\r
+ return Update.Battle;\r
+ }\r
return Update.None;\r
}\r
\r
{\r
get { return _achievement; }\r
}\r
+\r
+ public BattleInfo Battle\r
+ {\r
+ get { return _battleInfo; }\r
+ }\r
}\r
\r
public class NameAndTimer\r