diff --git a/source/Grabacr07.KanColleViewer/Models/Migration/_KanColleClientSettings.cs b/source/Grabacr07.KanColleViewer/Models/Migration/_KanColleClientSettings.cs index 9e718d668..8f522676c 100644 --- a/source/Grabacr07.KanColleViewer/Models/Migration/_KanColleClientSettings.cs +++ b/source/Grabacr07.KanColleViewer/Models/Migration/_KanColleClientSettings.cs @@ -23,6 +23,8 @@ public class _KanColleClientSettings : IKanColleClientSettings public bool EnableTranslations { get; set; } public bool EnableUpdates { get; set; } public bool EnableAutosubmission { get; set; } + public bool IsViewRangeCalcIncludeFirstFleet { get; set; } + public bool IsViewRangeCalcIncludeSecondFleet { get; set; } public bool CheckFlagshipIsRepairShip { get; set; } event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged diff --git a/source/Grabacr07.KanColleViewer/Models/Settings/KanColleSettings.cs b/source/Grabacr07.KanColleViewer/Models/Settings/KanColleSettings.cs index fecb5ad69..171031641 100644 --- a/source/Grabacr07.KanColleViewer/Models/Settings/KanColleSettings.cs +++ b/source/Grabacr07.KanColleViewer/Models/Settings/KanColleSettings.cs @@ -27,6 +27,18 @@ public class KanColleSettings : IKanColleClientSettings public static SerializableProperty ViewRangeCalcType { get; } = new SerializableProperty(GetKey(), Providers.Roaming, new ViewRangeType1().Id); + /// + /// 索敵計算に第1艦隊を含めるかどうかの設定値を取得します。 + /// + public static SerializableProperty IsViewRangeCalcIncludeFirstFleet { get; } + = new SerializableProperty(GetKey(), Providers.Roaming, true); + + /// + /// 索敵計算に第2艦隊を含めるかどうかの設定値を取得します。 + /// + public static SerializableProperty IsViewRangeCalcIncludeSecondFleet { get; } + = new SerializableProperty(GetKey(), Providers.Roaming, false); + /// /// 建造完了時に通知するかどうかを示す設定値を取得します。 /// @@ -111,6 +123,8 @@ public KanColleSettings() NotificationShorteningTime.Subscribe(_ => this.RaisePropertyChanged(nameof(NotificationShorteningTime))); ReSortieCondition.Subscribe(_ => this.RaisePropertyChanged(nameof(ReSortieCondition))); ViewRangeCalcType.Subscribe(_ => this.RaisePropertyChanged(nameof(ViewRangeCalcType))); + IsViewRangeCalcIncludeFirstFleet.Subscribe(_ => this.RaisePropertyChanged(nameof(IsViewRangeCalcIncludeFirstFleet))); + IsViewRangeCalcIncludeSecondFleet.Subscribe(_ => this.RaisePropertyChanged(nameof(IsViewRangeCalcIncludeSecondFleet))); GeneralSettings.Culture.Subscribe(_ => this.RaisePropertyChanged(GeneralSettings.Culture)); } @@ -129,6 +143,10 @@ protected void RaisePropertyChanged([CallerMemberName] string propertyName = nul string IKanColleClientSettings.ViewRangeCalcType => ViewRangeCalcType.Value; + bool IKanColleClientSettings.IsViewRangeCalcIncludeFirstFleet => IsViewRangeCalcIncludeFirstFleet.Value; + + bool IKanColleClientSettings.IsViewRangeCalcIncludeSecondFleet => IsViewRangeCalcIncludeSecondFleet.Value; + string IKanColleClientSettings.Culture => GeneralSettings.Culture.Value; bool IKanColleClientSettings.EnableTranslations => EnableTranslations.Value; diff --git a/source/Grabacr07.KanColleViewer/Properties/AssemblyInfo.cs b/source/Grabacr07.KanColleViewer/Properties/AssemblyInfo.cs index d0dfa6739..c941db5c2 100644 --- a/source/Grabacr07.KanColleViewer/Properties/AssemblyInfo.cs +++ b/source/Grabacr07.KanColleViewer/Properties/AssemblyInfo.cs @@ -21,4 +21,4 @@ ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] -[assembly: AssemblyVersion("4.2.4.0")] +[assembly: AssemblyVersion("4.2.5.0")] diff --git a/source/Grabacr07.KanColleViewer/ViewModels/Settings/SettingsViewModel.cs b/source/Grabacr07.KanColleViewer/ViewModels/Settings/SettingsViewModel.cs index 97a61c058..766486685 100644 --- a/source/Grabacr07.KanColleViewer/ViewModels/Settings/SettingsViewModel.cs +++ b/source/Grabacr07.KanColleViewer/ViewModels/Settings/SettingsViewModel.cs @@ -13,6 +13,7 @@ using Livet; using Livet.EventListeners; using MetroTrilithon.Mvvm; +using Livet; namespace Grabacr07.KanColleViewer.ViewModels.Settings { @@ -50,9 +51,9 @@ public override string Name #region ViewRangeSettingsCollection 変更通知プロパティ - private List _ViewRangeSettingsCollection; + private List _ViewRangeSettingsCollection; - public List ViewRangeSettingsCollection + public List ViewRangeSettingsCollection { get { return this._ViewRangeSettingsCollection; } set @@ -67,6 +68,26 @@ public List ViewRangeSettingsCollection #endregion + #region SelectedViewRangeCalcType 変更通知プロパティ + + private ICalcViewRange _SelectedViewRangeCalcType; + + public ICalcViewRange SelectedViewRangeCalcType + { + get { return this._SelectedViewRangeCalcType; } + set + { + if (this._SelectedViewRangeCalcType != value) + { + this._SelectedViewRangeCalcType = value; + KanColleSettings.ViewRangeCalcType.Value = value.Id; + this.RaisePropertyChanged(); + } + } + } + + #endregion + private SettingsViewModel() { this.ScreenshotSettings = new ScreenshotSettingsViewModel().AddTo(this); @@ -96,15 +117,16 @@ private SettingsViewModel() return list; }); + this.ViewRangeSettingsCollection = ViewRangeCalcLogic.Logics.ToList(); + this.SelectedViewRangeCalcType = this.ViewRangeSettingsCollection + .FirstOrDefault(x => x.Id == KanColleSettings.ViewRangeCalcType) + ?? this.ViewRangeSettingsCollection.First(); + this.CompositeDisposable.Add(new PropertyChangedEventListener(KanColleClient.Current.Translations) { (sender, args) => this.RaisePropertyChanged(args.PropertyName), }); - this.ViewRangeSettingsCollection = ViewRangeCalcLogic.Logics - .Select(x => new ViewRangeSettingsViewModel(x)) - .ToList(); - this.LoadedPlugins = new List( PluginService.Current.Plugins.Select(x => new PluginViewModel(x))); @@ -119,56 +141,5 @@ public void Initialize() this.NetworkSettings.Initialize(); this.UserStyleSheetSettings.Initialize(); } - - - public class ViewRangeSettingsViewModel : ViewModel - { - private bool selected; - - public ICalcViewRange Logic { get; set; } - - public string Name => GetLocalisedStrings(Logic.Id); - - public string Description => GetLocalisedStrings(Logic.Id, true); - - public bool Selected - { - get { return this.selected; } - set - { - this.selected = value; - if (value) - { - KanColleSettings.ViewRangeCalcType.Value = this.Logic.Id; - } - } - } - - public ViewRangeSettingsViewModel(ICalcViewRange logic) - { - this.Logic = logic; - this.selected = KanColleSettings.ViewRangeCalcType == logic.Id; - ResourceService.Current.Subscribe(x => - { - this.RaisePropertyChanged(nameof(Name)); - this.RaisePropertyChanged(nameof(Description)); - }); - } - - private string GetLocalisedStrings(string type, bool desc = false) - { - switch (type) - { - case "KanColleViewer.Type1": - return !desc ? Resources.ViewRangeLogic_Type1_Name : Resources.ViewRangeLogic_Type1_Desc; - case "KanColleViewer.Type2": - return !desc ? Resources.ViewRangeLogic_Type2_Name : Resources.ViewRangeLogic_Type2_Desc; - case "KanColleViewer.Type3": - return !desc ? Resources.ViewRangeLogic_Type3_Name : Resources.ViewRangeLogic_Type3_Desc; - } - - return !desc ? Logic.Name : Logic.Description; - } - } } } diff --git a/source/Grabacr07.KanColleViewer/Views/Settings/Operation.xaml b/source/Grabacr07.KanColleViewer/Views/Settings/Operation.xaml index 3feb2f354..34da0a244 100644 --- a/source/Grabacr07.KanColleViewer/Views/Settings/Operation.xaml +++ b/source/Grabacr07.KanColleViewer/Views/Settings/Operation.xaml @@ -67,24 +67,31 @@ - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/source/Grabacr07.KanColleViewer/bin/Debug/Plugins/EventMapHpViewer.dll b/source/Grabacr07.KanColleViewer/bin/Debug/Plugins/EventMapHpViewer.dll index 838c21504..1d220d1c9 100644 Binary files a/source/Grabacr07.KanColleViewer/bin/Debug/Plugins/EventMapHpViewer.dll and b/source/Grabacr07.KanColleViewer/bin/Debug/Plugins/EventMapHpViewer.dll differ diff --git a/source/Grabacr07.KanColleViewer/bin/Release (beta)/Plugins/EventMapHpViewer.dll b/source/Grabacr07.KanColleViewer/bin/Release (beta)/Plugins/EventMapHpViewer.dll index 1d6e8639c..521c4d44b 100644 Binary files a/source/Grabacr07.KanColleViewer/bin/Release (beta)/Plugins/EventMapHpViewer.dll and b/source/Grabacr07.KanColleViewer/bin/Release (beta)/Plugins/EventMapHpViewer.dll differ diff --git a/source/Grabacr07.KanColleViewer/bin/Release/Plugins/EventMapHpViewer.dll b/source/Grabacr07.KanColleViewer/bin/Release/Plugins/EventMapHpViewer.dll index 1d6e8639c..521c4d44b 100644 Binary files a/source/Grabacr07.KanColleViewer/bin/Release/Plugins/EventMapHpViewer.dll and b/source/Grabacr07.KanColleViewer/bin/Release/Plugins/EventMapHpViewer.dll differ diff --git a/source/Grabacr07.KanColleWrapper/Calculator.cs b/source/Grabacr07.KanColleWrapper/Calculator.cs index faa5f677e..8abb31095 100644 --- a/source/Grabacr07.KanColleWrapper/Calculator.cs +++ b/source/Grabacr07.KanColleWrapper/Calculator.cs @@ -42,6 +42,7 @@ public static int CalcMinAirSuperiorityPotential(this Ship ship) { return ship.EquippedItems .Select(x => (x.Item.Info.Type == SlotItemType.艦上戦闘機 + || x.Item.Info.Type == SlotItemType.水上戦闘機 ? x.Item.CalcAirSuperiorityPotential(x.Current) : 0) + x.Item.CalcMinAirecraftAdeptBonus(x.Current)) @@ -71,8 +72,9 @@ private static double CalcMinAirecraftAdeptBonus(this SlotItem slotItem, int ons { if(onslot < 1) return 0; return slotItem.Info.Type == SlotItemType.艦上戦闘機 + || slotItem.Info.Type == SlotItemType.水上戦闘機 ? slotItem.CalcAirecraftAdeptBonusOfType() + slotItem.CalcMinInternalAirecraftAdeptBonus() - : 0; // 艦戦以外は簡単に吹き飛ぶので最小値としては計算に入れない + : 0; // 艦戦・水戦以外は簡単に吹き飛ぶので最小値としては計算に入れない } /// @@ -92,6 +94,7 @@ private static double CalcMaxAirecraftAdeptBonus(this SlotItem slotItem, int ons /// private static int CalcAirecraftAdeptBonusOfType(this SlotItem slotItem) => slotItem.Info.Type == SlotItemType.艦上戦闘機 + || slotItem.Info.Type == SlotItemType.水上戦闘機 ? slotItem.Adept == 1 ? 0 : slotItem.Adept == 2 ? 2 : slotItem.Adept == 3 ? 5 @@ -147,7 +150,7 @@ private static double CalcMaxInternalAirecraftAdeptBonus(this SlotItem slotItem) public static double CalcViewRange(this Fleet fleet) { - return ViewRangeCalcLogic.Get(KanColleClient.Current.Settings.ViewRangeCalcType).Calc(fleet.Ships); + return ViewRangeCalcLogic.Get(KanColleClient.Current.Settings.ViewRangeCalcType).Calc(new[] { fleet }); } public static bool IsHeavilyDamage(this LimitedValue hp) diff --git a/source/Grabacr07.KanColleWrapper/Homeport.cs b/source/Grabacr07.KanColleWrapper/Homeport.cs index 0d10ab84d..c9eb24584 100644 --- a/source/Grabacr07.KanColleWrapper/Homeport.cs +++ b/source/Grabacr07.KanColleWrapper/Homeport.cs @@ -78,12 +78,12 @@ internal Homeport(KanColleProxy proxy) proxy.api_port.TryParse().Subscribe(x => { + this.UpdateAdmiral(x.Data.api_basic); this.Organization.Update(x.Data.api_ship); this.Repairyard.Update(x.Data.api_ndock); this.Organization.Update(x.Data.api_deck_port); this.Organization.Combined = x.Data.api_combined_flag != 0; this.Materials.Update(x.Data.api_material); - this.UpdateAdmiral(x.Data.api_basic); }); proxy.api_get_member_basic.TryParse().Subscribe(x => this.UpdateAdmiral(x.Data)); proxy.api_req_member_updatecomment.TryParse().Subscribe(this.UpdateComment); diff --git a/source/Grabacr07.KanColleWrapper/IKanColleClientSettings.cs b/source/Grabacr07.KanColleWrapper/IKanColleClientSettings.cs index ceed1bd4c..813a42ef3 100644 --- a/source/Grabacr07.KanColleWrapper/IKanColleClientSettings.cs +++ b/source/Grabacr07.KanColleWrapper/IKanColleClientSettings.cs @@ -22,6 +22,8 @@ public interface IKanColleClientSettings : INotifyPropertyChanged /// 索敵計算に使用するロジックを識別する文字列を取得します。 /// string ViewRangeCalcType { get; } + bool IsViewRangeCalcIncludeFirstFleet { get; } + bool IsViewRangeCalcIncludeSecondFleet { get; } string Culture { get; } diff --git a/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.cs b/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.cs index 118a2c0fc..3eaad1e03 100644 --- a/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.cs +++ b/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.cs @@ -174,6 +174,14 @@ public IObservable api_req_kaisou_slot_exchange_index get { return this.ApiSessionSource.Where(x => x.Request.PathAndQuery == "/kcsapi/api_req_kaisou/slot_exchange_index"); } } + /// + /// エンド ポイント "/kcsapi/api_req_kaisou/slot_deprive" からのセッションを配信します。 + /// + public IObservable api_req_kaisou_slot_deprive + { + get { return this.ApiSessionSource.Where(x => x.Request.PathAndQuery == "/kcsapi/api_req_kaisou/slot_deprive"); } + } + /// /// エンド ポイント "/kcsapi/api_req_kousyou/getship" からのセッションを配信します。 /// diff --git a/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.tt b/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.tt index b889642b7..a2eb7f5c2 100644 --- a/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.tt +++ b/source/Grabacr07.KanColleWrapper/KanColleProxy.Endpoints.tt @@ -28,6 +28,7 @@ "/kcsapi/api_req_hokyu/charge", "/kcsapi/api_req_kaisou/powerup", "/kcsapi/api_req_kaisou/slot_exchange_index", + "/kcsapi/api_req_kaisou/slot_deprive", "/kcsapi/api_req_kousyou/getship", "/kcsapi/api_req_kousyou/createitem", "/kcsapi/api_req_kousyou/createship", diff --git a/source/Grabacr07.KanColleWrapper/KanColleWrapper.csproj b/source/Grabacr07.KanColleWrapper/KanColleWrapper.csproj index a293e9e86..a1b478faf 100644 --- a/source/Grabacr07.KanColleWrapper/KanColleWrapper.csproj +++ b/source/Grabacr07.KanColleWrapper/KanColleWrapper.csproj @@ -150,6 +150,7 @@ + diff --git a/source/Grabacr07.KanColleWrapper/Models/FleetState.cs b/source/Grabacr07.KanColleWrapper/Models/FleetState.cs index 018c2d715..54d033878 100644 --- a/source/Grabacr07.KanColleWrapper/Models/FleetState.cs +++ b/source/Grabacr07.KanColleWrapper/Models/FleetState.cs @@ -240,7 +240,9 @@ public FleetState(Homeport homeport, params Fleet[] fleets) this.CompositeDisposable.Add(this.Condition); this.CompositeDisposable.Add(new PropertyChangedWeakEventListener(KanColleClient.Current.Settings) { - { nameof(IKanColleClientSettings.ViewRangeCalcType), (sender, args) => this.Calculate() }, + { nameof(IKanColleClientSettings.ViewRangeCalcType), (_, __) => this.Calculate() }, + { nameof(IKanColleClientSettings.IsViewRangeCalcIncludeFirstFleet), (_, __) => this.Calculate() }, + { nameof(IKanColleClientSettings.IsViewRangeCalcIncludeSecondFleet), (_, __) => this.Calculate() }, }); } @@ -265,7 +267,7 @@ public void Calculate() : FleetSpeed.Hybrid; var logic = ViewRangeCalcLogic.Get(KanColleClient.Current.Settings.ViewRangeCalcType); - this.ViewRange = logic.Calc(ships.ToArray()); + this.ViewRange = logic.Calc(this.source); this.ViewRangeCalcType = logic.Name; this.Calculated?.Invoke(this, new EventArgs()); diff --git a/source/Grabacr07.KanColleWrapper/Models/Raw/kcsapi_slot_deprive.cs b/source/Grabacr07.KanColleWrapper/Models/Raw/kcsapi_slot_deprive.cs new file mode 100644 index 000000000..df5571ea9 --- /dev/null +++ b/source/Grabacr07.KanColleWrapper/Models/Raw/kcsapi_slot_deprive.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +// ReSharper disable InconsistentNaming + +namespace Grabacr07.KanColleWrapper.Models.Raw +{ + public class kcsapi_slot_deprive + { + public kcsapi_slot_deprive_ship_data api_ship_data { get; set; } + public kcsapi_slot_deprive_unset_List api_unset_list { get; set; } + } + + public class kcsapi_slot_deprive_ship_data + { + public kcsapi_ship2 api_set_ship { get; set; } + public kcsapi_ship2 api_unset_ship { get; set; } + } + + public class kcsapi_slot_deprive_unset_List + { + public int api_type3No { get; set; } + public int[] api_slot_list { get; set; } + } +} diff --git a/source/Grabacr07.KanColleWrapper/Models/SlotItemInfo.cs b/source/Grabacr07.KanColleWrapper/Models/SlotItemInfo.cs index 649031eb1..42371c567 100644 --- a/source/Grabacr07.KanColleWrapper/Models/SlotItemInfo.cs +++ b/source/Grabacr07.KanColleWrapper/Models/SlotItemInfo.cs @@ -91,14 +91,16 @@ public class SlotItemInfo : RawDataWrapper, IIdentifiable public bool IsAirSuperiorityFighter => this.Type == SlotItemType.艦上戦闘機 || this.Type == SlotItemType.艦上攻撃機 || this.Type == SlotItemType.艦上爆撃機 - || this.Type == SlotItemType.水上爆撃機; + || this.Type == SlotItemType.水上爆撃機 + || this.Type == SlotItemType.水上戦闘機; public bool IsNumerable => this.Type == SlotItemType.艦上偵察機 || this.Type == SlotItemType.艦上戦闘機 || this.Type == SlotItemType.艦上攻撃機 || this.Type == SlotItemType.艦上爆撃機 || this.Type == SlotItemType.水上偵察機 - || this.Type == SlotItemType.水上爆撃機; + || this.Type == SlotItemType.水上爆撃機 + || this.Type == SlotItemType.水上戦闘機; public SlotItemEquipType EquipType { get; } diff --git a/source/Grabacr07.KanColleWrapper/Models/SlotItemType.cs b/source/Grabacr07.KanColleWrapper/Models/SlotItemType.cs index 39f789045..5b9b7007e 100644 --- a/source/Grabacr07.KanColleWrapper/Models/SlotItemType.cs +++ b/source/Grabacr07.KanColleWrapper/Models/SlotItemType.cs @@ -8,6 +8,11 @@ namespace Grabacr07.KanColleWrapper.Models { public enum SlotItemType { + 小口径主砲 = 1, + 中口径主砲 = 2, + 大口径主砲 = 3, + 副砲 = 4, + 魚雷 = 5, 艦上戦闘機 = 6, 艦上爆撃機 = 7, 艦上攻撃機 = 8, @@ -16,9 +21,42 @@ public enum SlotItemType 水上爆撃機 = 11, 小型電探 = 12, 大型電探 = 13, - + ソナー = 14, + 爆雷 = 15, + 追加装甲 = 16, + 機関部強化 = 17, + 対空強化弾 = 18, + 対艦強化弾 = 19, + VT信管 = 20, + 対空機銃 = 21, + 特殊潜航艇 = 22, 応急修理要員 = 23, - + 上陸用舟艇 = 24, + オートジャイロ = 25, + 対潜哨戒機 = 26, + 追加装甲_中型 = 27, + 追加装甲_大型 = 28, 探照灯 = 29, + 簡易輸送部材 = 30, + 艦艇修理施設 = 31, + 潜水艦魚雷 = 32, + 照明弾 = 33, + 司令部施設 = 34, + 航空要員 = 35, + 高射装置 = 36, + 対地装備 = 37, + 大口径主砲_II = 38, + 水上艦要員 = 39, + 大型ソナー = 40, + 大型飛行艇 = 41, + 大型探照灯 = 42, + 戦闘糧食 = 43, + 補給物資 = 44, + 水上戦闘機 = 45, + 特型内火艇 = 46, + 陸上攻撃機 = 47, + 局地戦闘機 = 48, + 大型電探_II = 93, + 艦上偵察機_II = 94, } } diff --git a/source/Grabacr07.KanColleWrapper/Models/ViewRange.cs b/source/Grabacr07.KanColleWrapper/Models/ViewRange.cs index 2a97f9581..742488bfd 100644 --- a/source/Grabacr07.KanColleWrapper/Models/ViewRange.cs +++ b/source/Grabacr07.KanColleWrapper/Models/ViewRange.cs @@ -18,7 +18,9 @@ public interface ICalcViewRange string Description { get; } - double Calc(Ship[] ships); + bool HasCombinedSettings { get; } + + double Calc(Fleet[] fleets); } @@ -41,13 +43,15 @@ static ViewRangeCalcLogic() new ViewRangeType1(); new ViewRangeType2(); new ViewRangeType3(); + new ViewRangeType4(); // ReSharper restore ObjectCreationAsStatement } public abstract string Id { get; } public abstract string Name { get; } public abstract string Description { get; } - public abstract double Calc(Ship[] ships); + public virtual bool HasCombinedSettings { get; } = false; + public abstract double Calc(Fleet[] fleets); protected ViewRangeCalcLogic() { @@ -66,11 +70,11 @@ public class ViewRangeType1 : ViewRangeCalcLogic public override string Description => "艦娘と装備の索敵値の単純な合計値"; - public override double Calc(Ship[] ships) + public override double Calc(Fleet[] fleets) { - if (ships == null || ships.Length == 0) return 0; + if (fleets == null || fleets.Length == 0) return 0; - return ships.Sum(x => x.ViewRange); + return fleets.SelectMany(x => x.Ships).Sum(x => x.ViewRange); } } @@ -83,9 +87,10 @@ public class ViewRangeType2 : ViewRangeCalcLogic public override string Description => "(偵察機 × 2) + (電探) + √(装備込みの艦隊索敵値合計 - 偵察機 - 電探)"; - public override double Calc(Ship[] ships) + public override double Calc(Fleet[] fleets) { - if (ships == null || ships.Length == 0) return 0; + if (fleets == null || fleets.Length == 0) return 0; + var ships = fleets.SelectMany(x => x.Ships).ToArray(); // http://wikiwiki.jp/kancolle/?%C6%EE%C0%BE%BD%F4%C5%E7%B3%A4%B0%E8#area5 // [索敵装備と装備例] によって示されている計算式 @@ -120,9 +125,10 @@ public class ViewRangeType3 : ViewRangeCalcLogic + (小型電探 × 1.00) + (大型電探 × .99) + (√各艦毎の素索敵 × 1.69) + (司令部レベルを 5 の倍数に切り上げ × -0.61)"; - public override double Calc(Ship[] ships) + public override double Calc(Fleet[] fleets) { - if (ships == null || ships.Length == 0) return 0; + if (fleets == null || fleets.Length == 0) return 0; + var ships = fleets.SelectMany(x => x.Ships).ToArray(); // http://wikiwiki.jp/kancolle/?%C6%EE%C0%BE%BD%F4%C5%E7%B3%A4%B0%E8#search-calc // > 2-5式では説明出来ない事象を解決するため膨大な検証報告数より導き出した新式。2014年11月に改良され精度があがった。 @@ -186,4 +192,121 @@ private static double GetScore(SlotItemType type, int score) return .0; } } + + + public class ViewRangeType4 : ViewRangeCalcLogic + { + public override sealed string Id => "KanColleViewer.Type4"; + + public override string Name => "33 式"; + + public override string Description => + @"((各スロットの装備の索敵値 + 改修効果) × 装備タイプ係数)の和 + (√各艦の素索敵値)の和 +- (司令部レベル × 0.4)の小数点以下切り上げ + 艦隊の空き数 × 2 +※艦隊の空き数は退避した艦を除いて算出"; + + public override bool HasCombinedSettings { get; } = true; + + public override double Calc(Fleet[] fleets) + { + if (fleets == null || fleets.Length == 0) return 0; + + var ships = this.GetTargetShips(fleets) + .Where(x => !x.Situation.HasFlag(ShipSituation.Evacuation)) + .Where(x => !x.Situation.HasFlag(ShipSituation.Tow)) + .ToArray(); + + if (!ships.Any()) return 0; + + var itemScore = ships + .SelectMany(x => x.EquippedItems) + .Select(x => x.Item) + .Sum(x => (x.Info.ViewRange + GetAdeptCoefficient(x)) * GetTypeCoefficient(x.Info.Type)); + + var shipScore = ships + .Select(x => x.ViewRange - x.EquippedItems.Sum(s => s.Item.Info.RawData.api_saku)) + .Sum(x => Math.Sqrt(x)); + + var admiralScore = Math.Ceiling(KanColleClient.Current.Homeport.Admiral.Level * 0.4); + + var isCombined = 1 < fleets.Count() + && KanColleClient.Current.Settings.IsViewRangeCalcIncludeFirstFleet + && KanColleClient.Current.Settings.IsViewRangeCalcIncludeSecondFleet; + var vacancyScore = ((isCombined ? 12 : 6) - ships.Length) * 2; + + return itemScore + shipScore - admiralScore + vacancyScore; + } + + private Ship[] GetTargetShips(Fleet[] fleets) + { + if (fleets.Count() == 1) + return fleets.Single().Ships; + + if(KanColleClient.Current.Settings.IsViewRangeCalcIncludeFirstFleet + && KanColleClient.Current.Settings.IsViewRangeCalcIncludeSecondFleet) + return fleets.SelectMany(x => x.Ships).ToArray(); + + if (KanColleClient.Current.Settings.IsViewRangeCalcIncludeFirstFleet) + return fleets.First().Ships; + + if (KanColleClient.Current.Settings.IsViewRangeCalcIncludeSecondFleet) + return fleets.Last().Ships; + + return new Ship[0]; + } + + private static double GetAdeptCoefficient(SlotItem item) + { + switch (item.Info.Type) + { + case SlotItemType.水上偵察機: + return Math.Sqrt(item.Adept) * 1.2; + + case SlotItemType.小型電探: + case SlotItemType.大型電探: + case SlotItemType.大型電探_II: + return Math.Sqrt(item.Adept) * 1.25; + + default: + return 0; + } + } + + private static double GetTypeCoefficient(SlotItemType type) + { + switch (type) + { + case SlotItemType.艦上戦闘機: + case SlotItemType.艦上爆撃機: + case SlotItemType.小型電探: + case SlotItemType.大型電探: + case SlotItemType.対潜哨戒機: + case SlotItemType.探照灯: + case SlotItemType.司令部施設: + case SlotItemType.航空要員: + case SlotItemType.水上艦要員: + case SlotItemType.大型ソナー: + case SlotItemType.大型飛行艇: + case SlotItemType.大型探照灯: + case SlotItemType.水上戦闘機: + return 0.6; + + case SlotItemType.艦上攻撃機: + return 0.8; + + case SlotItemType.艦上偵察機: + case SlotItemType.艦上偵察機_II: + return 1.0; + + case SlotItemType.水上爆撃機: + return 1.1; + + case SlotItemType.水上偵察機: + return 1.2; + + default: + return .0; + } + } + } } diff --git a/source/Grabacr07.KanColleWrapper/Organization.cs b/source/Grabacr07.KanColleWrapper/Organization.cs index fd86646d5..a3ce762bc 100644 --- a/source/Grabacr07.KanColleWrapper/Organization.cs +++ b/source/Grabacr07.KanColleWrapper/Organization.cs @@ -9,512 +9,524 @@ namespace Grabacr07.KanColleWrapper { - /// - /// 艦娘と艦隊の編成を表します。 - /// - public class Organization : Notifier - { - private readonly Homeport homeport; - - private readonly List evacuatedShipsIds = new List(); - private readonly List towShipIds = new List(); - - #region Ships 変更通知プロパティ - - private MemberTable _Ships; - - /// - /// 母港に所属する艦娘のコレクションを取得します。 - /// - public MemberTable Ships - { - get { return this._Ships; } - private set - { - if (this._Ships != value) - { - this._Ships = value; - this.RaisePropertyChanged(); - } - } - } - - #endregion - - #region Fleets 変更通知プロパティ - - private MemberTable _Fleets; - - /// - /// 編成された艦隊のコレクションを取得します。 - /// - public MemberTable Fleets - { - get { return this._Fleets; } - private set - { - if (this._Fleets != value) - { - this._Fleets = value; - this.RaisePropertyChanged(); - } - } - } - - #endregion - - #region Combined 変更通知プロパティ - - private bool _Combined; - - /// - /// 第一・第二艦隊による連合艦隊が編成されているかどうかを示す値を取得または設定します。 - /// - public bool Combined - { - get { return this._Combined; } - set - { - if (this._Combined != value) - { - this._Combined = value; - this.Combine(value); - this.RaisePropertyChanged(); - } - } - } - - #endregion - - #region CombinedFleet 変更通知プロパティ - - private CombinedFleet _CombinedFleet; - - public CombinedFleet CombinedFleet - { - get { return this._CombinedFleet; } - set - { - if (this._CombinedFleet != value) - { - this._CombinedFleet = value; - this.RaisePropertyChanged(); - } - } - } - - #endregion - - - public Organization(Homeport parent, KanColleProxy proxy) - { - this.homeport = parent; - - this.Ships = new MemberTable(); - this.Fleets = new MemberTable(); - - proxy.api_get_member_ship.TryParse().Subscribe(x => this.Update(x.Data)); - proxy.api_get_member_ship2.TryParse().Subscribe(x => - { - this.Update(x.Data); - this.Update(x.Fleets); - }); - proxy.api_get_member_ship3.TryParse().Subscribe(x => - { - this.Update(x.Data.api_ship_data); - this.Update(x.Data.api_deck_data); - }); - - proxy.api_get_member_deck.TryParse().Subscribe(x => this.Update(x.Data)); - proxy.api_get_member_deck_port.TryParse().Subscribe(x => this.Update(x.Data)); - proxy.api_get_member_ship_deck.TryParse().Subscribe(x => this.Update(x.Data)); - proxy.api_req_hensei_preset_select.TryParse().Subscribe(x => this.Update(x.Data)); - - proxy.api_req_hensei_change.TryParse().Subscribe(this.Change); - proxy.api_req_hokyu_charge.TryParse().Subscribe(x => this.Charge(x.Data)); - proxy.api_req_kaisou_powerup.TryParse().Subscribe(this.Powerup); - proxy.api_req_kaisou_slot_exchange_index.TryParse().Subscribe(this.ExchangeSlot); - proxy.api_req_kousyou_getship.TryParse().Subscribe(x => this.GetShip(x.Data)); - proxy.api_req_kousyou_destroyship.TryParse().Subscribe(this.DestoryShip); - proxy.api_req_member_updatedeckname.TryParse().Subscribe(this.UpdateFleetName); - - proxy.api_req_hensei_combined.TryParse() - .Subscribe(x => this.Combined = x.Data.api_combined != 0); - - this.SubscribeSortieSessions(proxy); - } - - - /// - /// 指定した ID の艦娘が所属する艦隊を取得します。 - /// - internal Fleet GetFleet(int shipId) - { - return this.Fleets.Select(x => x.Value).SingleOrDefault(x => x.Ships.Any(s => s.Id == shipId)); - } - - private void UpdateFleetName(SvData data) - { - if (data == null || !data.IsSuccess) return; - - try - { - var fleet = this.Fleets[int.Parse(data.Request["api_deck_id"])]; - var name = data.Request["api_name"]; - - fleet.Name = name; - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("艦隊名の変更に失敗しました: {0}", ex); - } - } - - private void RaiseShipsChanged() - { - this.RaisePropertyChanged("Ships"); - } - - - #region 母港 / 艦隊編成 (Update / Change) - - /// - /// 指定した 型の配列を使用して、 プロパティ値を更新します。 - /// - internal void Update(kcsapi_ship2[] source) - { - if (source.Length <= 1) - { - foreach (var ship in source) - { - var target = this.Ships[ship.api_id]; - if (target == null) continue; - - target.Update(ship); - this.GetFleet(target.Id)?.State.Calculate(); - } - } - else - { - this.Ships = new MemberTable(source.Select(x => new Ship(this.homeport, x))); - - if (KanColleClient.Current.IsInSortie) - { - foreach (var id in this.evacuatedShipsIds) this.Ships[id].Situation |= ShipSituation.Evacuation; - foreach (var id in this.towShipIds) this.Ships[id].Situation |= ShipSituation.Tow; - } - - foreach (var fleet in this.Fleets.Values) - { - fleet.State.Update(); - fleet.State.Calculate(); - } - } - } - - - /// - /// 指定した 型の配列を使用して、 プロパティ値を更新します。 - /// - internal void Update(kcsapi_deck[] source) - { - if (this.Fleets.Count == source.Length) - { - foreach (var raw in source) this.Fleets[raw.api_id]?.Update(raw); - } - else - { - foreach (var fleet in this.Fleets) fleet.Value?.Dispose(); - this.Fleets = new MemberTable(source.Select(x => new Fleet(this.homeport, x))); - } - } - - - internal void Update(kcsapi_deck source) - { - var fleet = this.Fleets[source.api_id]; - if (fleet != null) - { - fleet.Update(source); - fleet.RaiseShipsUpdated(); - } - } - - - private void Change(SvData data) - { - if (data == null || !data.IsSuccess) return; - - try - { - var fleet = this.Fleets[int.Parse(data.Request["api_id"])]; - fleet.RaiseShipsUpdated(); - - var index = int.Parse(data.Request["api_ship_idx"]); - if (index == -1) - { - // 旗艦以外をすべて外すケース - fleet.UnsetAll(); - return; - } - - var ship = this.Ships[int.Parse(data.Request["api_ship_id"])]; - if (ship == null) - { - // 艦を外すケース - fleet.Unset(index); - return; - } - - var currentFleet = this.GetFleet(ship.Id); - if (currentFleet == null) - { - // ship が、現状どの艦隊にも所属していないケース - fleet.Change(index, ship); - return; - } - - // ship が、現状いずれかの艦隊に所属しているケース - var currentIndex = Array.IndexOf(currentFleet.Ships, ship); - var old = fleet.Change(index, ship); - - // Fleet.Change(int, Ship) は、変更前の艦を返す (= old) ので、 - // ship の移動元 (currentFleet + currentIndex) に old を書き込みにいく - currentFleet.Change(currentIndex, old); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("編成の変更に失敗しました: {0}", ex); - } - } - - - private void Combine(bool combine) - { - this.CombinedFleet?.Dispose(); - this.CombinedFleet = combine - ? new CombinedFleet(this.homeport, this.Fleets.OrderBy(x => x.Key).Select(x => x.Value).Take(2).ToArray()) - : null; - } - - private void ExchangeSlot(SvData data) - { - try - { - var ship = this.Ships[int.Parse(data.Request["api_id"])]; - if (ship == null) return; - - ship.RawData.api_slot = data.Data.api_slot; - ship.UpdateSlots(); - - var fleet = this.Fleets.Values.FirstOrDefault(x => x.Ships.Any(y => y.Id == ship.Id)); - if (fleet == null) return; - - fleet.State.Calculate(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("装備の入れ替えに失敗しました: {0}", ex); - } - } - - #endregion - - #region 補給 / 近代化改修 (Charge / Powerup) - - private void Charge(kcsapi_charge source) - { - Fleet fleet = null; // 補給した艦が所属している艦隊。艦隊をまたいで補給はできないので、必ず 1 つに絞れる - - foreach (var ship in source.api_ship) - { - var target = this.Ships[ship.api_id]; - if (target == null) continue; - - target.Charge(ship.api_fuel, ship.api_bull, ship.api_onslot); - - if (fleet == null) - { - fleet = this.GetFleet(target.Id); - } - } - - if (fleet != null) - { - fleet.State.Update(); - fleet.State.Calculate(); - } - } - - private void Powerup(SvData svd) - { - try - { - this.Ships[svd.Data.api_ship.api_id]?.Update(svd.Data.api_ship); - - var items = svd.Request["api_id_items"] - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(int.Parse) - .Where(x => this.Ships.ContainsKey(x)) - .Select(x => this.Ships[x]) - .ToArray(); - - // (改修に使った艦娘のこと item って呼ぶのどうなの…) - - foreach (var x in items) - { - this.homeport.Itemyard.RemoveFromShip(x); - this.Ships.Remove(x); - } - - this.RaiseShipsChanged(); - this.Update(svd.Data.api_deck); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("近代化改修による更新に失敗しました: {0}", ex); - } - } - - #endregion - - #region 工廠 (Get / Destroy) - - private void GetShip(kcsapi_kdock_getship source) - { - this.homeport.Itemyard.AddFromDock(source); - - this.Ships.Add(new Ship(this.homeport, source.api_ship)); - this.RaiseShipsChanged(); - } - - private void DestoryShip(SvData svd) - { - try - { - var ship = this.Ships[int.Parse(svd.Request["api_ship_id"])]; - if (ship != null) - { - this.homeport.Itemyard.RemoveFromShip(ship); - - this.Ships.Remove(ship); - this.RaiseShipsChanged(); - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("解体による更新に失敗しました: {0}", ex); - } - } - - #endregion - - #region 出撃 (Sortie / Homing / Escape) - - private void SubscribeSortieSessions(KanColleProxy proxy) - { - proxy.ApiSessionSource - .SkipUntil(proxy.api_req_map_start.TryParse().Do(this.Sortie)) - .TakeUntil(proxy.api_port) - .Finally(this.Homing) - .Repeat() - .Subscribe(); - - int[] evacuationOfferedShipIds = null; - int[] towOfferedShipIds = null; - - proxy.api_req_combined_battle_battleresult - .TryParse() - .Where(x => x.Data.api_escape != null) - .Select(x => x.Data) - .Subscribe(x => - { - if (this.CombinedFleet == null) return; - var ships = this.CombinedFleet.Fleets.SelectMany(f => f.Ships).ToArray(); - evacuationOfferedShipIds = x.api_escape.api_escape_idx.Select(idx => ships[idx - 1].Id).ToArray(); - towOfferedShipIds = x.api_escape.api_tow_idx.Select(idx => ships[idx - 1].Id).ToArray(); - }); - proxy.api_req_combined_battle_goback_port - .Subscribe(_ => - { - if (KanColleClient.Current.IsInSortie - && evacuationOfferedShipIds != null - && evacuationOfferedShipIds.Length >= 1 - && towOfferedShipIds != null - && towOfferedShipIds.Length >= 1) - { - this.evacuatedShipsIds.Add(evacuationOfferedShipIds[0]); - this.towShipIds.Add(towOfferedShipIds[0]); - } - }); - proxy.api_get_member_ship_deck - .Subscribe(_ => - { - evacuationOfferedShipIds = null; - towOfferedShipIds = null; - }); - } - - - private void Sortie(SvData data) - { - if (data == null || !data.IsSuccess) return; - - try - { - var id = int.Parse(data.Request["api_deck_id"]); - var fleet = this.Fleets[id]; - fleet.Sortie(); - - if (this.Combined && id == 1) this.Fleets[2].Sortie(); - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine("艦隊の出撃を検知できませんでした: {0}", ex); - } - } - - private void Homing() - { - this.evacuatedShipsIds.Clear(); - this.towShipIds.Clear(); - - foreach (var ship in this.Ships.Values) - { - if (ship.Situation.HasFlag(ShipSituation.Evacuation)) ship.Situation &= ~ShipSituation.Evacuation; - if (ship.Situation.HasFlag(ShipSituation.Tow)) ship.Situation &= ~ShipSituation.Tow; - } - - foreach (var target in this.Fleets.Values) - { - target.Homing(); - } - } - - private void Update(kcsapi_ship_deck source) - { - if (source.api_ship_data != null) - { - foreach (var ship in source.api_ship_data) - { - var target = this.Ships[ship.api_id]; - target.Update(ship); - - if (this.evacuatedShipsIds.Any(x => target.Id == x)) target.Situation |= ShipSituation.Evacuation; - if (this.towShipIds.Any(x => target.Id == x)) target.Situation |= ShipSituation.Tow; - } - } - - if (source.api_deck_data != null) - { - foreach (var deck in source.api_deck_data) - { - var target = this.Fleets[deck.api_id]; - target.Update(deck); - } - } - } - - #endregion - } + /// + /// 艦娘と艦隊の編成を表します。 + /// + public class Organization : Notifier + { + private readonly Homeport homeport; + + private readonly List evacuatedShipsIds = new List(); + private readonly List towShipIds = new List(); + + #region Ships 変更通知プロパティ + + private MemberTable _Ships; + + /// + /// 母港に所属する艦娘のコレクションを取得します。 + /// + public MemberTable Ships + { + get { return this._Ships; } + private set + { + if (this._Ships != value) + { + this._Ships = value; + this.RaisePropertyChanged(); + } + } + } + + #endregion + + #region Fleets 変更通知プロパティ + + private MemberTable _Fleets; + + /// + /// 編成された艦隊のコレクションを取得します。 + /// + public MemberTable Fleets + { + get { return this._Fleets; } + private set + { + if (this._Fleets != value) + { + this._Fleets = value; + this.RaisePropertyChanged(); + } + } + } + + #endregion + + #region Combined 変更通知プロパティ + + private bool _Combined; + + /// + /// 第一・第二艦隊による連合艦隊が編成されているかどうかを示す値を取得または設定します。 + /// + public bool Combined + { + get { return this._Combined; } + set + { + if (this._Combined != value) + { + this._Combined = value; + this.Combine(value); + this.RaisePropertyChanged(); + } + } + } + + #endregion + + #region CombinedFleet 変更通知プロパティ + + private CombinedFleet _CombinedFleet; + + public CombinedFleet CombinedFleet + { + get { return this._CombinedFleet; } + set + { + if (this._CombinedFleet != value) + { + this._CombinedFleet = value; + this.RaisePropertyChanged(); + } + } + } + + #endregion + + + public Organization(Homeport parent, KanColleProxy proxy) + { + this.homeport = parent; + + this.Ships = new MemberTable(); + this.Fleets = new MemberTable(); + + proxy.api_get_member_ship.TryParse().Subscribe(x => this.Update(x.Data)); + proxy.api_get_member_ship2.TryParse().Subscribe(x => + { + this.Update(x.Data); + this.Update(x.Fleets); + }); + proxy.api_get_member_ship3.TryParse().Subscribe(x => + { + this.Update(x.Data.api_ship_data); + this.Update(x.Data.api_deck_data); + }); + + proxy.api_get_member_deck.TryParse().Subscribe(x => this.Update(x.Data)); + proxy.api_get_member_deck_port.TryParse().Subscribe(x => this.Update(x.Data)); + proxy.api_get_member_ship_deck.TryParse().Subscribe(x => this.Update(x.Data)); + proxy.api_req_hensei_preset_select.TryParse().Subscribe(x => this.Update(x.Data)); + + proxy.api_req_hensei_change.TryParse().Subscribe(this.Change); + proxy.api_req_hokyu_charge.TryParse().Subscribe(x => this.Charge(x.Data)); + proxy.api_req_kaisou_powerup.TryParse().Subscribe(this.Powerup); + proxy.api_req_kaisou_slot_exchange_index.TryParse().Subscribe(this.ExchangeSlot); + proxy.api_req_kaisou_slot_deprive.TryParse().Subscribe(x => this.DepriveSlotItem(x.Data)); + + proxy.api_req_kousyou_getship.TryParse().Subscribe(x => this.GetShip(x.Data)); + proxy.api_req_kousyou_destroyship.TryParse().Subscribe(this.DestoryShip); + proxy.api_req_member_updatedeckname.TryParse().Subscribe(this.UpdateFleetName); + + proxy.api_req_hensei_combined.TryParse() + .Subscribe(x => this.Combined = x.Data.api_combined != 0); + + this.SubscribeSortieSessions(proxy); + } + + + /// + /// 指定した ID の艦娘が所属する艦隊を取得します。 + /// + internal Fleet GetFleet(int shipId) + { + return this.Fleets.Select(x => x.Value).SingleOrDefault(x => x.Ships.Any(s => s.Id == shipId)); + } + + private void UpdateFleetName(SvData data) + { + if (data == null || !data.IsSuccess) return; + + try + { + var fleet = this.Fleets[int.Parse(data.Request["api_deck_id"])]; + var name = data.Request["api_name"]; + + fleet.Name = name; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("艦隊名の変更に失敗しました: {0}", ex); + } + } + + private void RaiseShipsChanged() + { + this.RaisePropertyChanged("Ships"); + } + + + #region 母港 / 艦隊編成 (Update / Change) + + /// + /// 指定した 型の配列を使用して、 プロパティ値を更新します。 + /// + internal void Update(kcsapi_ship2[] source) + { + if (source.Length <= 1) + { + foreach (var ship in source) + { + var target = this.Ships[ship.api_id]; + if (target == null) continue; + + target.Update(ship); + this.GetFleet(target.Id)?.State.Calculate(); + } + } + else + { + this.Ships = new MemberTable(source.Select(x => new Ship(this.homeport, x))); + + if (KanColleClient.Current.IsInSortie) + { + foreach (var id in this.evacuatedShipsIds) this.Ships[id].Situation |= ShipSituation.Evacuation; + foreach (var id in this.towShipIds) this.Ships[id].Situation |= ShipSituation.Tow; + } + + foreach (var fleet in this.Fleets.Values) + { + fleet.State.Update(); + fleet.State.Calculate(); + } + } + } + + + /// + /// 指定した 型の配列を使用して、 プロパティ値を更新します。 + /// + internal void Update(kcsapi_deck[] source) + { + if (this.Fleets.Count == source.Length) + { + foreach (var raw in source) this.Fleets[raw.api_id]?.Update(raw); + } + else + { + foreach (var fleet in this.Fleets) fleet.Value?.Dispose(); + this.Fleets = new MemberTable(source.Select(x => new Fleet(this.homeport, x))); + } + } + + + internal void Update(kcsapi_deck source) + { + var fleet = this.Fleets[source.api_id]; + if (fleet != null) + { + fleet.Update(source); + fleet.RaiseShipsUpdated(); + } + } + + + private void Change(SvData data) + { + if (data == null || !data.IsSuccess) return; + + try + { + var fleet = this.Fleets[int.Parse(data.Request["api_id"])]; + fleet.RaiseShipsUpdated(); + + var index = int.Parse(data.Request["api_ship_idx"]); + if (index == -1) + { + // 旗艦以外をすべて外すケース + fleet.UnsetAll(); + return; + } + + var ship = this.Ships[int.Parse(data.Request["api_ship_id"])]; + if (ship == null) + { + // 艦を外すケース + fleet.Unset(index); + return; + } + + var currentFleet = this.GetFleet(ship.Id); + if (currentFleet == null) + { + // ship が、現状どの艦隊にも所属していないケース + fleet.Change(index, ship); + return; + } + + // ship が、現状いずれかの艦隊に所属しているケース + var currentIndex = Array.IndexOf(currentFleet.Ships, ship); + var old = fleet.Change(index, ship); + + // Fleet.Change(int, Ship) は、変更前の艦を返す (= old) ので、 + // ship の移動元 (currentFleet + currentIndex) に old を書き込みにいく + currentFleet.Change(currentIndex, old); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("編成の変更に失敗しました: {0}", ex); + } + } + + + private void Combine(bool combine) + { + this.CombinedFleet?.Dispose(); + this.CombinedFleet = combine + ? new CombinedFleet(this.homeport, this.Fleets.OrderBy(x => x.Key).Select(x => x.Value).Take(2).ToArray()) + : null; + } + + private void ExchangeSlot(SvData data) + { + try + { + var ship = this.Ships[int.Parse(data.Request["api_id"])]; + if (ship == null) return; + + ship.RawData.api_slot = data.Data.api_slot; + ship.UpdateSlots(); + + var fleet = this.Fleets.Values.FirstOrDefault(x => x.Ships.Any(y => y.Id == ship.Id)); + if (fleet == null) return; + + fleet.State.Calculate(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("装備の入れ替えに失敗しました: {0}", ex); + } + } + + #endregion + + #region 補給 / 近代化改修 (Charge / Powerup) + + private void Charge(kcsapi_charge source) + { + Fleet fleet = null; // 補給した艦が所属している艦隊。艦隊をまたいで補給はできないので、必ず 1 つに絞れる + + foreach (var ship in source.api_ship) + { + var target = this.Ships[ship.api_id]; + if (target == null) continue; + + target.Charge(ship.api_fuel, ship.api_bull, ship.api_onslot); + + if (fleet == null) + { + fleet = this.GetFleet(target.Id); + } + } + + if (fleet != null) + { + fleet.State.Update(); + fleet.State.Calculate(); + } + } + + private void Powerup(SvData svd) + { + try + { + this.Ships[svd.Data.api_ship.api_id]?.Update(svd.Data.api_ship); + + var items = svd.Request["api_id_items"] + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(int.Parse) + .Where(x => this.Ships.ContainsKey(x)) + .Select(x => this.Ships[x]) + .ToArray(); + + // (改修に使った艦娘のこと item って呼ぶのどうなの…) + + foreach (var x in items) + { + this.homeport.Itemyard.RemoveFromShip(x); + this.Ships.Remove(x); + } + + this.RaiseShipsChanged(); + this.Update(svd.Data.api_deck); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("近代化改修による更新に失敗しました: {0}", ex); + } + } + + #endregion + + #region 改装 (DepriveSlotItem) + + private void DepriveSlotItem(kcsapi_slot_deprive source) + { + this.Ships[source.api_ship_data.api_unset_ship.api_id]?.Update(source.api_ship_data.api_unset_ship); + this.Ships[source.api_ship_data.api_set_ship.api_id]?.Update(source.api_ship_data.api_set_ship); + } + + #endregion + + #region 工廠 (Get / Destroy) + + private void GetShip(kcsapi_kdock_getship source) + { + this.homeport.Itemyard.AddFromDock(source); + + this.Ships.Add(new Ship(this.homeport, source.api_ship)); + this.RaiseShipsChanged(); + } + + private void DestoryShip(SvData svd) + { + try + { + var ship = this.Ships[int.Parse(svd.Request["api_ship_id"])]; + if (ship != null) + { + this.homeport.Itemyard.RemoveFromShip(ship); + + this.Ships.Remove(ship); + this.RaiseShipsChanged(); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("解体による更新に失敗しました: {0}", ex); + } + } + + #endregion + + #region 出撃 (Sortie / Homing / Escape) + + private void SubscribeSortieSessions(KanColleProxy proxy) + { + proxy.ApiSessionSource + .SkipUntil(proxy.api_req_map_start.TryParse().Do(this.Sortie)) + .TakeUntil(proxy.api_port) + .Finally(this.Homing) + .Repeat() + .Subscribe(); + + int[] evacuationOfferedShipIds = null; + int[] towOfferedShipIds = null; + + proxy.api_req_combined_battle_battleresult + .TryParse() + .Where(x => x.Data.api_escape != null) + .Select(x => x.Data) + .Subscribe(x => + { + if (this.CombinedFleet == null) return; + var ships = this.CombinedFleet.Fleets.SelectMany(f => f.Ships).ToArray(); + evacuationOfferedShipIds = x.api_escape.api_escape_idx.Select(idx => ships[idx - 1].Id).ToArray(); + towOfferedShipIds = x.api_escape.api_tow_idx.Select(idx => ships[idx - 1].Id).ToArray(); + }); + proxy.api_req_combined_battle_goback_port + .Subscribe(_ => + { + if (KanColleClient.Current.IsInSortie + && evacuationOfferedShipIds != null + && evacuationOfferedShipIds.Length >= 1 + && towOfferedShipIds != null + && towOfferedShipIds.Length >= 1) + { + this.evacuatedShipsIds.Add(evacuationOfferedShipIds[0]); + this.towShipIds.Add(towOfferedShipIds[0]); + } + }); + proxy.api_get_member_ship_deck + .Subscribe(_ => + { + evacuationOfferedShipIds = null; + towOfferedShipIds = null; + }); + } + + + private void Sortie(SvData data) + { + if (data == null || !data.IsSuccess) return; + + try + { + var id = int.Parse(data.Request["api_deck_id"]); + var fleet = this.Fleets[id]; + fleet.Sortie(); + + if (this.Combined && id == 1) this.Fleets[2].Sortie(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine("艦隊の出撃を検知できませんでした: {0}", ex); + } + } + + private void Homing() + { + this.evacuatedShipsIds.Clear(); + this.towShipIds.Clear(); + + foreach (var ship in this.Ships.Values) + { + if (ship.Situation.HasFlag(ShipSituation.Evacuation)) ship.Situation &= ~ShipSituation.Evacuation; + if (ship.Situation.HasFlag(ShipSituation.Tow)) ship.Situation &= ~ShipSituation.Tow; + } + + foreach (var target in this.Fleets.Values) + { + target.Homing(); + } + } + + private void Update(kcsapi_ship_deck source) + { + if (source.api_ship_data != null) + { + foreach (var ship in source.api_ship_data) + { + var target = this.Ships[ship.api_id]; + target.Update(ship); + + if (this.evacuatedShipsIds.Any(x => target.Id == x)) target.Situation |= ShipSituation.Evacuation; + if (this.towShipIds.Any(x => target.Id == x)) target.Situation |= ShipSituation.Tow; + } + } + + if (source.api_deck_data != null) + { + foreach (var deck in source.api_deck_data) + { + var target = this.Fleets[deck.api_id]; + target.Update(deck); + } + } + } + + #endregion + } } diff --git a/source/Grabacr07.KanColleWrapper/Properties/AssemblyInfo.cs b/source/Grabacr07.KanColleWrapper/Properties/AssemblyInfo.cs index 766a2ab93..62d964519 100644 --- a/source/Grabacr07.KanColleWrapper/Properties/AssemblyInfo.cs +++ b/source/Grabacr07.KanColleWrapper/Properties/AssemblyInfo.cs @@ -10,5 +10,5 @@ [assembly: ComVisible(false)] [assembly: Guid("8A13AB28-4E9F-423D-80AD-23EEF2821959")] -[assembly: AssemblyVersion("1.5.1")] -[assembly: AssemblyInformationalVersion("1.5.1")] +[assembly: AssemblyVersion("1.5.2")] +[assembly: AssemblyInformationalVersion("1.5.2")] diff --git a/source/Plugins/TaskbarProgress/HpProgress.cs b/source/Plugins/TaskbarProgress/HpProgress.cs index 887895492..ee3d602ec 100644 --- a/source/Plugins/TaskbarProgress/HpProgress.cs +++ b/source/Plugins/TaskbarProgress/HpProgress.cs @@ -86,7 +86,7 @@ public void Update() ships = org.Fleets.Values .Where(x => x.IsInSortie) .SelectMany(x => x.Ships) - .Where(s => (s.Situation & (ShipSituation.Tow | ShipSituation.Evacuation)) == 0) + .Where(x => !x.Situation.HasFlag(ShipSituation.Tow) && x.Situation.HasFlag(ShipSituation.Evacuation)) .ToArray(); } else