1 // Copyright (C) 2013, 2014, 2015 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 static System.Math;
\r
21 namespace KancolleSniffer
\r
23 public class ItemSpec
\r
25 public static bool IncreaceLandPowerTp = false;
\r
29 public string TypeName;
\r
30 public int Firepower;
\r
31 public int IconType;
\r
34 public int AntiSubmarine;
\r
37 public int Interception;
\r
38 public int AntiBomber;
\r
39 public int Distance;
\r
47 public bool CanAirCombat
\r
67 // http://ja.kancolle.wikia.com/wiki/%E3%83%9E%E3%83%83%E3%83%97%E7%B4%A2%E6%95%B5
\r
68 public double LoSScaleFactor
\r
88 public bool IsAircraft
\r
100 case 25: // オートジャイロ
\r
116 public bool IsDiveBomber => Type == 7 || Type == 11 || Type == 57;
\r
118 public bool IsTorpedoBomber => Type == 8 || Type == 58;
\r
120 public int EffectiveAntiSubmarine
\r
126 case 1: // 小口径(12.7cm連装高角砲(後期型))
\r
128 case 12: // 小型電探(22号対水上電探改四)
\r
132 return AntiSubmarine;
\r
137 public bool IsSonar => Type == 14 || // ソナー
\r
138 Type == 40; // 大型ソナー
\r
140 public bool IsDepthCharge => Type == 15;
\r
142 public bool IsRepairFacility => Type == 31;
\r
144 public bool IsAntiAirGun => Type == 21;
\r
146 public double ContactTriggerRate
\r
162 public Func<double> GetItemTp { get; set; }
\r
164 public double TransportPoint
\r
168 var tp = GetItemTp?.Invoke();
\r
173 case 75: // ドラム缶(輸送用)
\r
179 case 166: // 大発動艇(八九式中戦車&陸戦隊)
\r
181 case 167: // 特二式内火艇
\r
183 case 230: // 特大発動艇+戦車第11連隊
\r
187 case 150: // 秋刀魚の缶詰
\r
189 case 241: // 戦闘糧食(特別なおにぎり)
\r
197 public double ReconPlaneInterceptionBonus
\r
204 return LoS <= 7 ? 1.2 : 1.3;
\r
207 return LoS <= 7 ? 1.1 :
\r
208 LoS <= 8 ? 1.13 : 1.16;
\r
224 return Color.FromArgb(209, 89, 89);
\r
226 return Color.FromArgb(253, 233, 0);
\r
228 return Color.FromArgb(88, 134, 170);
\r
230 return Color.FromArgb(93, 179, 108);
\r
232 return Color.FromArgb(223, 102, 102);
\r
234 return Color.FromArgb(95, 173, 234);
\r
236 return Color.FromArgb(254, 191, 0);
\r
239 return Color.FromArgb(142, 203, 152);
\r
241 return Color.FromArgb(231, 153, 53);
\r
243 return Color.FromArgb(69, 175, 88);
\r
245 return Color.FromArgb(254, 254, 254);
\r
248 return Color.FromArgb(102, 204, 118);
\r
251 return Color.FromArgb(126, 203, 215);
\r
253 return Color.FromArgb(254, 195, 77);
\r
256 return Color.FromArgb(154, 163, 90);
\r
257 case 21: // オートジャイロ
\r
258 return Color.FromArgb(99, 203, 115);
\r
260 return Color.FromArgb(125, 205, 217);
\r
262 return Color.FromArgb(152, 124, 172);
\r
265 return Color.FromArgb(254, 155, 0);
\r
267 return Color.FromArgb(161, 161, 160);
\r
269 return Color.FromArgb(175, 156, 126);
\r
271 return Color.FromArgb(204, 172, 252);
\r
273 return Color.FromArgb(206, 166, 108);
\r
275 return Color.FromArgb(137, 153, 77);
\r
277 return Color.FromArgb(253, 49, 49);
\r
279 return Color.FromArgb(188, 238, 155);
\r
281 return Color.FromArgb(142, 203, 152);
\r
283 return Color.FromArgb(254, 254, 254);
\r
285 return Color.FromArgb(90, 200, 155);
\r
289 return Color.FromArgb(57, 182, 78);
\r
292 return Color.FromArgb(72, 178, 141);
\r
294 return Color.FromArgb(158, 187, 226);
\r
297 return Color.FromArgb(128, 121, 161);
\r
298 case 47: // 陸上対潜哨戒機
\r
299 return Color.FromArgb(91, 113, 209);
\r
301 return SystemColors.Control;
\r
307 public class ItemStatus
\r
309 public int Id { get; set; }
\r
310 public ItemSpec Spec { get; set; } = new ItemSpec();
\r
311 public int Level { get; set; }
\r
312 public int Alv { get; set; }
\r
313 public ShipStatus Holder { get; set; }
\r
315 public ItemStatus()
\r
320 public ItemStatus(int id)
\r
325 public int[] CalcFighterPower(int slot)
\r
327 if (!Spec.CanAirCombat || slot == 0)
\r
328 return new[] {0, 0};
\r
329 var unskilled = (Spec.AntiAir + FighterPowerLevelBonus) * Sqrt(slot);
\r
330 return AlvBonus.Select(bonus => (int)(unskilled + bonus)).ToArray();
\r
333 public int[] CalcFighterPowerInBase(int slot, bool airDefence)
\r
335 if (!Spec.IsAircraft || slot == 0)
\r
336 return new[] {0, 0};
\r
337 var airDefenceBonus = airDefence ? Spec.AntiBomber * 2 + Spec.Interception : Spec.Interception * 1.5;
\r
338 var unskilled = (Spec.AntiAir + airDefenceBonus + FighterPowerLevelBonus) * Sqrt(slot);
\r
339 return AlvBonusInBase.Select(bonus => (int)(unskilled + bonus)).ToArray();
\r
342 private readonly double[] _alvBonusMin =
\r
344 Sqrt(0.0), Sqrt(1.0), Sqrt(2.5), Sqrt(4.0), Sqrt(5.5), Sqrt(7.0),
\r
345 Sqrt(8.5), Sqrt(10.0)
\r
348 private readonly double[] _alvBonusMax =
\r
350 Sqrt(0.9), Sqrt(2.4), Sqrt(3.9), Sqrt(5.4), Sqrt(6.9), Sqrt(8.4),
\r
351 Sqrt(9.9), Sqrt(12.0)
\r
354 private int[] AlvTypeBonusTable
\r
364 return new[] {0, 0, 2, 5, 9, 14, 14, 22};
\r
368 case 57: // 噴式戦闘爆撃機
\r
370 return new[] {0, 0, 0, 0, 0, 0, 0, 0};
\r
372 return new[] {0, 0, 1, 1, 1, 3, 3, 6};
\r
379 private double[] AlvBonus
\r
383 var table = AlvTypeBonusTable;
\r
385 return new[] {0.0, 0.0};
\r
386 return new[] {table[Alv] + _alvBonusMin[Alv], table[Alv] + _alvBonusMax[Alv]};
\r
390 private double[] AlvBonusInBase
\r
399 return new[] {_alvBonusMin[Alv], _alvBonusMax[Alv]};
\r
406 private double FighterPowerLevelBonus
\r
415 return 0.2 * Level;
\r
416 case 7: // 改修可能なのは爆戦のみ
\r
417 return 0.25 * Level;
\r
423 public double LoSLevelBonus
\r
430 return 1.2 * Sqrt(Level);
\r
432 return 1.15 * Sqrt(Level);
\r
434 return 1.25 * Sqrt(Level);
\r
436 return 1.4 * Sqrt(Level);
\r
437 case 94: // 艦上偵察機(II)
\r
438 return 1.2 * Sqrt(Level);
\r
445 public double FirepowerLevelBonus
\r
460 return Sqrt(Level);
\r
462 return 1.5 * Sqrt(Level);
\r
464 return SecondaryGunLevelBonus;
\r
467 return Spec.Id == 226 // 九五式爆雷
\r
469 : 0.75 * Sqrt(Level);
\r
476 public double SecondaryGunLevelBonus
\r
482 case 10: // 12.7cm連装高角砲
\r
484 case 220: // 8cm高角砲改+増設機銃
\r
485 case 275: // 10cm連装高角砲改+増設機銃
\r
486 return 0.2 * Level;
\r
487 case 12: // 15.5cm三連装副砲
\r
488 case 234: // 15.5cm三連装副砲改
\r
489 case 247: // 15.2cm三連装砲
\r
490 return 0.3 * Level;
\r
492 return Sqrt(Level);
\r
497 public double TorpedoLevelBonus
\r
505 return 1.2 * Sqrt(Level);
\r
512 public double AntiSubmarineLevelBonus
\r
520 return Sqrt(Level);
\r
527 public double BomberLevelBonus => Spec.Type == 11 /* 水爆 */ ? 0.2 * Level : 0;
\r
529 public double NightBattleLevelBonus
\r
545 return Sqrt(Level);
\r
547 return SecondaryGunLevelBonus;
\r
554 public double EffectiveAntiAirForShip
\r
558 switch (Spec.IconType)
\r
561 return 6 * Spec.AntiAir + 4 * Sqrt(Level);
\r
563 return 4 * Spec.AntiAir + (Spec.AntiAir >= 8 ? 3 : 2) * Sqrt(Level);
\r
565 return 3 * Spec.AntiAir;
\r
567 return 4 * Spec.AntiAir + 2 * Sqrt(Level);
\r
573 public double EffectiveAntiAirForFleet
\r
577 switch (Spec.IconType)
\r
586 return 0.2 * Spec.AntiAir;
\r
588 return 0.4 * Spec.AntiAir + 1.5 * Sqrt(Level);
\r
590 return 0.6 * Spec.AntiAir;
\r
592 return 0.35 * Spec.AntiAir + (Spec.AntiAir >= 8 ? 3 : 2) * Sqrt(Level);
\r
594 return 0.35 * Spec.AntiAir + 2 * Sqrt(Level);
\r
596 if (Spec.Id == 9) // 46cm三連装砲
\r
597 return 0.25 * Spec.AntiAir;
\r
598 if (Spec.Type == 10) // 水偵
\r
599 return 0.2 * Spec.AntiAir;
\r
607 public class ItemInfo
\r
609 private int _nowShips, _nowEquips;
\r
610 private readonly Dictionary<int, ItemSpec> _itemSpecs = new Dictionary<int, ItemSpec>();
\r
611 private readonly Dictionary<int, ItemStatus> _itemInfo = new Dictionary<int, ItemStatus>();
\r
612 private readonly Dictionary<int, string> _useItemName = new Dictionary<int, string>();
\r
614 public int MaxShips { get; private set; }
\r
615 public int MarginShips { get; set; }
\r
616 public bool AlarmShips { get; set; }
\r
617 public int MaxEquips { get; private set; }
\r
618 public int MarginEquips { get; set; }
\r
619 public bool AlarmEquips { get; set; }
\r
621 public int NowShips
\r
628 var limit = MaxShips - MarginShips;
\r
629 AlarmShips = AlarmShips || _nowShips < limit && value >= limit;
\r
635 public bool TooManyShips => MaxShips != 0 && NowShips >= MaxShips - MarginShips;
\r
637 public int NowEquips
\r
642 if (MaxEquips != 0)
\r
644 var limit = MaxEquips - MarginEquips;
\r
645 AlarmEquips = AlarmEquips || _nowEquips < limit && value >= limit;
\r
647 _nowEquips = value;
\r
651 public bool TooManyEquips => MaxEquips != 0 && NowEquips >= MaxEquips - MarginEquips;
\r
659 public AdditionalData AdditionalData { get; set; }
\r
661 public void InspectBasic(dynamic json)
\r
663 MaxShips = (int)json.api_max_chara;
\r
664 var check = MaxEquips == 0;
\r
665 MaxEquips = (int)json.api_max_slotitem;
\r
667 AlarmEquips = NowEquips >= MaxEquips - MarginEquips;
\r
670 public void InspectMaster(dynamic json)
\r
672 var dict = new Dictionary<int, string>();
\r
673 foreach (var entry in json.api_mst_slotitem_equiptype)
\r
674 dict[(int)entry.api_id] = entry.api_name;
\r
675 AdditionalData.LoadTpSpec();
\r
676 foreach (var entry in json.api_mst_slotitem)
\r
678 var type = (int)entry.api_type[2];
\r
679 var id = (int)entry.api_id;
\r
680 _itemSpecs[(int)entry.api_id] = new ItemSpec
\r
683 Name = (string)entry.api_name,
\r
685 TypeName = dict.TryGetValue(type, out var typeName) ? typeName : "不明",
\r
686 IconType = (int)entry.api_type[3],
\r
687 Firepower = (int)entry.api_houg,
\r
688 AntiAir = (int)entry.api_tyku,
\r
689 LoS = (int)entry.api_saku,
\r
690 AntiSubmarine = (int)entry.api_tais,
\r
691 Torpedo = (int)entry.api_raig,
\r
692 Bomber = (int)entry.api_baku,
\r
693 Interception = type == 48 ? (int)entry.api_houk : 0, // 局地戦闘機は回避の値が迎撃
\r
694 AntiBomber = type == 48 ? (int)entry.api_houm : 0, // 〃命中の値が対爆
\r
695 Distance = entry.api_distance() ? (int)entry.api_distance : 0,
\r
696 GetItemTp = () => AdditionalData.ItemTp(id)
\r
699 _itemSpecs[-1] = _itemSpecs[0] = new ItemSpec();
\r
700 foreach (var entry in json.api_mst_useitem)
\r
701 _useItemName[(int)entry.api_id] = entry.api_name;
\r
704 public void InspectSlotItem(dynamic json, bool full = false)
\r
707 json = new[] {json};
\r
711 _itemInfo[-1] = new ItemStatus();
\r
713 foreach (var entry in json)
\r
715 var id = (int)entry.api_id;
\r
716 _itemInfo[id] = new ItemStatus(id)
\r
718 Spec = _itemSpecs[(int)entry.api_slotitem_id],
\r
719 Level = entry.api_level() ? (int)entry.api_level : 0,
\r
720 Alv = entry.api_alv() ? (int)entry.api_alv : 0
\r
723 NowEquips = _itemInfo.Count - 1;
\r
726 public void InspectCreateItem(dynamic json)
\r
728 if (!json.IsDefined("api_slot_item"))
\r
730 InspectSlotItem(json.api_slot_item);
\r
733 public void InspectGetShip(dynamic json)
\r
736 if (json.api_slotitem == null) // まるゆにはスロットがない
\r
738 InspectSlotItem(json.api_slotitem);
\r
741 public void InspectDestroyItem(string request, dynamic json)
\r
743 var values = HttpUtility.ParseQueryString(request);
\r
744 DeleteItems(values["api_slotitem_ids"].Split(',').Select(int.Parse).ToArray());
\r
747 public void InspectRemodelSlot(dynamic json)
\r
749 if (json.api_after_slot())
\r
750 InspectSlotItem(json.api_after_slot);
\r
751 if (!json.api_use_slot_id())
\r
753 DeleteItems((int[])json.api_use_slot_id);
\r
756 public void DeleteItems(IEnumerable<ItemStatus> items)
\r
758 DeleteItems(items.Select(item => item.Id));
\r
761 private void DeleteItems(IEnumerable<int> ids)
\r
763 foreach (var id in ids.Where(id => id != -1))
\r
765 _itemInfo.Remove(id);
\r
770 public ItemSpec GetSpecByItemId(int id) => _itemSpecs.TryGetValue(id, out var spec) ? spec : new ItemSpec();
\r
772 public string GetName(int id) => GetStatus(id).Spec.Name;
\r
774 public ItemStatus GetStatus(int id)
\r
776 return _itemInfo.TryGetValue(id, out var item) ? item : new ItemStatus(id);
\r
779 public void ClearHolder()
\r
781 foreach (var item in _itemInfo.Values)
\r
782 item.Holder = new ShipStatus();
\r
785 public ItemStatus[] ItemList => (from e in _itemInfo where e.Key != -1 select e.Value).ToArray();
\r
787 public string GetUseItemName(int id) => _useItemName[id];
\r
789 public void InjectItemSpec(IEnumerable<ItemSpec> specs)
\r
791 foreach (var spec in specs)
\r
792 _itemSpecs.Add(spec.Id, spec);
\r
795 public ItemStatus[] InjectItems(IEnumerable<int> itemIds)
\r
797 var id = _itemInfo.Keys.Count + 1;
\r
798 return itemIds.Select(itemId =>
\r
800 if (!_itemSpecs.TryGetValue(itemId, out var spec))
\r
802 spec = new ItemSpec {Id = itemId};
\r
803 _itemSpecs.Add(itemId, spec);
\r
805 var item = new ItemStatus {Id = id++, Spec = spec};
\r
806 _itemInfo.Add(item.Id, item);
\r